pax_global_header00006660000000000000000000000064143340010530014503gustar00rootroot0000000000000052 comment=e808652986707e85e5fd6cae35070efbe7262a15 tea-qt-62.0.2/000077500000000000000000000000001433400105300127655ustar00rootroot00000000000000tea-qt-62.0.2/.gitignore000066400000000000000000000014271433400105300147610ustar00rootroot00000000000000# 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-62.0.2/ABOUT-PL.txt000066400000000000000000000043141433400105300147130ustar00rootroot00000000000000TEA 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-62.0.2/AUTHORS000066400000000000000000000051731433400105300140430ustar00rootroot00000000000000Code and design: Peter Semiletov 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 and Zlib stuff by: Zlib - (C) 1995-2005 Jean-loup Gailly and Mark Adler Zip/Unzip - (C) 1998-2005 Gilles Vollant QuaZIP - (C) 2005-2018 Sergey A. Tachenov 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 And of course, thanks for the inspiration to Group Image, Can, Nirvana, Scorn, The Pixies, Family, Frank Black, Bonzo Dog Doo Dah Band, Little Grey Girlfriend, Melt Banana, Tuxedomoon, Grajdanskaya Oborona, Guano Apes, Fall, Juno Reactor, Meathookseed, Broadcast, Napalm Death, NoMeansNo, Bach, Radiohead, Copout, Shaolin Wooden Men, Vladymir Vysotsky, Captain Beefheart and His Magic Band, coldwave projects, and demo-groups: Stravaganza, Haujobb, Conspiracy. tea-qt-62.0.2/CMakeLists.txt000066400000000000000000000140301433400105300155230ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) 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) enable_language(CXX) enable_language(C) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) if(Qt${QT_VERSION_MAJOR} STREQUAL "Qt6") find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Core5Compat REQUIRED) endif() if(Qt${QT_VERSION_MAJOR} STREQUAL "Qt5") find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) endif() message(Qt${QT_VERSION_MAJOR} " found") include_directories(${Qt${QT_VERSION_MAJOR}Widgets_INCLUDE_DIRS}) add_definitions(${Qt${QT_VERSION_MAJOR}Widgets_DEFINITIONS}) add_definitions(${Qt${QT_VERSION_MAJOR}Widgets_COMPILE_DEFINITIONS}) set(PROJECT "tea-qt") project ($PROJECT VERSION 62.0.1 LANGUAGES CXX C) add_definitions(-DVERSION_NUMBER="\\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\\"") add_definitions(-DNOCRYPT=1) add_definitions(-DNOUNCRYPT=1) add_definitions(-DQUAZIP_STATIC=1) option(USE_ASPELL "Use Aspell" OFF) option(USE_PRINTER "Use printer support" OFF) option(USE_PDF "Use libpoppler" OFF) option(USE_DJVU "Use djvu support" 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}") find_package(PkgConfig REQUIRED) pkg_check_modules(hunspell QUIET hunspell) if(hunspell_FOUND) add_definitions(-DHUNSPELL_ENABLE) message("+ hunspell support") endif() find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED Core Widgets) if(BUILD_TESTING) find_package(Qt${Test}${QT_VERSION_MAJOR} CONFIG REQUIRED) endif() if(Qt${QT_VERSION_MAJOR} STREQUAL "Qt6") qt6_add_resources(QT_RESOURCES resources.qrc) endif() if (Qt${QT_VERSION_MAJOR} MATCHES "Qt5") qt5_add_resources(QT_RESOURCES resources.qrc) endif() #qt6_add_resources(QT_RESOURCES resources.qrc) #Qt${QT_VERSION_MAJOR}_add_resources(QT_RESOURCES resources.qrc) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt${QT_VERSION_MAJOR}Widgets_EXECUTABLE_COMPILE_FLAGS}") file(GLOB tea_SRCS "*.c" "*.cpp") file(GLOB tea_HEADERS "*.h" ".*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 --prefix=${PROJECT}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}/ master | bzip2 >${PROJECT}-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.tar.bz2 #WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} #) 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_PRINTER) find_package(Qt${QT_VERSION_MAJOR}PrintSupport) if (Qt${PrintSupport}_FOUND) Message ("+ printer support") add_definitions(-DPRINTER_ENABLE) target_link_libraries(tea Qt${QT_VERSION_MAJOR}::PrintSupport) 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) if(Qt${QT_VERSION_MAJOR} STREQUAL "Qt5") pkg_check_modules(popplerqt5 QUIET poppler-qt5) if(popplerqt5_FOUND) add_definitions(-DPOPPLER_ENABLE) target_link_libraries(tea ${popplerqt5_LIBRARIES}) include_directories(${popplerqt5_INCLUDE_DIRS}) message("+ poppler support") endif() endif() if(Qt${QT_VERSION_MAJOR} STREQUAL "Qt6") pkg_check_modules(popplerqt6 QUIET poppler-qt6) if(popplerqt6_FOUND) add_definitions(-DPOPPLER_ENABLE) target_link_libraries(tea ${popplerqt6_LIBRARIES}) include_directories(${popplerqt6_INCLUDE_DIRS}) message("+ poppler support") endif() 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(Qt${QT_VERSION_MAJOR} STREQUAL "Qt6") target_link_libraries(tea Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Core Qt6::Core5Compat) endif() if(Qt${QT_VERSION_MAJOR} STREQUAL "Qt5") target_link_libraries(tea Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Core) endif() 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-62.0.2/COPYING000066400000000000000000001045131433400105300140240ustar00rootroot00000000000000 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-62.0.2/ChangeLog000066400000000000000000000664761433400105300145620ustar00rootroot00000000000000//it's lazy updated file, so the real ChangeLog is the activity on github * 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-62.0.2/FUNDING.yml000066400000000000000000000000221433400105300145740ustar00rootroot00000000000000patreon: semiletovtea-qt-62.0.2/INSTALL000066400000000000000000000000161433400105300140130ustar00rootroot00000000000000See the READMEtea-qt-62.0.2/NEWS000066400000000000000000000015231433400105300134650ustar00rootroot00000000000000====NEWS ABOUT TEA==== TEA 62.0.1, November 2022 --------------------------------------------------- http://tea.ourproject.org --------------------------------------------------- From the dawn of the humanity I've heard that TEA's interface is obscure. And I know why - due to the built-in file manager that is used by default instead of the traditional file save/open dialogs. TEA all that time has such dialogs, but they are turned off by default. Now, in the sake of the user-friendliness, behold! The file save/open dialogs are turned on by default, when you start TEA in a first time. For all old users things remains the same as has been. Beside this new outstanding feature, TEA 62 has all copy/paste stuff rewritten from the ground to avoid the strange clipboard interaction at some Qt 6.x release Stay tuned. Peter Semiletov tea-qt-62.0.2/NEWS-RU000066400000000000000000000032241433400105300140110ustar00rootroot00000000000000=====НОВОСТИ ПРО ТИА==== ТИА 62.0.1, ноябрь 2022 ====================== http://tea.ourproject.org --------------------------------------------------- Со времен зари человечества я слышу, что у ТИА странный интерфейс. Это досадное заблуждение проистекает от того, что вместо привычных диалоговых окон открытия и сохранения файла, в ТИА по умолчанию используется встроенный файловый менеджер, он же приказчик. Хотя с незапамятных времен есть и эти самые диалоговые окна, просто по умолчанию они выключены. Что же, пора сделать шаг в сторону так называемой дружественности интерфейса! Теперь при запуске ТИА в первый раз (если ранее его не запускали), по умолчанию эти самые диалоговые окна будут включены. Также в новом ТИА полностью переписаны все дела, связанные с буфером обмена, ибо в Qt6 в каком-то там релизе с этим делом чудеса, и во избежание я написал код работы с буфером обмена, который точно работает для Qt 4, 5 и 6. С кирпичным пролетарским приветом, Петр Семилетов!tea-qt-62.0.2/README-PL.txt000066400000000000000000000136611433400105300150030ustar00rootroot00000000000000ZAWARTOŚĆ 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-62.0.2/README.md000066400000000000000000000141541433400105300142510ustar00rootroot00000000000000# 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 BTC: 1PCo2zznEGMFJey4qFKGQ8CoFK2nzNnJJf Patreon: https://www.patreon.com/semiletov Communities > https://t.me/teaqt https://vk.com/teaeditor 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 4 ways, using build systems qmake/make, meson/ninja, cmake/make, cmake/ninja. But first, you need to install some development libraries. **Mandatory:** Qt 4.8 or Qt 5.4+ or Qt 6, zlib **Optional:** libaspell (for spell checking engine), libhunspell (for spell checking engine), poppler-qt5 or poppler-qt6 (to read the text from PDF), ddjvuap (to read the text from DJVU) **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 meson or 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 MESON #### With meson, TEA supports Qt5 build. To build and install TEA with meson/ninja and GCC, do: mkdir b meson cd b ninja ninja install To build and install TEA with meson/ninja and CLANG, do: mkdir b CC=clang CXX=clang++ meson b cd b ninja ninja install To enable PDF and DJVU text extraction support use, and Aspell support (disabled by default as well as the printing support): mkdir b meson b meson configure -Dpdf=enabled -Ddjvu=enabled -Daspell=enabled b cd b ninja ninja install #### 01.03 QMAKE #### With qmake, TEA 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-qt5 or qt4 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. /* 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 sudo apt-get install libqt5qml5 libqt5quick5 qtdeclarative5-dev qmake make sudo make install Snippet for Ubuntu users (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 one for TEA. I recommend to use cmake to build TEA package. **meson** - I use it internally. Does not have the printer support. 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-62.0.2/TODO000066400000000000000000000054451433400105300134650ustar00rootroot00000000000000 переопределить контекстное меню! * темы оформления в Win32 глючат при стиле интерфейса Windows XP //так и должно быть make single application with Dbus? опять single app не пашет при открытии файлов из thunar add Qt6 to Meson or drop Meson support at all? 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-62.0.2/calendar.cpp000066400000000000000000000201401433400105300152370ustar00rootroot00000000000000/* 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-62.0.2/calendar.h000066400000000000000000000020011433400105300147000ustar00rootroot00000000000000#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-62.0.2/desktop/000077500000000000000000000000001433400105300144365ustar00rootroot00000000000000tea-qt-62.0.2/desktop/tea.desktop000066400000000000000000000010201433400105300165730ustar00rootroot00000000000000[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/rtf;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-62.0.2/document.cpp000066400000000000000000002303551433400105300153170ustar00rootroot00000000000000/*************************************************************************** * 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 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) { 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; QTextCodec *codec = QTextCodec::codecForName (charset.toUtf8().data()); if (! codec) return false; QByteArray ba = codec->fromUnicode (toPlainText()); file.write (ba); file.close(); 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_triplex (const QString &triplex) { 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()); int i = recent_files.indexOf (act->text()); if (i == -1) return; CDocument *d = open_file_triplex (recent_files[i]); 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-62.0.2/document.h000066400000000000000000000233521433400105300147610ustar00rootroot00000000000000/*************************************************************************** * 2007-2022 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_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-62.0.2/encsign/000077500000000000000000000000001433400105300144135ustar00rootroot00000000000000tea-qt-62.0.2/encsign/CP1251000066400000000000000000000001221433400105300151440ustar00rootroot00000000000000 , , , , , , , , , , , , , , tea-qt-62.0.2/encsign/CP866000066400000000000000000000001231433400105300151000ustar00rootroot00000000000000 , , , , , , , , , , , , , , ,tea-qt-62.0.2/encsign/KOI8-R000066400000000000000000000001221433400105300152420ustar00rootroot00000000000000 , , , , , , , , , , , , , , tea-qt-62.0.2/encsign/KOI8-U000066400000000000000000000001151433400105300152470ustar00rootroot00000000000000 , צ , ˦ , , Φ , , , ͦ , ' , Ц , , ަ , tea-qt-62.0.2/exif_reader.cpp000066400000000000000000000275211433400105300157550ustar00rootroot00000000000000#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-62.0.2/exif_reader.h000066400000000000000000000043341433400105300154170ustar00rootroot00000000000000#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-62.0.2/fman.cpp000066400000000000000000000341471433400105300144230ustar00rootroot00000000000000 /************************************************************************** * 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 #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-62.0.2/fman.h000066400000000000000000000054241433400105300140640ustar00rootroot00000000000000 /************************************************************************** * 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 #include "tzipper.h" 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-62.0.2/gui_utils.cpp000066400000000000000000000123251433400105300155000ustar00rootroot00000000000000/* 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 = menu->addAction (*i); handler->connect (act, SIGNAL(triggered()), handler, method); } } } //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; } tea-qt-62.0.2/gui_utils.h000066400000000000000000000031101433400105300151350ustar00rootroot00000000000000#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); #endif // GUI_UTILS_H tea-qt-62.0.2/hls/000077500000000000000000000000001433400105300135535ustar00rootroot00000000000000tea-qt-62.0.2/hls/awk.xml000066400000000000000000000025241433400105300150620ustar00rootroot00000000000000 \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-62.0.2/hls/bash.xml000066400000000000000000000021211433400105300152060ustar00rootroot00000000000000 \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-62.0.2/hls/basic.xml000066400000000000000000000031131433400105300153540ustar00rootroot00000000000000 \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-62.0.2/hls/clike.xml000066400000000000000000000035051433400105300153670ustar00rootroot00000000000000 \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-62.0.2/hls/cs.xml000066400000000000000000000026351433400105300147100ustar00rootroot00000000000000 \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-62.0.2/hls/d.xml000066400000000000000000000024401433400105300145200ustar00rootroot00000000000000 \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-62.0.2/hls/fortran.xml000066400000000000000000000025561433400105300157600ustar00rootroot00000000000000 \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-62.0.2/hls/hs.xml000066400000000000000000000023111433400105300147040ustar00rootroot00000000000000 \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-62.0.2/hls/java.xml000066400000000000000000000027211433400105300152200ustar00rootroot00000000000000 \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-62.0.2/hls/lilypond.xml000066400000000000000000000012211433400105300161230ustar00rootroot00000000000000 \\[A-Za-z]+\b ("[^\"]*") ('[^']*') %[^\n]* %\{ %\} %{%s%} %%s tea-qt-62.0.2/hls/lout.xml000066400000000000000000000011371433400105300152620ustar00rootroot00000000000000 @[A-Z,a-z]+ ("[^\"]*") ('[^']*') #[^\n]* /\* \*/ #%s tea-qt-62.0.2/hls/lua.xml000066400000000000000000000024221433400105300150560ustar00rootroot00000000000000 \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-62.0.2/hls/nasm.xml000066400000000000000000000326161433400105300152430ustar00rootroot00000000000000 \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-62.0.2/hls/nsis.xml000066400000000000000000000032731433400105300152560ustar00rootroot00000000000000 \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-62.0.2/hls/pascal.xml000066400000000000000000000043051433400105300155420ustar00rootroot00000000000000 [-=\+\*\/\|] [\(\)\[\]] \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-62.0.2/hls/perl.xml000066400000000000000000000017031433400105300152400ustar00rootroot00000000000000 \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-62.0.2/hls/php.xml000066400000000000000000000024531433400105300150700ustar00rootroot00000000000000 \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-62.0.2/hls/po.xml000066400000000000000000000010551433400105300147140ustar00rootroot00000000000000 \b(msgid|msgstr)\b ("[^\"]*") ^#.* #%s /\* \*/ tea-qt-62.0.2/hls/python.xml000066400000000000000000000030231433400105300156140ustar00rootroot00000000000000 \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-62.0.2/hls/r.xml000066400000000000000000000027671433400105300145520ustar00rootroot00000000000000 \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-62.0.2/hls/rust.xml000066400000000000000000000027341433400105300153000ustar00rootroot00000000000000 \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-62.0.2/hls/seed7.xml000066400000000000000000000023031433400105300153020ustar00rootroot00000000000000 \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-62.0.2/hls/subtitles.xml000066400000000000000000000003051433400105300163110ustar00rootroot00000000000000 \b\d{1,}:\d{1,}:\d{1,}(\,|\.)\d{1,}\b tea-qt-62.0.2/hls/tex.xml000066400000000000000000000011561433400105300151000ustar00rootroot00000000000000 \\(?:[\w@]+|.) (``[^\``]*'') (`[^`]*') %.* %%s /\* \*/ tea-qt-62.0.2/hls/vala.xml000066400000000000000000000031111433400105300152140ustar00rootroot00000000000000 [-=\+\*\/\|] [\(\)\[\]] \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-62.0.2/hls/verilog.xml000066400000000000000000000044251433400105300157510ustar00rootroot00000000000000 \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-62.0.2/hls/wikitext.xml000066400000000000000000000015371433400105300161530ustar00rootroot00000000000000 (<[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-62.0.2/hls/xml.xml000066400000000000000000000011551433400105300150770ustar00rootroot00000000000000 <(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+> ("[^\"]*"|'[^']*') <!-- --> <!-- %s --> <!-- %s --> tea-qt-62.0.2/icons/000077500000000000000000000000001433400105300141005ustar00rootroot00000000000000tea-qt-62.0.2/icons/128/000077500000000000000000000000001433400105300144125ustar00rootroot00000000000000tea-qt-62.0.2/icons/128/tea.png000066400000000000000000000223051433400105300156730ustar00rootroot00000000000000PNG  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-62.0.2/icons/32/000077500000000000000000000000001433400105300143245ustar00rootroot00000000000000tea-qt-62.0.2/icons/32/tea.png000066400000000000000000000037631433400105300156140ustar00rootroot00000000000000PNG  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-62.0.2/icons/48/000077500000000000000000000000001433400105300143335ustar00rootroot00000000000000tea-qt-62.0.2/icons/48/tea.png000066400000000000000000000072041433400105300156150ustar00rootroot00000000000000PNG  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-62.0.2/icons/64/000077500000000000000000000000001433400105300143315ustar00rootroot00000000000000tea-qt-62.0.2/icons/64/tea.png000066400000000000000000000113151433400105300156110ustar00rootroot00000000000000PNG  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-62.0.2/icons/create-dir.png000066400000000000000000000044501433400105300166300ustar00rootroot00000000000000PNG  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-62.0.2/icons/current-list.png000066400000000000000000000031311433400105300172370ustar00rootroot00000000000000PNG  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-62.0.2/icons/edit-copy-active.png000066400000000000000000000031151433400105300177540ustar00rootroot00000000000000PNG  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-62.0.2/icons/edit-copy.png000066400000000000000000000024351433400105300165070ustar00rootroot00000000000000PNG  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-62.0.2/icons/edit-cut-active.png000066400000000000000000000047241433400105300176040ustar00rootroot00000000000000PNG  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-62.0.2/icons/edit-cut.png000066400000000000000000000046651433400105300163370ustar00rootroot00000000000000PNG  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-62.0.2/icons/edit-paste-active.png000066400000000000000000000054431433400105300201240ustar00rootroot00000000000000PNG  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-62.0.2/icons/edit-paste.png000066400000000000000000000057751433400105300166630ustar00rootroot00000000000000PNG  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-62.0.2/icons/file-new.png000066400000000000000000000012351433400105300163150ustar00rootroot00000000000000PNG  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-62.0.2/icons/file-save-active.png000066400000000000000000000017441433400105300177400ustar00rootroot00000000000000PNG  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-62.0.2/icons/file-save.png000066400000000000000000000017761433400105300164740ustar00rootroot00000000000000PNG  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-62.0.2/icons/fn-spell-check.png000066400000000000000000000030521433400105300174010ustar00rootroot00000000000000PNG  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-62.0.2/icons/labels.png000066400000000000000000000024311433400105300160500ustar00rootroot00000000000000PNG  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-62.0.2/icons/svg/000077500000000000000000000000001433400105300146775ustar00rootroot00000000000000tea-qt-62.0.2/icons/svg/tea.svg000066400000000000000000000117701433400105300161770ustar00rootroot00000000000000 image/svg+xml tea-qt-62.0.2/icons/tea-icon-v3-01.png000066400000000000000000000223051433400105300170530ustar00rootroot00000000000000PNG  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-62.0.2/icons/tea-icon-v3-02.png000066400000000000000000000215721433400105300170610ustar00rootroot00000000000000PNG  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-62.0.2/icons/tea-icon-v3-03.png000066400000000000000000000235031433400105300170560ustar00rootroot00000000000000PNG  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-62.0.2/icons/tea_icon_v2.png000066400000000000000000000065471433400105300170120ustar00rootroot00000000000000PNG  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-62.0.2/images/000077500000000000000000000000001433400105300142325ustar00rootroot00000000000000tea-qt-62.0.2/images/moon-phases.png000066400000000000000000001114361433400105300171770ustar00rootroot00000000000000PNG  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 #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-62.0.2/img_viewer.h000066400000000000000000000020611433400105300152720ustar00rootroot00000000000000#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-62.0.2/ioapi.h000066400000000000000000000145341433400105300142460ustar00rootroot00000000000000/* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) Modified by Sergey A. Tachenov to allow QIODevice API usage. For more info read MiniZip_info.txt Changes Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. More if/def section may be needed to support other platforms Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. (but you should use iowin32.c for windows instead) */ #ifndef _ZLIBIOAPI64_H #define _ZLIBIOAPI64_H #if (!defined(_WIN32)) && (!defined(WIN32)) // Linux needs this to support file operation on files larger then 4+GB // But might need better if/def to select just the platforms that needs them. #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef __USE_LARGEFILE64 #define __USE_LARGEFILE64 #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BIT #define _FILE_OFFSET_BIT 64 #endif #endif #include #include #include "zlib.h" #if defined(USE_FILE32API) #define fopen64 fopen #define ftello64 ftell #define fseeko64 fseek #endif #ifdef HAVE_MINIZIP64_CONF_H #include "mz64conf.h" #endif /* a type choosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else #ifdef HAS_STDINT_H #include "stdint.h" typedef uint64_t ZPOS64_T; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; #else typedef unsigned long long int ZPOS64_T; #endif #endif #endif #ifdef __cplusplus extern "C" { #endif #ifndef OF #define OF _Z_OF #endif #define ZLIB_FILEFUNC_SEEK_CUR (1) #define ZLIB_FILEFUNC_SEEK_END (2) #define ZLIB_FILEFUNC_SEEK_SET (0) #define ZLIB_FILEFUNC_MODE_READ (1) #define ZLIB_FILEFUNC_MODE_WRITE (2) #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) #define ZLIB_FILEFUNC_MODE_EXISTING (4) #define ZLIB_FILEFUNC_MODE_CREATE (8) #ifndef ZCALLBACK #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, voidpf file, int mode)); typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); typedef uLong (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); /* here is the "old" 32 bits structure structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; read_file_func zread_file; write_file_func zwrite_file; tell_file_func ztell_file; seek_file_func zseek_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc_def; typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, voidpf file, int mode)); typedef struct zlib_filefunc64_def_s { open64_file_func zopen64_file; read_file_func zread_file; write_file_func zwrite_file; tell64_file_func ztell64_file; seek64_file_func zseek64_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; close_file_func zfakeclose_file; // for no-auto-close flag } zlib_filefunc64_def; void fill_qiodevice64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); void fill_qiodevice_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s { zlib_filefunc64_def zfile_func64; open_file_func zopen32_file; tell_file_func ztell32_file; seek_file_func zseek32_file; } zlib_filefunc64_32_def; #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZFAKECLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zfakeclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf file,int mode)); int call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) #define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) #ifdef __cplusplus } #endif #endif tea-qt-62.0.2/libretta_calc.cpp000066400000000000000000000115631433400105300162670ustar00rootroot00000000000000#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-62.0.2/libretta_calc.h000066400000000000000000000002111433400105300157200ustar00rootroot00000000000000#ifndef LIBRETTA_CALC_H #define LIBRETTA_CALC_H #include using namespace std; double calculate (string expression); #endif tea-qt-62.0.2/logmemo.cpp000066400000000000000000000101041433400105300151240ustar00rootroot00000000000000/*************************************************************************** * 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-62.0.2/logmemo.h000066400000000000000000000036321433400105300146010ustar00rootroot00000000000000/*************************************************************************** * 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-62.0.2/main.cpp000066400000000000000000000054121433400105300144170ustar00rootroot00000000000000/*************************************************************************** * Copyleft 2007-2021 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-62.0.2/manuals/000077500000000000000000000000001433400105300144255ustar00rootroot00000000000000tea-qt-62.0.2/manuals/en.html000066400000000000000000002124351433400105300157240ustar00rootroot00000000000000 TEA Fine Manual

TEA Fine Manual


Table of content



Introduction
Interface
File manager
Dates panel
File
Calendar
Edit
Markup
Search
Functions
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 has two homesites: semiletov.org/tea and tea.ourproject.org. If one of them is down, go to another one. Happy Arch/Manjaro/etc. users can use my "tea-qt" package from AUR (aur.archlinux.org/packages/tea-qt). 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 flash drive 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. There is also a "?" button. Select some file and press this button - it tries to autodetect the file's character set and activate it at the encodings list. Currently only HTML files or Russian charsets are supported for built-in automatic detection, and other via enca utility (See Options - Common).

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.

There is a small secret at the File open dialog window. The character set combobox. Yes. With that fine tool in the box, you can open a text file in any encoding that is known by a human being (actually, libiconv). But be careful! You must know exactly which encoding you need to use to read your file correctly. Otherwise you shall see many weird letters that can harm your spiritual calmness. If so, close your eyes quickly and type "hello" at seven times! Or you can try to load your file with another encoding selected. So choose wise. By the way, TEA can try to auto-detect the encoding - just press the "?" button (on the right from the character set list). Better to turn on the option Use Enca for charset detection at Options - Common page, if Enca is installed.

TEA can open all plain text files, and also ODT (OpenDocument Text), KWD (old KWord's format), FB2, DOCX, RTF, SXW (old OOo/StarOffice format), ABW (Abiword) formats and gzipped files. 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. 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. For example, if you will look at the source of that document, mailto:tea@list.ru will look like the heap of a garbage. I hope that spam harvesters do not understand it.



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 the charset that is selected at the charset list on the Files tab.

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 two spell checking engines: Aspell and Hunspell. 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.

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 last step the spell checking will fail. Also select the language in this way after you change the engine from Aspell to Hunspell or vice versa.

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 aspell (from the distro or so). To spell check, 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 a language of your locale. 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- this function is Hunspell-only. An updated dictionary will be loaded after the next session.




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, put the processed images into the new directory and zip that 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. At the Options > Images check the Zip directory with processed images option.
4. 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.

TEA as a ZIP packer/unpacker

TEA provides simple functions to pack files (not directories) to the archive. To do such thing, set the archive name first (do that at the directory where you want to create the archive):
Fm > ZIP > Create new ZIP
The input dialog box will be provided. Then, walk through your directories. Select any files and use Add to ZIP to add them into the archive. Please note - all that happens virtually until you do not use a Save ZIP function. To pack the files physically, apply Save ZIP.

All files will be packed into the archive that has a one subdirectory names as the archive name, but without .zip-extension.

You can also unpack the selected ZIP-file into the current directory. Use the Unpack ZIP to current directory function. Please note that the file names charset (inside of the ZIP-archive) are controlled via Options - Common - ZIP unpacking: file names charset/ZIP packing: file names charset options. So, if you see weird characters after you unpacked ZIP-file, try to Options the charset. To do this less painful, select some charset at ZIP unpacking: file names charset and try how is looking file name - use List ZIP content menu item. It simply lists the archive content (file names) into the Logmemo using selected charset.

And another note - TEA does not support password-protected ZIPs.


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 home site with the prose, programs, music and other things.

My home site with the prose, programs, music and other things.

My music


Support the development

If you want to support TEA development, here is my Patreon page..

tea-qt-62.0.2/manuals/pl.html000066400000000000000000005660451433400105300157460ustar00rootroot00000000000000 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-62.0.2/manuals/ru.html000066400000000000000000004105351433400105300157510ustar00rootroot00000000000000 Руководство

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


Содержание



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

Введение

История 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 два сайта: semiletov.org/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 попытается определить кодировку и выберет её в списке. В ТИА встроен определитель кодировок, который распознает кодировки HTML-файлов, а также обычных текстовых, если они в русских или украинских кодировках. Для всех других языков удобно дополнительно припахать внешнюю утилиту Enca, включив ее использование в Наладка - Общие.

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

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

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

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


Файл

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

Открыть - открыть файл. Появится файловый приказчик либо диалоговое окно открытия файла (смотря по настройкам). Там есть список "Кодировка". Выберите нужную, потом открывайте файл. Пользователи Windows версии 10, стандартная русская кодировка в этой системе - Windows 1251. Кнопочка "?" справа от списка кодировок - чтобы TEA попытался определить кодировку выбранного файла автоматически. Чтя историю, ТИА поддерживает древние славянские кодировки Windows 1251, KOI8-R, KOI8-U, DOS 866. Но в обиходе я советую использовать универсальную UTF-8.

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

О поддерживаемых форматах. TEA читает/пишет обычные текстовые файлы, а также может вытаскивать текст из ODT (OpenOffice.org Writer), FB2, DOCX, RTF, KWD (старый формат KWord), ABW (Abiword) и SXW (старый формат OOo/StarOffice). А еще читает файлы, сжатые gzip'ом. Относительно офисных форматов, то поддержка их в состоянии разной степени кривизны. Но читать можно. Если же ТИА собран с поддержкой 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 - текущее время.

Вёрстка

В этом меню собраны функции для быстрой разметки текста в режимах 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, которые понимаются 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


Функции > Проверка правописания

Для проверки правописания в TEA используются движкиASpell и Hunspell.

Во-первых надо решить, какой движок использовать. Выбрать движок можно на странице Наладка > Функции, там есть список Движок проверки правописания. Количество пунктов в нем зависит от вашей сборки TEA - я не знаю, какая у вас. В Linux, TEA может быть собран с обоими движками, с каким-то одним, или вообще без проверки правописания. Для Windows я делаю сборки сам, с обоими движками. Сам использую в работе 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 или OpenOffice.org. При установленном в локальный каталог пользователя Firefox, словари лежат в firefox/dictionaries.

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

Удалить из словаря - удаляет слово (по месту курсора) из пользовательского словаря, только для Hunspell. Измененный словарь подгружается при перезапуске движка - это происходит после повторного запуска редактора, либо смены текущего движка.


Нав

Метки, Обновить метки. Эта штука полезна для правки больших текстов. Вы можете прямо в тексте делать поименованные метки и потом быстро перемещаться между ними при помощи меню Метки либо через выпадающий список рядом с кнопкой на панели инструментов. Как создавать метки? Пишете к тексте любое слово и обрамляете его "[?" и "?]". Эти вот начальные и замыкающие отметки можно переопределить в Наладка - Функции - Метки. Итак, написали такую метку, нажали или выбрали пункт меню Обновить метки, и к меню Меток и к выпадающему списку добавится новая метка. Выбираем её - перемещаемся в то место текста, где она написана. Метки обновляются только вручную, то есть если вы загрузили файл с метками, надо всё равно их обновить.

Пример. Создадим в разных местах текста метки "[? собаки ?]" и "[? коты ?]". Обновляем метки. Готово!

К строке - перемещает курсор на строку по номеру, указанному в Знаменитом поле ввода.

Запомнить место/К сохраненному месту - парные функции, служащие для быстрого перемещения туда-сюда по документу. Запомнили место - Запомнить место, потом прокрутили куда надо, посмотрели, потом захотели вернуться назад - выбрали К сохраненному месту. Удобно в работе с большими документами!


ИДЕ

В 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 обоих каналов.

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

Подменю ZIP

У TEA есть простые средства для создания и распаковки ZIP-архивов (без паролей). Чтобы создать такой архив, сначала надо дать ему имя (перейдя в файловом приказчике в каталог, где хотите создать архив) с помощью пункта меню Создать новый ZIP. Затем вы, блуждая по каталогам, добавляете в архив новые файлы, отмечая их (Ctrl-A чтобы выбрать все, или Ctrl-щелчок мыши - чтобы несколько, иначе берется файл под курсором) и вызывая пункт меню Добавить в ZIP. Когда вы наполните свой архив, его надо сохранить менюшкой Сохранить ZIP - ведь до этого файлы на деле добавлялись не в архив, а в список подготавливаемых к упаковке файлов. Внутри архива создается каталог с тем же именем, что и архив, только без расширения zip. В этот каталог внутри архива и помещаются все упаковываемые файлы.

Для распаковки ZIP'а в текущий каталог надо выделить ZIP-файл, и применить функцию Распаковать ZIP в текущий каталог. Иногда бывают косяки с кодировкой имен файлов, находящихся в ZIP-архивах - однако в TEA вы можете выбрать кодировку имен, делается это в Наладка - Общие - ZIP-распаковка: кодировка имен файлов/ZIP-упаковка: кодировка имен файлов. Обычно это либо IBM866, либо CP1251 или UTF-8. Чтобы не ошибиться с выбором кодировки, можете выставить какую-то кодировку и потом попробовать её, не производя физическую распаковку - используйте пункт меню Показать содержимое ZIP. TEA в логмемо отобразит имена файлов из выбранного архива. Если там крякозяблы - подберите другую кодировку.

Подменю Картинки

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

Итак, чтобы масштабировать картинки, выделите их, а затем, введя в Знаменитое поле ввода количество пикселов или процентов, примените один из пунктов меню: Масштабировать по стороне или Масштабировать в процентах. В чем разница?

Масштабировать в процентах - допустим, вы хотите уменьшить каждую картинку на 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 - включен.


Клавиатура

Вдобавок к привычной работе с клавиатурой, в TEA можно включить режим для левшей и амбидекстеров, делается это в Наладка - Общие - Использовать левый Alt + WASD. В этом режиме, удерживая левый Alt, клавиши WASD служат для перемещения курсора, на подмогу им - E, C (как PageUp/Down).

То же самое, но с удерживаемой левой клавишей Win - выделяет текст.

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


Слово про автосохранение

Расцвела буйным цветом в саду вишня. В ТИА есть ряд функций, которые объединены общим понятием автосохранения, но они имеют разную природу.

Во-первых, есть возможность временно автоматически сохранять несохраненные новые файлы. Пока вы их не сохранили под каким-то именем, можно считать их безымянными буферами. ТИА может сохранять их при выходе (не просто при закрытии вкладки, а именно при выходе из ТИА), если включить опцию Наладка - Общие - Автосохранение - Временно сохранять несохраненные буферы при выходе.

Также в ТИА есть понятие автосохраняемых файлов. Любой текущий файл может быть добавлен в список автосохраняемых через меню Файл - Действия над файлом - Пометить как автосохраняемый файл (и ниже есть пункт отмены этого дела). Такой автосохраняемый файл будет сохраняться автоматически при закрытии (закрытии вкладки или просто закрытии ТИА вообще).

Буфера и автосохранямые файлы могут также сохраняться периодически, по времени, что управляется через Наладка - Общие - Автосохранение - Сохранять буферы и автосохраняемые файлы каждые 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=window-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, кроме исходника. Почему? Хлопот меньше. Не надо таскать с программой лишние лицензии.


Мои иные проекты

Личный сайт - тут вы найдете мою прозу, музыку, программы и другое.

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


Поддержать разработку

Поддержать разработку ТИА вы можете подпиской на моей страничке на Патреоне.

tea-qt-62.0.2/meson.build000066400000000000000000000074511433400105300151360ustar00rootroot00000000000000project('tea', ['cpp','c'], default_options : ['cpp_std=c++11'], version : '62.0.1', license : 'GPLv3') add_global_arguments('-DVERSION_NUMBER="62.0.1"', language : 'cpp') add_global_arguments('-DNOCRYPT="1"', language : 'cpp') add_global_arguments('-DNOUNCRYPT="1"', language : 'cpp') add_global_arguments('-DQUAZIP_STATIC="1"', language : 'cpp') add_global_arguments('-DNOCRYPT="1"', language : 'c') add_global_arguments('-DNOUNCRYPT="1"', language : 'c') #harcoded for now moc_params = ['-DQT_VERSION=0x050000'] if build_machine.system() == 'linux' moc_params += ['-DQ_OS_LINUX=1', '-DQ_OS_UNIX=1'] endif compiler = meson.get_compiler('cpp') if compiler.has_header('/usr/include/linux/joystick.h') moc_params += ['-DJOYSTICK_SUPPORTED=1'] add_global_arguments('-DJOYSTICK_SUPPORTED=1', language : 'cpp') endif aspell_dep = compiler.find_library('aspell', required : false) if get_option('aspell').enabled() if aspell_dep.found() add_global_arguments('-DASPELL_ENABLE=1', language : 'cpp') moc_params += ['-DASPELL_ENABLE=1'] endif endif hunspell_dep = dependency('hunspell', required : false) if hunspell_dep.found() add_global_arguments('-DHUNSPELL_ENABLE=1', language : 'cpp') moc_params += ['-DHUNSPELL_ENABLE=1'] endif poppler_dep = dependency('poppler-qt5', required : false) if get_option('pdf').enabled() if poppler_dep.found() add_global_arguments('-DPOPPLER_ENABLE=1', language : 'cpp') moc_params += ['-DPOPPLER_ENABLE=1'] endif endif ddjvuapi_dep = dependency('ddjvuapi', required : false) if get_option('djvu').enabled() if ddjvuapi_dep.found() add_global_arguments('-DDJVU_ENABLE=1', language : 'cpp') moc_params += ['-DDJVU_ENABLE=1'] endif endif zlib_dep = dependency('zlib', required : true) qt5_dep = dependency('qt5', modules : ['Core', 'Gui', 'Widgets']) qt5 = import('qt5') tea_headers_moc = [ 'quagzipfile.h', 'quaziodevice.h', 'quazipfile.h', 'tea.h', 'todo.h', 'document.h', 'calendar.h', 'fman.h', 'shortcuts.h', 'logmemo.h', 'img_viewer.h', 'tio.h', 'tzipper.h', 'single_application_shared.h', 'myjoystick.h', 'pugixml.hpp', 'pugiconfig.hpp'] src_processed = qt5.preprocess( moc_headers : tea_headers_moc, moc_extra_arguments: moc_params, qresources : 'resources.qrc' ) tea_source = ['tea.cpp', 'main.cpp', 'todo.cpp', 'textproc.cpp', 'libretta_calc.cpp', 'wavinfo.cpp', 'calendar.cpp', 'gui_utils.cpp', 'document.cpp', 'utils.cpp', 'spellchecker.cpp', 'fman.cpp', 'shortcuts.cpp', 'logmemo.cpp', 'img_viewer.cpp', 'tio.cpp', 'tzipper.cpp', 'single_application_shared.cpp', 'exif_reader.cpp', 'myjoystick.cpp', 'qioapi.cpp', 'quagzipfile.cpp', 'quaziodevice.cpp', 'quazip.cpp', 'quazipdir.cpp', 'quazipfile.cpp', 'quazipfileinfo.cpp', 'quazipnewinfo.cpp', 'unzip.c', 'zip.c', 'pugixml.cpp'] tea_exe = executable ('tea', sources : [src_processed, tea_source], install : true, dependencies : [qt5_dep, hunspell_dep, aspell_dep, zlib_dep, poppler_dep, ddjvuapi_dep] ) install_data(['icons/32/tea.png'], install_dir : 'share/icons/hicolor/32x32/apps') install_data(['icons/48/tea.png'], install_dir : 'share/icons/hicolor/48x48/apps') install_data(['icons/64/tea.png'], install_dir : 'share/icons/hicolor/64x64/apps') install_data(['icons/128/tea.png'], install_dir : 'share/icons/hicolor/128x128/apps') install_data(['icons/svg/tea.svg'], install_dir : 'share/icons/hicolor/scalable/apps') install_data(['desktop/tea.desktop'], install_dir : 'share/applications') tea-qt-62.0.2/meson_options.txt000066400000000000000000000002401433400105300164160ustar00rootroot00000000000000option('pdf', type : 'feature', value : 'disabled') option('djvu', type : 'feature', value : 'disabled') option('aspell', type : 'feature', value : 'disabled') tea-qt-62.0.2/myjoystick.cpp000066400000000000000000000035741433400105300157070ustar00rootroot00000000000000//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-62.0.2/myjoystick.h000066400000000000000000000024111433400105300153410ustar00rootroot00000000000000//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-62.0.2/palettes/000077500000000000000000000000001433400105300146065ustar00rootroot00000000000000tea-qt-62.0.2/palettes/Berry000066400000000000000000000006761433400105300156250ustar00rootroot00000000000000class=#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-62.0.2/palettes/Linux MC000066400000000000000000000006171433400105300161140ustar00rootroot00000000000000class=#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-62.0.2/palettes/Morning000066400000000000000000000006711433400105300161460ustar00rootroot00000000000000class=#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-62.0.2/palettes/Navigator000066400000000000000000000006711433400105300164670ustar00rootroot00000000000000class=#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-62.0.2/palettes/Pines000066400000000000000000000006331433400105300156110ustar00rootroot00000000000000class=#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-62.0.2/palettes/TEA000066400000000000000000000006721433400105300151470ustar00rootroot00000000000000class=#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-62.0.2/palettes/Turbo90000066400000000000000000000006231433400105300157760ustar00rootroot00000000000000class=#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-62.0.2/palettes/UNIX000066400000000000000000000006031433400105300153130ustar00rootroot00000000000000class=#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-62.0.2/palettes/Vinyl000066400000000000000000000006441433400105300156360ustar00rootroot00000000000000class=#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-62.0.2/palettes/paper-old000066400000000000000000000006751433400105300164240ustar00rootroot00000000000000class=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-62.0.2/palettes/paper-toilet000066400000000000000000000006731433400105300171440ustar00rootroot00000000000000class=#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-62.0.2/pugiconfig.hpp000066400000000000000000000055201433400105300156320ustar00rootroot00000000000000/** * pugixml parser - version 1.12 * -------------------------------------------------------- * Copyright (C) 2006-2022, 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-2022 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-62.0.2/pugixml.cpp000066400000000000000000012233741433400105300151720ustar00rootroot00000000000000/** * pugixml parser - version 1.12 * -------------------------------------------------------- * Copyright (C) 2006-2022, 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 #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__NO_INLINE __declspec(noinline) #elif defined(__GNUC__) # define PUGI__NO_INLINE __attribute__((noinline)) #else # define PUGI__NO_INLINE #endif // Branch weight controls #if defined(__GNUC__) && !defined(__c2__) # define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) #else # define PUGI__UNLIKELY(cond) (cond) #endif // Simple static assertion #define PUGI__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__DMC_VOLATILE volatile #else # define PUGI__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__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow"))) # else # define PUGI__UNSIGNED_OVERFLOW # endif #else # define PUGI__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 // 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__MSVC_CRT_VERSION _MSC_VER #elif defined(_WIN32_WCE) # define PUGI__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__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) #elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 # define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__) #else # define PUGI__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__NS_BEGIN namespace pugi { namespace impl { # define PUGI__NS_END } } # define PUGI__FN inline # define PUGI__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__NS_BEGIN namespace pugi { namespace impl { # define PUGI__NS_END } } # else # define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { # define PUGI__NS_END } } } # endif # define PUGI__FN # define PUGI__FN_NO_INLINE PUGI__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__NS_BEGIN PUGI__FN void* default_allocate(size_t size) { return malloc(size); } PUGI__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__NS_END // String utilities PUGI__NS_BEGIN // Get string length PUGI__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__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__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__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__NS_END // auto_ptr-like object for exception recovery PUGI__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__NS_END #ifdef PUGIXML_COMPACT PUGI__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__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__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__NS_END #endif PUGI__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__GETHEADER_IMPL(object, page, flags) // unused #define PUGI__GETPAGE_IMPL(header) (header).get_page() #else #define PUGI__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__GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8)))) #endif #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header) #define PUGI__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__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__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__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__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__NS_END #ifdef PUGIXML_COMPACT PUGI__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__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__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__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__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__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__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__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__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__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__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__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__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__GETPAGE(object)->allocator; } template inline xml_document_struct& get_document(const Object* object) { assert(object); return *static_cast(PUGI__GETPAGE(object)->allocator); } PUGI__NS_END // Low-level DOM operations PUGI__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__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__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; if (node->next_sibling) node->next_sibling->prev_sibling_c = child; else parent->first_child->prev_sibling_c = child; child->next_sibling = node->next_sibling; 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; if (node->prev_sibling_c->next_sibling) node->prev_sibling_c->next_sibling = child; else parent->first_child = child; child->prev_sibling_c = node->prev_sibling_c; child->next_sibling = node; node->prev_sibling_c = child; } inline void remove_node(xml_node_struct* node) { xml_node_struct* parent = node->parent; if (node->next_sibling) node->next_sibling->prev_sibling_c = node->prev_sibling_c; else parent->first_child->prev_sibling_c = node->prev_sibling_c; if (node->prev_sibling_c->next_sibling) node->prev_sibling_c->next_sibling = node->next_sibling; else parent->first_child = node->next_sibling; 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) { if (place->next_attribute) place->next_attribute->prev_attribute_c = attr; else node->first_attribute->prev_attribute_c = attr; attr->next_attribute = place->next_attribute; 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) { if (place->prev_attribute_c->next_attribute) place->prev_attribute_c->next_attribute = attr; else node->first_attribute = attr; attr->prev_attribute_c = place->prev_attribute_c; attr->next_attribute = place; place->prev_attribute_c = attr; } inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) { if (attr->next_attribute) attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; else node->first_attribute->prev_attribute_c = attr->prev_attribute_c; if (attr->prev_attribute_c->next_attribute) attr->prev_attribute_c->next_attribute = attr->next_attribute; else node->first_attribute = attr->next_attribute; attr->prev_attribute_c = 0; attr->next_attribute = 0; } PUGI__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__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__NS_END // Helper classes for code generation PUGI__NS_BEGIN struct opt_false { enum { value = 0 }; }; struct opt_true { enum { value = 1 }; }; PUGI__NS_END // Unicode utilities PUGI__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__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__NS_END PUGI__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__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) #else #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) #endif #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) PUGI__FN bool is_little_endian() { unsigned int ui = 1; return *reinterpret_cast(&ui) == 1; } PUGI__FN xml_encoding get_wchar_encoding() { PUGI__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__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) { #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__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__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__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o'); PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g'); // S? = S? PUGI__SCANCHARTYPE(ct_space); PUGI__SCANCHAR('='); PUGI__SCANCHARTYPE(ct_space); // the only two valid delimiters are ' and " uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; PUGI__SCANCHAR(delimiter); size_t start = offset; out_encoding = data + offset; PUGI__SCANCHARTYPE(ct_symbol); out_length = offset - start; PUGI__SCANCHAR(delimiter); return true; } } return false; #undef PUGI__SCANCHAR #undef PUGI__SCANCHARTYPE } PUGI__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__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__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__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__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__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__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__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__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__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__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__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__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__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__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) { if (source_length == 0) { // empty string and null pointer are equivalent, so just deallocate old memory xml_allocator* alloc = PUGI__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__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__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__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } #define PUGI__POPNODE() { cursor = cursor->parent; } #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } #define PUGI__SCANWHILE(X) { while (X) ++s; } #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } } #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) { gap g; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__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__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__FN char_t* strconv_cdata(char_t* s, char_t endch) { gap g; while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__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__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__SCANWHILE_UNROLL(!PUGI__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__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__IS_CHARTYPE(end[-1], ct_space)) --end; *end = 0; return s; } else ++s; } } }; PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) { PUGI__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__IS_CHARTYPE(*s, ct_space)) { char_t* str = s; do ++str; while (PUGI__IS_CHARTYPE(*str, ct_space)); g.push(s, str - s); } while (true) { PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); if (*s == end_quote) { char_t* str = g.flush(s); do *str-- = 0; while (PUGI__IS_CHARTYPE(*str, ct_space)); return s + 1; } else if (PUGI__IS_CHARTYPE(*s, ct_space)) { *s++ = ' '; if (PUGI__IS_CHARTYPE(*s, ct_space)) { char_t* str = s + 1; while (PUGI__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__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); if (*s == end_quote) { *g.flush(s) = 0; return s + 1; } else if (PUGI__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__SCANWHILE_UNROLL(!PUGI__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__SCANWHILE_UNROLL(!PUGI__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__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) { PUGI__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__SCANFOR(*s == ch); if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s++; } else if (s[0] == '<' && s[1] == '?') { // s += 2; PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 2; } else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') { s += 4; PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); s += 3; } else PUGI__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__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__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__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) { s = strconv_comment(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); } else { // Scan for terminating '-->'. PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_comment, s); if (PUGI__OPTSET(parse_comments)) *s = 0; // Zero-terminate this segment at the first terminating '-'. s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. } } else PUGI__THROW_ERROR(status_bad_comment, s); } else if (*s == '[') { // 'value = s; // Save the offset. if (PUGI__OPTSET(parse_eol)) { s = strconv_cdata(s, endch); if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); } else { // Scan for terminating ']]>'. PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); PUGI__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__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); PUGI__CHECK_ERROR(status_bad_cdata, s); ++s; } s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. } else PUGI__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__ENDSWITH(s[6], 'E')) { s -= 2; if (cursor->parent) PUGI__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__OPTSET(parse_doctype)) { while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; PUGI__PUSHNODE(node_doctype); cursor->value = mark; } } else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); else PUGI__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__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); PUGI__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__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) { if (declaration) { // disallow non top-level declarations if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); PUGI__PUSHNODE(node_declaration); } else { PUGI__PUSHNODE(node_pi); } cursor->name = target; PUGI__ENDSEG(); // parse value/attributes if (ch == '?') { // empty node if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); s += (*s == '>'); PUGI__POPNODE(); } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // scan for tag end char_t* value = s; PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); PUGI__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__POPNODE(); PUGI__ENDSEG(); s += (*s == '>'); } } else PUGI__THROW_ERROR(status_bad_pi, s); } else { // scan for tag end PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); PUGI__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; while (*s != 0) { if (*s == '<') { ++s; LOC_TAG: if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' { PUGI__PUSHNODE(node_element); // Append a new node to the tree. cursor->name = s; PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. if (ch == '>') { // end of tag } else if (PUGI__IS_CHARTYPE(ch, ct_space)) { LOC_ATTRIBUTES: while (true) { PUGI__SKIPWS(); // Eat any whitespace. if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... { xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); a->name = s; // Save the offset. PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. if (PUGI__IS_CHARTYPE(ch, ct_space)) { PUGI__SKIPWS(); // Eat any whitespace. ch = *s; ++s; } if (ch == '=') // '<... #=...' { PUGI__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__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__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else PUGI__THROW_ERROR(status_bad_attribute, s); } else if (*s == '/') { ++s; if (*s == '>') { PUGI__POPNODE(); s++; break; } else if (*s == 0 && endch == '>') { PUGI__POPNODE(); break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '>') { ++s; break; } else if (*s == 0 && endch == '>') { break; } else PUGI__THROW_ERROR(status_bad_start_element, s); } // !!! } else if (ch == '/') // '<#.../' { if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); PUGI__POPNODE(); // Pop. s += (*s == '>'); } else if (ch == 0) { // we stepped over null terminator, backtrack & handle closing tag --s; if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); } else PUGI__THROW_ERROR(status_bad_start_element, s); } else if (*s == '/') { ++s; mark = s; char_t* name = cursor->name; if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark); while (PUGI__IS_CHARTYPE(*s, ct_symbol)) { if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark); } if (*name) { if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); else PUGI__THROW_ERROR(status_end_element_mismatch, mark); } PUGI__POPNODE(); // Pop. PUGI__SKIPWS(); if (*s == 0) { if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); } else { if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); ++s; } } else if (*s == '?') // 'first_child) continue; } } if (!PUGI__OPTSET(parse_trim_pcdata)) s = mark; if (cursor->parent || PUGI__OPTSET(parse_fragment)) { if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) { cursor->value = s; // Save the offset. } else { PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. cursor->value = s; // Save the offset. PUGI__POPNODE(); // Pop since this is a standalone. } s = strconv_pcdata(s); if (!*s) break; } else { PUGI__SCANFOR(*s == '<'); // '...<' if (!*s) break; ++s; } // We're after '<' goto LOC_TAG; } } // check that last tag is closed if (cursor != root) PUGI__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__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__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__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__FN xml_encoding get_write_native_encoding() { #ifdef PUGIXML_WCHAR_MODE return get_wchar_encoding(); #else return encoding_utf8; #endif } PUGI__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__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) { PUGI__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__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__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__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__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__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__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__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__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__SCANWHILE_UNROLL(!PUGI__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__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__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__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__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__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__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__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__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__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__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__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__NODETYPE(node) == node_pcdata || PUGI__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__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__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__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__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__NODETYPE(child); if (type == node_declaration) return true; if (type == node_element) return false; } return false; } PUGI__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__FN bool allow_insert_attribute(xml_node_type parent) { return parent == node_element || parent == node_declaration; } PUGI__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__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__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); 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__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__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__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__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__NODETYPE(node); return type == node_pcdata || type == node_cdata; } // get value with conversion functions template PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv) { U result = 0; const char_t* s = value; while (PUGI__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__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__FN int get_value_int(const char_t* value) { return string_to_integer(value, static_cast(INT_MIN), INT_MAX); } PUGI__FN unsigned int get_value_uint(const char_t* value) { return string_to_integer(value, 0, UINT_MAX); } PUGI__FN double get_value_double(const char_t* value) { #ifdef PUGIXML_WCHAR_MODE return wcstod(value, 0); #else return strtod(value, 0); #endif } PUGI__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__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__FN long long get_value_llong(const char_t* value) { return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX); } PUGI__FN unsigned long long get_value_ullong(const char_t* value) { return string_to_integer(value, 0, ULLONG_MAX); } #endif template PUGI__FN PUGI__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__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__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__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision) { char buf[128]; PUGI__SNPRINTF(buf, "%.*g", precision, double(value)); return set_value_ascii(dest, header, header_mask, buf); } template PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision) { char buf[128]; PUGI__SNPRINTF(buf, "%.*g", precision, value); return set_value_ascii(dest, header, header_mask, buf); } template PUGI__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__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); // 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); // 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__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) { #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__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__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__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__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__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__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__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__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 FILE* file = 0; return _wfopen_s(&file, path, mode) == 0 ? file : 0; #else return _wfopen(path, mode); #endif } #else PUGI__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__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__FN FILE* open_file(const char* path, const char* mode) { #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 FILE* file = 0; return fopen_s(&file, path, mode) == 0 ? file : 0; #else return fopen(path, mode); #endif } PUGI__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 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__NS_END namespace pugi { PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) { } PUGI__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__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) { } PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) { } PUGI__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__FN xml_tree_walker::xml_tree_walker(): _depth(0) { } PUGI__FN xml_tree_walker::~xml_tree_walker() { } PUGI__FN int xml_tree_walker::depth() const { return _depth; } PUGI__FN bool xml_tree_walker::begin(xml_node&) { return true; } PUGI__FN bool xml_tree_walker::end(xml_node&) { return true; } PUGI__FN xml_attribute::xml_attribute(): _attr(0) { } PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) { } PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) { } PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const { return _attr ? unspecified_bool_xml_attribute : 0; } PUGI__FN bool xml_attribute::operator!() const { return !_attr; } PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const { return (_attr == r._attr); } PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const { return (_attr != r._attr); } PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const { return (_attr < r._attr); } PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const { return (_attr > r._attr); } PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const { return (_attr <= r._attr); } PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const { return (_attr >= r._attr); } PUGI__FN xml_attribute xml_attribute::next_attribute() const { return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_attribute::previous_attribute() const { return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); } PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const { return (_attr && _attr->value) ? _attr->value + 0 : def; } PUGI__FN int xml_attribute::as_int(int def) const { return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; } PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const { return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; } PUGI__FN double xml_attribute::as_double(double def) const { return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; } PUGI__FN float xml_attribute::as_float(float def) const { return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; } PUGI__FN bool xml_attribute::as_bool(bool def) const { return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_attribute::as_llong(long long def) const { return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def; } PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const { return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; } #endif PUGI__FN bool xml_attribute::empty() const { return !_attr; } PUGI__FN const char_t* xml_attribute::name() const { return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_attribute::value() const { return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT(""); } PUGI__FN size_t xml_attribute::hash_value() const { return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); } PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const { return _attr; } PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(long rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) { set_value(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) { set_value(rhs); return *this; } PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) { set_value(rhs); return *this; } #endif PUGI__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__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__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__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__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__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__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__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__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__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__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__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__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__FN bool operator&&(const xml_attribute& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node::xml_node(): _root(0) { } PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) { } PUGI__FN static void unspecified_bool_xml_node(xml_node***) { } PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const { return _root ? unspecified_bool_xml_node : 0; } PUGI__FN bool xml_node::operator!() const { return !_root; } PUGI__FN xml_node::iterator xml_node::begin() const { return iterator(_root ? _root->first_child + 0 : 0, _root); } PUGI__FN xml_node::iterator xml_node::end() const { return iterator(0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const { return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root); } PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const { return attribute_iterator(0, _root); } PUGI__FN xml_object_range xml_node::children() const { return xml_object_range(begin(), end()); } PUGI__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__FN xml_object_range xml_node::attributes() const { return xml_object_range(attributes_begin(), attributes_end()); } PUGI__FN bool xml_node::operator==(const xml_node& r) const { return (_root == r._root); } PUGI__FN bool xml_node::operator!=(const xml_node& r) const { return (_root != r._root); } PUGI__FN bool xml_node::operator<(const xml_node& r) const { return (_root < r._root); } PUGI__FN bool xml_node::operator>(const xml_node& r) const { return (_root > r._root); } PUGI__FN bool xml_node::operator<=(const xml_node& r) const { return (_root <= r._root); } PUGI__FN bool xml_node::operator>=(const xml_node& r) const { return (_root >= r._root); } PUGI__FN bool xml_node::empty() const { return !_root; } PUGI__FN const char_t* xml_node::name() const { return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT(""); } PUGI__FN xml_node_type xml_node::type() const { return _root ? PUGI__NODETYPE(_root) : node_null; } PUGI__FN const char_t* xml_node::value() const { return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT(""); } PUGI__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) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__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) if (i->name && impl::strequal(name_, i->name)) return xml_attribute(i); return xml_attribute(); } PUGI__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) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__FN xml_node xml_node::next_sibling() const { return _root ? xml_node(_root->next_sibling) : xml_node(); } PUGI__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) if (i->name && impl::strequal(name_, i->name)) return xml_node(i); return xml_node(); } PUGI__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) if (i->name && impl::strequal(name_, i->name)) { // 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) if (j->name && impl::strequal(name_, j->name)) { // update hint to maximize efficiency of searching for consecutive attributes hint_._attr = j->next_attribute; return xml_attribute(j); } return xml_attribute(); } PUGI__FN xml_node xml_node::previous_sibling() const { if (!_root) return xml_node(); if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); else return xml_node(); } PUGI__FN xml_node xml_node::parent() const { return _root ? xml_node(_root->parent) : xml_node(); } PUGI__FN xml_node xml_node::root() const { return _root ? xml_node(&impl::get_document(_root)) : xml_node(); } PUGI__FN xml_text xml_node::text() const { return xml_text(_root); } PUGI__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__NODETYPE(_root) == node_element && _root->value) return _root->value; for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) if (impl::is_text_node(i) && i->value) return i->value; return PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const { return child(name_).child_value(); } PUGI__FN xml_attribute xml_node::first_attribute() const { return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); } PUGI__FN xml_attribute xml_node::last_attribute() const { return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); } PUGI__FN xml_node xml_node::first_child() const { return _root ? xml_node(_root->first_child) : xml_node(); } PUGI__FN xml_node xml_node::last_child() const { return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); } PUGI__FN bool xml_node::set_name(const char_t* rhs) { xml_node_type type_ = _root ? PUGI__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__FN bool xml_node::set_value(const char_t* rhs) { xml_node_type type_ = _root ? PUGI__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__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__FN bool xml_node::remove_attribute(const char_t* name_) { return remove_attribute(attribute(name_)); } PUGI__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__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__FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); } PUGI__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__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__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); // 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__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) if (i->name && impl::strequal(name_, i->name)) { for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) return xml_node(i); } return xml_node(); } PUGI__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) if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__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) { offset += (i != _root); offset += i->name ? impl::strlength(i->name) : 0; } string_t result; result.resize(offset); for (xml_node_struct* j = _root; j; j = j->parent) { if (j != _root) result[--offset] = delimiter; if (j->name) { size_t length = impl::strlength(j->name); offset -= length; memcpy(&result[offset], j->name, length * sizeof(char_t)); } } assert(offset == 0); return result; } #endif PUGI__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) { if (j->name && impl::strequalrange(j->name, 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__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__FN size_t xml_node::hash_value() const { return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); } PUGI__FN xml_node_struct* xml_node::internal_object() const { return _root; } PUGI__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__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__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__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__FN bool operator&&(const xml_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) { } PUGI__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__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__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__FN xml_text::xml_text(): _root(0) { } PUGI__FN static void unspecified_bool_xml_text(xml_text***) { } PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const { return _data() ? unspecified_bool_xml_text : 0; } PUGI__FN bool xml_text::operator!() const { return !_data(); } PUGI__FN bool xml_text::empty() const { return _data() == 0; } PUGI__FN const char_t* xml_text::get() const { xml_node_struct* d = _data(); return (d && d->value) ? d->value + 0 : PUGIXML_TEXT(""); } PUGI__FN const char_t* xml_text::as_string(const char_t* def) const { xml_node_struct* d = _data(); return (d && d->value) ? d->value + 0 : def; } PUGI__FN int xml_text::as_int(int def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_int(d->value) : def; } PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_uint(d->value) : def; } PUGI__FN double xml_text::as_double(double def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_double(d->value) : def; } PUGI__FN float xml_text::as_float(float def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_float(d->value) : def; } PUGI__FN bool xml_text::as_bool(bool def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_bool(d->value) : def; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN long long xml_text::as_llong(long long def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_llong(d->value) : def; } PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const { xml_node_struct* d = _data(); return (d && d->value) ? impl::get_value_ullong(d->value) : def; } #endif PUGI__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__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__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__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__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__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__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__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__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__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__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__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__FN xml_text& xml_text::operator=(const char_t* rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(long rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned long rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(double rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(float rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(bool rhs) { set(rhs); return *this; } #ifdef PUGIXML_HAS_LONG_LONG PUGI__FN xml_text& xml_text::operator=(long long rhs) { set(rhs); return *this; } PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) { set(rhs); return *this; } #endif PUGI__FN xml_node xml_text::data() const { return xml_node(_data()); } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xml_text& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__FN xml_node_iterator::xml_node_iterator() { } PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) { } PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const { return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; } PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const { return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; } PUGI__FN xml_node& xml_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI__FN xml_node* xml_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC5 workaround } PUGI__FN xml_node_iterator& xml_node_iterator::operator++() { assert(_wrap._root); _wrap._root = _wrap._root->next_sibling; return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) { xml_node_iterator temp = *this; ++*this; return temp; } PUGI__FN xml_node_iterator& xml_node_iterator::operator--() { _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); return *this; } PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) { xml_node_iterator temp = *this; --*this; return temp; } PUGI__FN xml_attribute_iterator::xml_attribute_iterator() { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) { } PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) { } PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const { return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; } PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const { return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; } PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const { assert(_wrap._attr); return _wrap; } PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const { assert(_wrap._attr); return const_cast(&_wrap); // BCC5 workaround } PUGI__FN xml_attribute_iterator& xml_attribute_iterator::operator++() { assert(_wrap._attr); _wrap._attr = _wrap._attr->next_attribute; return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) { xml_attribute_iterator temp = *this; ++*this; return temp; } PUGI__FN xml_attribute_iterator& xml_attribute_iterator::operator--() { _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); return *this; } PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) { xml_attribute_iterator temp = *this; --*this; return temp; } PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) { } PUGI__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__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__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__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__FN xml_node& xml_named_node_iterator::operator*() const { assert(_wrap._root); return _wrap; } PUGI__FN xml_node* xml_named_node_iterator::operator->() const { assert(_wrap._root); return const_cast(&_wrap); // BCC5 workaround } PUGI__FN xml_named_node_iterator& xml_named_node_iterator::operator++() { assert(_wrap._root); _wrap = _wrap.next_sibling(_name); return *this; } PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) { xml_named_node_iterator temp = *this; ++*this; return temp; } PUGI__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__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) { xml_named_node_iterator temp = *this; --*this; return temp; } PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) { } PUGI__FN xml_parse_result::operator bool() const { return status == status_ok; } PUGI__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__FN xml_document::xml_document(): _buffer(0) { _create(); } PUGI__FN xml_document::~xml_document() { _destroy(); } #ifdef PUGIXML_HAS_MOVE PUGI__FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0) { _create(); _move(rhs); } PUGI__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__FN void xml_document::reset() { _destroy(); _create(); } PUGI__FN void xml_document::reset(const xml_document& proto) { reset(); impl::node_copy_tree(_root, proto._root); } PUGI__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__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__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__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__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__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__GETPAGE(doc); assert(doc_page && !doc_page->prev && !doc_page->next); impl::xml_memory_page* other_page = PUGI__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__GETPAGE(other)); rhs._buffer = 0; } #endif #ifndef PUGIXML_NO_STL PUGI__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__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__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__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) { return load_string(contents, options); } PUGI__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__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__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__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__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__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__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__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__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); } PUGI__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); } PUGI__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__NODETYPE(i) == node_element) return xml_node(i); return xml_node(); } #ifndef PUGIXML_NO_STL PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) { assert(str); return impl::as_utf8_impl(str, impl::strlength_wide(str)); } PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) { return impl::as_utf8_impl(str.c_str(), str.size()); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) { assert(str); return impl::as_wide_impl(str, strlen(str)); } PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) { return impl::as_wide_impl(str.c_str(), str.size()); } #endif PUGI__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__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() { return impl::xml_memory::allocate; } PUGI__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__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__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__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) { return std::bidirectional_iterator_tag(); } PUGI__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__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__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__FN void reverse(I begin, I end) { while (end - begin > 1) swap(*begin++, *--end); } template PUGI__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__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__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__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__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__NS_END // Allocator used for AST and evaluation stacks PUGI__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__NS_END // String class PUGI__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__NS_END PUGI__NS_BEGIN PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) { while (*pattern && *string == *pattern) { string++; pattern++; } return *pattern == 0; } PUGI__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__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__FN char_t tolower_ascii(char_t ch) { return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; } PUGI__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__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__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__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__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__FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) PUGI__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__FN bool is_nan(double value) { #if defined(PUGI__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__FN const char_t* convert_number_to_string_special(double value) { #if defined(PUGI__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__FN bool convert_number_to_boolean(double value) { return (value != 0 && !is_nan(value)); } PUGI__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__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 PUGI__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__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__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] == '.'); // 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__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__FN bool check_string_to_number_format(const char_t* string) { // parse leading whitespace while (PUGI__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__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; // parse integer part while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; // parse decimal part if (*string == '.') { ++string; while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; } // parse trailing whitespace while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; return *string == 0; } PUGI__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__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__FN double round_nearest(double value) { return floor(value + 0.5); } PUGI__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__FN const char_t* qualified_name(const xpath_node& node) { return node.attribute() ? node.attribute().name() : node.node().name(); } PUGI__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__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__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__FN const char_t* namespace_uri(const xpath_node& node) { return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); } PUGI__FN char_t* normalize_space(char_t* buffer) { char_t* write = buffer; for (char_t* it = buffer; *it; ) { char_t ch = *it++; if (PUGI__IS_CHARTYPE(ch, ct_space)) { // replace whitespace sequence with single space while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; // avoid leading spaces if (write != buffer) *write++ = ' '; } else *write++ = ch; } // remove trailing space if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; // zero-terminate *write = 0; return write; } PUGI__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__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__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__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__FN PUGI__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__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__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__FN void delete_xpath_variable(T* var) { var->~T(); xml_memory::deallocate(var); } PUGI__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__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__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__NS_END // Internal node set class PUGI__NS_BEGIN PUGI__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__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__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__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__NS_END PUGI__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__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__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname { cur++; // : while (PUGI__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__IS_CHARTYPEX(*(cur+1), ctx_digit)) { _cur_lexeme_contents.begin = cur; // . ++cur; while (PUGI__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__IS_CHARTYPEX(*cur, ctx_digit)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; if (*cur == '.') { cur++; while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; } _cur_lexeme_contents.end = cur; _cur_lexeme = lex_number; } else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) { _cur_lexeme_contents.begin = cur; while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; if (cur[0] == ':') { if (cur[1] == '*') // namespace test ncname:* { cur += 2; // :* } else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname { cur++; // : while (PUGI__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_context c(xpath_node(), 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__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__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__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__NS_END namespace pugi { #ifndef PUGIXML_NO_EXCEPTIONS PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) { assert(_result.error); } PUGI__FN const char* xpath_exception::what() const throw() { return _result.error; } PUGI__FN const xpath_parse_result& xpath_exception::result() const { return _result; } #endif PUGI__FN xpath_node::xpath_node() { } PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) { } PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) { } PUGI__FN xml_node xpath_node::node() const { return _attribute ? xml_node() : _node; } PUGI__FN xml_attribute xpath_node::attribute() const { return _attribute; } PUGI__FN xml_node xpath_node::parent() const { return _attribute ? _node : _node.parent(); } PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) { } PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const { return (_node || _attribute) ? unspecified_bool_xpath_node : 0; } PUGI__FN bool xpath_node::operator!() const { return !(_node || _attribute); } PUGI__FN bool xpath_node::operator==(const xpath_node& n) const { return _node == n._node && _attribute == n._attribute; } PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const { return _node != n._node || _attribute != n._attribute; } #ifdef __BORLANDC__ PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) { return (bool)lhs && rhs; } PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) { return (bool)lhs || rhs; } #endif PUGI__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__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__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage) { } PUGI__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__FN xpath_node_set::~xpath_node_set() { if (_begin != _storage) impl::xml_memory::deallocate(_begin); } PUGI__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__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__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage) { _move(rhs); } PUGI__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__FN xpath_node_set::type_t xpath_node_set::type() const { return _type; } PUGI__FN size_t xpath_node_set::size() const { return _end - _begin; } PUGI__FN bool xpath_node_set::empty() const { return _begin == _end; } PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const { assert(index < size()); return _begin[index]; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const { return _begin; } PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const { return _end; } PUGI__FN void xpath_node_set::sort(bool reverse) { _type = impl::xpath_sort(_begin, _end, _type, reverse); } PUGI__FN xpath_node xpath_node_set::first() const { return impl::xpath_first(_begin, _end, _type); } PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) { } PUGI__FN xpath_parse_result::operator bool() const { return error == 0; } PUGI__FN const char* xpath_parse_result::description() const { return error ? error : "No error"; } PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) { } PUGI__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__FN xpath_value_type xpath_variable::type() const { return _type; } PUGI__FN bool xpath_variable::get_boolean() const { return (_type == xpath_type_boolean) ? static_cast(this)->value : false; } PUGI__FN double xpath_variable::get_number() const { return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); } PUGI__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__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__FN bool xpath_variable::set(bool value) { if (_type != xpath_type_boolean) return false; static_cast(this)->value = value; return true; } PUGI__FN bool xpath_variable::set(double value) { if (_type != xpath_type_number) return false; static_cast(this)->value = value; return true; } PUGI__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__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__FN xpath_variable_set::xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _data[i] = 0; } PUGI__FN xpath_variable_set::~xpath_variable_set() { for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) _destroy(_data[i]); } PUGI__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__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__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__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__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__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__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__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__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__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__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__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__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__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__FN xpath_variable* xpath_variable_set::get(const char_t* name) { return _find(name); } PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const { return _find(name); } PUGI__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__FN xpath_query::xpath_query(): _impl(0) { } PUGI__FN xpath_query::~xpath_query() { if (_impl) impl::xpath_query_impl::destroy(static_cast(_impl)); } #ifdef PUGIXML_HAS_MOVE PUGI__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__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__FN xpath_value_type xpath_query::return_type() const { if (!_impl) return xpath_type_none; return static_cast(_impl)->root->rettype(); } PUGI__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__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__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__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__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__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__FN const xpath_parse_result& xpath_query::result() const { return _result; } PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) { } PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const { return _impl ? unspecified_bool_xpath_query : 0; } PUGI__FN bool xpath_query::operator!() const { return !_impl; } PUGI__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__FN xpath_node xml_node::select_node(const xpath_query& query) const { return query.evaluate_node(*this); } PUGI__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__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const { return query.evaluate_node_set(*this); } PUGI__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__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__NO_INLINE #undef PUGI__UNLIKELY #undef PUGI__STATIC_ASSERT #undef PUGI__DMC_VOLATILE #undef PUGI__UNSIGNED_OVERFLOW #undef PUGI__MSVC_CRT_VERSION #undef PUGI__SNPRINTF #undef PUGI__NS_BEGIN #undef PUGI__NS_END #undef PUGI__FN #undef PUGI__FN_NO_INLINE #undef PUGI__GETHEADER_IMPL #undef PUGI__GETPAGE_IMPL #undef PUGI__GETPAGE #undef PUGI__NODETYPE #undef PUGI__IS_CHARTYPE_IMPL #undef PUGI__IS_CHARTYPE #undef PUGI__IS_CHARTYPEX #undef PUGI__ENDSWITH #undef PUGI__SKIPWS #undef PUGI__OPTSET #undef PUGI__PUSHNODE #undef PUGI__POPNODE #undef PUGI__SCANFOR #undef PUGI__SCANWHILE #undef PUGI__SCANWHILE_UNROLL #undef PUGI__ENDSEG #undef PUGI__THROW_ERROR #undef PUGI__CHECK_ERROR #endif /** * Copyright (c) 2006-2022 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-62.0.2/pugixml.hpp000066400000000000000000001503121433400105300151650ustar00rootroot00000000000000/** * pugixml parser - version 1.12 * -------------------------------------------------------- * Copyright (C) 2006-2022, 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 PUGIXML_VERSION // 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 # define PUGIXML_VERSION 1110 #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 # 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; // 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_value(const char_t* rhs); // 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_value(const char_t* rhs); // 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 children(const char_t* name) const; xml_object_range attributes() 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); // 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 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-2022 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-62.0.2/qioapi.cpp000066400000000000000000000256641433400105300147700ustar00rootroot00000000000000/* ioapi.c -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01e, February 12th, 2005 Copyright (C) 1998-2005 Gilles Vollant Modified by Sergey A. Tachenov to integrate with Qt. */ #include #include #include #include "zlib.h" #include "ioapi.h" #include "quazip_global.h" #include #if (QT_VERSION_MAJOR >= 5 && QT_VERSION_MINOR >=1) #define QUAZIP_QSAVEFILE_BUG_WORKAROUND #endif #ifdef QUAZIP_QSAVEFILE_BUG_WORKAROUND #include #endif /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,voidpf file,int mode) { if (pfilefunc->zfile_func64.zopen64_file != NULL) return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,file,mode); else { return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,file,mode); } } int call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); else { uLong offsetTruncated = (uLong)offset; if (offsetTruncated != offset) return -1; else return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); } } ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); else { uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); if ((tell_uLong) == ((uLong)-1)) return (ZPOS64_T)-1; else return tell_uLong; } } /// @cond internal struct QIODevice_descriptor { // Position only used for writing to sequential devices. qint64 pos; inline QIODevice_descriptor(): pos(0) {} }; /// @endcond voidpf ZCALLBACK qiodevice_open_file_func ( voidpf opaque, voidpf file, int mode) { QIODevice_descriptor *d = reinterpret_cast(opaque); QIODevice *iodevice = reinterpret_cast(file); QIODevice::OpenMode desiredMode; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) desiredMode = QIODevice::ReadOnly; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) desiredMode = QIODevice::ReadWrite; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) desiredMode = QIODevice::WriteOnly; if (iodevice->isOpen()) { if ((iodevice->openMode() & desiredMode) == desiredMode) { if (desiredMode != QIODevice::WriteOnly && iodevice->isSequential()) { // We can use sequential devices only for writing. delete d; return NULL; } else { if ((desiredMode & QIODevice::WriteOnly) != 0) { // open for writing, need to seek existing device if (!iodevice->isSequential()) { iodevice->seek(0); } else { d->pos = iodevice->pos(); } } } return iodevice; } else { delete d; return NULL; } } iodevice->open(desiredMode); if (iodevice->isOpen()) { if (desiredMode != QIODevice::WriteOnly && iodevice->isSequential()) { // We can use sequential devices only for writing. iodevice->close(); delete d; return NULL; } else { return iodevice; } } else { delete d; return NULL; } } uLong ZCALLBACK qiodevice_read_file_func ( voidpf opaque, voidpf stream, void* buf, uLong size) { QIODevice_descriptor *d = reinterpret_cast(opaque); QIODevice *iodevice = reinterpret_cast(stream); qint64 ret64 = iodevice->read((char*)buf,size); uLong ret; ret = (uLong) ret64; if (ret64 != -1) { d->pos += ret64; } return ret; } uLong ZCALLBACK qiodevice_write_file_func ( voidpf opaque, voidpf stream, const void* buf, uLong size) { QIODevice_descriptor *d = reinterpret_cast(opaque); QIODevice *iodevice = reinterpret_cast(stream); uLong ret; qint64 ret64 = iodevice->write((char*)buf,size); if (ret64 != -1) { d->pos += ret64; } ret = (uLong) ret64; return ret; } uLong ZCALLBACK qiodevice_tell_file_func ( voidpf opaque, voidpf stream) { QIODevice_descriptor *d = reinterpret_cast(opaque); QIODevice *iodevice = reinterpret_cast(stream); uLong ret; qint64 ret64; if (iodevice->isSequential()) { ret64 = d->pos; } else { ret64 = iodevice->pos(); } ret = static_cast(ret64); return ret; } ZPOS64_T ZCALLBACK qiodevice64_tell_file_func ( voidpf opaque, voidpf stream) { QIODevice_descriptor *d = reinterpret_cast(opaque); QIODevice *iodevice = reinterpret_cast(stream); qint64 ret; if (iodevice->isSequential()) { ret = d->pos; } else { ret = iodevice->pos(); } return static_cast(ret); } int ZCALLBACK qiodevice_seek_file_func ( voidpf /*opaque UNUSED*/, voidpf stream, uLong offset, int origin) { QIODevice *iodevice = reinterpret_cast(stream); if (iodevice->isSequential()) { if (origin == ZLIB_FILEFUNC_SEEK_END && offset == 0) { // sequential devices are always at end (needed in mdAppend) return 0; } else { qWarning("qiodevice_seek_file_func() called for sequential device"); return -1; } } uLong qiodevice_seek_result=0; int ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : qiodevice_seek_result = ((QIODevice*)stream)->pos() + offset; break; case ZLIB_FILEFUNC_SEEK_END : qiodevice_seek_result = ((QIODevice*)stream)->size() - offset; break; case ZLIB_FILEFUNC_SEEK_SET : qiodevice_seek_result = offset; break; default: return -1; } ret = !iodevice->seek(qiodevice_seek_result); return ret; } int ZCALLBACK qiodevice64_seek_file_func ( voidpf /*opaque UNUSED*/, voidpf stream, ZPOS64_T offset, int origin) { QIODevice *iodevice = reinterpret_cast(stream); if (iodevice->isSequential()) { if (origin == ZLIB_FILEFUNC_SEEK_END && offset == 0) { // sequential devices are always at end (needed in mdAppend) return 0; } else { qWarning("qiodevice_seek_file_func() called for sequential device"); return -1; } } qint64 qiodevice_seek_result=0; int ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : qiodevice_seek_result = ((QIODevice*)stream)->pos() + offset; break; case ZLIB_FILEFUNC_SEEK_END : qiodevice_seek_result = ((QIODevice*)stream)->size() - offset; break; case ZLIB_FILEFUNC_SEEK_SET : qiodevice_seek_result = offset; break; default: return -1; } ret = !iodevice->seek(qiodevice_seek_result); return ret; } int ZCALLBACK qiodevice_close_file_func ( voidpf opaque, voidpf stream) { QIODevice_descriptor *d = reinterpret_cast(opaque); delete d; QIODevice *device = reinterpret_cast(stream); #ifdef QUAZIP_QSAVEFILE_BUG_WORKAROUND // QSaveFile terribly breaks the is-a idiom: // it IS a QIODevice, but it is NOT compatible with it: close() is private QSaveFile *file = qobject_cast(device); if (file != NULL) { // We have to call the ugly commit() instead: return file->commit() ? 0 : -1; } #endif device->close(); return 0; } int ZCALLBACK qiodevice_fakeclose_file_func ( voidpf opaque, voidpf /*stream*/) { QIODevice_descriptor *d = reinterpret_cast(opaque); delete d; return 0; } int ZCALLBACK qiodevice_error_file_func ( voidpf /*opaque UNUSED*/, voidpf /*stream UNUSED*/) { // can't check for error due to the QIODevice API limitation return 0; } void fill_qiodevice_filefunc ( zlib_filefunc_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen_file = qiodevice_open_file_func; pzlib_filefunc_def->zread_file = qiodevice_read_file_func; pzlib_filefunc_def->zwrite_file = qiodevice_write_file_func; pzlib_filefunc_def->ztell_file = qiodevice_tell_file_func; pzlib_filefunc_def->zseek_file = qiodevice_seek_file_func; pzlib_filefunc_def->zclose_file = qiodevice_close_file_func; pzlib_filefunc_def->zerror_file = qiodevice_error_file_func; pzlib_filefunc_def->opaque = new QIODevice_descriptor; } void fill_qiodevice64_filefunc ( zlib_filefunc64_def* pzlib_filefunc_def) { // Open functions are the same for Qt. pzlib_filefunc_def->zopen64_file = qiodevice_open_file_func; pzlib_filefunc_def->zread_file = qiodevice_read_file_func; pzlib_filefunc_def->zwrite_file = qiodevice_write_file_func; pzlib_filefunc_def->ztell64_file = qiodevice64_tell_file_func; pzlib_filefunc_def->zseek64_file = qiodevice64_seek_file_func; pzlib_filefunc_def->zclose_file = qiodevice_close_file_func; pzlib_filefunc_def->zerror_file = qiodevice_error_file_func; pzlib_filefunc_def->opaque = new QIODevice_descriptor; pzlib_filefunc_def->zfakeclose_file = qiodevice_fakeclose_file_func; } void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) { p_filefunc64_32->zfile_func64.zopen64_file = NULL; p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; p_filefunc64_32->zfile_func64.ztell64_file = NULL; p_filefunc64_32->zfile_func64.zseek64_file = NULL; p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; p_filefunc64_32->zfile_func64.zfakeclose_file = NULL; p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; } tea-qt-62.0.2/quagzipfile.cpp000066400000000000000000000102131433400105300160060ustar00rootroot00000000000000/* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include #include "quagzipfile.h" /// \cond internal class QuaGzipFilePrivate { friend class QuaGzipFile; QString fileName; gzFile gzd; inline QuaGzipFilePrivate(): gzd(NULL) {} inline QuaGzipFilePrivate(const QString &fileName): fileName(fileName), gzd(NULL) {} template bool open(FileId id, QIODevice::OpenMode mode, QString &error); gzFile open(int fd, const char *modeString); gzFile open(const QString &name, const char *modeString); }; gzFile QuaGzipFilePrivate::open(const QString &name, const char *modeString) { return gzopen(QFile::encodeName(name).constData(), modeString); } gzFile QuaGzipFilePrivate::open(int fd, const char *modeString) { return gzdopen(fd, modeString); } template bool QuaGzipFilePrivate::open(FileId id, QIODevice::OpenMode mode, QString &error) { char modeString[2]; modeString[0] = modeString[1] = '\0'; if ((mode & QIODevice::Append) != 0) { error = "QIODevice::Append is not supported for GZIP"; return false; } if ((mode & QIODevice::ReadOnly) != 0 && (mode & QIODevice::WriteOnly) != 0) { error = "Opening gzip for both reading and writing is not supported"; return false; } else if ((mode & QIODevice::ReadOnly) != 0) { modeString[0] = 'r'; } else if ((mode & QIODevice::WriteOnly) != 0) { modeString[0] = 'w'; } else { error = "You can open a gzip either for reading or for writing. Which is it?"; return false; } gzd = open(id, modeString); if (gzd == NULL) { error = "Could not gzopen() file"; return false; } return true; } /// \endcond QuaGzipFile::QuaGzipFile(): d(new QuaGzipFilePrivate()) { } QuaGzipFile::QuaGzipFile(QObject *parent): QIODevice(parent), d(new QuaGzipFilePrivate()) { } QuaGzipFile::QuaGzipFile(const QString &fileName, QObject *parent): QIODevice(parent), d(new QuaGzipFilePrivate(fileName)) { } QuaGzipFile::~QuaGzipFile() { if (isOpen()) // close(); { QIODevice::close(); gzclose(d->gzd); }; delete d; } void QuaGzipFile::setFileName(const QString& fileName) { d->fileName = fileName; } QString QuaGzipFile::getFileName() const { return d->fileName; } bool QuaGzipFile::isSequential() const { return true; } bool QuaGzipFile::open(QIODevice::OpenMode mode) { QString error; if (!d->open(d->fileName, mode, error)) { setErrorString(error); return false; } return QIODevice::open(mode); } bool QuaGzipFile::open(int fd, QIODevice::OpenMode mode) { QString error; if (!d->open(fd, mode, error)) { setErrorString(error); return false; } return QIODevice::open(mode); } bool QuaGzipFile::flush() { return gzflush(d->gzd, Z_SYNC_FLUSH) == Z_OK; } void QuaGzipFile::close() { QIODevice::close(); gzclose(d->gzd); } qint64 QuaGzipFile::readData(char *data, qint64 maxSize) { return gzread(d->gzd, (voidp)data, (unsigned)maxSize); } qint64 QuaGzipFile::writeData(const char *data, qint64 maxSize) { if (maxSize == 0) return 0; int written = gzwrite(d->gzd, (voidp)data, (unsigned)maxSize); if (written == 0) return -1; else return written; } tea-qt-62.0.2/quagzipfile.h000066400000000000000000000071521433400105300154630ustar00rootroot00000000000000#ifndef QUAZIP_QUAGZIPFILE_H #define QUAZIP_QUAGZIPFILE_H /* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include #include "quazip_global.h" #include class QuaGzipFilePrivate; /// GZIP file /* This class is a wrapper around GZIP file access functions in zlib. Unlike QuaZip classes, it doesn't allow reading from a GZIP file opened as QIODevice, for example, if your GZIP file is in QBuffer. It only provides QIODevice access to a GZIP file contents, but the GZIP file itself must be identified by its name on disk or by descriptor id. */ class QuaGzipFile: public QIODevice { Q_OBJECT public: /// Empty constructor. /** Must call setFileName() before trying to open. */ QuaGzipFile(); /// Empty constructor with a parent. /** Must call setFileName() before trying to open. \param parent The parent object, as per QObject logic. */ QuaGzipFile(QObject *parent); /// Constructor. /** \param fileName The name of the GZIP file. \param parent The parent object, as per QObject logic. */ QuaGzipFile(const QString &fileName, QObject *parent = NULL); /// Destructor. virtual ~QuaGzipFile(); /// Sets the name of the GZIP file to be opened. void setFileName(const QString& fileName); /// Returns the name of the GZIP file. QString getFileName() const; /// Returns true. /** Strictly speaking, zlib supports seeking for GZIP files, but it is poorly implemented, because there is no way to implement it properly. For reading, seeking backwards is very slow, and for writing, it is downright impossible. Therefore, QuaGzipFile does not support seeking at all. */ virtual bool isSequential() const; /// Opens the file. /** \param mode Can be either QIODevice::Write or QIODevice::Read. ReadWrite and Append aren't supported. */ /* virtual*/ bool open(QIODevice::OpenMode mode); /// Opens the file. /** \overload \param fd The file descriptor to read/write the GZIP file from/to. \param mode Can be either QIODevice::Write or QIODevice::Read. ReadWrite and Append aren't supported. */ /*virtual*/ bool open(int fd, QIODevice::OpenMode mode); /// Flushes data to file. /** The data is written using Z_SYNC_FLUSH mode. Doesn't make any sense when reading. */ virtual bool flush(); /// Closes the file. /*virtual*/ void close(); protected: /// Implementation of QIODevice::readData(). /*virtual*/ qint64 readData(char *data, qint64 maxSize); /// Implementation of QIODevice::writeData(). /*virtual*/ qint64 writeData(const char *data, qint64 maxSize); private: // not implemented by design to disable copy QuaGzipFile(const QuaGzipFile &that); QuaGzipFile& operator=(const QuaGzipFile &that); QuaGzipFilePrivate *d; }; #endif // QUAZIP_QUAGZIPFILE_H tea-qt-62.0.2/quaziodevice.cpp000066400000000000000000000216461433400105300161720ustar00rootroot00000000000000/* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include "quaziodevice.h" #define QUAZIO_INBUFSIZE 4096 #define QUAZIO_OUTBUFSIZE 4096 /// \cond internal class QuaZIODevicePrivate { friend class QuaZIODevice; QuaZIODevicePrivate(QIODevice *io); ~QuaZIODevicePrivate(); QIODevice *io; z_stream zins; z_stream zouts; char *inBuf; int inBufPos; int inBufSize; char *outBuf; int outBufPos; int outBufSize; bool zBufError; bool atEnd; int doFlush(QString &error); }; QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io): io(io), inBuf(NULL), inBufPos(0), inBufSize(0), outBuf(NULL), outBufPos(0), outBufSize(0), zBufError(false), atEnd(false) { zins.zalloc = (alloc_func) NULL; zins.zfree = (free_func) NULL; zins.opaque = NULL; zouts.zalloc = (alloc_func) NULL; zouts.zfree = (free_func) NULL; zouts.opaque = NULL; inBuf = new char[QUAZIO_INBUFSIZE]; outBuf = new char[QUAZIO_OUTBUFSIZE]; #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.setFileName("debug.out"); debug.open(QIODevice::WriteOnly); #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.setFileName("debug.in"); indebug.open(QIODevice::WriteOnly); #endif } QuaZIODevicePrivate::~QuaZIODevicePrivate() { #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.close(); #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.close(); #endif if (inBuf != NULL) delete[] inBuf; if (outBuf != NULL) delete[] outBuf; } int QuaZIODevicePrivate::doFlush(QString &error) { int flushed = 0; while (outBufPos < outBufSize) { int more = io->write(outBuf + outBufPos, outBufSize - outBufPos); if (more == -1) { error = io->errorString(); return -1; } if (more == 0) break; outBufPos += more; flushed += more; } if (outBufPos == outBufSize) { outBufPos = outBufSize = 0; } return flushed; } /// \endcond #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT #include static QFile debug; #endif #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT #include static QFile indebug; #endif QuaZIODevice::QuaZIODevice(QIODevice *io, QObject *parent): QIODevice(parent), d(new QuaZIODevicePrivate(io)) { connect(io, SIGNAL(readyRead()), SIGNAL(readyRead())); } QuaZIODevice::~QuaZIODevice() { if (isOpen()) QuaZIODevice::close(); delete d; } QIODevice *QuaZIODevice::getIoDevice() const { return d->io; } bool QuaZIODevice::open(QIODevice::OpenMode mode) { if ((mode & QIODevice::Append) != 0) { setErrorString("QIODevice::Append is not supported for QuaZIODevice"); return false; } if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) { setErrorString("QIODevice::ReadWrite is not supported for QuaZIODevice"); return false; } if ((mode & QIODevice::ReadOnly) != 0) { if (inflateInit(&d->zins) != Z_OK) { setErrorString(d->zins.msg); return false; } } if ((mode & QIODevice::WriteOnly) != 0) { if (deflateInit(&d->zouts, Z_DEFAULT_COMPRESSION) != Z_OK) { setErrorString(d->zouts.msg); return false; } } return QIODevice::open(mode); } void QuaZIODevice::close() { if ((openMode() & QIODevice::ReadOnly) != 0) { if (inflateEnd(&d->zins) != Z_OK) { setErrorString(d->zins.msg); } } if ((openMode() & QIODevice::WriteOnly) != 0) { QuaZIODevice::flush(); if (deflateEnd(&d->zouts) != Z_OK) { setErrorString(d->zouts.msg); } } QIODevice::close(); } qint64 QuaZIODevice::readData(char *data, qint64 maxSize) { int read = 0; while (read < maxSize) { if (d->inBufPos == d->inBufSize) { d->inBufPos = 0; d->inBufSize = d->io->read(d->inBuf, QUAZIO_INBUFSIZE); if (d->inBufSize == -1) { d->inBufSize = 0; setErrorString(d->io->errorString()); return -1; } if (d->inBufSize == 0) break; } while (read < maxSize && d->inBufPos < d->inBufSize) { d->zins.next_in = (Bytef *) (d->inBuf + d->inBufPos); d->zins.avail_in = d->inBufSize - d->inBufPos; d->zins.next_out = (Bytef *) (data + read); d->zins.avail_out = (uInt) (maxSize - read); // hope it's less than 2GB int more = 0; switch (inflate(&d->zins, Z_SYNC_FLUSH)) { case Z_OK: read = (char *) d->zins.next_out - data; d->inBufPos = (char *) d->zins.next_in - d->inBuf; break; case Z_STREAM_END: read = (char *) d->zins.next_out - data; d->inBufPos = (char *) d->zins.next_in - d->inBuf; d->atEnd = true; return read; case Z_BUF_ERROR: // this should never happen, but just in case if (!d->zBufError) { qWarning("Z_BUF_ERROR detected with %d/%d in/out, weird", d->zins.avail_in, d->zins.avail_out); d->zBufError = true; } memmove(d->inBuf, d->inBuf + d->inBufPos, d->inBufSize - d->inBufPos); d->inBufSize -= d->inBufPos; d->inBufPos = 0; more = d->io->read(d->inBuf + d->inBufSize, QUAZIO_INBUFSIZE - d->inBufSize); if (more == -1) { setErrorString(d->io->errorString()); return -1; } if (more == 0) return read; d->inBufSize += more; break; default: setErrorString(QString::fromLocal8Bit(d->zins.msg)); return -1; } } } #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT indebug.write(data, read); #endif return read; } qint64 QuaZIODevice::writeData(const char *data, qint64 maxSize) { int written = 0; QString error; if (d->doFlush(error) == -1) { setErrorString(error); return -1; } while (written < maxSize) { // there is some data waiting in the output buffer if (d->outBufPos < d->outBufSize) return written; d->zouts.next_in = (Bytef *) (data + written); d->zouts.avail_in = (uInt) (maxSize - written); // hope it's less than 2GB d->zouts.next_out = (Bytef *) d->outBuf; d->zouts.avail_out = QUAZIO_OUTBUFSIZE; switch (deflate(&d->zouts, Z_NO_FLUSH)) { case Z_OK: written = (char *) d->zouts.next_in - data; d->outBufSize = (char *) d->zouts.next_out - d->outBuf; break; default: setErrorString(QString::fromLocal8Bit(d->zouts.msg)); return -1; } if (d->doFlush(error) == -1) { setErrorString(error); return -1; } } #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT debug.write(data, written); #endif return written; } bool QuaZIODevice::flush() { QString error; if (d->doFlush(error) < 0) { setErrorString(error); return false; } // can't flush buffer, some data is still waiting if (d->outBufPos < d->outBufSize) return true; Bytef c = 0; d->zouts.next_in = &c; // fake input buffer d->zouts.avail_in = 0; // of zero size do { d->zouts.next_out = (Bytef *) d->outBuf; d->zouts.avail_out = QUAZIO_OUTBUFSIZE; switch (deflate(&d->zouts, Z_SYNC_FLUSH)) { case Z_OK: d->outBufSize = (char *) d->zouts.next_out - d->outBuf; if (d->doFlush(error) < 0) { setErrorString(error); return false; } if (d->outBufPos < d->outBufSize) return true; break; case Z_BUF_ERROR: // nothing to write? return true; default: setErrorString(QString::fromLocal8Bit(d->zouts.msg)); return false; } } while (d->zouts.avail_out == 0); return true; } bool QuaZIODevice::isSequential() const { return true; } bool QuaZIODevice::atEnd() const { // Here we MUST check QIODevice::bytesAvailable() because WE // might have reached the end, but QIODevice didn't-- // it could have simply pre-buffered all remaining data. return (openMode() == NotOpen) || (QIODevice::bytesAvailable() == 0 && d->atEnd); } qint64 QuaZIODevice::bytesAvailable() const { // If we haven't recevied Z_STREAM_END, it means that // we have at least one more input byte available. // Plus whatever QIODevice has buffered. return (d->atEnd ? 0 : 1) + QIODevice::bytesAvailable(); } tea-qt-62.0.2/quaziodevice.h000066400000000000000000000065551433400105300156410ustar00rootroot00000000000000#ifndef QUAZIP_QUAZIODEVICE_H #define QUAZIP_QUAZIODEVICE_H /* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include #include "quazip_global.h" #include class QuaZIODevicePrivate; /// A class to compress/decompress QIODevice. /** This class can be used to compress any data written to QIODevice or decompress it back. Compressing data sent over a QTcpSocket is a good example. */ class QuaZIODevice: public QIODevice { Q_OBJECT public: /// Constructor. /** \param io The QIODevice to read/write. \param parent The parent object, as per QObject logic. */ QuaZIODevice(QIODevice *io, QObject *parent = NULL); /// Destructor. ~QuaZIODevice(); /// Flushes data waiting to be written. /** Unfortunately, as QIODevice doesn't support flush() by itself, the only thing this method does is write the compressed data into the device using Z_SYNC_FLUSH mode. If you need the compressed data to actually be flushed from the buffer of the underlying QIODevice, you need to call its flush() method as well, providing it supports it (like QTcpSocket does). Example: \code QuaZIODevice dev(&sock); dev.open(QIODevice::Write); dev.write(yourDataGoesHere); dev.flush(); sock->flush(); // this actually sends data to network \endcode This may change in the future versions of QuaZIP by implementing an ugly hack: trying to cast the QIODevice using qobject_cast to known flush()-supporting subclasses, and calling flush if the resulting pointer is not zero. */ virtual bool flush(); /// Opens the device. /** \param mode Neither QIODevice::ReadWrite nor QIODevice::Append are not supported. */ /* virtual*/ bool open(QIODevice::OpenMode mode); /// Closes this device, but not the underlying one. /** The underlying QIODevice is not closed in case you want to write something else to it. */ /*virtual */void close(); /// Returns the underlying device. QIODevice *getIoDevice() const; /// Returns true. /* virtual*/ bool isSequential() const; /// Returns true iff the end of the compressed stream is reached. /* virtual */bool atEnd() const; /// Returns the number of the bytes buffered. /* virtual */qint64 bytesAvailable() const; protected: /// Implementation of QIODevice::readData(). /* virtual */qint64 readData(char *data, qint64 maxSize); /// Implementation of QIODevice::writeData(). /* virtual */qint64 writeData(const char *data, qint64 maxSize); private: QuaZIODevicePrivate *d; }; #endif // QUAZIP_QUAZIODEVICE_H tea-qt-62.0.2/quazip.cpp000066400000000000000000000524571433400105300150170ustar00rootroot00000000000000/* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include #include #include #include "quazip.h" /// All the internal stuff for the QuaZip class. /** \internal This class keeps all the private stuff for the QuaZip class so it can be changed without breaking binary compatibility, according to the Pimpl idiom. */ class QuaZipPrivate { friend class QuaZip; private: Q_DISABLE_COPY(QuaZipPrivate) /// The pointer to the corresponding QuaZip instance. QuaZip *q; /// The codec for file names. QTextCodec *fileNameCodec; /// The codec for comments. QTextCodec *commentCodec; /// The archive file name. QString zipName; /// The device to access the archive. QIODevice *ioDevice; /// The global comment. QString comment; /// The open mode. QuaZip::Mode mode; union { /// The internal handle for UNZIP modes. unzFile unzFile_f; /// The internal handle for ZIP modes. zipFile zipFile_f; }; /// Whether a current file is set. bool hasCurrentFile_f; /// The last error. int zipError; /// Whether \ref QuaZip::setDataDescriptorWritingEnabled() "the data descriptor writing mode" is enabled. bool dataDescriptorWritingEnabled; /// The zip64 mode. bool zip64; /// The auto-close flag. bool autoClose; inline QTextCodec *getDefaultFileNameCodec() { if (defaultFileNameCodec == NULL) { return QTextCodec::codecForLocale(); } else { return defaultFileNameCodec; } } /// The constructor for the corresponding QuaZip constructor. inline QuaZipPrivate(QuaZip *q1): q(q1), fileNameCodec(getDefaultFileNameCodec()), commentCodec(QTextCodec::codecForLocale()), ioDevice(NULL), mode(QuaZip::mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false), autoClose(true) { unzFile_f = NULL; zipFile_f = NULL; lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } /// The constructor for the corresponding QuaZip constructor. inline QuaZipPrivate(QuaZip *q1, const QString &zipName): q(q1), fileNameCodec(getDefaultFileNameCodec()), commentCodec(QTextCodec::codecForLocale()), zipName(zipName), ioDevice(NULL), mode(QuaZip::mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false), autoClose(true) { unzFile_f = NULL; zipFile_f = NULL; lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } /// The constructor for the corresponding QuaZip constructor. inline QuaZipPrivate(QuaZip *q1, QIODevice *ioDevice): q(q1), fileNameCodec(getDefaultFileNameCodec()), commentCodec(QTextCodec::codecForLocale()), ioDevice(ioDevice), mode(QuaZip::mdNotOpen), hasCurrentFile_f(false), zipError(UNZ_OK), dataDescriptorWritingEnabled(true), zip64(false), autoClose(true) { unzFile_f = NULL; zipFile_f = NULL; lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } /// Returns either a list of file names or a list of QuaZipFileInfo. template bool getFileInfoList(QList *result) const; /// Stores map of filenames and file locations for unzipping inline void clearDirectoryMap(); inline void addCurrentFileToDirectoryMap(const QString &fileName); bool goToFirstUnmappedFile(); QHash directoryCaseSensitive; QHash directoryCaseInsensitive; unz64_file_pos lastMappedDirectoryEntry; static QTextCodec *defaultFileNameCodec; }; QTextCodec *QuaZipPrivate::defaultFileNameCodec = NULL; void QuaZipPrivate::clearDirectoryMap() { directoryCaseInsensitive.clear(); directoryCaseSensitive.clear(); lastMappedDirectoryEntry.num_of_file = 0; lastMappedDirectoryEntry.pos_in_zip_directory = 0; } void QuaZipPrivate::addCurrentFileToDirectoryMap(const QString &fileName) { if (!hasCurrentFile_f || fileName.isEmpty()) { return; } // Adds current file to filename map as fileName unz64_file_pos fileDirectoryPos; unzGetFilePos64(unzFile_f, &fileDirectoryPos); directoryCaseSensitive.insert(fileName, fileDirectoryPos); // Only add lowercase to directory map if not already there // ensures only map the first one seen QString lower = fileName.toLower(); if (!directoryCaseInsensitive.contains(lower)) directoryCaseInsensitive.insert(lower, fileDirectoryPos); // Mark last one if (fileDirectoryPos.pos_in_zip_directory > lastMappedDirectoryEntry.pos_in_zip_directory) lastMappedDirectoryEntry = fileDirectoryPos; } bool QuaZipPrivate::goToFirstUnmappedFile() { zipError = UNZ_OK; if (mode != QuaZip::mdUnzip) { qWarning("QuaZipPrivate::goToNextUnmappedFile(): ZIP is not open in mdUnzip mode"); return false; } // If not mapped anything, go to beginning if (lastMappedDirectoryEntry.pos_in_zip_directory == 0) { unzGoToFirstFile(unzFile_f); } else { // Goto the last one mapped, plus one unzGoToFilePos64(unzFile_f, &lastMappedDirectoryEntry); unzGoToNextFile(unzFile_f); } hasCurrentFile_f=zipError==UNZ_OK; if(zipError==UNZ_END_OF_LIST_OF_FILE) zipError=UNZ_OK; return hasCurrentFile_f; } QuaZip::QuaZip(): p(new QuaZipPrivate(this)) { } QuaZip::QuaZip(const QString& zipName): p(new QuaZipPrivate(this, zipName)) { } QuaZip::QuaZip(QIODevice *ioDevice): p(new QuaZipPrivate(this, ioDevice)) { } QuaZip::~QuaZip() { if(isOpen()) close(); delete p; } bool QuaZip::open(Mode mode, zlib_filefunc_def* ioApi) { p->zipError=UNZ_OK; if(isOpen()) { qWarning("QuaZip::open(): ZIP already opened"); return false; } QIODevice *ioDevice = p->ioDevice; if (ioDevice == NULL) { if (p->zipName.isEmpty()) { qWarning("QuaZip::open(): set either ZIP file name or IO device first"); return false; } else { ioDevice = new QFile(p->zipName); } } unsigned flags = 0; switch(mode) { case mdUnzip: if (ioApi == NULL) { if (p->autoClose) flags |= UNZ_AUTO_CLOSE; p->unzFile_f=unzOpenInternal(ioDevice, NULL, 1, flags); } else { // QuaZIP pre-zip64 compatibility mode p->unzFile_f=unzOpen2(ioDevice, ioApi); if (p->unzFile_f != NULL) { if (p->autoClose) { unzSetFlags(p->unzFile_f, UNZ_AUTO_CLOSE); } else { unzClearFlags(p->unzFile_f, UNZ_AUTO_CLOSE); } } } if(p->unzFile_f!=NULL) { if (ioDevice->isSequential()) { unzClose(p->unzFile_f); if (!p->zipName.isEmpty()) delete ioDevice; qWarning("QuaZip::open(): " "only mdCreate can be used with " "sequential devices"); return false; } p->mode=mode; p->ioDevice = ioDevice; return true; } else { p->zipError=UNZ_OPENERROR; if (!p->zipName.isEmpty()) delete ioDevice; return false; } case mdCreate: case mdAppend: case mdAdd: if (ioApi == NULL) { if (p->autoClose) flags |= ZIP_AUTO_CLOSE; if (p->dataDescriptorWritingEnabled) flags |= ZIP_WRITE_DATA_DESCRIPTOR; p->zipFile_f=zipOpen3(ioDevice, mode==mdCreate?APPEND_STATUS_CREATE: mode==mdAppend?APPEND_STATUS_CREATEAFTER: APPEND_STATUS_ADDINZIP, NULL, NULL, flags); } else { // QuaZIP pre-zip64 compatibility mode p->zipFile_f=zipOpen2(ioDevice, mode==mdCreate?APPEND_STATUS_CREATE: mode==mdAppend?APPEND_STATUS_CREATEAFTER: APPEND_STATUS_ADDINZIP, NULL, ioApi); if (p->zipFile_f != NULL) { zipSetFlags(p->zipFile_f, flags); } } if(p->zipFile_f!=NULL) { if (ioDevice->isSequential()) { if (mode != mdCreate) { zipClose(p->zipFile_f, NULL); qWarning("QuaZip::open(): " "only mdCreate can be used with " "sequential devices"); if (!p->zipName.isEmpty()) delete ioDevice; return false; } zipSetFlags(p->zipFile_f, ZIP_SEQUENTIAL); } p->mode=mode; p->ioDevice = ioDevice; return true; } else { p->zipError=UNZ_OPENERROR; if (!p->zipName.isEmpty()) delete ioDevice; return false; } default: qWarning("QuaZip::open(): unknown mode: %d", (int)mode); if (!p->zipName.isEmpty()) delete ioDevice; return false; break; } } void QuaZip::close() { p->zipError=UNZ_OK; switch(p->mode) { case mdNotOpen: qWarning("QuaZip::close(): ZIP is not open"); return; case mdUnzip: p->zipError=unzClose(p->unzFile_f); break; case mdCreate: case mdAppend: case mdAdd: p->zipError=zipClose(p->zipFile_f, p->comment.isNull() ? NULL : p->commentCodec->fromUnicode(p->comment).constData()); break; default: qWarning("QuaZip::close(): unknown mode: %d", (int)p->mode); return; } // opened by name, need to delete the internal IO device if (!p->zipName.isEmpty()) { delete p->ioDevice; p->ioDevice = NULL; } p->clearDirectoryMap(); if(p->zipError==UNZ_OK) p->mode=mdNotOpen; } void QuaZip::setZipName(const QString& zipName) { if(isOpen()) { qWarning("QuaZip::setZipName(): ZIP is already open!"); return; } p->zipName=zipName; p->ioDevice = NULL; } void QuaZip::setIoDevice(QIODevice *ioDevice) { if(isOpen()) { qWarning("QuaZip::setIoDevice(): ZIP is already open!"); return; } p->ioDevice = ioDevice; p->zipName = QString(); } int QuaZip::getEntriesCount()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getEntriesCount(): ZIP is not open in mdUnzip mode"); return -1; } unz_global_info64 globalInfo; if((fakeThis->p->zipError=unzGetGlobalInfo64(p->unzFile_f, &globalInfo))!=UNZ_OK) return p->zipError; return (int)globalInfo.number_entry; } QString QuaZip::getComment()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getComment(): ZIP is not open in mdUnzip mode"); return QString(); } unz_global_info64 globalInfo; QByteArray comment; if((fakeThis->p->zipError=unzGetGlobalInfo64(p->unzFile_f, &globalInfo))!=UNZ_OK) return QString(); comment.resize(globalInfo.size_comment); if((fakeThis->p->zipError=unzGetGlobalComment(p->unzFile_f, comment.data(), comment.size())) < 0) return QString(); fakeThis->p->zipError = UNZ_OK; return p->commentCodec->toUnicode(comment); } bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs) { p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::setCurrentFile(): ZIP is not open in mdUnzip mode"); return false; } if(fileName.isEmpty()) { p->hasCurrentFile_f=false; return true; } // Unicode-aware reimplementation of the unzLocateFile function if(p->unzFile_f==NULL) { p->zipError=UNZ_PARAMERROR; return false; } if(fileName.length()>MAX_FILE_NAME_LENGTH) { p->zipError=UNZ_PARAMERROR; return false; } // Find the file by name bool sens = convertCaseSensitivity(cs) == Qt::CaseSensitive; QString lower, current; if(!sens) lower=fileName.toLower(); p->hasCurrentFile_f=false; // Check the appropriate Map unz64_file_pos fileDirPos; fileDirPos.pos_in_zip_directory = 0; if (sens) { if (p->directoryCaseSensitive.contains(fileName)) fileDirPos = p->directoryCaseSensitive.value(fileName); } else { if (p->directoryCaseInsensitive.contains(lower)) fileDirPos = p->directoryCaseInsensitive.value(lower); } if (fileDirPos.pos_in_zip_directory != 0) { p->zipError = unzGoToFilePos64(p->unzFile_f, &fileDirPos); p->hasCurrentFile_f = p->zipError == UNZ_OK; } if (p->hasCurrentFile_f) return p->hasCurrentFile_f; // Not mapped yet, start from where we have got to so far for(bool more=p->goToFirstUnmappedFile(); more; more=goToNextFile()) { current=getCurrentFileName(); if(current.isEmpty()) return false; if(sens) { if(current==fileName) break; } else { if(current.toLower()==lower) break; } } return p->hasCurrentFile_f; } bool QuaZip::goToFirstFile() { p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); return false; } p->zipError=unzGoToFirstFile(p->unzFile_f); p->hasCurrentFile_f=p->zipError==UNZ_OK; return p->hasCurrentFile_f; } bool QuaZip::goToNextFile() { p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::goToFirstFile(): ZIP is not open in mdUnzip mode"); return false; } p->zipError=unzGoToNextFile(p->unzFile_f); p->hasCurrentFile_f=p->zipError==UNZ_OK; if(p->zipError==UNZ_END_OF_LIST_OF_FILE) p->zipError=UNZ_OK; return p->hasCurrentFile_f; } bool QuaZip::getCurrentFileInfo(QuaZipFileInfo *info)const { QuaZipFileInfo64 info64; if (info == NULL) { // Very unlikely because of the overloads return false; } if (getCurrentFileInfo(&info64)) { info64.toQuaZipFileInfo(*info); return true; } else { return false; } } bool QuaZip::getCurrentFileInfo(QuaZipFileInfo64 *info)const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getCurrentFileInfo(): ZIP is not open in mdUnzip mode"); return false; } unz_file_info64 info_z; QByteArray fileName; QByteArray extra; QByteArray comment; if(info==NULL) return false; if(!isOpen()||!hasCurrentFile()) return false; if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, &info_z, NULL, 0, NULL, 0, NULL, 0))!=UNZ_OK) return false; fileName.resize(info_z.size_filename); extra.resize(info_z.size_file_extra); comment.resize(info_z.size_file_comment); if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, NULL, fileName.data(), fileName.size(), extra.data(), extra.size(), comment.data(), comment.size()))!=UNZ_OK) return false; info->versionCreated=info_z.version; info->versionNeeded=info_z.version_needed; info->flags=info_z.flag; info->method=info_z.compression_method; info->crc=info_z.crc; info->compressedSize=info_z.compressed_size; info->uncompressedSize=info_z.uncompressed_size; info->diskNumberStart=info_z.disk_num_start; info->internalAttr=info_z.internal_fa; info->externalAttr=info_z.external_fa; info->name=p->fileNameCodec->toUnicode(fileName); info->comment=p->commentCodec->toUnicode(comment); info->extra=extra; info->dateTime=QDateTime( QDate(info_z.tmu_date.tm_year, info_z.tmu_date.tm_mon+1, info_z.tmu_date.tm_mday), QTime(info_z.tmu_date.tm_hour, info_z.tmu_date.tm_min, info_z.tmu_date.tm_sec)); // Add to directory map p->addCurrentFileToDirectoryMap(info->name); return true; } QString QuaZip::getCurrentFileName()const { QuaZip *fakeThis=(QuaZip*)this; // non-const fakeThis->p->zipError=UNZ_OK; if(p->mode!=mdUnzip) { qWarning("QuaZip::getCurrentFileName(): ZIP is not open in mdUnzip mode"); return QString(); } if(!isOpen()||!hasCurrentFile()) return QString(); QByteArray fileName(MAX_FILE_NAME_LENGTH, 0); if((fakeThis->p->zipError=unzGetCurrentFileInfo64(p->unzFile_f, NULL, fileName.data(), fileName.size(), NULL, 0, NULL, 0))!=UNZ_OK) return QString(); QString result = p->fileNameCodec->toUnicode(fileName.constData()); if (result.isEmpty()) return result; // Add to directory map p->addCurrentFileToDirectoryMap(result); return result; } void QuaZip::setFileNameCodec(QTextCodec *fileNameCodec) { p->fileNameCodec=fileNameCodec; } void QuaZip::setFileNameCodec(const char *fileNameCodecName) { p->fileNameCodec=QTextCodec::codecForName(fileNameCodecName); } QTextCodec *QuaZip::getFileNameCodec()const { return p->fileNameCodec; } void QuaZip::setCommentCodec(QTextCodec *commentCodec) { p->commentCodec=commentCodec; } void QuaZip::setCommentCodec(const char *commentCodecName) { p->commentCodec=QTextCodec::codecForName(commentCodecName); } QTextCodec *QuaZip::getCommentCodec()const { return p->commentCodec; } QString QuaZip::getZipName() const { return p->zipName; } QIODevice *QuaZip::getIoDevice() const { if (!p->zipName.isEmpty()) // opened by name, using an internal QIODevice return NULL; return p->ioDevice; } QuaZip::Mode QuaZip::getMode()const { return p->mode; } bool QuaZip::isOpen()const { return p->mode!=mdNotOpen; } int QuaZip::getZipError() const { return p->zipError; } void QuaZip::setComment(const QString& comment) { p->comment=comment; } bool QuaZip::hasCurrentFile()const { return p->hasCurrentFile_f; } unzFile QuaZip::getUnzFile() { return p->unzFile_f; } zipFile QuaZip::getZipFile() { return p->zipFile_f; } void QuaZip::setDataDescriptorWritingEnabled(bool enabled) { p->dataDescriptorWritingEnabled = enabled; } bool QuaZip::isDataDescriptorWritingEnabled() const { return p->dataDescriptorWritingEnabled; } template TFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok); template<> QuaZipFileInfo QuaZip_getFileInfo(QuaZip *zip, bool *ok) { QuaZipFileInfo info; *ok = zip->getCurrentFileInfo(&info); return info; } template<> QuaZipFileInfo64 QuaZip_getFileInfo(QuaZip *zip, bool *ok) { QuaZipFileInfo64 info; *ok = zip->getCurrentFileInfo(&info); return info; } template<> QString QuaZip_getFileInfo(QuaZip *zip, bool *ok) { QString name = zip->getCurrentFileName(); *ok = !name.isEmpty(); return name; } template bool QuaZipPrivate::getFileInfoList(QList *result) const { QuaZipPrivate *fakeThis=const_cast(this); fakeThis->zipError=UNZ_OK; if (mode!=QuaZip::mdUnzip) { qWarning("QuaZip::getFileNameList/getFileInfoList(): " "ZIP is not open in mdUnzip mode"); return false; } QString currentFile; if (q->hasCurrentFile()) { currentFile = q->getCurrentFileName(); } if (q->goToFirstFile()) { do { bool ok; result->append(QuaZip_getFileInfo(q, &ok)); if (!ok) return false; } while (q->goToNextFile()); } if (zipError != UNZ_OK) return false; if (currentFile.isEmpty()) { if (!q->goToFirstFile()) return false; } else { if (!q->setCurrentFile(currentFile)) return false; } return true; } QStringList QuaZip::getFileNameList() const { QStringList list; if (p->getFileInfoList(&list)) return list; else return QStringList(); } QList QuaZip::getFileInfoList() const { QList list; if (p->getFileInfoList(&list)) return list; else return QList(); } QList QuaZip::getFileInfoList64() const { QList list; if (p->getFileInfoList(&list)) return list; else return QList(); } Qt::CaseSensitivity QuaZip::convertCaseSensitivity(QuaZip::CaseSensitivity cs) { if (cs == csDefault) { #ifdef Q_OS_WIN return Qt::CaseInsensitive; #else return Qt::CaseSensitive; #endif } else { return cs == csSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; } } void QuaZip::setDefaultFileNameCodec(QTextCodec *codec) { QuaZipPrivate::defaultFileNameCodec = codec; } void QuaZip::setDefaultFileNameCodec(const char *codecName) { setDefaultFileNameCodec(QTextCodec::codecForName(codecName)); } void QuaZip::setZip64Enabled(bool zip64) { p->zip64 = zip64; } bool QuaZip::isZip64Enabled() const { return p->zip64; } bool QuaZip::isAutoClose() const { return p->autoClose; } void QuaZip::setAutoClose(bool autoClose) const { p->autoClose = autoClose; } tea-qt-62.0.2/quazip.h000066400000000000000000000613151433400105300144550ustar00rootroot00000000000000#ifndef QUA_ZIP_H #define QUA_ZIP_H /* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include #include #include "zip.h" #include "unzip.h" #include "quazip_global.h" #include "quazipfileinfo.h" // just in case it will be defined in the later versions of the ZIP/UNZIP #ifndef UNZ_OPENERROR // define additional error code #define UNZ_OPENERROR -1000 #endif class QuaZipPrivate; /// ZIP archive. /** \class QuaZip quazip.h * This class implements basic interface to the ZIP archive. It can be * used to read table contents of the ZIP archive and retreiving * information about the files inside it. * * You can also use this class to open files inside archive by passing * pointer to the instance of this class to the constructor of the * QuaZipFile class. But see QuaZipFile::QuaZipFile(QuaZip*, QObject*) * for the possible pitfalls. * * This class is indended to provide interface to the ZIP subpackage of * the ZIP/UNZIP package as well as to the UNZIP subpackage. But * currently it supports only UNZIP. * * The use of this class is simple - just create instance using * constructor, then set ZIP archive file name using setFile() function * (if you did not passed the name to the constructor), then open() and * then use different functions to work with it! Well, if you are * paranoid, you may also wish to call close before destructing the * instance, to check for errors on close. * * You may also use getUnzFile() and getZipFile() functions to get the * ZIP archive handle and use it with ZIP/UNZIP package API directly. * * This class supports localized file names inside ZIP archive, but you * have to set up proper codec with setCodec() function. By default, * locale codec will be used, which is probably ok for UNIX systems, but * will almost certainly fail with ZIP archives created in Windows. This * is because Windows ZIP programs have strange habit of using DOS * encoding for file names in ZIP archives. For example, ZIP archive * with cyrillic names created in Windows will have file names in \c * IBM866 encoding instead of \c WINDOWS-1251. I think that calling one * function is not much trouble, but for true platform independency it * would be nice to have some mechanism for file name encoding auto * detection using locale information. Does anyone know a good way to do * it? **/ class QuaZip { friend class QuaZipPrivate; public: /// Useful constants. enum Constants { MAX_FILE_NAME_LENGTH=256 /**< Maximum file name length. Taken from \c UNZ_MAXFILENAMEINZIP constant in unzip.c. */ }; /// Open mode of the ZIP file. enum Mode { mdNotOpen, ///< ZIP file is not open. This is the initial mode. mdUnzip, ///< ZIP file is open for reading files inside it. mdCreate, ///< ZIP file was created with open() call. mdAppend, /**< ZIP file was opened in append mode. This refers to * \c APPEND_STATUS_CREATEAFTER mode in ZIP/UNZIP package * and means that zip is appended to some existing file * what is useful when that file contains * self-extractor code. This is obviously \em not what * you whant to use to add files to the existing ZIP * archive. **/ mdAdd ///< ZIP file was opened for adding files in the archive. }; /// Case sensitivity for the file names. /** This is what you specify when accessing files in the archive. * Works perfectly fine with any characters thanks to Qt's great * unicode support. This is different from ZIP/UNZIP API, where * only US-ASCII characters was supported. **/ enum CaseSensitivity { csDefault=0, ///< Default for platform. Case sensitive for UNIX, not for Windows. csSensitive=1, ///< Case sensitive. csInsensitive=2 ///< Case insensitive. }; /// Returns the actual case sensitivity for the specified QuaZIP one. /** \param cs The value to convert. \returns If CaseSensitivity::csDefault, then returns the default file name case sensitivity for the platform. Otherwise, just returns the appropriate value from the Qt::CaseSensitivity enum. */ static Qt::CaseSensitivity convertCaseSensitivity( CaseSensitivity cs); private: QuaZipPrivate *p; // not (and will not be) implemented QuaZip(const QuaZip& that); // not (and will not be) implemented QuaZip& operator=(const QuaZip& that); public: /// Constructs QuaZip object. /** Call setName() before opening constructed object. */ QuaZip(); /// Constructs QuaZip object associated with ZIP file \a zipName. QuaZip(const QString& zipName); /// Constructs QuaZip object associated with ZIP file represented by \a ioDevice. /** The IO device must be seekable, otherwise an error will occur when opening. */ QuaZip(QIODevice *ioDevice); /// Destroys QuaZip object. /** Calls close() if necessary. */ ~QuaZip(); /// Opens ZIP file. /** * Argument \a mode specifies open mode of the ZIP archive. See Mode * for details. Note that there is zipOpen2() function in the * ZIP/UNZIP API which accepts \a globalcomment argument, but it * does not use it anywhere, so this open() function does not have this * argument. See setComment() if you need to set global comment. * * If the ZIP file is accessed via explicitly set QIODevice, then * this device is opened in the necessary mode. If the device was * already opened by some other means, then QuaZIP checks if the * open mode is compatible to the mode needed for the requested operation. * If necessary, seeking is performed to position the device properly. * * \return \c true if successful, \c false otherwise. * * \note ZIP/UNZIP API open calls do not return error code - they * just return \c NULL indicating an error. But to make things * easier, quazip.h header defines additional error code \c * UNZ_ERROROPEN and getZipError() will return it if the open call * of the ZIP/UNZIP API returns \c NULL. * * Argument \a ioApi specifies IO function set for ZIP/UNZIP * package to use. See unzip.h, zip.h and ioapi.h for details. Note * that IO API for QuaZip is different from the original package. * The file path argument was changed to be of type \c voidpf, and * QuaZip passes a QIODevice pointer there. This QIODevice is either * set explicitly via setIoDevice() or the QuaZip(QIODevice*) * constructor, or it is created internally when opening the archive * by its file name. The default API (qioapi.cpp) just delegates * everything to the QIODevice API. Not only this allows to use a * QIODevice instead of file name, but also has a nice side effect * of raising the file size limit from 2G to 4G (in non-zip64 archives). * * \note If the zip64 support is needed, the ioApi argument \em must be NULL * because due to the backwards compatibility issues it can be used to * provide a 32-bit API only. * * \note If the \ref QuaZip::setAutoClose() "no-auto-close" feature is used, * then the \a ioApi argument \em should be NULL because the old API * doesn't support the 'fake close' operation, causing slight memory leaks * and other possible troubles (like closing the output device in case * when an error occurs during opening). * * In short: just forget about the \a ioApi argument and you'll be * fine. **/ bool open(Mode mode, zlib_filefunc_def *ioApi =NULL); /// Closes ZIP file. /** Call getZipError() to determine if the close was successful. * * If the file was opened by name, then the underlying QIODevice is closed * and deleted. * * If the underlying QIODevice was set explicitly using setIoDevice() or * the appropriate constructor, then it is closed if the auto-close flag * is set (which it is by default). Call setAutoClose() to clear the * auto-close flag if this behavior is undesirable. * * Since Qt 5.1, the QSaveFile was introduced. It breaks the QIODevice API * by making close() private and crashing the application if it is called * from the base class where it is public. It is an excellent example * of poor design that illustrates why you should never ever break * an is-a relationship between the base class and a subclass. QuaZIP * works around this bug by checking if the QIODevice is an instance * of QSaveFile, using qobject_cast<>, and if it is, calls * QSaveFile::commit() instead of close(). It is a really ugly hack, * but at least it makes your programs work instead of crashing. Note that * if the auto-close flag is cleared, then this is a non-issue, and * commit() isn't called. */ void close(); /// Sets the codec used to encode/decode file names inside archive. /** This is necessary to access files in the ZIP archive created * under Windows with non-latin characters in file names. For * example, file names with cyrillic letters will be in \c IBM866 * encoding. **/ void setFileNameCodec(QTextCodec *fileNameCodec); /// Sets the codec used to encode/decode file names inside archive. /** \overload * Equivalent to calling setFileNameCodec(QTextCodec::codecForName(codecName)); **/ void setFileNameCodec(const char *fileNameCodecName); /// Returns the codec used to encode/decode comments inside archive. QTextCodec* getFileNameCodec() const; /// Sets the codec used to encode/decode comments inside archive. /** This codec defaults to locale codec, which is probably ok. **/ void setCommentCodec(QTextCodec *commentCodec); /// Sets the codec used to encode/decode comments inside archive. /** \overload * Equivalent to calling setCommentCodec(QTextCodec::codecForName(codecName)); **/ void setCommentCodec(const char *commentCodecName); /// Returns the codec used to encode/decode comments inside archive. QTextCodec* getCommentCodec() const; /// Returns the name of the ZIP file. /** Returns null string if no ZIP file name has been set, for * example when the QuaZip instance is set up to use a QIODevice * instead. * \sa setZipName(), setIoDevice(), getIoDevice() **/ QString getZipName() const; /// Sets the name of the ZIP file. /** Does nothing if the ZIP file is open. * * Does not reset error code returned by getZipError(). * \sa setIoDevice(), getIoDevice(), getZipName() **/ void setZipName(const QString& zipName); /// Returns the device representing this ZIP file. /** Returns null string if no device has been set explicitly, for * example when opening a ZIP file by name. * \sa setIoDevice(), getZipName(), setZipName() **/ QIODevice *getIoDevice() const; /// Sets the device representing the ZIP file. /** Does nothing if the ZIP file is open. * * Does not reset error code returned by getZipError(). * \sa getIoDevice(), getZipName(), setZipName() **/ void setIoDevice(QIODevice *ioDevice); /// Returns the mode in which ZIP file was opened. Mode getMode() const; /// Returns \c true if ZIP file is open, \c false otherwise. bool isOpen() const; /// Returns the error code of the last operation. /** Returns \c UNZ_OK if the last operation was successful. * * Error code resets to \c UNZ_OK every time you call any function * that accesses something inside ZIP archive, even if it is \c * const (like getEntriesCount()). open() and close() calls reset * error code too. See documentation for the specific functions for * details on error detection. **/ int getZipError() const; /// Returns number of the entries in the ZIP central directory. /** Returns negative error code in the case of error. The same error * code will be returned by subsequent getZipError() call. **/ int getEntriesCount() const; /// Returns global comment in the ZIP file. QString getComment() const; /// Sets the global comment in the ZIP file. /** The comment will be written to the archive on close operation. * QuaZip makes a distinction between a null QByteArray() comment * and an empty "" comment in the QuaZip::mdAdd mode. * A null comment is the default and it means "don't change * the comment". An empty comment removes the original comment. * * \sa open() **/ void setComment(const QString& comment); /// Sets the current file to the first file in the archive. /** Returns \c true on success, \c false otherwise. Call * getZipError() to get the error code. **/ bool goToFirstFile(); /// Sets the current file to the next file in the archive. /** Returns \c true on success, \c false otherwise. Call * getZipError() to determine if there was an error. * * Should be used only in QuaZip::mdUnzip mode. * * \note If the end of file was reached, getZipError() will return * \c UNZ_OK instead of \c UNZ_END_OF_LIST_OF_FILE. This is to make * things like this easier: * \code * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { * // do something * } * if(zip.getZipError()==UNZ_OK) { * // ok, there was no error * } * \endcode **/ bool goToNextFile(); /// Sets current file by its name. /** Returns \c true if successful, \c false otherwise. Argument \a * cs specifies case sensitivity of the file name. Call * getZipError() in the case of a failure to get error code. * * This is not a wrapper to unzLocateFile() function. That is * because I had to implement locale-specific case-insensitive * comparison. * * Here are the differences from the original implementation: * * - If the file was not found, error code is \c UNZ_OK, not \c * UNZ_END_OF_LIST_OF_FILE (see also goToNextFile()). * - If this function fails, it unsets the current file rather than * resetting it back to what it was before the call. * * If \a fileName is null string then this function unsets the * current file and return \c true. Note that you should close the * file first if it is open! See * QuaZipFile::QuaZipFile(QuaZip*,QObject*) for the details. * * Should be used only in QuaZip::mdUnzip mode. * * \sa setFileNameCodec(), CaseSensitivity **/ bool setCurrentFile(const QString& fileName, CaseSensitivity cs =csDefault); /// Returns \c true if the current file has been set. bool hasCurrentFile() const; /// Retrieves information about the current file. /** Fills the structure pointed by \a info. Returns \c true on * success, \c false otherwise. In the latter case structure pointed * by \a info remains untouched. If there was an error, * getZipError() returns error code. * * Should be used only in QuaZip::mdUnzip mode. * * Does nothing and returns \c false in any of the following cases. * - ZIP is not open; * - ZIP does not have current file. * * In both cases getZipError() returns \c UNZ_OK since there * is no ZIP/UNZIP API call. * * This overload doesn't support zip64, but will work OK on zip64 archives * except that if one of the sizes (compressed or uncompressed) is greater * than 0xFFFFFFFFu, it will be set to exactly 0xFFFFFFFFu. * * \sa getCurrentFileInfo(QuaZipFileInfo64* info)const * \sa QuaZipFileInfo64::toQuaZipFileInfo(QuaZipFileInfo&)const **/ bool getCurrentFileInfo(QuaZipFileInfo* info)const; /// Retrieves information about the current file. /** \overload * * This function supports zip64. If the archive doesn't use zip64, it is * completely equivalent to getCurrentFileInfo(QuaZipFileInfo* info) * except for the argument type. * * \sa **/ bool getCurrentFileInfo(QuaZipFileInfo64* info)const; /// Returns the current file name. /** Equivalent to calling getCurrentFileInfo() and then getting \c * name field of the QuaZipFileInfo structure, but faster and more * convenient. * * Should be used only in QuaZip::mdUnzip mode. **/ QString getCurrentFileName()const; /// Returns \c unzFile handle. /** You can use this handle to directly call UNZIP part of the * ZIP/UNZIP package functions (see unzip.h). * * \warning When using the handle returned by this function, please * keep in mind that QuaZip class is unable to detect any changes * you make in the ZIP file state (e. g. changing current file, or * closing the handle). So please do not do anything with this * handle that is possible to do with the functions of this class. * Or at least return the handle in the original state before * calling some another function of this class (including implicit * destructor calls and calls from the QuaZipFile objects that refer * to this QuaZip instance!). So if you have changed the current * file in the ZIP archive - then change it back or you may * experience some strange behavior or even crashes. **/ unzFile getUnzFile(); /// Returns \c zipFile handle. /** You can use this handle to directly call ZIP part of the * ZIP/UNZIP package functions (see zip.h). Warnings about the * getUnzFile() function also apply to this function. **/ zipFile getZipFile(); /// Changes the data descriptor writing mode. /** According to the ZIP format specification, a file inside archive may have a data descriptor immediately following the file data. This is reflected by a special flag in the local file header and in the central directory. By default, QuaZIP sets this flag and writes the data descriptor unless both method and level were set to 0, in which case it operates in 1.0-compatible mode and never writes data descriptors. By setting this flag to false, it is possible to disable data descriptor writing, thus increasing compatibility with archive readers that don't understand this feature of the ZIP file format. Setting this flag affects all the QuaZipFile instances that are opened after this flag is set. The data descriptor writing mode is enabled by default. Note that if the ZIP archive is written into a QIODevice for which QIODevice::isSequential() returns \c true, then the data descriptor is mandatory and will be written even if this flag is set to false. \param enabled If \c true, enable local descriptor writing, disable it otherwise. \sa QuaZipFile::isDataDescriptorWritingEnabled() */ void setDataDescriptorWritingEnabled(bool enabled); /// Returns the data descriptor default writing mode. /** \sa setDataDescriptorWritingEnabled() */ bool isDataDescriptorWritingEnabled() const; /// Returns a list of files inside the archive. /** \return A list of file names or an empty list if there was an error or if the archive is empty (call getZipError() to figure out which). \sa getFileInfoList() */ QStringList getFileNameList() const; /// Returns information list about all files inside the archive. /** \return A list of QuaZipFileInfo objects or an empty list if there was an error or if the archive is empty (call getZipError() to figure out which). This function doesn't support zip64, but will still work with zip64 archives, converting results using QuaZipFileInfo64::toQuaZipFileInfo(). If all file sizes are below 4 GB, it will work just fine. \sa getFileNameList() \sa getFileInfoList64() */ QList getFileInfoList() const; /// Returns information list about all files inside the archive. /** \overload This function supports zip64. \sa getFileNameList() \sa getFileInfoList() */ QList getFileInfoList64() const; /// Enables the zip64 mode. /** * @param zip64 If \c true, the zip64 mode is enabled, disabled otherwise. * * Once this is enabled, all new files (until the mode is disabled again) * will be created in the zip64 mode, thus enabling the ability to write * files larger than 4 GB. By default, the zip64 mode is off due to * compatibility reasons. * * Note that this does not affect the ability to read zip64 archives in any * way. * * \sa isZip64Enabled() */ void setZip64Enabled(bool zip64); /// Returns whether the zip64 mode is enabled. /** * @return \c true if and only if the zip64 mode is enabled. * * \sa setZip64Enabled() */ bool isZip64Enabled() const; /// Returns the auto-close flag. /** @sa setAutoClose() */ bool isAutoClose() const; /// Sets or unsets the auto-close flag. /** By default, QuaZIP opens the underlying QIODevice when open() is called, and closes it when close() is called. In some cases, when the device is set explicitly using setIoDevice(), it may be desirable to leave the device open. If the auto-close flag is unset using this method, then the device isn't closed automatically if it was set explicitly. If it is needed to clear this flag, it is recommended to do so before opening the archive because otherwise QuaZIP may close the device during the open() call if an error is encountered after the device is opened. If the device was not set explicitly, but rather the setZipName() or the appropriate constructor was used to set the ZIP file name instead, then the auto-close flag has no effect, and the internal device is closed nevertheless because there is no other way to close it. @sa isAutoClose() @sa setIoDevice() */ void setAutoClose(bool autoClose) const; /// Sets the default file name codec to use. /** * The default codec is used by the constructors, so calling this function * won't affect the QuaZip instances already created at that moment. * * The codec specified here can be overriden by calling setFileNameCodec(). * If neither function is called, QTextCodec::codecForLocale() will be used * to decode or encode file names. Use this function with caution if * the application uses other libraries that depend on QuaZIP. Those * libraries can either call this function by themselves, thus overriding * your setting or can rely on the default encoding, thus failing * mysteriously if you change it. For these reasons, it isn't recommended * to use this function if you are developing a library, not an application. * Instead, ask your library users to call it in case they need specific * encoding. * * In most cases, using setFileNameCodec() instead is the right choice. * However, if you depend on third-party code that uses QuaZIP, then the * reasons stated above can actually become a reason to use this function * in case the third-party code in question fails because it doesn't * understand the encoding you need and doesn't provide a way to specify it. * This applies to the JlCompress class as well, as it was contributed and * doesn't support explicit encoding parameters. * * In short: use setFileNameCodec() when you can, resort to * setDefaultFileNameCodec() when you don't have access to the QuaZip * instance. * * @param codec The codec to use by default. If NULL, resets to default. */ static void setDefaultFileNameCodec(QTextCodec *codec); /** * @overload * Equivalent to calling * setDefltFileNameCodec(QTextCodec::codecForName(codecName)). */ static void setDefaultFileNameCodec(const char *codecName); }; #endif tea-qt-62.0.2/quazip_global.h000066400000000000000000000022331433400105300157670ustar00rootroot00000000000000#ifndef QUAZIP_GLOBAL_H #define QUAZIP_GLOBAL_H /* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include #ifdef __GNUC__ #define QUAZIP_UNUSED __attribute__((__unused__)) #else #define QUAZIP_UNUSED #endif #define QUAZIP_EXTRA_NTFS_MAGIC 0x000Au #define QUAZIP_EXTRA_NTFS_TIME_MAGIC 0x0001u #endif // QUAZIP_GLOBAL_H tea-qt-62.0.2/quazipdir.cpp000066400000000000000000000401571433400105300155100ustar00rootroot00000000000000/* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include #include #include #include "quazipdir.h" /// \cond internal class QuaZipDirPrivate: public QSharedData { friend class QuaZipDir; private: QuaZipDirPrivate(QuaZip *zip, const QString &dir = QString()): zip(zip), dir(dir), caseSensitivity(QuaZip::csDefault), filter(QDir::NoFilter), sorting(QDir::NoSort) {} QuaZip *zip; QString dir; QuaZip::CaseSensitivity caseSensitivity; QDir::Filters filter; QStringList nameFilters; QDir::SortFlags sorting; template bool entryInfoList(QStringList nameFilters, QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const; inline QString simplePath() const {return QDir::cleanPath(dir);} }; /// \endcond QuaZipDir::QuaZipDir(const QuaZipDir &that): d(that.d) { } QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir): d(new QuaZipDirPrivate(zip, dir)) { if (d->dir.startsWith('/')) d->dir = d->dir.mid(1); } QuaZipDir::~QuaZipDir() { } bool QuaZipDir::operator==(const QuaZipDir &that) { return d->zip == that.d->zip && d->dir == that.d->dir; } QuaZipDir& QuaZipDir::operator=(const QuaZipDir &that) { this->d = that.d; return *this; } QString QuaZipDir::operator[](int pos) const { return entryList().at(pos); } QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const { return d->caseSensitivity; } bool QuaZipDir::cd(const QString &directoryName) { if (directoryName == "/") { d->dir = ""; return true; } QString dirName = directoryName; if (dirName.endsWith('/')) dirName.chop(1); if (dirName.contains('/')) { QuaZipDir dir(*this); if (dirName.startsWith('/')) { #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::cd(%s): going to /", dirName.toUtf8().constData()); #endif if (!dir.cd("/")) return false; } // QStringList path = dirName.split('/', QString::SkipEmptyParts); QStringList path = dirName.split('/'); for (QStringList::const_iterator i = path.constBegin(); i != path.end(); ++i) { const QString &step = *i; #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::cd(%s): going to %s", dirName.toUtf8().constData(), step.toUtf8().constData()); #endif if (!dir.cd(step)) return false; } d->dir = dir.path(); return true; } else { // no '/' if (dirName == ".") { return true; } else if (dirName == "..") { if (isRoot()) { return false; } else { int slashPos = d->dir.lastIndexOf('/'); if (slashPos == -1) { d->dir = ""; } else { d->dir = d->dir.left(slashPos); } return true; } } else { // a simple subdirectory if (exists(dirName)) { if (isRoot()) d->dir = dirName; else d->dir += "/" + dirName; return true; } else { return false; } } } } bool QuaZipDir::cdUp() { return cd(".."); } uint QuaZipDir::count() const { return entryList().count(); } QString QuaZipDir::dirName() const { return QDir(d->dir).dirName(); } QuaZipFileInfo64 QuaZipDir_getFileInfo(QuaZip *zip, bool *ok, const QString &relativeName, bool isReal) { QuaZipFileInfo64 info; if (isReal) { *ok = zip->getCurrentFileInfo(&info); } else { *ok = true; info.compressedSize = 0; info.crc = 0; info.diskNumberStart = 0; info.externalAttr = 0; info.flags = 0; info.internalAttr = 0; info.method = 0; info.uncompressedSize = 0; info.versionCreated = info.versionNeeded = 0; } info.name = relativeName; return info; } static void QuaZipDir_convertInfoList(const QList &from, QList &to) { to = from; } static void QuaZipDir_convertInfoList(const QList &from, QStringList &to) { to.clear(); for (QList::const_iterator i = from.constBegin(); i != from.constEnd(); ++i) { to.append(i->name); } } static void QuaZipDir_convertInfoList(const QList &from, QList &to) { to.clear(); for (QList::const_iterator i = from.constBegin(); i != from.constEnd(); ++i) { QuaZipFileInfo info32; i->toQuaZipFileInfo(info32); to.append(info32); } } /// \cond internal /** An utility class to restore the current file. */ class QuaZipDirRestoreCurrent { public: inline QuaZipDirRestoreCurrent(QuaZip *zip): zip(zip), currentFile(zip->getCurrentFileName()) {} inline ~QuaZipDirRestoreCurrent() { zip->setCurrentFile(currentFile); } private: QuaZip *zip; QString currentFile; }; /// \endcond /// \cond internal class QuaZipDirComparator { private: QDir::SortFlags sort; static QString getExtension(const QString &name); int compareStrings(const QString &string1, const QString &string2); public: inline QuaZipDirComparator(QDir::SortFlags sort): sort(sort) {} bool operator()(const QuaZipFileInfo64 &info1, const QuaZipFileInfo64 &info2); }; QString QuaZipDirComparator::getExtension(const QString &name) { if (name.endsWith('.') || name.indexOf('.', 1) == -1) { return ""; } else { return name.mid(name.lastIndexOf('.') + 1); } } int QuaZipDirComparator::compareStrings(const QString &string1, const QString &string2) { if (sort & QDir::LocaleAware) { if (sort & QDir::IgnoreCase) { return string1.toLower().localeAwareCompare(string2.toLower()); } else { return string1.localeAwareCompare(string2); } } else { return string1.compare(string2, (sort & QDir::IgnoreCase) ? Qt::CaseInsensitive : Qt::CaseSensitive); } } bool QuaZipDirComparator::operator()(const QuaZipFileInfo64 &info1, const QuaZipFileInfo64 &info2) { QDir::SortFlags order = sort & (QDir::Name | QDir::Time | QDir::Size | QDir::Type); if ((sort & QDir::DirsFirst) == QDir::DirsFirst || (sort & QDir::DirsLast) == QDir::DirsLast) { if (info1.name.endsWith('/') && !info2.name.endsWith('/')) return (sort & QDir::DirsFirst) == QDir::DirsFirst; else if (!info1.name.endsWith('/') && info2.name.endsWith('/')) return (sort & QDir::DirsLast) == QDir::DirsLast; } bool result; int extDiff; switch (order) { case QDir::Name: result = compareStrings(info1.name, info2.name) < 0; break; case QDir::Type: extDiff = compareStrings(getExtension(info1.name), getExtension(info2.name)); if (extDiff == 0) { result = compareStrings(info1.name, info2.name) < 0; } else { result = extDiff < 0; } break; case QDir::Size: if (info1.uncompressedSize == info2.uncompressedSize) { result = compareStrings(info1.name, info2.name) < 0; } else { result = info1.uncompressedSize < info2.uncompressedSize; } break; case QDir::Time: if (info1.dateTime == info2.dateTime) { result = compareStrings(info1.name, info2.name) < 0; } else { result = info1.dateTime < info2.dateTime; } break; default: qWarning("QuaZipDirComparator(): Invalid sort mode 0x%2X", static_cast(sort)); return false; } return (sort & QDir::Reversed) ? !result : result; } template bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const { QString basePath = simplePath(); if (!basePath.isEmpty()) basePath += "/"; int baseLength = basePath.length(); result.clear(); QuaZipDirRestoreCurrent saveCurrent(zip); if (!zip->goToFirstFile()) { return zip->getZipError() == UNZ_OK; } QDir::Filters fltr = filter; if (fltr == QDir::NoFilter) fltr = this->filter; if (fltr == QDir::NoFilter) fltr = QDir::AllEntries; QStringList nmfltr = nameFilters; if (nmfltr.isEmpty()) nmfltr = this->nameFilters; QSet dirsFound; QList list; do { QString name = zip->getCurrentFileName(); if (!name.startsWith(basePath)) continue; QString relativeName = name.mid(baseLength); if (relativeName.isEmpty()) continue; bool isDir = false; bool isReal = true; if (relativeName.contains('/')) { int indexOfSlash = relativeName.indexOf('/'); // something like "subdir/" isReal = indexOfSlash == relativeName.length() - 1; relativeName = relativeName.left(indexOfSlash + 1); if (dirsFound.contains(relativeName)) continue; isDir = true; } dirsFound.insert(relativeName); if ((fltr & QDir::Dirs) == 0 && isDir) continue; if ((fltr & QDir::Files) == 0 && !isDir) continue; if (!nmfltr.isEmpty() && !QDir::match(nmfltr, relativeName)) continue; bool ok; QuaZipFileInfo64 info = QuaZipDir_getFileInfo(zip, &ok, relativeName, isReal); if (!ok) { return false; } list.append(info); } while (zip->goToNextFile()); QDir::SortFlags srt = sort; if (srt == QDir::NoSort) srt = sorting; #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDirPrivate::entryInfoList(): before sort:"); /* foreach (QuaZipFileInfo64 info, list) { qDebug("%s\t%s", info.name.toUtf8().constData(), info.dateTime.toString(Qt::ISODate).toUtf8().constData()); }*/ #endif if (srt != QDir::NoSort && (srt & QDir::Unsorted) != QDir::Unsorted) { if (QuaZip::convertCaseSensitivity(caseSensitivity) == Qt::CaseInsensitive) srt |= QDir::IgnoreCase; QuaZipDirComparator lessThan(srt); //qSort(list.begin(), list.end(), lessThan); std::sort(list.begin(), list.end(), lessThan); } QuaZipDir_convertInfoList(list, result); return true; } /// \endcond QList QuaZipDir::entryInfoList(const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort) const { QList result; if (d->entryInfoList(nameFilters, filters, sort, result)) return result; else return QList(); } QList QuaZipDir::entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const { return entryInfoList(QStringList(), filters, sort); } QList QuaZipDir::entryInfoList64(const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort) const { QList result; if (d->entryInfoList(nameFilters, filters, sort, result)) return result; else return QList(); } QList QuaZipDir::entryInfoList64(QDir::Filters filters, QDir::SortFlags sort) const { return entryInfoList64(QStringList(), filters, sort); } QStringList QuaZipDir::entryList(const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort) const { QStringList result; if (d->entryInfoList(nameFilters, filters, sort, result)) return result; else return QStringList(); } QStringList QuaZipDir::entryList(QDir::Filters filters, QDir::SortFlags sort) const { return entryList(QStringList(), filters, sort); } bool QuaZipDir::exists(const QString &filePath) const { if (filePath == "/" || filePath.isEmpty()) return true; QString fileName = filePath; if (fileName.endsWith('/')) fileName.chop(1); if (fileName.contains('/')) { QFileInfo fileInfo(fileName); #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, " "fileInfo.path()=%s", fileName.toUtf8().constData(), fileInfo.fileName().toUtf8().constData(), fileInfo.path().toUtf8().constData()); #endif QuaZipDir dir(*this); return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName()); } else { if (fileName == "..") { return !isRoot(); } else if (fileName == ".") { return true; } else { QStringList entries = entryList(QDir::AllEntries, QDir::NoSort); #ifdef QUAZIP_QUAZIPDIR_DEBUG qDebug("QuaZipDir::exists(): looking for %s", fileName.toUtf8().constData()); for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) { qDebug("QuaZipDir::exists(): entry: %s", i->toUtf8().constData()); } #endif Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity( d->caseSensitivity); if (filePath.endsWith('/')) { return entries.contains(filePath, cs); } else { return entries.contains(fileName, cs) || entries.contains(fileName + "/", cs); } } } } bool QuaZipDir::exists() const { return QuaZipDir(d->zip).exists(d->dir); } QString QuaZipDir::filePath(const QString &fileName) const { return QDir(d->dir).filePath(fileName); } QDir::Filters QuaZipDir::filter() { return d->filter; } bool QuaZipDir::isRoot() const { return d->simplePath().isEmpty(); } QStringList QuaZipDir::nameFilters() const { return d->nameFilters; } QString QuaZipDir::path() const { return d->dir; } QString QuaZipDir::relativeFilePath(const QString &fileName) const { return QDir("/" + d->dir).relativeFilePath(fileName); } void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity) { d->caseSensitivity = caseSensitivity; } void QuaZipDir::setFilter(QDir::Filters filters) { d->filter = filters; } void QuaZipDir::setNameFilters(const QStringList &nameFilters) { d->nameFilters = nameFilters; } void QuaZipDir::setPath(const QString &path) { QString newDir = path; if (newDir == "/") { d->dir = ""; } else { if (newDir.endsWith('/')) newDir.chop(1); if (newDir.startsWith('/')) newDir = newDir.mid(1); d->dir = newDir; } } void QuaZipDir::setSorting(QDir::SortFlags sort) { d->sorting = sort; } QDir::SortFlags QuaZipDir::sorting() const { return d->sorting; } tea-qt-62.0.2/quazipdir.h000066400000000000000000000201361433400105300151500ustar00rootroot00000000000000#ifndef QUAZIP_QUAZIPDIR_H #define QUAZIP_QUAZIPDIR_H /* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ class QuaZipDirPrivate; #include "quazip.h" #include "quazipfileinfo.h" #include #include #include /// Provides ZIP archive navigation. /** * This class is modelled after QDir, and is designed to provide similar * features for ZIP archives. * * The only significant difference from QDir is that the root path is not * '/', but an empty string since that's how the file paths are stored in * the archive. However, QuaZipDir understands the paths starting with * '/'. It is important in a few places: * * - In the cd() function. * - In the constructor. * - In the exists() function. * - In the relativePath() function. * * Note that since ZIP uses '/' on all platforms, the '\' separator is * not supported. */ class QuaZipDir { private: QSharedDataPointer d; public: /// The copy constructor. QuaZipDir(const QuaZipDir &that); /// Constructs a QuaZipDir instance pointing to the specified directory. /** If \a dir is not specified, points to the root of the archive. The same happens if the \a dir is "/". */ QuaZipDir(QuaZip *zip, const QString &dir = QString()); /// Destructor. ~QuaZipDir(); /// The assignment operator. bool operator==(const QuaZipDir &that); /// operator!= /** \return \c true if either this and \a that use different QuaZip instances or if they point to different directories. */ inline bool operator!=(const QuaZipDir &that) {return !operator==(that);} /// operator== /** \return \c true if both this and \a that use the same QuaZip instance and point to the same directory. */ QuaZipDir& operator=(const QuaZipDir &that); /// Returns the name of the entry at the specified position. QString operator[](int pos) const; /// Returns the current case sensitivity mode. QuaZip::CaseSensitivity caseSensitivity() const; /// Changes the 'current' directory. /** * If the path starts with '/', it is interpreted as an absolute * path from the root of the archive. Otherwise, it is interpreted * as a path relative to the current directory as was set by the * previous cd() or the constructor. * * Note that the subsequent path() call will not return a path * starting with '/' in all cases. */ bool cd(const QString &dirName); /// Goes up. bool cdUp(); /// Returns the number of entries in the directory. uint count() const; /// Returns the current directory name. /** The name doesn't include the path. */ QString dirName() const; /// Returns the list of the entries in the directory. /** \param nameFilters The list of file patterns to list, uses the same syntax as QDir. \param filters The entry type filters, only Files and Dirs are accepted. \param sort Sorting mode. */ QList entryInfoList(const QStringList &nameFilters, QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entries in the directory. /** \overload The same as entryInfoList(QStringList(), filters, sort). */ QList entryInfoList(QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entries in the directory with zip64 support. /** \param nameFilters The list of file patterns to list, uses the same syntax as QDir. \param filters The entry type filters, only Files and Dirs are accepted. \param sort Sorting mode. */ QList entryInfoList64(const QStringList &nameFilters, QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entries in the directory with zip64 support. /** \overload The same as entryInfoList64(QStringList(), filters, sort). */ QList entryInfoList64(QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entry names in the directory. /** The same as entryInfoList(nameFilters, filters, sort), but only returns entry names. */ QStringList entryList(const QStringList &nameFilters, QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns the list of the entry names in the directory. /** \overload The same as entryList(QStringList(), filters, sort). */ QStringList entryList(QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort) const; /// Returns \c true if the entry with the specified name exists. /** The ".." is considered to exist if the current directory is not root. The "." and "/" are considered to always exist. Paths starting with "/" are relative to the archive root, other paths are relative to the current dir. */ bool exists(const QString &fileName) const; /// Return \c true if the directory pointed by this QuaZipDir exists. bool exists() const; /// Returns the full path to the specified file. /** Doesn't check if the file actually exists. */ QString filePath(const QString &fileName) const; /// Returns the default filter. QDir::Filters filter(); /// Returns if the QuaZipDir points to the root of the archive. /** Not that the root path is the empty string, not '/'. */ bool isRoot() const; /// Return the default name filter. QStringList nameFilters() const; /// Returns the path to the current dir. /** The path never starts with '/', and the root path is an empty string. */ QString path() const; /// Returns the path to the specified file relative to the current dir. /** * This function is mostly useless, provided only for the sake of * completeness. * * @param fileName The path to the file, should start with "/" * if relative to the archive root. * @return Path relative to the current dir. */ QString relativeFilePath(const QString &fileName) const; /// Sets the default case sensitivity mode. void setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity); /// Sets the default filter. void setFilter(QDir::Filters filters); /// Sets the default name filter. void setNameFilters(const QStringList &nameFilters); /// Goes to the specified path. /** The difference from cd() is that this function never checks if the path actually exists and doesn't use relative paths, so it's possible to go to the root directory with setPath(""). Note that this function still chops the trailing and/or leading '/' and treats a single '/' as the root path (path() will still return an empty string). */ void setPath(const QString &path); /// Sets the default sorting mode. void setSorting(QDir::SortFlags sort); /// Returns the default sorting mode. QDir::SortFlags sorting() const; }; #endif // QUAZIP_QUAZIPDIR_H tea-qt-62.0.2/quazipfile.cpp000066400000000000000000000345721433400105300156550ustar00rootroot00000000000000/* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include "quazipfile.h" using namespace std; /// The implementation class for QuaZip. /** \internal This class contains all the private stuff for the QuaZipFile class, thus allowing to preserve binary compatibility between releases, the technique known as the Pimpl (private implementation) idiom. */ class QuaZipFilePrivate { friend class QuaZipFile; private: Q_DISABLE_COPY(QuaZipFilePrivate) /// The pointer to the associated QuaZipFile instance. QuaZipFile *q; /// The QuaZip object to work with. QuaZip *zip; /// The file name. QString fileName; /// Case sensitivity mode. QuaZip::CaseSensitivity caseSensitivity; /// Whether this file is opened in the raw mode. bool raw; /// Write position to keep track of. /** QIODevice::pos() is broken for non-seekable devices, so we need our own position. */ qint64 writePos; /// Uncompressed size to write along with a raw file. quint64 uncompressedSize; /// CRC to write along with a raw file. quint32 crc; /// Whether \ref zip points to an internal QuaZip instance. /** This is true if the archive was opened by name, rather than by supplying an existing QuaZip instance. */ bool internal; /// The last error. int zipError; /// Resets \ref zipError. inline void resetZipError() const {setZipError(UNZ_OK);} /// Sets the zip error. /** This function is marked as const although it changes one field. This allows to call it from const functions that don't change anything by themselves. */ void setZipError(int zipError) const; /// The constructor for the corresponding QuaZipFile constructor. inline QuaZipFilePrivate(QuaZipFile *q1): q(q1), zip(NULL), caseSensitivity(QuaZip::csDefault), raw(false), writePos(0), uncompressedSize(0), crc(0), internal(true), zipError(UNZ_OK) {} /// The constructor for the corresponding QuaZipFile constructor. inline QuaZipFilePrivate(QuaZipFile *q1, const QString &zipName): q(q1), caseSensitivity(QuaZip::csDefault), raw(false), writePos(0), uncompressedSize(0), crc(0), internal(true), zipError(UNZ_OK) { zip=new QuaZip(zipName); } /// The constructor for the corresponding QuaZipFile constructor. inline QuaZipFilePrivate(QuaZipFile *q1, const QString &zipName, const QString &fileName, QuaZip::CaseSensitivity cs): q(q1), raw(false), writePos(0), uncompressedSize(0), crc(0), internal(true), zipError(UNZ_OK) { zip=new QuaZip(zipName); this->fileName=fileName; if (this->fileName.startsWith('/')) this->fileName = this->fileName.mid(1); this->caseSensitivity=cs; } /// The constructor for the QuaZipFile constructor accepting a file name. inline QuaZipFilePrivate(QuaZipFile *q1, QuaZip *zip): q(q1), zip(zip), raw(false), writePos(0), uncompressedSize(0), crc(0), internal(false), zipError(UNZ_OK) {} /// The destructor. inline ~QuaZipFilePrivate() { if (internal) delete zip; } }; QuaZipFile::QuaZipFile(): p(new QuaZipFilePrivate(this)) { } QuaZipFile::QuaZipFile(QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this)) { } QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this, zipName)) { } QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName, QuaZip::CaseSensitivity cs, QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this, zipName, fileName, cs)) { } QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent): QIODevice(parent), p(new QuaZipFilePrivate(this, zip)) { } QuaZipFile::~QuaZipFile() { if (isOpen()) QuaZipFile::close(); delete p; } QString QuaZipFile::getZipName() const { return p->zip==NULL ? QString() : p->zip->getZipName(); } QuaZip *QuaZipFile::getZip() const { return p->internal ? NULL : p->zip; } QString QuaZipFile::getActualFileName()const { p->setZipError(UNZ_OK); if (p->zip == NULL || (openMode() & WriteOnly)) return QString(); QString name=p->zip->getCurrentFileName(); if(name.isNull()) p->setZipError(p->zip->getZipError()); return name; } void QuaZipFile::setZipName(const QString& zipName) { if(isOpen()) { qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name"); return; } if(p->zip!=NULL && p->internal) delete p->zip; p->zip=new QuaZip(zipName); p->internal=true; } void QuaZipFile::setZip(QuaZip *zip) { if(isOpen()) { qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP"); return; } if(p->zip!=NULL && p->internal) delete p->zip; p->zip=zip; p->fileName=QString(); p->internal=false; } void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs) { if(p->zip==NULL) { qWarning("QuaZipFile::setFileName(): call setZipName() first"); return; } if(!p->internal) { qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip"); return; } if(isOpen()) { qWarning("QuaZipFile::setFileName(): can not set file name for already opened file"); return; } p->fileName=fileName; if (p->fileName.startsWith('/')) p->fileName = p->fileName.mid(1); p->caseSensitivity=cs; } void QuaZipFilePrivate::setZipError(int zipError) const { QuaZipFilePrivate *fakeThis = const_cast(this); // non-const fakeThis->zipError=zipError; if(zipError==UNZ_OK) q->setErrorString(QString()); else q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError)); } bool QuaZipFile::open(OpenMode mode) { return open(mode, NULL); } bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password) { p->resetZipError(); if(isOpen()) { qWarning("QuaZipFile::open(): already opened"); return false; } if(mode&Unbuffered) { qWarning("QuaZipFile::open(): Unbuffered mode is not supported"); return false; } if((mode&ReadOnly)&&!(mode&WriteOnly)) { if(p->internal) { if(!p->zip->open(QuaZip::mdUnzip)) { p->setZipError(p->zip->getZipError()); return false; } if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) { p->setZipError(p->zip->getZipError()); p->zip->close(); return false; } } else { if(p->zip==NULL) { qWarning("QuaZipFile::open(): zip is NULL"); return false; } if(p->zip->getMode()!=QuaZip::mdUnzip) { qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", (int)mode, (int)p->zip->getMode()); return false; } if(!p->zip->hasCurrentFile()) { qWarning("QuaZipFile::open(): zip does not have current file"); return false; } } p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password)); if(p->zipError==UNZ_OK) { setOpenMode(mode); p->raw=raw; return true; } else return false; } qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); return false; } bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info, const char *password, quint32 crc, int method, int level, bool raw, int windowBits, int memLevel, int strategy) { zip_fileinfo info_z; p->resetZipError(); if(isOpen()) { qWarning("QuaZipFile::open(): already opened"); return false; } if((mode&WriteOnly)&&!(mode&ReadOnly)) { if(p->internal) { qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach"); return false; } if(p->zip==NULL) { qWarning("QuaZipFile::open(): zip is NULL"); return false; } if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) { qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", (int)mode, (int)p->zip->getMode()); return false; } info_z.tmz_date.tm_year=info.dateTime.date().year(); info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1; info_z.tmz_date.tm_mday=info.dateTime.date().day(); info_z.tmz_date.tm_hour=info.dateTime.time().hour(); info_z.tmz_date.tm_min=info.dateTime.time().minute(); info_z.tmz_date.tm_sec=info.dateTime.time().second(); info_z.dosDate = 0; info_z.internal_fa=(uLong)info.internalAttr; info_z.external_fa=(uLong)info.externalAttr; if (p->zip->isDataDescriptorWritingEnabled()) zipSetFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); else zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); p->setZipError(zipOpenNewFileInZip3_64(p->zip->getZipFile(), p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z, info.extraLocal.constData(), info.extraLocal.length(), info.extraGlobal.constData(), info.extraGlobal.length(), p->zip->getCommentCodec()->fromUnicode(info.comment).constData(), method, level, (int)raw, windowBits, memLevel, strategy, password, (uLong)crc, p->zip->isZip64Enabled())); if(p->zipError==UNZ_OK) { p->writePos=0; setOpenMode(mode); p->raw=raw; if(raw) { p->crc=crc; p->uncompressedSize=info.uncompressedSize; } return true; } else return false; } qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); return false; } bool QuaZipFile::isSequential()const { return true; } qint64 QuaZipFile::pos()const { if(p->zip==NULL) { qWarning("QuaZipFile::pos(): call setZipName() or setZip() first"); return -1; } if(!isOpen()) { qWarning("QuaZipFile::pos(): file is not open"); return -1; } if(openMode()&ReadOnly) // QIODevice::pos() is broken for sequential devices, // but thankfully bytesAvailable() returns the number of // bytes buffered, so we know how far ahead we are. return unztell64(p->zip->getUnzFile()) - QIODevice::bytesAvailable(); else return p->writePos; } bool QuaZipFile::atEnd()const { if(p->zip==NULL) { qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first"); return false; } if(!isOpen()) { qWarning("QuaZipFile::atEnd(): file is not open"); return false; } if(openMode()&ReadOnly) // the same problem as with pos() return QIODevice::bytesAvailable() == 0 && unzeof(p->zip->getUnzFile())==1; else return true; } qint64 QuaZipFile::size()const { if(!isOpen()) { qWarning("QuaZipFile::atEnd(): file is not open"); return -1; } if(openMode()&ReadOnly) return p->raw?csize():usize(); else return p->writePos; } qint64 QuaZipFile::csize()const { unz_file_info64 info_z; p->setZipError(UNZ_OK); if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); if(p->zipError!=UNZ_OK) return -1; return info_z.compressed_size; } qint64 QuaZipFile::usize()const { unz_file_info64 info_z; p->setZipError(UNZ_OK); if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); if(p->zipError!=UNZ_OK) return -1; return info_z.uncompressed_size; } bool QuaZipFile::getFileInfo(QuaZipFileInfo *info) { QuaZipFileInfo64 info64; if (getFileInfo(&info64)) { info64.toQuaZipFileInfo(*info); return true; } else { return false; } } bool QuaZipFile::getFileInfo(QuaZipFileInfo64 *info) { if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false; p->zip->getCurrentFileInfo(info); p->setZipError(p->zip->getZipError()); return p->zipError==UNZ_OK; } void QuaZipFile::close() { p->resetZipError(); if(p->zip==NULL||!p->zip->isOpen()) return; if(!isOpen()) { qWarning("QuaZipFile::close(): file isn't open"); return; } if(openMode()&ReadOnly) p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile())); else if(openMode()&WriteOnly) if(isRaw()) p->setZipError(zipCloseFileInZipRaw64(p->zip->getZipFile(), p->uncompressedSize, p->crc)); else p->setZipError(zipCloseFileInZip(p->zip->getZipFile())); else { qWarning("Wrong open mode: %d", (int)openMode()); return; } if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen); else return; if(p->internal) { p->zip->close(); p->setZipError(p->zip->getZipError()); } } qint64 QuaZipFile::readData(char *data, qint64 maxSize) { p->setZipError(UNZ_OK); qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize); if (bytesRead < 0) { p->setZipError((int) bytesRead); return -1; } return bytesRead; } qint64 QuaZipFile::writeData(const char* data, qint64 maxSize) { p->setZipError(ZIP_OK); p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize)); if(p->zipError!=ZIP_OK) return -1; else { p->writePos+=maxSize; return maxSize; } } QString QuaZipFile::getFileName() const { return p->fileName; } QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const { return p->caseSensitivity; } bool QuaZipFile::isRaw() const { return p->raw; } int QuaZipFile::getZipError() const { return p->zipError; } qint64 QuaZipFile::bytesAvailable() const { return size() - pos(); } tea-qt-62.0.2/quazipfile.h000066400000000000000000000470611433400105300153170ustar00rootroot00000000000000#ifndef QUA_ZIPFILE_H #define QUA_ZIPFILE_H /* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant, see quazip/(un)zip.h files for details, basically it's zlib license. **/ #include #include "quazip_global.h" #include "quazip.h" #include "quazipnewinfo.h" class QuaZipFilePrivate; /// A file inside ZIP archive. /** \class QuaZipFile quazipfile.h * This is the most interesting class. Not only it provides C++ * interface to the ZIP/UNZIP package, but also integrates it with Qt by * subclassing QIODevice. This makes possible to access files inside ZIP * archive using QTextStream or QDataStream, for example. Actually, this * is the main purpose of the whole QuaZIP library. * * You can either use existing QuaZip instance to create instance of * this class or pass ZIP archive file name to this class, in which case * it will create internal QuaZip object. See constructors' descriptions * for details. Writing is only possible with the existing instance. * * Note that due to the underlying library's limitation it is not * possible to use multiple QuaZipFile instances to open several files * in the same archive at the same time. If you need to write to * multiple files in parallel, then you should write to temporary files * first, then pack them all at once when you have finished writing. If * you need to read multiple files inside the same archive in parallel, * you should extract them all into a temporary directory first. * * \section quazipfile-sequential Sequential or random-access? * * At the first thought, QuaZipFile has fixed size, the start and the * end and should be therefore considered random-access device. But * there is one major obstacle to making it random-access: ZIP/UNZIP API * does not support seek() operation and the only way to implement it is * through reopening the file and re-reading to the required position, * but this is prohibitively slow. * * Therefore, QuaZipFile is considered to be a sequential device. This * has advantage of availability of the ungetChar() operation (QIODevice * does not implement it properly for non-sequential devices unless they * support seek()). Disadvantage is a somewhat strange behaviour of the * size() and pos() functions. This should be kept in mind while using * this class. * **/ class QuaZipFile: public QIODevice { friend class QuaZipFilePrivate; Q_OBJECT private: QuaZipFilePrivate *p; // these are not supported nor implemented QuaZipFile(const QuaZipFile& that); QuaZipFile& operator=(const QuaZipFile& that); protected: /// Implementation of the QIODevice::readData(). qint64 readData(char *data, qint64 maxSize); /// Implementation of the QIODevice::writeData(). qint64 writeData(const char *data, qint64 maxSize); public: /// Constructs a QuaZipFile instance. /** You should use setZipName() and setFileName() or setZip() before * trying to call open() on the constructed object. **/ QuaZipFile(); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object. * * You should use setZipName() and setFileName() or setZip() before * trying to call open() on the constructed object. **/ QuaZipFile(QObject *parent); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object and \a * zipName specifies ZIP archive file name. * * You should use setFileName() before trying to call open() on the * constructed object. * * QuaZipFile constructed by this constructor can be used for read * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. **/ QuaZipFile(const QString& zipName, QObject *parent =NULL); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object, \a * zipName specifies ZIP archive file name and \a fileName and \a cs * specify a name of the file to open inside archive. * * QuaZipFile constructed by this constructor can be used for read * only access. Use QuaZipFile(QuaZip*,QObject*) for writing. * * \sa QuaZip::setCurrentFile() **/ QuaZipFile(const QString& zipName, const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault, QObject *parent =NULL); /// Constructs a QuaZipFile instance. /** \a parent argument specifies this object's parent object. * * \a zip is the pointer to the existing QuaZip object. This * QuaZipFile object then can be used to read current file in the * \a zip or to write to the file inside it. * * \warning Using this constructor for reading current file can be * tricky. Let's take the following example: * \code * QuaZip zip("archive.zip"); * zip.open(QuaZip::mdUnzip); * zip.setCurrentFile("file-in-archive"); * QuaZipFile file(&zip); * file.open(QIODevice::ReadOnly); * // ok, now we can read from the file * file.read(somewhere, some); * zip.setCurrentFile("another-file-in-archive"); // oops... * QuaZipFile anotherFile(&zip); * anotherFile.open(QIODevice::ReadOnly); * anotherFile.read(somewhere, some); // this is still ok... * file.read(somewhere, some); // and this is NOT * \endcode * So, what exactly happens here? When we change current file in the * \c zip archive, \c file that references it becomes invalid * (actually, as far as I understand ZIP/UNZIP sources, it becomes * closed, but QuaZipFile has no means to detect it). * * Summary: do not close \c zip object or change its current file as * long as QuaZipFile is open. Even better - use another constructors * which create internal QuaZip instances, one per object, and * therefore do not cause unnecessary trouble. This constructor may * be useful, though, if you already have a QuaZip instance and do * not want to access several files at once. Good example: * \code * QuaZip zip("archive.zip"); * zip.open(QuaZip::mdUnzip); * // first, we need some information about archive itself * QByteArray comment=zip.getComment(); * // and now we are going to access files inside it * QuaZipFile file(&zip); * for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) { * file.open(QIODevice::ReadOnly); * // do something cool with file here * file.close(); // do not forget to close! * } * zip.close(); * \endcode **/ QuaZipFile(QuaZip *zip, QObject *parent =NULL); /// Destroys a QuaZipFile instance. /** Closes file if open, destructs internal QuaZip object (if it * exists and \em is internal, of course). **/ virtual ~QuaZipFile(); /// Returns the ZIP archive file name. /** If this object was created by passing QuaZip pointer to the * constructor, this function will return that QuaZip's file name * (or null string if that object does not have file name yet). * * Otherwise, returns associated ZIP archive file name or null * string if there are no name set yet. * * \sa setZipName() getFileName() **/ QString getZipName()const; /// Returns a pointer to the associated QuaZip object. /** Returns \c NULL if there is no associated QuaZip or it is * internal (so you will not mess with it). **/ QuaZip* getZip()const; /// Returns file name. /** This function returns file name you passed to this object either * by using * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) * or by calling setFileName(). Real name of the file may differ in * case if you used case-insensitivity. * * Returns null string if there is no file name set yet. This is the * case when this QuaZipFile operates on the existing QuaZip object * (constructor QuaZipFile(QuaZip*,QObject*) or setZip() was used). * * \sa getActualFileName **/ QString getFileName() const; /// Returns case sensitivity of the file name. /** This function returns case sensitivity argument you passed to * this object either by using * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*) * or by calling setFileName(). * * Returns unpredictable value if getFileName() returns null string * (this is the case when you did not used setFileName() or * constructor above). * * \sa getFileName **/ QuaZip::CaseSensitivity getCaseSensitivity() const; /// Returns the actual file name in the archive. /** This is \em not a ZIP archive file name, but a name of file inside * archive. It is not necessary the same name that you have passed * to the * QuaZipFile(const QString&,const QString&,QuaZip::CaseSensitivity,QObject*), * setFileName() or QuaZip::setCurrentFile() - this is the real file * name inside archive, so it may differ in case if the file name * search was case-insensitive. * * Equivalent to calling getCurrentFileName() on the associated * QuaZip object. Returns null string if there is no associated * QuaZip object or if it does not have a current file yet. And this * is the case if you called setFileName() but did not open the * file yet. So this is perfectly fine: * \code * QuaZipFile file("somezip.zip"); * file.setFileName("somefile"); * QString name=file.getName(); // name=="somefile" * QString actual=file.getActualFileName(); // actual is null string * file.open(QIODevice::ReadOnly); * QString actual=file.getActualFileName(); // actual can be "SoMeFiLe" on Windows * \endcode * * \sa getZipName(), getFileName(), QuaZip::CaseSensitivity **/ QString getActualFileName()const; /// Sets the ZIP archive file name. /** Automatically creates internal QuaZip object and destroys * previously created internal QuaZip object, if any. * * Will do nothing if this file is already open. You must close() it * first. **/ void setZipName(const QString& zipName); /// Returns \c true if the file was opened in raw mode. /** If the file is not open, the returned value is undefined. * * \sa open(OpenMode,int*,int*,bool,const char*) **/ bool isRaw() const; /// Binds to the existing QuaZip instance. /** This function destroys internal QuaZip object, if any, and makes * this QuaZipFile to use current file in the \a zip object for any * further operations. See QuaZipFile(QuaZip*,QObject*) for the * possible pitfalls. * * Will do nothing if the file is currently open. You must close() * it first. **/ void setZip(QuaZip *zip); /// Sets the file name. /** Will do nothing if at least one of the following conditions is * met: * - ZIP name has not been set yet (getZipName() returns null * string). * - This QuaZipFile is associated with external QuaZip. In this * case you should call that QuaZip's setCurrentFile() function * instead! * - File is already open so setting the name is meaningless. * * \sa QuaZip::setCurrentFile **/ void setFileName(const QString& fileName, QuaZip::CaseSensitivity cs =QuaZip::csDefault); /// Opens a file for reading. /** Returns \c true on success, \c false otherwise. * Call getZipError() to get error code. * * \note Since ZIP/UNZIP API provides buffered reading only, * QuaZipFile does not support unbuffered reading. So do not pass * QIODevice::Unbuffered flag in \a mode, or open will fail. **/ virtual bool open(OpenMode mode); /// Opens a file for reading. /** \overload * Argument \a password specifies a password to decrypt the file. If * it is NULL then this function behaves just like open(OpenMode). **/ inline bool open(OpenMode mode, const char *password) {return open(mode, NULL, NULL, false, password);} /// Opens a file for reading. /** \overload * Argument \a password specifies a password to decrypt the file. * * An integers pointed by \a method and \a level will receive codes * of the compression method and level used. See unzip.h. * * If raw is \c true then no decompression is performed. * * \a method should not be \c NULL. \a level can be \c NULL if you * don't want to know the compression level. **/ bool open(OpenMode mode, int *method, int *level, bool raw, const char *password =NULL); /// Opens a file for writing. /** \a info argument specifies information about file. It should at * least specify a correct file name. Also, it is a good idea to * specify correct timestamp (by default, current time will be * used). See QuaZipNewInfo. * * The \a password argument specifies the password for crypting. Pass NULL * if you don't need any crypting. The \a crc argument was supposed * to be used for crypting too, but then it turned out that it's * false information, so you need to set it to 0 unless you want to * use the raw mode (see below). * * Arguments \a method and \a level specify compression method and * level. The only method supported is Z_DEFLATED, but you may also * specify 0 for no compression. If all of the files in the archive * use both method 0 and either level 0 is explicitly specified or * data descriptor writing is disabled with * QuaZip::setDataDescriptorWritingEnabled(), then the * resulting archive is supposed to be compatible with the 1.0 ZIP * format version, should you need that. Except for this, \a level * has no other effects with method 0. * * If \a raw is \c true, no compression is performed. In this case, * \a crc and uncompressedSize field of the \a info are required. * * Arguments \a windowBits, \a memLevel, \a strategy provide zlib * algorithms tuning. See deflateInit2() in zlib. **/ bool open(OpenMode mode, const QuaZipNewInfo& info, const char *password =NULL, quint32 crc =0, int method =Z_DEFLATED, int level =Z_DEFAULT_COMPRESSION, bool raw =false, int windowBits =-MAX_WBITS, int memLevel =DEF_MEM_LEVEL, int strategy =Z_DEFAULT_STRATEGY); /// Returns \c true, but \ref quazipfile-sequential "beware"! virtual bool isSequential()const; /// Returns current position in the file. /** Implementation of the QIODevice::pos(). When reading, this * function is a wrapper to the ZIP/UNZIP unztell(), therefore it is * unable to keep track of the ungetChar() calls (which is * non-virtual and therefore is dangerous to reimplement). So if you * are using ungetChar() feature of the QIODevice, this function * reports incorrect value until you get back characters which you * ungot. * * When writing, pos() returns number of bytes already written * (uncompressed unless you use raw mode). * * \note Although * \ref quazipfile-sequential "QuaZipFile is a sequential device" * and therefore pos() should always return zero, it does not, * because it would be misguiding. Keep this in mind. * * This function returns -1 if the file or archive is not open. * * Error code returned by getZipError() is not affected by this * function call. **/ /* virtual */qint64 pos()const; /// Returns \c true if the end of file was reached. /** This function returns \c false in the case of error. This means * that you called this function on either not open file, or a file * in the not open archive or even on a QuaZipFile instance that * does not even have QuaZip instance associated. Do not do that * because there is no means to determine whether \c false is * returned because of error or because end of file was reached. * Well, on the other side you may interpret \c false return value * as "there is no file open to check for end of file and there is * no end of file therefore". * * When writing, this function always returns \c true (because you * are always writing to the end of file). * * Error code returned by getZipError() is not affected by this * function call. **/ /* virtual */bool atEnd()const; /// Returns file size. /** This function returns csize() if the file is open for reading in * raw mode, usize() if it is open for reading in normal mode and * pos() if it is open for writing. * * Returns -1 on error, call getZipError() to get error code. * * \note This function returns file size despite that * \ref quazipfile-sequential "QuaZipFile is considered to be sequential device", * for which size() should return bytesAvailable() instead. But its * name would be very misguiding otherwise, so just keep in mind * this inconsistence. **/ virtual qint64 size()const; /// Returns compressed file size. /** Equivalent to calling getFileInfo() and then getting * compressedSize field, but more convenient and faster. * * File must be open for reading before calling this function. * * Returns -1 on error, call getZipError() to get error code. **/ qint64 csize()const; /// Returns uncompressed file size. /** Equivalent to calling getFileInfo() and then getting * uncompressedSize field, but more convenient and faster. See * getFileInfo() for a warning. * * File must be open for reading before calling this function. * * Returns -1 on error, call getZipError() to get error code. **/ qint64 usize()const; /// Gets information about current file. /** This function does the same thing as calling * QuaZip::getCurrentFileInfo() on the associated QuaZip object, * but you can not call getCurrentFileInfo() if the associated * QuaZip is internal (because you do not have access to it), while * you still can call this function in that case. * * File must be open for reading before calling this function. * * \return \c false in the case of an error. * * This function doesn't support zip64, but will still work fine on zip64 * archives if file sizes are below 4 GB, otherwise the values will be set * as if converted using QuaZipFileInfo64::toQuaZipFileInfo(). * * \sa getFileInfo(QuaZipFileInfo64*) **/ bool getFileInfo(QuaZipFileInfo *info); /// Gets information about current file with zip64 support. /** * @overload * * \sa getFileInfo(QuaZipFileInfo*) */ bool getFileInfo(QuaZipFileInfo64 *info); /// Closes the file. /** Call getZipError() to determine if the close was successful. **/ /*virtual */void close(); /// Returns the error code returned by the last ZIP/UNZIP API call. int getZipError() const; /// Returns the number of bytes available for reading. virtual qint64 bytesAvailable() const; }; #endif tea-qt-62.0.2/quazipfileinfo.cpp000066400000000000000000000147401433400105300165240ustar00rootroot00000000000000/* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include "quazipfileinfo.h" static QFile::Permissions permissionsFromExternalAttr(quint32 externalAttr) { quint32 uPerm = (externalAttr & 0xFFFF0000u) >> 16; QFile::Permissions perm;// = 0; if ((uPerm & 0400) != 0) perm |= QFile::ReadOwner; if ((uPerm & 0200) != 0) perm |= QFile::WriteOwner; if ((uPerm & 0100) != 0) perm |= QFile::ExeOwner; if ((uPerm & 0040) != 0) perm |= QFile::ReadGroup; if ((uPerm & 0020) != 0) perm |= QFile::WriteGroup; if ((uPerm & 0010) != 0) perm |= QFile::ExeGroup; if ((uPerm & 0004) != 0) perm |= QFile::ReadOther; if ((uPerm & 0002) != 0) perm |= QFile::WriteOther; if ((uPerm & 0001) != 0) perm |= QFile::ExeOther; return perm; } QFile::Permissions QuaZipFileInfo::getPermissions() const { return permissionsFromExternalAttr(externalAttr); } QFile::Permissions QuaZipFileInfo64::getPermissions() const { return permissionsFromExternalAttr(externalAttr); } bool QuaZipFileInfo64::toQuaZipFileInfo(QuaZipFileInfo &info) const { bool noOverflow = true; info.name = name; info.versionCreated = versionCreated; info.versionNeeded = versionNeeded; info.flags = flags; info.method = method; info.dateTime = dateTime; info.crc = crc; if (compressedSize > 0xFFFFFFFFu) { info.compressedSize = 0xFFFFFFFFu; noOverflow = false; } else { info.compressedSize = compressedSize; } if (uncompressedSize > 0xFFFFFFFFu) { info.uncompressedSize = 0xFFFFFFFFu; noOverflow = false; } else { info.uncompressedSize = uncompressedSize; } info.diskNumberStart = diskNumberStart; info.internalAttr = internalAttr; info.externalAttr = externalAttr; info.comment = comment; info.extra = extra; return noOverflow; } static QDateTime getNTFSTime(const QByteArray &extra, int position, int *fineTicks) { QDateTime dateTime; for (int i = 0; i <= extra.size() - 4; ) { unsigned type = static_cast(static_cast( extra.at(i))) | (static_cast(static_cast( extra.at(i + 1))) << 8); i += 2; unsigned length = static_cast(static_cast( extra.at(i))) | (static_cast(static_cast( extra.at(i + 1))) << 8); i += 2; if (type == QUAZIP_EXTRA_NTFS_MAGIC && length >= 32) { i += 4; // reserved while (i <= extra.size() - 4) { unsigned tag = static_cast( static_cast(extra.at(i))) | (static_cast( static_cast(extra.at(i + 1))) << 8); i += 2; int tagsize = static_cast( static_cast(extra.at(i))) | (static_cast( static_cast(extra.at(i + 1))) << 8); i += 2; if (tag == QUAZIP_EXTRA_NTFS_TIME_MAGIC && tagsize >= position + 8) { i += position; quint64 mtime = static_cast( static_cast(extra.at(i))) | (static_cast(static_cast( extra.at(i + 1))) << 8) | (static_cast(static_cast( extra.at(i + 2))) << 16) | (static_cast(static_cast( extra.at(i + 3))) << 24) | (static_cast(static_cast( extra.at(i + 4))) << 32) | (static_cast(static_cast( extra.at(i + 5))) << 40) | (static_cast(static_cast( extra.at(i + 6))) << 48) | (static_cast(static_cast( extra.at(i + 7))) << 56); // the NTFS time is measured from 1601 for whatever reason QDateTime base(QDate(1601, 1, 1), QTime(0, 0), Qt::UTC); dateTime = base.addMSecs(mtime / 10000); if (fineTicks != NULL) { *fineTicks = static_cast(mtime % 10000); } i += tagsize - position; } else { i += tagsize; } } } else { i += length; } } if (fineTicks != NULL && dateTime.isNull()) { *fineTicks = 0; } return dateTime; } QDateTime QuaZipFileInfo64::getNTFSmTime(int *fineTicks) const { return getNTFSTime(extra, 0, fineTicks); } QDateTime QuaZipFileInfo64::getNTFSaTime(int *fineTicks) const { return getNTFSTime(extra, 8, fineTicks); } QDateTime QuaZipFileInfo64::getNTFScTime(int *fineTicks) const { return getNTFSTime(extra, 16, fineTicks); } tea-qt-62.0.2/quazipfileinfo.h000066400000000000000000000135701433400105300161710ustar00rootroot00000000000000#ifndef QUA_ZIPFILEINFO_H #define QUA_ZIPFILEINFO_H /* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include #include #include #include "quazip_global.h" /// Information about a file inside archive. /** * \deprecated Use QuaZipFileInfo64 instead. Not only it supports large files, * but also more convenience methods as well. * * Call QuaZip::getCurrentFileInfo() or QuaZipFile::getFileInfo() to * fill this structure. */ struct QuaZipFileInfo { /// File name. QString name; /// Version created by. quint16 versionCreated; /// Version needed to extract. quint16 versionNeeded; /// General purpose flags. quint16 flags; /// Compression method. quint16 method; /// Last modification date and time. QDateTime dateTime; /// CRC. quint32 crc; /// Compressed file size. quint32 compressedSize; /// Uncompressed file size. quint32 uncompressedSize; /// Disk number start. quint16 diskNumberStart; /// Internal file attributes. quint16 internalAttr; /// External file attributes. quint32 externalAttr; /// Comment. QString comment; /// Extra field. QByteArray extra; /// Get the file permissions. /** Returns the high 16 bits of external attributes converted to QFile::Permissions. */ QFile::Permissions getPermissions() const; }; /// Information about a file inside archive (with zip64 support). /** Call QuaZip::getCurrentFileInfo() or QuaZipFile::getFileInfo() to * fill this structure. */ struct QuaZipFileInfo64 { /// File name. QString name; /// Version created by. quint16 versionCreated; /// Version needed to extract. quint16 versionNeeded; /// General purpose flags. quint16 flags; /// Compression method. quint16 method; /// Last modification date and time. /** * This is the time stored in the standard ZIP header. This format only allows * to store time with 2-second precision, so the seconds will always be even * and the milliseconds will always be zero. If you need more precise * date and time, you can try to call the getNTFSmTime() function or * its siblings, provided that the archive itself contains these NTFS times. */ QDateTime dateTime; /// CRC. quint32 crc; /// Compressed file size. quint64 compressedSize; /// Uncompressed file size. quint64 uncompressedSize; /// Disk number start. quint16 diskNumberStart; /// Internal file attributes. quint16 internalAttr; /// External file attributes. quint32 externalAttr; /// Comment. QString comment; /// Extra field. QByteArray extra; /// Get the file permissions. /** Returns the high 16 bits of external attributes converted to QFile::Permissions. */ QFile::Permissions getPermissions() const; /// Converts to QuaZipFileInfo /** If any of the fields are greater than 0xFFFFFFFFu, they are set to 0xFFFFFFFFu exactly, not just truncated. This function should be mainly used for compatibility with the old code expecting QuaZipFileInfo, in the cases when it's impossible or otherwise unadvisable (due to ABI compatibility reasons, for example) to modify that old code to use QuaZipFileInfo64. \return \c true if all fields converted correctly, \c false if an overflow occured. */ bool toQuaZipFileInfo(QuaZipFileInfo &info) const; /// Returns the NTFS modification time /** * The getNTFS*Time() functions only work if there is an NTFS extra field * present. Otherwise, they all return invalid null timestamps. * @param fineTicks If not NULL, the fractional part of milliseconds returned * there, measured in 100-nanosecond ticks. Will be set to * zero if there is no NTFS extra field. * @sa dateTime * @sa getNTFSaTime() * @sa getNTFScTime() * @return The NTFS modification time, UTC */ QDateTime getNTFSmTime(int *fineTicks = NULL) const; /// Returns the NTFS access time /** * The getNTFS*Time() functions only work if there is an NTFS extra field * present. Otherwise, they all return invalid null timestamps. * @param fineTicks If not NULL, the fractional part of milliseconds returned * there, measured in 100-nanosecond ticks. Will be set to * zero if there is no NTFS extra field. * @sa dateTime * @sa getNTFSmTime() * @sa getNTFScTime() * @return The NTFS access time, UTC */ QDateTime getNTFSaTime(int *fineTicks = NULL) const; /// Returns the NTFS creation time /** * The getNTFS*Time() functions only work if there is an NTFS extra field * present. Otherwise, they all return invalid null timestamps. * @param fineTicks If not NULL, the fractional part of milliseconds returned * there, measured in 100-nanosecond ticks. Will be set to * zero if there is no NTFS extra field. * @sa dateTime * @sa getNTFSmTime() * @sa getNTFSaTime() * @return The NTFS creation time, UTC */ QDateTime getNTFScTime(int *fineTicks = NULL) const; /// Checks whether the file is encrypted. bool isEncrypted() const {return (flags & 1) != 0;} }; #endif tea-qt-62.0.2/quazipnewinfo.cpp000066400000000000000000000250771433400105300164030ustar00rootroot00000000000000/* Copyright (C) 2005-2014 Sergey A. Tachenov This file is part of QuaZIP. QuaZIP is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. QuaZIP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with QuaZIP. If not, see . See COPYING file for the full LGPL text. Original ZIP package is copyrighted by Gilles Vollant and contributors, see quazip/(un)zip.h files for details. Basically it's the zlib license. */ #include #include #include "quazipnewinfo.h" static void QuaZipNewInfo_setPermissions(QuaZipNewInfo *info, QFile::Permissions perm, bool isDir, bool isSymLink = false) { quint32 uPerm = isDir ? 0040000 : 0100000; if ( isSymLink ) { #ifdef Q_OS_WIN uPerm = 0200000; #else uPerm = 0120000; #endif } if ((perm & QFile::ReadOwner) != 0) uPerm |= 0400; if ((perm & QFile::WriteOwner) != 0) uPerm |= 0200; if ((perm & QFile::ExeOwner) != 0) uPerm |= 0100; if ((perm & QFile::ReadGroup) != 0) uPerm |= 0040; if ((perm & QFile::WriteGroup) != 0) uPerm |= 0020; if ((perm & QFile::ExeGroup) != 0) uPerm |= 0010; if ((perm & QFile::ReadOther) != 0) uPerm |= 0004; if ((perm & QFile::WriteOther) != 0) uPerm |= 0002; if ((perm & QFile::ExeOther) != 0) uPerm |= 0001; info->externalAttr = (info->externalAttr & ~0xFFFF0000u) | (uPerm << 16); } template void QuaZipNewInfo_init(QuaZipNewInfo &self, const FileInfo &existing) { self.name = existing.name; self.dateTime = existing.dateTime; self.internalAttr = existing.internalAttr; self.externalAttr = existing.externalAttr; self.comment = existing.comment; self.extraLocal = existing.extra; self.extraGlobal = existing.extra; self.uncompressedSize = existing.uncompressedSize; } QuaZipNewInfo::QuaZipNewInfo(const QuaZipFileInfo &existing) { QuaZipNewInfo_init(*this, existing); } QuaZipNewInfo::QuaZipNewInfo(const QuaZipFileInfo64 &existing) { QuaZipNewInfo_init(*this, existing); } QuaZipNewInfo::QuaZipNewInfo(const QString& name): name(name), dateTime(QDateTime::currentDateTime()), internalAttr(0), externalAttr(0), uncompressedSize(0) { } QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file): name(name), internalAttr(0), externalAttr(0), uncompressedSize(0) { QFileInfo info(file); QDateTime lm = info.lastModified(); if (!info.exists()) { dateTime = QDateTime::currentDateTime(); } else { dateTime = lm; QuaZipNewInfo_setPermissions(this, info.permissions(), info.isDir(), info.isSymLink()); } } void QuaZipNewInfo::setFileDateTime(const QString& file) { QFileInfo info(file); QDateTime lm = info.lastModified(); if (info.exists()) dateTime = lm; } void QuaZipNewInfo::setFilePermissions(const QString &file) { QFileInfo info = QFileInfo(file); QFile::Permissions perm = info.permissions(); QuaZipNewInfo_setPermissions(this, perm, info.isDir(), info.isSymLink()); } void QuaZipNewInfo::setPermissions(QFile::Permissions permissions) { QuaZipNewInfo_setPermissions(this, permissions, name.endsWith('/')); } void QuaZipNewInfo::setFileNTFSTimes(const QString &fileName) { QFileInfo fi(fileName); if (!fi.exists()) { qWarning("QuaZipNewInfo::setFileNTFSTimes(): '%s' doesn't exist", fileName.toUtf8().constData()); return; } setFileNTFSmTime(fi.lastModified()); setFileNTFSaTime(fi.lastRead()); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) setFileNTFScTime(fi.birthTime()); #else setFileNTFScTime(fi.created()); #endif } static void setNTFSTime(QByteArray &extra, const QDateTime &time, int position, int fineTicks) { int ntfsPos = -1, timesPos = -1; unsigned ntfsLength = 0, ntfsTimesLength = 0; for (int i = 0; i <= extra.size() - 4; ) { unsigned type = static_cast(static_cast( extra.at(i))) | (static_cast(static_cast( extra.at(i + 1))) << 8); i += 2; unsigned length = static_cast(static_cast( extra.at(i))) | (static_cast(static_cast( extra.at(i + 1))) << 8); i += 2; if (type == QUAZIP_EXTRA_NTFS_MAGIC) { ntfsPos = i - 4; // the beginning of the NTFS record ntfsLength = length; if (length <= 4) { break; // no times in the NTFS record } i += 4; // reserved while (i <= extra.size() - 4) { unsigned tag = static_cast( static_cast(extra.at(i))) | (static_cast( static_cast(extra.at(i + 1))) << 8); i += 2; unsigned tagsize = static_cast( static_cast(extra.at(i))) | (static_cast( static_cast(extra.at(i + 1))) << 8); i += 2; if (tag == QUAZIP_EXTRA_NTFS_TIME_MAGIC) { timesPos = i - 4; // the beginning of the NTFS times tag ntfsTimesLength = tagsize; break; } else { i += tagsize; } } break; // I ain't going to search for yet another NTFS record! } else { i += length; } } if (ntfsPos == -1) { // No NTFS record, need to create one. ntfsPos = extra.size(); ntfsLength = 32; extra.resize(extra.size() + 4 + ntfsLength); // the NTFS record header extra[ntfsPos] = static_cast(QUAZIP_EXTRA_NTFS_MAGIC); extra[ntfsPos + 1] = static_cast(QUAZIP_EXTRA_NTFS_MAGIC >> 8); extra[ntfsPos + 2] = 32; // the 2-byte size in LittleEndian extra[ntfsPos + 3] = 0; // zero the record memset(extra.data() + ntfsPos + 4, 0, 32); timesPos = ntfsPos + 8; // now set the tag data extra[timesPos] = static_cast(QUAZIP_EXTRA_NTFS_TIME_MAGIC); extra[timesPos + 1] = static_cast(QUAZIP_EXTRA_NTFS_TIME_MAGIC >> 8); // the size: extra[timesPos + 2] = 24; extra[timesPos + 3] = 0; ntfsTimesLength = 24; } if (timesPos == -1) { // No time tag in the NTFS record, need to add one. timesPos = ntfsPos + 4 + ntfsLength; extra.resize(extra.size() + 28); // Now we need to move the rest of the field // (possibly zero bytes, but memmove() is OK with that). // 0 ......... ntfsPos .. ntfsPos + 4 ... timesPos //
memmove(extra.data() + timesPos + 28, extra.data() + timesPos, extra.size() - 28 - timesPos); ntfsLength += 28; // now set the tag data extra[timesPos] = static_cast(QUAZIP_EXTRA_NTFS_TIME_MAGIC); extra[timesPos + 1] = static_cast(QUAZIP_EXTRA_NTFS_TIME_MAGIC >> 8); // the size: extra[timesPos + 2] = 24; extra[timesPos + 3] = 0; // zero the record memset(extra.data() + timesPos + 4, 0, 24); ntfsTimesLength = 24; } if (ntfsTimesLength < 24) { // Broken times field. OK, this is really unlikely, but just in case... size_t timesEnd = timesPos + 4 + ntfsTimesLength; extra.resize(extra.size() + (24 - ntfsTimesLength)); // Move it! // 0 ......... timesPos .... timesPos + 4 .. timesEnd //