Import Upstream version 5.92.0
This commit is contained in:
commit
01fbafca6a
|
@ -0,0 +1,4 @@
|
|||
#clang-format/tidy
|
||||
02f0505a0f56fd0680582eb565e439117f320b10
|
||||
c3cce473d8e3dab1d69617c48e76755326a99017
|
||||
f7d760797b5bb879e666110b3659b9963cb43a3a
|
|
@ -0,0 +1,27 @@
|
|||
# Ignore the following files
|
||||
*~
|
||||
*.diff
|
||||
*.kate-swp
|
||||
*.kdev4
|
||||
.kdev_include_paths
|
||||
*.kdevelop.pcs
|
||||
*.moc
|
||||
*.moc.cpp
|
||||
*.orig
|
||||
*.user
|
||||
.*.swp
|
||||
.swp.*
|
||||
Doxyfile
|
||||
Makefile
|
||||
avail
|
||||
random_seed
|
||||
/build*/
|
||||
CMakeLists.txt.user*
|
||||
*.unc-backup*
|
||||
.cmake/
|
||||
/.clang-format
|
||||
/compile_commands.json
|
||||
.clangd
|
||||
.idea
|
||||
/cmake-build*
|
||||
.cache
|
|
@ -0,0 +1,9 @@
|
|||
# SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
include:
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
|
|
@ -0,0 +1,7 @@
|
|||
Dependencies:
|
||||
- 'on': ['@all']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@same'
|
||||
|
||||
Options:
|
||||
test-before-installing: True
|
|
@ -0,0 +1,10 @@
|
|||
Maintainers:
|
||||
Mario Bensi <mbensi@ipsquad.net>
|
||||
David Faure <faure@kde.org>
|
||||
|
||||
Many other contributors, see git log.
|
||||
|
||||
For questions about this package, email kde-frameworks-devel@kde.org.
|
||||
|
||||
For bug reports, please use http://bugs.kde.org
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(KF_VERSION "5.92.0") # handled by release scripts
|
||||
project(KArchive VERSION ${KF_VERSION})
|
||||
|
||||
include(FeatureSummary)
|
||||
find_package(ECM 5.92.0 NO_MODULE)
|
||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
||||
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
|
||||
|
||||
include(KDEInstallDirs)
|
||||
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
|
||||
include(KDECMakeSettings)
|
||||
include(KDEGitCommitHooks)
|
||||
|
||||
include(ECMGenerateExportHeader)
|
||||
|
||||
set(REQUIRED_QT_VERSION 5.15.2)
|
||||
find_package(Qt${QT_MAJOR_VERSION}Core ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
find_package(ZLIB)
|
||||
set_package_properties(ZLIB PROPERTIES
|
||||
URL "http://www.zlib.net"
|
||||
DESCRIPTION "Support for gzip compressed files and data streams"
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Required by the core KDE libraries and some critical kioslaves"
|
||||
)
|
||||
|
||||
find_package(BZip2)
|
||||
set_package_properties(BZip2 PROPERTIES
|
||||
URL "https://sourceware.org/bzip2/"
|
||||
DESCRIPTION "Support for BZip2 compressed files and data streams"
|
||||
TYPE RECOMMENDED
|
||||
PURPOSE "Support for BZip2 compressed files and data streams"
|
||||
)
|
||||
|
||||
find_package(LibLZMA)
|
||||
set_package_properties(LibLZMA PROPERTIES
|
||||
URL "http://tukaani.org/xz/"
|
||||
DESCRIPTION "Support for xz compressed files and data streams"
|
||||
PURPOSE "Support for xz compressed files and data streams"
|
||||
)
|
||||
|
||||
find_package(PkgConfig)
|
||||
if (PkgConfig_FOUND)
|
||||
pkg_check_modules(LibZstd IMPORTED_TARGET "libzstd")
|
||||
endif()
|
||||
add_feature_info(LibZstd LibZstd_FOUND
|
||||
"Support for zstd compressed files and data streams"
|
||||
)
|
||||
|
||||
include(ECMSetupVersion)
|
||||
include(ECMGenerateHeaders)
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
include(ECMAddQch)
|
||||
include(ECMDeprecationSettings)
|
||||
|
||||
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
|
||||
|
||||
option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
|
||||
add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
|
||||
|
||||
ecm_setup_version(PROJECT
|
||||
VARIABLE_PREFIX KARCHIVE
|
||||
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/karchive_version.h"
|
||||
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5ArchiveConfigVersion.cmake"
|
||||
SOVERSION 5)
|
||||
|
||||
ecm_set_disabled_deprecation_versions(
|
||||
QT 5.15.2
|
||||
)
|
||||
|
||||
|
||||
add_subdirectory(src)
|
||||
if (BUILD_TESTING)
|
||||
add_subdirectory(autotests)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# create a Config.cmake and a ConfigVersion.cmake file and install them
|
||||
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Archive")
|
||||
|
||||
if (BUILD_QCH)
|
||||
ecm_install_qch_export(
|
||||
TARGETS KF5Archive_QCH
|
||||
FILE KF5ArchiveQchTargets.cmake
|
||||
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||
COMPONENT Devel
|
||||
)
|
||||
set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5ArchiveQchTargets.cmake\")")
|
||||
endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/KF5ArchiveConfig.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/KF5ArchiveConfig.cmake"
|
||||
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/karchive_version.h
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KArchive
|
||||
COMPONENT Devel)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/KF5ArchiveConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/KF5ArchiveConfigVersion.cmake"
|
||||
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||
COMPONENT Devel)
|
||||
|
||||
install(EXPORT KF5ArchiveTargets
|
||||
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||
FILE KF5ArchiveTargets.cmake
|
||||
NAMESPACE KF5::)
|
||||
|
||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
|
|
@ -0,0 +1,23 @@
|
|||
Here's how to build this framework:
|
||||
|
||||
* First create a convenient folder to build the code in:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
* Next run CMake to create the configuration files to be used in the build:
|
||||
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=debug \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr/local
|
||||
Various options can be passed to CMake to control how a project gets built, but these are the most common:
|
||||
|
||||
The first line tells CMake where it can find the source code that is to be built.
|
||||
The second line tells CMake what type of build is required, in this example a debug build that will include useful information for when we are debugging any the software.
|
||||
The third line tells CMake where to install the software.
|
||||
|
||||
* Finally compile and install
|
||||
|
||||
make
|
||||
make install
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(Qt@QT_MAJOR_VERSION@Core @REQUIRED_QT_VERSION@)
|
||||
|
||||
|
||||
set(KArchive_HAVE_ZLIB "@ZLIB_FOUND@")
|
||||
set(KArchive_HAVE_BZIP2 "@BZIP2_FOUND@")
|
||||
set(KArchive_HAVE_LZMA "@LIBLZMA_FOUND@")
|
||||
set(KArchive_HAVE_ZSTD "@LibZstd_FOUND@")
|
||||
|
||||
if (NOT @BUILD_SHARED_LIBS@)
|
||||
if (@ZLIB_FOUND@)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if (@BZIP2_FOUND@)
|
||||
find_dependency(BZip2)
|
||||
endif()
|
||||
|
||||
if (@LIBLZMA_FOUND@)
|
||||
find_dependency(LibLZMA)
|
||||
endif()
|
||||
|
||||
if (@LibZstd_FOUND@)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(LibZstd IMPORTED_TARGET "libzstd")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/KF5ArchiveTargets.cmake")
|
||||
@PACKAGE_INCLUDE_QCHTARGETS@
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) <year> <owner>. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,121 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
|
@ -0,0 +1,446 @@
|
|||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc.
|
||||
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||
document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the library GPL. It is numbered 2 because
|
||||
it goes with version 2 of the ordinary GPL.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom to share
|
||||
and change it. By contrast, the GNU General Public Licenses are intended to
|
||||
guarantee your freedom to share and change free software--to make sure the
|
||||
software is free for all its users.
|
||||
|
||||
This license, the Library General Public License, applies to some specially
|
||||
designated Free Software Foundation software, and to any other libraries whose
|
||||
authors decide to use it. You can use it for your libraries, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our
|
||||
General Public Licenses are designed to make sure that you have the freedom
|
||||
to distribute copies of free software (and charge for this service if you
|
||||
wish), that you receive source code or can get it if you want it, that you
|
||||
can change the software or use pieces of it in new free programs; and that
|
||||
you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid anyone to
|
||||
deny you these rights or to ask you to surrender the rights. These restrictions
|
||||
translate to certain responsibilities for you if you distribute copies of
|
||||
the library, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis or for
|
||||
a fee, you must give the recipients all the rights that we gave you. You must
|
||||
make sure that they, too, receive or can get the source code. If you link
|
||||
a program with the library, you must provide complete object files to the
|
||||
recipients so that they can relink them with the library, after making changes
|
||||
to the library and recompiling it. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Our method of protecting your rights has two steps: (1) copyright the library,
|
||||
and (2) offer you this license which gives you legal permission to copy, distribute
|
||||
and/or modify the library.
|
||||
|
||||
Also, for each distributor's protection, we want to make certain that everyone
|
||||
understands that there is no warranty for this free library. If the library
|
||||
is modified by someone else and passed on, we want its recipients to know
|
||||
that what they have is not the original version, so that any problems introduced
|
||||
by others will not reflect on the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software patents. We
|
||||
wish to avoid the danger that companies distributing free software will individually
|
||||
obtain patent licenses, thus in effect transforming the program into proprietary
|
||||
software. To prevent this, we have made it clear that any patent must be licensed
|
||||
for everyone's free use or not licensed at all.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary GNU
|
||||
General Public License, which was designed for utility programs. This license,
|
||||
the GNU Library General Public License, applies to certain designated libraries.
|
||||
This license is quite different from the ordinary one; be sure to read it
|
||||
in full, and don't assume that anything in it is the same as in the ordinary
|
||||
license.
|
||||
|
||||
The reason we have a separate public license for some libraries is that they
|
||||
blur the distinction we usually make between modifying or adding to a program
|
||||
and simply using it. Linking a program with a library, without changing the
|
||||
library, is in some sense simply using the library, and is analogous to running
|
||||
a utility program or application program. However, in a textual and legal
|
||||
sense, the linked executable is a combined work, a derivative of the original
|
||||
library, and the ordinary General Public License treats it as such.
|
||||
|
||||
Because of this blurred distinction, using the ordinary General Public License
|
||||
for libraries did not effectively promote software sharing, because most developers
|
||||
did not use the libraries. We concluded that weaker conditions might promote
|
||||
sharing better.
|
||||
|
||||
However, unrestricted linking of non-free programs would deprive the users
|
||||
of those programs of all benefit from the free status of the libraries themselves.
|
||||
This Library General Public License is intended to permit developers of non-free
|
||||
programs to use free libraries, while preserving your freedom as a user of
|
||||
such programs to change the free libraries that are incorporated in them.
|
||||
(We have not seen how to achieve this as regards changes in header files,
|
||||
but we have achieved it as regards changes in the actual functions of the
|
||||
Library.) The hope is that this will lead to faster development of free libraries.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification
|
||||
follow. Pay close attention to the difference between a "work based on the
|
||||
library" and a "work that uses the library". The former contains code derived
|
||||
from the library, while the latter only works together with the library.
|
||||
|
||||
Note that it is possible for a library to be covered by the ordinary General
|
||||
Public License rather than by this special one.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library which contains a
|
||||
notice placed by the copyright holder or other authorized party saying it
|
||||
may be distributed under the terms of this Library General Public License
|
||||
(also called "this License"). Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data prepared
|
||||
so as to be conveniently linked with application programs (which use some
|
||||
of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work which has
|
||||
been distributed under these terms. A "work based on the Library" means either
|
||||
the Library or any derivative work under copyright law: that is to say, a
|
||||
work containing the Library or a portion of it, either verbatim or with modifications
|
||||
and/or translated straightforwardly into another language. (Hereinafter, translation
|
||||
is included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for making modifications
|
||||
to it. For a library, complete source code means all the source code for all
|
||||
modules it contains, plus any associated interface definition files, plus
|
||||
the scripts used to control compilation and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not covered
|
||||
by this License; they are outside its scope. The act of running a program
|
||||
using the Library is not restricted, and output from such a program is covered
|
||||
only if its contents constitute a work based on the Library (independent of
|
||||
the use of the Library in a tool for writing it). Whether that is true depends
|
||||
on what the Library does and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's complete source
|
||||
code as you receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice and disclaimer
|
||||
of warranty; keep intact all the notices that refer to this License and to
|
||||
the absence of any warranty; and distribute a copy of this License along with
|
||||
the Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and you
|
||||
may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion of it,
|
||||
thus forming a work based on the Library, and copy and distribute such modifications
|
||||
or work under the terms of Section 1 above, provided that you also meet all
|
||||
of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices stating that
|
||||
you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no charge to all
|
||||
third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a table of
|
||||
data to be supplied by an application program that uses the facility, other
|
||||
than as an argument passed when the facility is invoked, then you must make
|
||||
a good faith effort to ensure that, in the event an application does not supply
|
||||
such function or table, the facility still operates, and performs whatever
|
||||
part of its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has a purpose
|
||||
that is entirely well-defined independent of the application. Therefore, Subsection
|
||||
2d requires that any application-supplied function or table used by this function
|
||||
must be optional: if the application does not supply it, the square root function
|
||||
must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If identifiable
|
||||
sections of that work are not derived from the Library, and can be reasonably
|
||||
considered independent and separate works in themselves, then this License,
|
||||
and its terms, do not apply to those sections when you distribute them as
|
||||
separate works. But when you distribute the same sections as part of a whole
|
||||
which is a work based on the Library, the distribution of the whole must be
|
||||
on the terms of this License, whose permissions for other licensees extend
|
||||
to the entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest your
|
||||
rights to work written entirely by you; rather, the intent is to exercise
|
||||
the right to control the distribution of derivative or collective works based
|
||||
on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library with
|
||||
the Library (or with a work based on the Library) on a volume of a storage
|
||||
or distribution medium does not bring the other work under the scope of this
|
||||
License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public License
|
||||
instead of this License to a given copy of the Library. To do this, you must
|
||||
alter all the notices that refer to this License, so that they refer to the
|
||||
ordinary GNU General Public License, version 2, instead of to this License.
|
||||
(If a newer version than version 2 of the ordinary GNU General Public License
|
||||
has appeared, then you can specify that version instead if you wish.) Do not
|
||||
make any other change in these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for that copy,
|
||||
so the ordinary GNU General Public License applies to all subsequent copies
|
||||
and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of the Library
|
||||
into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or derivative of
|
||||
it, under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you accompany it with the complete corresponding
|
||||
machine-readable source code, which must be distributed under the terms of
|
||||
Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy from a designated
|
||||
place, then offering equivalent access to copy the source code from the same
|
||||
place satisfies the requirement to distribute the source code, even though
|
||||
third parties are not compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the Library, but
|
||||
is designed to work with the Library by being compiled or linked with it,
|
||||
is called a "work that uses the Library". Such a work, in isolation, is not
|
||||
a derivative work of the Library, and therefore falls outside the scope of
|
||||
this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library creates an
|
||||
executable that is a derivative of the Library (because it contains portions
|
||||
of the Library), rather than a "work that uses the library". The executable
|
||||
is therefore covered by this License. Section 6 states terms for distribution
|
||||
of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file that
|
||||
is part of the Library, the object code for the work may be a derivative work
|
||||
of the Library even though the source code is not. Whether this is true is
|
||||
especially significant if the work can be linked without the Library, or if
|
||||
the work is itself a library. The threshold for this to be true is not precisely
|
||||
defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data structure layouts
|
||||
and accessors, and small macros and small inline functions (ten lines or less
|
||||
in length), then the use of the object file is unrestricted, regardless of
|
||||
whether it is legally a derivative work. (Executables containing this object
|
||||
code plus portions of the Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may distribute
|
||||
the object code for the work under the terms of Section 6. Any executables
|
||||
containing that work also fall under Section 6, whether or not they are linked
|
||||
directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also compile or link a "work
|
||||
that uses the Library" with the Library to produce a work containing portions
|
||||
of the Library, and distribute that work under terms of your choice, provided
|
||||
that the terms permit modification of the work for the customer's own use
|
||||
and reverse engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the Library
|
||||
is used in it and that the Library and its use are covered by this License.
|
||||
You must supply a copy of this License. If the work during execution displays
|
||||
copyright notices, you must include the copyright notice for the Library among
|
||||
them, as well as a reference directing the user to the copy of this License.
|
||||
Also, you must do one of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding machine-readable source
|
||||
code for the Library including whatever changes were used in the work (which
|
||||
must be distributed under Sections 1 and 2 above); and, if the work is an
|
||||
executable linked with the Library, with the complete machine-readable "work
|
||||
that uses the Library", as object code and/or source code, so that the user
|
||||
can modify the Library and then relink to produce a modified executable containing
|
||||
the modified Library. (It is understood that the user who changes the contents
|
||||
of definitions files in the Library will not necessarily be able to recompile
|
||||
the application to use the modified definitions.)
|
||||
|
||||
b) Accompany the work with a written offer, valid for at least three years,
|
||||
to give the same user the materials specified in Subsection 6a, above, for
|
||||
a charge no more than the cost of performing this distribution.
|
||||
|
||||
c) If distribution of the work is made by offering access to copy from a designated
|
||||
place, offer equivalent access to copy the above specified materials from
|
||||
the same place.
|
||||
|
||||
d) Verify that the user has already received a copy of these materials or
|
||||
that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the Library" must
|
||||
include any data and utility programs needed for reproducing the executable
|
||||
from it. However, as a special exception, the source code distributed need
|
||||
not include anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the operating
|
||||
system on which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license restrictions of
|
||||
other proprietary libraries that do not normally accompany the operating system.
|
||||
Such a contradiction means you cannot use both them and the Library together
|
||||
in an executable that you distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the Library side-by-side
|
||||
in a single library together with other library facilities not covered by
|
||||
this License, and distribute such a combined library, provided that the separate
|
||||
distribution of the work based on the Library and of the other library facilities
|
||||
is otherwise permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based on the
|
||||
Library, uncombined with any other library facilities. This must be distributed
|
||||
under the terms of the Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact that part of
|
||||
it is a work based on the Library, and explaining where to find the accompanying
|
||||
uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute the Library
|
||||
except as expressly provided under this License. Any attempt otherwise to
|
||||
copy, modify, sublicense, link with, or distribute the Library is void, and
|
||||
will automatically terminate your rights under this License. However, parties
|
||||
who have received copies, or rights, from you under this License will not
|
||||
have their licenses terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not signed
|
||||
it. However, nothing else grants you permission to modify or distribute the
|
||||
Library or its derivative works. These actions are prohibited by law if you
|
||||
do not accept this License. Therefore, by modifying or distributing the Library
|
||||
(or any work based on the Library), you indicate your acceptance of this License
|
||||
to do so, and all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the Library),
|
||||
the recipient automatically receives a license from the original licensor
|
||||
to copy, distribute, link with or modify the Library subject to these terms
|
||||
and conditions. You may not impose any further restrictions on the recipients'
|
||||
exercise of the rights granted herein. You are not responsible for enforcing
|
||||
compliance by third parties to this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent infringement
|
||||
or for any other reason (not limited to patent issues), conditions are imposed
|
||||
on you (whether by court order, agreement or otherwise) that contradict the
|
||||
conditions of this License, they do not excuse you from the conditions of
|
||||
this License. If you cannot distribute so as to satisfy simultaneously your
|
||||
obligations under this License and any other pertinent obligations, then as
|
||||
a consequence you may not distribute the Library at all. For example, if a
|
||||
patent license would not permit royalty-free redistribution of the Library
|
||||
by all those who receive copies directly or indirectly through you, then the
|
||||
only way you could satisfy both it and this License would be to refrain entirely
|
||||
from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any patents
|
||||
or other property right claims or to contest validity of any such claims;
|
||||
this section has the sole purpose of protecting the integrity of the free
|
||||
software distribution system which is implemented by public license practices.
|
||||
Many people have made generous contributions to the wide range of software
|
||||
distributed through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing to
|
||||
distribute software through any other system and a licensee cannot impose
|
||||
that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to be a
|
||||
consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in certain
|
||||
countries either by patents or by copyrighted interfaces, the original copyright
|
||||
holder who places the Library under this License may add an explicit geographical
|
||||
distribution limitation excluding those countries, so that distribution is
|
||||
permitted only in or among countries not thus excluded. In such case, this
|
||||
License incorporates the limitation as if written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new versions of
|
||||
the Library General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to address
|
||||
new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library specifies
|
||||
a version number of this License which applies to it and "any later version",
|
||||
you have the option of following the terms and conditions either of that version
|
||||
or of any later version published by the Free Software Foundation. If the
|
||||
Library does not specify a license version number, you may choose any version
|
||||
ever published by the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free programs
|
||||
whose distribution conditions are incompatible with these, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free Software
|
||||
Foundation, write to the Free Software Foundation; we sometimes make exceptions
|
||||
for this. Our decision will be guided by the two goals of preserving the free
|
||||
status of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
|
||||
THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
|
||||
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY
|
||||
"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
|
||||
OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
||||
THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE
|
||||
OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
|
||||
OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES
|
||||
OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH
|
||||
HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest possible
|
||||
use to the public, we recommend making it free software that everyone can
|
||||
redistribute and change. You can do so by permitting redistribution under
|
||||
these terms (or, alternatively, under the terms of the ordinary General Public
|
||||
License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is safest
|
||||
to attach them to the start of each source file to most effectively convey
|
||||
the exclusion of warranty; and each file should have at least the "copyright"
|
||||
line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the library's name and an idea of what it does.
|
||||
|
||||
Copyright (C) year name of author
|
||||
|
||||
This library is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Library General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your school,
|
||||
if any, to sign a "copyright disclaimer" for the library, if necessary. Here
|
||||
is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in
|
||||
|
||||
the library `Frob' (a library for tweaking knobs) written
|
||||
|
||||
by James Random Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1990
|
||||
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
|
@ -0,0 +1,22 @@
|
|||
# KArchive
|
||||
|
||||
Reading, creating, and manipulating file archives
|
||||
|
||||
## Introduction
|
||||
|
||||
KArchive provides classes for easy reading, creation and manipulation of
|
||||
"archive" formats like ZIP and TAR.
|
||||
|
||||
It also provides transparent compression and decompression of data, like the
|
||||
GZip format, via a subclass of QIODevice.
|
||||
|
||||
## Usage
|
||||
|
||||
If you want to read and write compressed data, just create an instance of
|
||||
KCompressionDevice and write to or read from that.
|
||||
|
||||
If you want to read and write archive formats, create an instance of the
|
||||
appropriate subclass of KArchive (eg: K7Zip for 7-Zip files). You may need to
|
||||
combine this with usage of KCompressionDevice (see the API documentation for the
|
||||
relevant KArchive subclass for details).
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
remove_definitions(-DQT_NO_CAST_FROM_ASCII)
|
||||
|
||||
include(ECMAddTests)
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION}Test ${REQUIRED_QT_VERSION} CONFIG QUIET)
|
||||
find_package(Qt${QT_MAJOR_VERSION}Network)
|
||||
|
||||
if(NOT Qt${QT_MAJOR_VERSION}Test_FOUND)
|
||||
message(STATUS "Qt${QT_MAJOR_VERSION}Test not found, autotests will not be built.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
ecm_add_tests(
|
||||
karchivetest.cpp
|
||||
kfiltertest.cpp
|
||||
deprecatedtest.cpp
|
||||
LINK_LIBRARIES KF5::Archive Qt${QT_MAJOR_VERSION}::Test
|
||||
)
|
||||
|
||||
target_link_libraries(kfiltertest ZLIB::ZLIB)
|
||||
|
||||
########### klimitediodevicetest ###############
|
||||
|
||||
ecm_add_test(
|
||||
klimitediodevicetest.cpp
|
||||
../src/klimitediodevice.cpp
|
||||
TEST_NAME klimitediodevicetest
|
||||
LINK_LIBRARIES Qt${QT_MAJOR_VERSION}::Test
|
||||
)
|
||||
target_include_directories(klimitediodevicetest
|
||||
PRIVATE $<TARGET_PROPERTY:KF5Archive,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
|
||||
########## kcompressiondevicetest ##############
|
||||
if (Qt${QT_MAJOR_VERSION}Network_FOUND)
|
||||
ecm_add_test(
|
||||
kcompressiondevicetest.cpp
|
||||
LINK_LIBRARIES KF5::Archive Qt${QT_MAJOR_VERSION}::Test Qt${QT_MAJOR_VERSION}::Network
|
||||
)
|
||||
|
||||
set(testDir $<TARGET_FILE_DIR:kcompressiondevicetest>)
|
||||
get_filename_component(topdir ${CMAKE_CURRENT_SOURCE_DIR}/.. ABSOLUTE)
|
||||
|
||||
add_custom_command(TARGET kcompressiondevicetest POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E tar czf
|
||||
${testDir}/kcompressiondevice_test.tar.gz examples
|
||||
WORKING_DIRECTORY ${topdir})
|
||||
add_custom_command(TARGET kcompressiondevicetest POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cjf
|
||||
${testDir}/kcompressiondevice_test.tar.bz2 examples
|
||||
WORKING_DIRECTORY ${topdir})
|
||||
|
||||
if (LIBLZMA_FOUND)
|
||||
add_custom_command(TARGET kcompressiondevicetest POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cJf
|
||||
${testDir}/kcompressiondevice_test.tar.xz examples
|
||||
WORKING_DIRECTORY ${topdir})
|
||||
endif()
|
||||
|
||||
if (LibZstd_FOUND)
|
||||
# cmake doesn't support creating zstd files so run tar directly
|
||||
# which is a bit annoying since not all tars support zstd either
|
||||
# so we first check that the installed tar has the --zstd
|
||||
# option and then we check that the zstd binary is available
|
||||
|
||||
execute_process(COMMAND tar --help OUTPUT_VARIABLE TAR_OUTPUT)
|
||||
if (TAR_OUTPUT MATCHES ".*--zstd.*")
|
||||
find_program(ZSTD_FOUND zstd)
|
||||
if (ZSTD_FOUND)
|
||||
add_custom_command(TARGET kcompressiondevicetest POST_BUILD
|
||||
COMMAND tar --zstd -cf
|
||||
${testDir}/kcompressiondevice_test.tar.zst examples
|
||||
WORKING_DIRECTORY ${topdir})
|
||||
|
||||
target_compile_definitions(kcompressiondevicetest PRIVATE HAVE_ZSTD_SUPPORT_FILE)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,59 @@
|
|||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2006, 2010 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <kzip.h>
|
||||
|
||||
#include <QTest>
|
||||
|
||||
static const char s_zipFileName[] = "deprecatedtest.zip";
|
||||
|
||||
class DeprecatedTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
#if KARCHIVE_ENABLE_DEPRECATED_SINCE(5, 0)
|
||||
private Q_SLOTS:
|
||||
void testKArchiveWriteFile()
|
||||
{
|
||||
KZip zip(s_zipFileName);
|
||||
|
||||
QVERIFY(zip.open(QIODevice::WriteOnly));
|
||||
|
||||
const QByteArray fileData("There could be a fire, if there is smoke.");
|
||||
const QString fileName = QStringLiteral("wisdom");
|
||||
QVERIFY(zip.writeFile(fileName, "konqi", "dragons", fileData.constData(), fileData.size()));
|
||||
|
||||
QVERIFY(zip.close());
|
||||
|
||||
QVERIFY(zip.open(QIODevice::ReadOnly));
|
||||
|
||||
const KArchiveDirectory *dir = zip.directory();
|
||||
QVERIFY(dir != nullptr);
|
||||
const QStringList listing = dir->entries();
|
||||
QCOMPARE(listing.count(), 1);
|
||||
QCOMPARE(listing.at(0), fileName);
|
||||
const KArchiveEntry *entry = dir->entry(listing.at(0));
|
||||
QCOMPARE(entry->permissions(), mode_t(0100644));
|
||||
QVERIFY(!entry->isDirectory());
|
||||
const KArchiveFile *fileEntry = static_cast<const KArchiveFile *>(entry);
|
||||
QCOMPARE(fileEntry->size(), fileData.size());
|
||||
QCOMPARE(fileEntry->data(), fileData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see QTest::cleanupTestCase()
|
||||
*/
|
||||
void cleanupTestCase()
|
||||
{
|
||||
QFile::remove(s_zipFileName);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
QTEST_MAIN(DeprecatedTest)
|
||||
|
||||
#include <deprecatedtest.moc>
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,128 @@
|
|||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2006 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KARCHIVETEST_H
|
||||
#define KARCHIVETEST_H
|
||||
|
||||
#include <QObject>
|
||||
#include <config-compression.h>
|
||||
|
||||
class KArchiveTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void setupData();
|
||||
void setup7ZipData();
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testEmptyFilename();
|
||||
void testNullDevice();
|
||||
void testNonExistentFile();
|
||||
void testCreateTar_data();
|
||||
void testCreateTar();
|
||||
void testCreateTarXXX_data()
|
||||
{
|
||||
setupData();
|
||||
}
|
||||
void testCreateTarXXX();
|
||||
void testReadTar_data()
|
||||
{
|
||||
setupData();
|
||||
}
|
||||
void testReadTar();
|
||||
void testUncompress_data()
|
||||
{
|
||||
setupData();
|
||||
}
|
||||
void testUncompress();
|
||||
void testTarFileData_data()
|
||||
{
|
||||
setupData();
|
||||
}
|
||||
void testTarFileData();
|
||||
void testTarCopyTo_data()
|
||||
{
|
||||
setupData();
|
||||
}
|
||||
void testTarCopyTo();
|
||||
void testTarReadWrite_data()
|
||||
{
|
||||
setupData();
|
||||
}
|
||||
void testTarReadWrite();
|
||||
void testTarMaxLength_data();
|
||||
void testTarMaxLength();
|
||||
void testTarGlobalHeader();
|
||||
void testTarPrefix();
|
||||
void testTarDirectoryForgotten();
|
||||
void testTarEmptyFileMissingDir();
|
||||
void testTarRootDir();
|
||||
void testTarDirectoryTwice();
|
||||
void testTarIgnoreRelativePathOutsideArchive();
|
||||
void testTarLongNonASCIINames();
|
||||
void testTarShortNonASCIINames();
|
||||
|
||||
void testCreateZip();
|
||||
void testCreateZipError();
|
||||
void testReadZipError();
|
||||
void testReadZip();
|
||||
void testZipFileData();
|
||||
void testZipCopyTo();
|
||||
void testZipMaxLength();
|
||||
void testZipWithNonLatinFileNames();
|
||||
void testZipWithOverwrittenFileName();
|
||||
void testZipAddLocalDirectory();
|
||||
void testZipReadRedundantDataDescriptor_data();
|
||||
void testZipReadRedundantDataDescriptor();
|
||||
void testZipDirectoryPermissions();
|
||||
void testZipUnusualButValid();
|
||||
void testZipDuplicateNames();
|
||||
void testZipWithinZip();
|
||||
|
||||
void testRcc();
|
||||
|
||||
void testAr();
|
||||
|
||||
#if HAVE_XZ_SUPPORT
|
||||
void testCreate7Zip_data()
|
||||
{
|
||||
setup7ZipData();
|
||||
}
|
||||
void testCreate7Zip();
|
||||
void testRead7Zip_data()
|
||||
{
|
||||
setup7ZipData();
|
||||
}
|
||||
void testRead7Zip();
|
||||
void test7ZipFileData_data()
|
||||
{
|
||||
setup7ZipData();
|
||||
}
|
||||
void test7ZipFileData();
|
||||
void test7ZipCopyTo_data()
|
||||
{
|
||||
setup7ZipData();
|
||||
}
|
||||
void test7ZipCopyTo();
|
||||
void test7ZipReadWrite_data()
|
||||
{
|
||||
setup7ZipData();
|
||||
}
|
||||
void test7ZipReadWrite();
|
||||
void test7ZipMaxLength_data()
|
||||
{
|
||||
setup7ZipData();
|
||||
}
|
||||
void test7ZipMaxLength();
|
||||
#endif
|
||||
|
||||
void cleanupTestCase();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,223 @@
|
|||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 Luiz Romário Santana Rios <luizromario@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kcompressiondevicetest.h"
|
||||
#include "kcompressiondevice_p.h"
|
||||
|
||||
#include <config-compression.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTest>
|
||||
#include <QVector>
|
||||
|
||||
QTEST_MAIN(KCompressionDeviceTest)
|
||||
|
||||
static QString archiveFileName(const QString &extension)
|
||||
{
|
||||
return QFINDTESTDATA(QString("kcompressiondevice_test.%1").arg(extension));
|
||||
}
|
||||
|
||||
QNetworkReply *KCompressionDeviceTest::getArchive(const QString &extension)
|
||||
{
|
||||
const QString kcompressionTest = archiveFileName(extension);
|
||||
QNetworkReply *r = qnam.get(QNetworkRequest(QUrl::fromLocalFile(kcompressionTest)));
|
||||
|
||||
QEventLoop l;
|
||||
connect(&qnam, &QNetworkAccessManager::finished, &l, &QEventLoop::quit);
|
||||
l.exec();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
QString KCompressionDeviceTest::formatExtension(KCompressionDevice::CompressionType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case KCompressionDevice::GZip:
|
||||
return "tar.gz";
|
||||
case KCompressionDevice::BZip2:
|
||||
return "tar.bz2";
|
||||
case KCompressionDevice::Xz:
|
||||
return "tar.xz";
|
||||
case KCompressionDevice::Zstd:
|
||||
return "tar.zst";
|
||||
case KCompressionDevice::None:
|
||||
return QString();
|
||||
}
|
||||
return QString(); // silence compiler warning
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::setDeviceToArchive(QIODevice *d, KCompressionDevice::CompressionType type)
|
||||
{
|
||||
KCompressionDevice *devRawPtr = new KCompressionDevice(d, true, type);
|
||||
archive.reset(new KTar(devRawPtr));
|
||||
device.reset(devRawPtr);
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testBufferedDevice(KCompressionDevice::CompressionType type)
|
||||
{
|
||||
QNetworkReply *r = getArchive(formatExtension(type));
|
||||
const QByteArray data = r->readAll();
|
||||
QVERIFY(!data.isEmpty());
|
||||
const int expectedSize = QFileInfo(archiveFileName(formatExtension(type))).size();
|
||||
QVERIFY(expectedSize > 0);
|
||||
QCOMPARE(data.size(), expectedSize);
|
||||
QBuffer *b = new QBuffer;
|
||||
b->setData(data);
|
||||
|
||||
setDeviceToArchive(b, type);
|
||||
testExtraction();
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testExtraction()
|
||||
{
|
||||
QTemporaryDir temp;
|
||||
QString oldCurrentDir = QDir::currentPath();
|
||||
QDir::setCurrent(temp.path());
|
||||
|
||||
QVERIFY(archive->open(QIODevice::ReadOnly));
|
||||
QVERIFY(archive->directory()->copyTo("."));
|
||||
QVERIFY(QDir("examples").exists());
|
||||
QVERIFY(QDir("examples/bzip2gzip").exists());
|
||||
QVERIFY(QDir("examples/helloworld").exists());
|
||||
QVERIFY(QDir("examples/tarlocalfiles").exists());
|
||||
QVERIFY(QDir("examples/unzipper").exists());
|
||||
|
||||
const QStringList fileList = {QStringLiteral("examples/bzip2gzip/CMakeLists.txt"),
|
||||
QStringLiteral("examples/bzip2gzip/main.cpp"),
|
||||
QStringLiteral("examples/helloworld/CMakeLists.txt"),
|
||||
QStringLiteral("examples/helloworld/helloworld.pro"),
|
||||
QStringLiteral("examples/helloworld/main.cpp"),
|
||||
QStringLiteral("examples/tarlocalfiles/CMakeLists.txt"),
|
||||
QStringLiteral("examples/tarlocalfiles/main.cpp"),
|
||||
QStringLiteral("examples/unzipper/CMakeLists.txt"),
|
||||
QStringLiteral("examples/unzipper/main.cpp")};
|
||||
|
||||
for (const QString &s : fileList) {
|
||||
QFileInfo extractedFile(s);
|
||||
QFileInfo sourceFile(QFINDTESTDATA("../" + s));
|
||||
|
||||
QVERIFY(extractedFile.exists());
|
||||
QCOMPARE(extractedFile.size(), sourceFile.size());
|
||||
}
|
||||
QDir::setCurrent(oldCurrentDir);
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::regularKTarUsage()
|
||||
{
|
||||
archive.reset(new KTar(QFINDTESTDATA("kcompressiondevice_test.tar.gz")));
|
||||
device.reset();
|
||||
|
||||
testExtraction();
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testGZipBufferedDevice()
|
||||
{
|
||||
testBufferedDevice(KCompressionDevice::GZip);
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testBZip2BufferedDevice()
|
||||
{
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
testBufferedDevice(KCompressionDevice::BZip2);
|
||||
#else
|
||||
QSKIP("This test needs bzip2 support");
|
||||
#endif
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testXzBufferedDevice()
|
||||
{
|
||||
#if HAVE_XZ_SUPPORT
|
||||
testBufferedDevice(KCompressionDevice::Xz);
|
||||
#else
|
||||
QSKIP("This test needs xz support");
|
||||
#endif
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testZstdBufferedDevice()
|
||||
{
|
||||
#ifdef HAVE_ZSTD_SUPPORT_FILE
|
||||
testBufferedDevice(KCompressionDevice::Zstd);
|
||||
#else
|
||||
QSKIP("This test needs zstd support");
|
||||
#endif
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testWriteErrorOnOpen()
|
||||
{
|
||||
// GIVEN
|
||||
QString fileName("/I/dont/exist/kcompressiondevicetest-write.gz");
|
||||
KCompressionDevice dev(fileName, KCompressionDevice::GZip);
|
||||
// WHEN
|
||||
QVERIFY(!dev.open(QIODevice::WriteOnly));
|
||||
// THEN
|
||||
QCOMPARE(dev.error(), QFileDevice::OpenError);
|
||||
#ifdef Q_OS_WIN
|
||||
QCOMPARE(dev.errorString(), QStringLiteral("The system cannot find the path specified."));
|
||||
#else
|
||||
QCOMPARE(dev.errorString(), QStringLiteral("No such file or directory"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testWriteErrorOnClose()
|
||||
{
|
||||
// GIVEN
|
||||
QFile file("kcompressiondevicetest-write.gz");
|
||||
KCompressionDevice dev(&file, false, KCompressionDevice::GZip);
|
||||
QVERIFY(dev.open(QIODevice::WriteOnly));
|
||||
const QByteArray data = "Hello world";
|
||||
QCOMPARE(dev.write(data), data.size());
|
||||
// This is nasty, it's just a way to try and trigger an error on flush, without filling up a partition first ;)
|
||||
file.close();
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
QTest::ignoreMessage(QtWarningMsg, "QIODevice::write (QFile, \"kcompressiondevicetest-write.gz\"): ReadOnly device");
|
||||
|
||||
// WHEN
|
||||
dev.close(); // I want a QVERIFY here... https://bugreports.qt.io/browse/QTBUG-70033
|
||||
|
||||
// THEN
|
||||
QCOMPARE(int(dev.error()), int(QFileDevice::WriteError));
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testSeekReadUncompressedBuffer_data()
|
||||
{
|
||||
QTest::addColumn<int>("dataSize");
|
||||
QTest::addColumn<int>("realDataPos");
|
||||
QTest::newRow("1.5buffer") << BUFFER_SIZE + BUFFER_SIZE / 2 << BUFFER_SIZE;
|
||||
QTest::newRow("5seekbuffer") << 5 * SEEK_BUFFER_SIZE << 4 * SEEK_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
void KCompressionDeviceTest::testSeekReadUncompressedBuffer()
|
||||
{
|
||||
QFETCH(int, dataSize);
|
||||
QFETCH(int, realDataPos);
|
||||
|
||||
QByteArray ba(dataSize, 0);
|
||||
|
||||
// all data is zero except after realDataPos that it's 0 to 9
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ba[realDataPos + i] = i;
|
||||
}
|
||||
|
||||
QBuffer b;
|
||||
b.setData(ba);
|
||||
QVERIFY(b.open(QIODevice::ReadOnly));
|
||||
|
||||
KCompressionDevice kcd(&b, false, KCompressionDevice::GZip);
|
||||
QVERIFY(kcd.open(QIODevice::ReadOnly));
|
||||
QVERIFY(kcd.seek(realDataPos));
|
||||
|
||||
// the 10 bytes after realDataPos should be 0 to 9
|
||||
const QByteArray kcdData = kcd.read(10);
|
||||
QCOMPARE(kcdData.size(), 10);
|
||||
for (int i = 0; i < kcdData.size(); ++i) {
|
||||
QCOMPARE(kcdData[i], i);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 Luiz Romário Santana Rios <luizromario@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPRESSIONDEVICETEST_H
|
||||
#define KCOMPRESSIONDEVICETEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include <KCompressionDevice>
|
||||
#include <KTar>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
class KCompressionDeviceTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QNetworkReply *getArchive(const QString &extension);
|
||||
QString formatExtension(KCompressionDevice::CompressionType type) const;
|
||||
|
||||
void setDeviceToArchive(QIODevice *d, KCompressionDevice::CompressionType type);
|
||||
|
||||
void testBufferedDevice(KCompressionDevice::CompressionType type);
|
||||
void testExtraction();
|
||||
|
||||
QNetworkAccessManager qnam;
|
||||
QScopedPointer<KCompressionDevice> device;
|
||||
QScopedPointer<KTar> archive;
|
||||
|
||||
private Q_SLOTS:
|
||||
void regularKTarUsage();
|
||||
void testGZipBufferedDevice();
|
||||
void testBZip2BufferedDevice();
|
||||
void testXzBufferedDevice();
|
||||
void testZstdBufferedDevice();
|
||||
|
||||
void testWriteErrorOnOpen();
|
||||
void testWriteErrorOnClose();
|
||||
|
||||
void testSeekReadUncompressedBuffer_data();
|
||||
void testSeekReadUncompressedBuffer();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2002-2005 David Faure <faure@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kfiltertest.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QTest>
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QTextCodec>
|
||||
#endif
|
||||
#include <QRandomGenerator>
|
||||
#include <QSaveFile>
|
||||
|
||||
#include "kcompressiondevice.h"
|
||||
#include "kfilterbase.h"
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QTextStream>
|
||||
#include <config-compression.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
QTEST_MAIN(KFilterTest)
|
||||
|
||||
void KFilterTest::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<KCompressionDevice::CompressionType>();
|
||||
const QString currentdir = QDir::currentPath();
|
||||
pathgz = currentdir + "/test.gz";
|
||||
pathbz2 = currentdir + "/test.bz2";
|
||||
pathxz = currentdir + "/test.xz";
|
||||
pathnone = currentdir + "/test.txt";
|
||||
pathzstd = currentdir + "/test.zst";
|
||||
|
||||
// warning, update the COMPAREs in test_block_write() if changing the test data...
|
||||
testData = "hello world\n";
|
||||
}
|
||||
|
||||
void KFilterTest::test_block_write(const QString &fileName, const QByteArray &data)
|
||||
{
|
||||
KCompressionDevice dev(fileName);
|
||||
bool ok = dev.open(QIODevice::WriteOnly);
|
||||
QVERIFY(ok);
|
||||
|
||||
const int ret = dev.write(data);
|
||||
QCOMPARE(ret, data.size());
|
||||
|
||||
dev.close();
|
||||
|
||||
QVERIFY(QFile::exists(fileName));
|
||||
}
|
||||
|
||||
void KFilterTest::test_block_write()
|
||||
{
|
||||
qDebug() << " -- test_block_write gzip -- ";
|
||||
test_block_write(pathgz, testData);
|
||||
QCOMPARE(QFileInfo(pathgz).size(), 33LL); // size of test.gz
|
||||
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
qDebug() << " -- test_block_write bzip2 -- ";
|
||||
test_block_write(pathbz2, testData);
|
||||
QCOMPARE(QFileInfo(pathbz2).size(), 52LL); // size of test.bz2
|
||||
#endif
|
||||
|
||||
#if HAVE_XZ_SUPPORT
|
||||
qDebug() << " -- test_block_write xz -- ";
|
||||
test_block_write(pathxz, testData);
|
||||
QCOMPARE(QFileInfo(pathxz).size(), 64LL); // size of test.lzma
|
||||
#endif
|
||||
|
||||
qDebug() << " -- test_block_write none -- ";
|
||||
test_block_write(pathnone, testData);
|
||||
QCOMPARE(QFileInfo(pathnone).size(), 12LL); // size of test.txt
|
||||
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
qDebug() << " -- test_block_write zstd -- ";
|
||||
test_block_write(pathzstd, testData);
|
||||
QCOMPARE(QFileInfo(pathzstd).size(), 24LL); // size of test.zst
|
||||
#endif
|
||||
}
|
||||
|
||||
void KFilterTest::test_biggerWrites()
|
||||
{
|
||||
const QString currentdir = QDir::currentPath();
|
||||
const QString outFile = currentdir + "/test_big.gz";
|
||||
// Find the out-of-bounds from #157706/#188415
|
||||
QByteArray data;
|
||||
data.reserve(10000);
|
||||
auto *generator = QRandomGenerator::global();
|
||||
// Prepare test data
|
||||
for (int i = 0; i < 8170; ++i) {
|
||||
data.append((char)(generator->bounded(256)));
|
||||
}
|
||||
QCOMPARE(data.size(), 8170);
|
||||
// 8170 random bytes compress to 8194 bytes due to the gzip header/footer.
|
||||
// Now we can go one by one until we pass 8192.
|
||||
// On 32 bit systems it crashed with data.size()=8173, before the "no room for footer yet" fix.
|
||||
int compressedSize = 0;
|
||||
while (compressedSize < 8200) {
|
||||
test_block_write(outFile, data);
|
||||
compressedSize = QFileInfo(outFile).size();
|
||||
qDebug() << data.size() << "compressed into" << compressedSize;
|
||||
// Test data is valid
|
||||
test_readall(outFile, QString::fromLatin1("application/gzip"), data);
|
||||
|
||||
data.append((char)(generator->bounded(256)));
|
||||
}
|
||||
}
|
||||
|
||||
void KFilterTest::test_block_read(const QString &fileName)
|
||||
{
|
||||
KCompressionDevice dev(fileName);
|
||||
bool ok = dev.open(QIODevice::ReadOnly);
|
||||
QVERIFY(ok);
|
||||
|
||||
QByteArray array(1024, '\0');
|
||||
QByteArray read;
|
||||
int n;
|
||||
while ((n = dev.read(array.data(), array.size()))) {
|
||||
QVERIFY(n > 0);
|
||||
read += QByteArray(array.constData(), n);
|
||||
// qDebug() << "read returned " << n;
|
||||
// qDebug() << "read='" << read << "'";
|
||||
|
||||
// pos() has no real meaning on sequential devices
|
||||
// Ah, but kzip uses kfilterdev as a non-sequential device...
|
||||
|
||||
QCOMPARE((int)dev.pos(), (int)read.size());
|
||||
// qDebug() << "dev.at = " << dev->at();
|
||||
}
|
||||
QCOMPARE(read, testData);
|
||||
|
||||
// Test seeking back
|
||||
ok = dev.seek(0);
|
||||
// test readAll
|
||||
read = dev.readAll();
|
||||
QCOMPARE(read.size(), testData.size());
|
||||
QCOMPARE(read, testData);
|
||||
|
||||
dev.close();
|
||||
}
|
||||
|
||||
void KFilterTest::test_block_read()
|
||||
{
|
||||
qDebug() << " -- test_block_read gzip -- ";
|
||||
test_block_read(pathgz);
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
qDebug() << " -- test_block_read bzip2 -- ";
|
||||
test_block_read(pathbz2);
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
qDebug() << " -- test_block_read lzma -- ";
|
||||
test_block_read(pathxz);
|
||||
#endif
|
||||
qDebug() << " -- test_block_read none -- ";
|
||||
test_block_read(pathnone);
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
qDebug() << " -- test_block_read zstd -- ";
|
||||
test_block_read(pathzstd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KFilterTest::test_getch(const QString &fileName)
|
||||
{
|
||||
KCompressionDevice dev(fileName);
|
||||
bool ok = dev.open(QIODevice::ReadOnly);
|
||||
QVERIFY(ok);
|
||||
QByteArray read;
|
||||
char ch;
|
||||
while (dev.getChar(&ch)) {
|
||||
// printf("%c",ch);
|
||||
read += ch;
|
||||
}
|
||||
dev.close();
|
||||
QCOMPARE(read, testData);
|
||||
}
|
||||
|
||||
void KFilterTest::test_getch()
|
||||
{
|
||||
qDebug() << " -- test_getch gzip -- ";
|
||||
test_getch(pathgz);
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
qDebug() << " -- test_getch bzip2 -- ";
|
||||
test_getch(pathbz2);
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
qDebug() << " -- test_getch lzma -- ";
|
||||
test_getch(pathxz);
|
||||
#endif
|
||||
qDebug() << " -- test_getch none -- ";
|
||||
test_getch(pathnone);
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
qDebug() << " -- test_getch zstd -- ";
|
||||
test_getch(pathzstd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KFilterTest::test_textstream(const QString &fileName)
|
||||
{
|
||||
KCompressionDevice dev(fileName);
|
||||
bool ok = dev.open(QIODevice::ReadOnly);
|
||||
QVERIFY(ok);
|
||||
QTextStream ts(&dev);
|
||||
QString readStr = ts.readAll();
|
||||
dev.close();
|
||||
|
||||
QByteArray read = readStr.toLatin1();
|
||||
QCOMPARE(read, testData);
|
||||
}
|
||||
|
||||
void KFilterTest::test_textstream()
|
||||
{
|
||||
qDebug() << " -- test_textstream gzip -- ";
|
||||
test_textstream(pathgz);
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
qDebug() << " -- test_textstream bzip2 -- ";
|
||||
test_textstream(pathbz2);
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
qDebug() << " -- test_textstream lzma -- ";
|
||||
test_textstream(pathxz);
|
||||
#endif
|
||||
qDebug() << " -- test_textstream none -- ";
|
||||
test_textstream(pathnone);
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
qDebug() << " -- test_textstream zstd -- ";
|
||||
test_textstream(pathzstd);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KFilterTest::test_readall(const QString &fileName, const QString &mimeType, const QByteArray &expectedData)
|
||||
{
|
||||
QFile file(fileName);
|
||||
KCompressionDevice::CompressionType type = KCompressionDevice::compressionTypeForMimeType(mimeType);
|
||||
KCompressionDevice flt(&file, false, type);
|
||||
bool ok = flt.open(QIODevice::ReadOnly);
|
||||
QVERIFY(ok);
|
||||
const QByteArray read = flt.readAll();
|
||||
QCOMPARE(read.size(), expectedData.size());
|
||||
QCOMPARE(read, expectedData);
|
||||
|
||||
// Now using QBuffer
|
||||
file.seek(0);
|
||||
QByteArray compressedData = file.readAll();
|
||||
QVERIFY(!compressedData.isEmpty());
|
||||
QBuffer buffer(&compressedData);
|
||||
KCompressionDevice device(&buffer, false, type);
|
||||
QVERIFY(device.open(QIODevice::ReadOnly));
|
||||
QCOMPARE(device.readAll(), expectedData);
|
||||
}
|
||||
|
||||
void KFilterTest::test_readall()
|
||||
{
|
||||
qDebug() << " -- test_readall gzip -- ";
|
||||
test_readall(pathgz, QString::fromLatin1("application/gzip"), testData);
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
qDebug() << " -- test_readall bzip2 -- ";
|
||||
test_readall(pathbz2, QString::fromLatin1("application/x-bzip"), testData);
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
qDebug() << " -- test_readall lzma -- ";
|
||||
test_readall(pathxz, QString::fromLatin1("application/x-xz"), testData);
|
||||
#endif
|
||||
qDebug() << " -- test_readall gzip-derived -- ";
|
||||
test_readall(pathgz, QString::fromLatin1("image/svg+xml-compressed"), testData);
|
||||
|
||||
qDebug() << " -- test_readall none -- ";
|
||||
test_readall(pathnone, QString::fromLatin1("text/plain"), testData);
|
||||
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
qDebug() << " -- test_readall zstd -- ";
|
||||
test_readall(pathzstd, QString::fromLatin1("application/zstd"), testData);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KFilterTest::test_uncompressed()
|
||||
{
|
||||
// Can KCompressionDevice handle uncompressed data even when using gzip decompression?
|
||||
qDebug() << " -- test_uncompressed -- ";
|
||||
QBuffer buffer(&testData);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
KCompressionDevice::CompressionType type = KCompressionDevice::compressionTypeForMimeType(QString::fromLatin1("application/gzip"));
|
||||
KCompressionDevice flt(&buffer, false, type);
|
||||
bool ok = flt.open(QIODevice::ReadOnly);
|
||||
QVERIFY(ok);
|
||||
QByteArray read = flt.readAll();
|
||||
QCOMPARE(read.size(), testData.size());
|
||||
QCOMPARE(read, testData);
|
||||
}
|
||||
|
||||
void KFilterTest::test_findFilterByMimeType_data()
|
||||
{
|
||||
QTest::addColumn<QString>("mimeType");
|
||||
QTest::addColumn<KCompressionDevice::CompressionType>("type");
|
||||
|
||||
// direct mimetype name
|
||||
QTest::newRow("application/gzip") << QString::fromLatin1("application/gzip") << KCompressionDevice::GZip;
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
QTest::newRow("application/x-bzip") << QString::fromLatin1("application/x-bzip") << KCompressionDevice::BZip2;
|
||||
QTest::newRow("application/x-bzip2") << QString::fromLatin1("application/x-bzip2") << KCompressionDevice::BZip2;
|
||||
#else
|
||||
QTest::newRow("application/x-bzip") << QString::fromLatin1("application/x-bzip") << KCompressionDevice::None;
|
||||
QTest::newRow("application/x-bzip2") << QString::fromLatin1("application/x-bzip2") << KCompressionDevice::None;
|
||||
#endif
|
||||
// indirect compressed mimetypes
|
||||
QTest::newRow("application/x-gzdvi") << QString::fromLatin1("application/x-gzdvi") << KCompressionDevice::GZip;
|
||||
|
||||
// non-compressed mimetypes
|
||||
QTest::newRow("text/plain") << QString::fromLatin1("text/plain") << KCompressionDevice::None;
|
||||
QTest::newRow("application/x-tar") << QString::fromLatin1("application/x-tar") << KCompressionDevice::None;
|
||||
}
|
||||
|
||||
void KFilterTest::test_findFilterByMimeType()
|
||||
{
|
||||
QFETCH(QString, mimeType);
|
||||
QFETCH(KCompressionDevice::CompressionType, type);
|
||||
|
||||
KCompressionDevice::CompressionType compressionType = KCompressionDevice::compressionTypeForMimeType(mimeType);
|
||||
QCOMPARE(compressionType, type);
|
||||
}
|
||||
|
||||
static void getCompressedData(QByteArray &data, QByteArray &compressedData)
|
||||
{
|
||||
data = "Hello world, this is a test for deflate, from bug 114830 / 117683";
|
||||
compressedData.resize(long(data.size() * 1.1f) + 12L); // requirements of zlib::compress2
|
||||
unsigned long out_bufferlen = compressedData.size();
|
||||
const int ret = compress2((Bytef *)compressedData.data(), &out_bufferlen, (const Bytef *)data.constData(), data.size(), 1);
|
||||
QCOMPARE(ret, Z_OK);
|
||||
compressedData.resize(out_bufferlen);
|
||||
}
|
||||
|
||||
void KFilterTest::test_deflateWithZlibHeader()
|
||||
{
|
||||
QByteArray data;
|
||||
QByteArray deflatedData;
|
||||
getCompressedData(data, deflatedData);
|
||||
|
||||
#if 0 // Can't use KFilterDev for this, we need to call KGzipFilter::init(QIODevice::ReadOnly, KGzipFilter::ZlibHeader);
|
||||
QBuffer buffer(&deflatedData);
|
||||
QIODevice *flt = KFilterDev::device(&buffer, "application/gzip", false);
|
||||
static_cast<KFilterDev *>(flt)->setSkipHeaders();
|
||||
bool ok = flt->open(QIODevice::ReadOnly);
|
||||
QVERIFY(ok);
|
||||
const QByteArray read = flt->readAll();
|
||||
#else
|
||||
// Copied from HTTPFilter (which isn't linked into any kdelibs library)
|
||||
KFilterBase *mFilterDevice = KCompressionDevice::filterForCompressionType(KCompressionDevice::GZip);
|
||||
mFilterDevice->setFilterFlags(KFilterBase::ZlibHeaders);
|
||||
mFilterDevice->init(QIODevice::ReadOnly);
|
||||
|
||||
mFilterDevice->setInBuffer(deflatedData.constData(), deflatedData.size());
|
||||
char buf[8192];
|
||||
mFilterDevice->setOutBuffer(buf, sizeof(buf));
|
||||
KFilterBase::Result result = mFilterDevice->uncompress();
|
||||
QCOMPARE(result, KFilterBase::End);
|
||||
const int bytesOut = sizeof(buf) - mFilterDevice->outBufferAvailable();
|
||||
QVERIFY(bytesOut);
|
||||
QByteArray read(buf, bytesOut);
|
||||
mFilterDevice->terminate();
|
||||
delete mFilterDevice;
|
||||
#endif
|
||||
QCOMPARE(QString::fromLatin1(read.constData()), QString::fromLatin1(data.constData())); // more readable output than the line below
|
||||
QCOMPARE(read, data);
|
||||
|
||||
// For the same test with HTTPFilter: see httpfiltertest.cpp
|
||||
}
|
||||
|
||||
void KFilterTest::test_pushData() // ### UNFINISHED
|
||||
{
|
||||
// HTTPFilter says KFilterDev doesn't support the case where compressed data
|
||||
// is arriving in chunks. Let's test that.
|
||||
QFile file(pathgz);
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
const QByteArray compressed = file.readAll();
|
||||
const int firstChunkSize = compressed.size() / 2;
|
||||
QByteArray firstData(compressed.constData(), firstChunkSize);
|
||||
QBuffer inBuffer(&firstData);
|
||||
QVERIFY(inBuffer.open(QIODevice::ReadWrite));
|
||||
KCompressionDevice::CompressionType type = KCompressionDevice::compressionTypeForMimeType(QString::fromLatin1("application/gzip"));
|
||||
KCompressionDevice flt(&inBuffer, false, type);
|
||||
QVERIFY(flt.open(QIODevice::ReadOnly));
|
||||
QByteArray read = flt.readAll();
|
||||
qDebug() << QString::fromLatin1(read.constData());
|
||||
|
||||
// And later...
|
||||
inBuffer.write(QByteArray(compressed.data() + firstChunkSize, compressed.size() - firstChunkSize));
|
||||
QCOMPARE(inBuffer.data().size(), compressed.size());
|
||||
read += flt.readAll();
|
||||
qDebug() << QString::fromLatin1(read.constData());
|
||||
// ### indeed, doesn't work currently. So we use HTTPFilter instead, for now.
|
||||
}
|
||||
|
||||
void KFilterTest::test_saveFile_data()
|
||||
{
|
||||
QTest::addColumn<QString>("fileName");
|
||||
QTest::addColumn<KCompressionDevice::CompressionType>("compressionType");
|
||||
|
||||
QTest::newRow("gz") << "test_saveFile.gz" << KCompressionDevice::GZip;
|
||||
QTest::newRow("none") << "test_saveFile" << KCompressionDevice::None;
|
||||
}
|
||||
|
||||
void KFilterTest::test_saveFile()
|
||||
{
|
||||
QFETCH(QString, fileName);
|
||||
QFETCH(KCompressionDevice::CompressionType, compressionType);
|
||||
|
||||
int numLines = 1000;
|
||||
const QString lineTemplate = QStringLiteral("Hello world, this is the text for line %1");
|
||||
const QString currentdir = QDir::currentPath();
|
||||
const QString outFile = QDir::currentPath() + '/' + fileName;
|
||||
{
|
||||
QSaveFile file(outFile);
|
||||
file.setDirectWriteFallback(true);
|
||||
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||
KCompressionDevice device(&file, false, compressionType);
|
||||
QVERIFY(device.open(QIODevice::WriteOnly));
|
||||
QTextStream stream(&device);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
#endif
|
||||
for (int i = 0; i < numLines; ++i) {
|
||||
stream << lineTemplate.arg(i);
|
||||
stream << QString("\n");
|
||||
}
|
||||
stream.flush();
|
||||
QCOMPARE(stream.status(), QTextStream::Ok);
|
||||
// device.write("The data to be compressed");
|
||||
device.close();
|
||||
QVERIFY(file.commit());
|
||||
}
|
||||
QVERIFY(QFile::exists(outFile));
|
||||
KCompressionDevice reader(outFile, compressionType);
|
||||
QVERIFY(reader.open(QIODevice::ReadOnly));
|
||||
QString expectedFullData;
|
||||
for (int i = 0; i < numLines; ++i) {
|
||||
QCOMPARE(QString::fromUtf8(reader.readLine()), QString(lineTemplate.arg(i) + '\n'));
|
||||
expectedFullData += QString(lineTemplate.arg(i) + '\n');
|
||||
}
|
||||
KCompressionDevice otherReader(outFile);
|
||||
QVERIFY(otherReader.open(QIODevice::ReadOnly));
|
||||
QCOMPARE(QString::fromLatin1(otherReader.readAll()), expectedFullData);
|
||||
QVERIFY(otherReader.atEnd());
|
||||
}
|
||||
|
||||
void KFilterTest::test_twofilesgztogether()
|
||||
{
|
||||
// Reported as 232843
|
||||
// twofiles generated with
|
||||
// echo foo > foo; echo bar > bar ; gzip -c foo > twofiles.gz; gzip -c bar >> twofiles.gz
|
||||
// as documented in the gzip manpage
|
||||
QString data = QFINDTESTDATA("data/twofiles.gz");
|
||||
KCompressionDevice dev(data);
|
||||
QVERIFY(dev.open(QIODevice::ReadOnly));
|
||||
QByteArray extractedData = dev.readAll();
|
||||
QByteArray expectedData{"foo\nbar\n"};
|
||||
QCOMPARE(extractedData, expectedData);
|
||||
}
|
||||
|
||||
void KFilterTest::test_threefilesgztogether()
|
||||
{
|
||||
// Generated similarly to the one above
|
||||
// This catches the case where there's more than two streams available in the same buffer fed to KGzipFilter
|
||||
QString data = QFINDTESTDATA("data/threefiles.gz");
|
||||
KCompressionDevice dev(data);
|
||||
QVERIFY(dev.open(QIODevice::ReadOnly));
|
||||
QByteArray extractedData = dev.readAll();
|
||||
QByteArray expectedData{"foo\nbar\nbaz\n"};
|
||||
QCOMPARE(extractedData, expectedData);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2002-2005 David Faure <faure@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KFILTERTEST_H
|
||||
#define KFILTERTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KFilterTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void test_block_write();
|
||||
void test_block_read();
|
||||
void test_biggerWrites();
|
||||
void test_getch();
|
||||
void test_textstream();
|
||||
void test_readall();
|
||||
void test_uncompressed();
|
||||
void test_findFilterByMimeType_data();
|
||||
void test_findFilterByMimeType();
|
||||
void test_deflateWithZlibHeader();
|
||||
void test_pushData();
|
||||
void test_saveFile_data();
|
||||
void test_saveFile();
|
||||
void test_twofilesgztogether();
|
||||
void test_threefilesgztogether();
|
||||
|
||||
private:
|
||||
void test_block_write(const QString &fileName, const QByteArray &data);
|
||||
void test_block_read(const QString &fileName);
|
||||
void test_getch(const QString &fileName);
|
||||
void test_textstream(const QString &fileName);
|
||||
void test_readall(const QString &fileName, const QString &mimeType, const QByteArray &expectedData);
|
||||
|
||||
private:
|
||||
QString pathgz;
|
||||
QString pathbz2;
|
||||
QString pathxz;
|
||||
QString pathnone;
|
||||
QString pathzstd;
|
||||
QByteArray testData;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2009 Pino Toscano <pino@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "klimitediodevicetest.h"
|
||||
|
||||
#include "klimitediodevice_p.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KLimitedIODeviceTest)
|
||||
|
||||
void KLimitedIODeviceTest::addChunk(const QByteArray &chunk)
|
||||
{
|
||||
ChunkData cd;
|
||||
cd.data = chunk;
|
||||
cd.offset = m_chunks.isEmpty() ? 0 : m_chunks.last().offset + m_chunks.last().data.size();
|
||||
m_chunks.append(cd);
|
||||
m_data.append(chunk);
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::initTestCase()
|
||||
{
|
||||
addChunk("Test of string");
|
||||
addChunk("second part of the large buffer");
|
||||
addChunk("... which will be used to test the KLimitedIODevice");
|
||||
|
||||
m_buffer.setBuffer(&m_data);
|
||||
m_buffer.open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::testReadChunks_data()
|
||||
{
|
||||
QTest::addColumn<int>("index");
|
||||
|
||||
for (int i = 0; i < m_chunks.count(); ++i) {
|
||||
const ChunkData &d = m_chunks.at(i);
|
||||
QTest::newRow(d.data.constData()) << i;
|
||||
}
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::testReadChunks()
|
||||
{
|
||||
QFETCH(int, index);
|
||||
|
||||
const ChunkData &chunk = m_chunks.at(index);
|
||||
|
||||
KLimitedIODevice dev(&m_buffer, chunk.offset, chunk.data.size());
|
||||
QVERIFY(dev.isOpen());
|
||||
QCOMPARE(dev.readAll(), chunk.data);
|
||||
}
|
||||
|
||||
void KLimitedIODeviceTest::testSeeking()
|
||||
{
|
||||
const ChunkData &chunk = m_chunks.at(2);
|
||||
|
||||
KLimitedIODevice dev(&m_buffer, chunk.offset, chunk.data.size());
|
||||
QVERIFY(dev.seek(dev.size() - 16));
|
||||
QCOMPARE(dev.readAll(), chunk.data.right(16));
|
||||
QVERIFY(dev.seek(0));
|
||||
QCOMPARE(dev.readAll(), chunk.data);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2009 Pino Toscano <pino@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KLIMITEDIODEVICETEST_H
|
||||
#define KLIMITEDIODEVICETEST_H
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QByteArray>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
struct ChunkData {
|
||||
QByteArray data;
|
||||
int offset;
|
||||
};
|
||||
|
||||
class KLimitedIODeviceTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void testReadChunks_data();
|
||||
void testReadChunks();
|
||||
void testSeeking();
|
||||
|
||||
private:
|
||||
void addChunk(const QByteArray &chunk);
|
||||
|
||||
QByteArray m_data;
|
||||
QBuffer m_buffer;
|
||||
QList<ChunkData> m_chunks;
|
||||
};
|
||||
|
||||
#endif
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,31 @@
|
|||
from conans import ConanFile, CMake, tools
|
||||
|
||||
class KArchiveConan(ConanFile):
|
||||
name = "KArchive"
|
||||
version = "5.37.0"
|
||||
license = "LGPL-2.1"
|
||||
url = "https://api.kde.org/frameworks/karchive/html/index.html"
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
|
||||
# build this as shared library by default, but static builds are an option
|
||||
options = {"shared": [True, False]}
|
||||
default_options = "shared=True"
|
||||
generators = "cmake"
|
||||
exports_sources = "*"
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
|
||||
# change the library install dir to just "lib" as that's what Conan expects in its packages
|
||||
args = ['-DCMAKE_INSTALL_PREFIX="%s"' % self.package_folder,
|
||||
'-DKDE_INSTALL_LIBDIR=lib']
|
||||
self.run('cmake %s %s %s' % (self.source_folder, cmake.command_line, " ".join(args)))
|
||||
self.run("cmake --build . --target install %s" % cmake.build_config)
|
||||
|
||||
def package(self):
|
||||
# ideally nothing here, cmake with install takes care of it
|
||||
pass
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.libs = ["KF5Archive"]
|
||||
self.cpp_info.includedirs = ['include/KF5', 'include/KF5/KArchive']
|
|
@ -0,0 +1,7 @@
|
|||
### KApiDox Project-specific Overrides File
|
||||
|
||||
# define so that deprecated API is not skipped
|
||||
PREDEFINED += \
|
||||
"KARCHIVE_ENABLE_DEPRECATED_SINCE(x, y)=1" \
|
||||
"KARCHIVE_BUILD_DEPRECATED_SINCE(x, y)=1" \
|
||||
"KARCHIVE_DEPRECATED_VERSION(x, y, t)="
|
|
@ -0,0 +1,6 @@
|
|||
project(BZip2GZip)
|
||||
|
||||
find_package(KF5Archive ${KF_VERSION} REQUIRED)
|
||||
|
||||
add_executable(bzip2gzip main.cpp)
|
||||
target_link_libraries(bzip2gzip KF5::Archive)
|
|
@ -0,0 +1,72 @@
|
|||
/* This file is part of the KDE project
|
||||
|
||||
SPDX-FileCopyrightText: 2014 Maarten De Meyer <de.meyer.maarten@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* bzip2gzip
|
||||
* This example shows the usage of KCompressionDevice.
|
||||
* It converts BZip2 files to GZip archives.
|
||||
*
|
||||
* api: KCompressionDevice(QIODevice * inputDevice, bool autoDeleteInputDevice, CompressionType type)
|
||||
* api: KCompressionDevice(const QString & fileName, CompressionType type)
|
||||
* api: QIODevice::readAll()
|
||||
* api: QIODevice::read(qint64 maxSize)
|
||||
* api: QIODevice::write(const QByteArray &data)
|
||||
*
|
||||
* Usage: ./bzip2gzip <archive.bz2>
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QStringList>
|
||||
|
||||
#include <KCompressionDevice>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
QStringList args(app.arguments());
|
||||
|
||||
if (args.size() != 2) {
|
||||
qWarning("Usage: ./bzip2gzip <archive.bz2>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QString inputFile = args.at(1);
|
||||
QFile file(inputFile);
|
||||
QFileInfo info(inputFile);
|
||||
|
||||
if (info.suffix() != QLatin1String("bz2")) {
|
||||
qCritical("Error: not a valid BZip2 file!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
//@@snippet_begin(kcompressiondevice_example)
|
||||
// Open the input archive
|
||||
KCompressionDevice input(&file, false, KCompressionDevice::BZip2);
|
||||
input.open(QIODevice::ReadOnly);
|
||||
|
||||
QString outputFile = (info.completeBaseName() + QLatin1String(".gz"));
|
||||
|
||||
// Open the new output file
|
||||
KCompressionDevice output(outputFile, KCompressionDevice::GZip);
|
||||
output.open(QIODevice::WriteOnly);
|
||||
|
||||
while (!input.atEnd()) {
|
||||
// Read and uncompress the data
|
||||
QByteArray data = input.read(512);
|
||||
|
||||
// Write data like you would to any other QIODevice
|
||||
output.write(data);
|
||||
}
|
||||
|
||||
input.close();
|
||||
output.close();
|
||||
//@@snippet_end
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
# For more information see the CMake documentation:
|
||||
# http://www.cmake.org/cmake/help/documentation.html
|
||||
# http://techbase.kde.org/Development/Tutorials/CMake
|
||||
|
||||
# Project name
|
||||
project(HelloWorld)
|
||||
|
||||
# Look for the KArchive module
|
||||
find_package(KF5Archive ${KF_VERSION} REQUIRED)
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION}Core REQUIRED)
|
||||
|
||||
add_executable(helloworld main.cpp)
|
||||
|
||||
# Link our executable with the KArchive library
|
||||
target_link_libraries(helloworld KF5::Archive Qt${QT_MAJOR_VERSION}::Core)
|
|
@ -0,0 +1,6 @@
|
|||
# Show how to use KArchive using qmake
|
||||
|
||||
# Set $QMAKEPATH to your KArchive install prefix if this is not found.
|
||||
QT += KArchive
|
||||
|
||||
SOURCES += main.cpp
|
|
@ -0,0 +1,64 @@
|
|||
/* This file is part of the KDE project
|
||||
|
||||
SPDX-FileCopyrightText: 2013 Maarten De Meyer <de.meyer.maarten@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* HelloWorld
|
||||
*
|
||||
* Example to show very basic usage of KArchive with CMake
|
||||
*
|
||||
* Usage:
|
||||
* mkdir build && cd build
|
||||
* cmake ..
|
||||
* make
|
||||
* ./helloworld
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <kzip.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
//@@snippet_begin(helloworld)
|
||||
// Create a zip archive
|
||||
KZip archive(QStringLiteral("hello.zip"));
|
||||
|
||||
// Open our archive for writing
|
||||
if (archive.open(QIODevice::WriteOnly)) {
|
||||
// The archive is open, we can now write data
|
||||
archive.writeFile(QStringLiteral("world"), // File name
|
||||
QByteArray("The whole world inside a hello."), // Data
|
||||
0100644, // Permissions
|
||||
QStringLiteral("owner"), // Owner
|
||||
QStringLiteral("users")); // Group
|
||||
|
||||
// Don't forget to close!
|
||||
archive.close();
|
||||
}
|
||||
|
||||
if (archive.open(QIODevice::ReadOnly)) {
|
||||
const KArchiveDirectory *dir = archive.directory();
|
||||
|
||||
const KArchiveEntry *e = dir->entry("world");
|
||||
if (!e) {
|
||||
qDebug() << "File not found!";
|
||||
return -1;
|
||||
}
|
||||
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
|
||||
QByteArray arr(f->data());
|
||||
qDebug() << arr; // the file contents
|
||||
|
||||
// To avoid reading everything into memory in one go, we can use createDevice() instead
|
||||
QIODevice *dev = f->createDevice();
|
||||
while (!dev->atEnd()) {
|
||||
qDebug() << dev->readLine();
|
||||
}
|
||||
delete dev;
|
||||
}
|
||||
//@@snippet_end
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
project(TarLocalFiles)
|
||||
|
||||
find_package(KF5Archive ${KF_VERSION} REQUIRED)
|
||||
|
||||
add_executable(tarlocalfiles main.cpp)
|
||||
target_link_libraries(tarlocalfiles KF5::Archive)
|
|
@ -0,0 +1,59 @@
|
|||
/* This file is part of the KDE project
|
||||
|
||||
SPDX-FileCopyrightText: 2013 Maarten De Meyer <de.meyer.maarten@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* TarLocalFiles
|
||||
* This example shows how to add local files and directories to a KArchive
|
||||
*
|
||||
* api: addLocalFile(fileName, destName)
|
||||
* api: addLocalDirectory(dirName, destName)
|
||||
*
|
||||
* Usage: ./tarlocalfiles <file-1> <file-n>
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <ktar.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
QStringList files(app.arguments());
|
||||
|
||||
// Create or open an archive
|
||||
KTar archive(QStringLiteral("myFiles.tar.gz"));
|
||||
|
||||
// Prepare the archive for writing.
|
||||
if (!archive.open(QIODevice::WriteOnly)) {
|
||||
// Failed to open file.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (files.size() <= 1) {
|
||||
// No files given.
|
||||
qWarning("Usage: ./tarlocalfiles <file>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 1; i < files.size(); ++i) {
|
||||
QFileInfo localFileOrDir(files.at(i));
|
||||
|
||||
if (localFileOrDir.isFile()) {
|
||||
QString name = localFileOrDir.fileName();
|
||||
archive.addLocalFile(name, name);
|
||||
} else if (localFileOrDir.isDir()) {
|
||||
QString name = QDir(files.at(i)).dirName();
|
||||
// Add this folder and all its contents
|
||||
archive.addLocalDirectory(name, name);
|
||||
}
|
||||
}
|
||||
|
||||
archive.close();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
project(Unzipper)
|
||||
|
||||
find_package(KF5Archive ${KF_VERSION} REQUIRED)
|
||||
|
||||
add_executable(unzipper main.cpp)
|
||||
target_link_libraries(unzipper KF5::Archive)
|
|
@ -0,0 +1,56 @@
|
|||
/* This file is part of the KDE project
|
||||
|
||||
SPDX-FileCopyrightText: 2014 Maarten De Meyer <de.meyer.maarten@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* Unzipper
|
||||
* This example shows how to extract all files from a zip archive.
|
||||
*
|
||||
* api: KArchive::directory()
|
||||
* api: KArchiveDirectory::copyTo(QString destination, bool recursive)
|
||||
*
|
||||
* Usage: ./unzipper <archive>
|
||||
*/
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
|
||||
#include <kzip.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
QStringList args(app.arguments());
|
||||
|
||||
if (args.size() != 2) {
|
||||
// Too many or too few arguments
|
||||
qWarning("Usage: ./unzipper <archive.zip>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QString file = args.at(1);
|
||||
KZip archive(file);
|
||||
|
||||
// Open the archive
|
||||
if (!archive.open(QIODevice::ReadOnly)) {
|
||||
qWarning("Cannot open " + file.toLatin1());
|
||||
qWarning("Is it a valid zip file?");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Take the root folder from the archive and create a KArchiveDirectory object.
|
||||
// KArchiveDirectory represents a directory in a KArchive.
|
||||
const KArchiveDirectory *root = archive.directory();
|
||||
|
||||
// We can extract all contents from a KArchiveDirectory to a destination.
|
||||
// recursive true will also extract subdirectories.
|
||||
QString destination = QDir::currentPath();
|
||||
bool recursive = true;
|
||||
root->copyTo(destination, recursive);
|
||||
|
||||
archive.close();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
maintainer: dfaure
|
||||
description: File compression
|
||||
tier: 1
|
||||
type: functional
|
||||
platforms:
|
||||
- name: Linux
|
||||
- name: FreeBSD
|
||||
- name: Windows
|
||||
- name: macOS
|
||||
- name: Android
|
||||
portingAid: false
|
||||
deprecated: false
|
||||
release: true
|
||||
libraries:
|
||||
- qmake: KArchive
|
||||
cmake: "KF5::Archive"
|
||||
cmakename: KF5Archive
|
||||
|
||||
public_lib: true
|
||||
group: Frameworks
|
||||
subgroup: Tier 1
|
|
@ -0,0 +1,155 @@
|
|||
set(HAVE_BZIP2_SUPPORT ${BZIP2_FOUND})
|
||||
if(BZIP2_FOUND AND BZIP2_NEED_PREFIX)
|
||||
set(NEED_BZ2_PREFIX 1)
|
||||
endif()
|
||||
|
||||
set(HAVE_XZ_SUPPORT ${LIBLZMA_FOUND})
|
||||
set(HAVE_ZSTD_SUPPORT ${LibZstd_FOUND})
|
||||
|
||||
configure_file(config-compression.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-compression.h)
|
||||
set(deprecated_HEADER_NAMES)
|
||||
|
||||
add_library(KF5Archive)
|
||||
add_library(KF5::Archive ALIAS KF5Archive)
|
||||
|
||||
if(BZIP2_FOUND)
|
||||
target_sources(KF5Archive PRIVATE kbzip2filter.cpp)
|
||||
target_link_libraries(KF5Archive PRIVATE BZip2::BZip2)
|
||||
endif()
|
||||
|
||||
if(LIBLZMA_FOUND)
|
||||
target_sources(KF5Archive PRIVATE kxzfilter.cpp k7zip.cpp)
|
||||
target_link_libraries(KF5Archive PRIVATE LibLZMA::LibLZMA)
|
||||
endif()
|
||||
|
||||
if (LibZstd_FOUND)
|
||||
target_sources(KF5Archive PRIVATE kzstdfilter.cpp)
|
||||
target_link_libraries(KF5Archive PRIVATE PkgConfig::LibZstd)
|
||||
endif()
|
||||
|
||||
|
||||
target_sources(KF5Archive PRIVATE karchive.cpp
|
||||
kar.cpp
|
||||
kcompressiondevice.cpp
|
||||
kfilterbase.cpp
|
||||
kgzipfilter.cpp
|
||||
klimitediodevice.cpp
|
||||
knonefilter.cpp
|
||||
ktar.cpp
|
||||
kzip.cpp
|
||||
krcc.cpp
|
||||
)
|
||||
|
||||
if(NOT EXCLUDE_DEPRECATED_BEFORE_AND_AT STREQUAL "CURRENT" AND
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT VERSION_LESS 5.85.0)
|
||||
list(APPEND deprecated_HEADER_NAMES KFilterDev)
|
||||
target_sources(KF5Archive PRIVATE kfilterdev.cpp)
|
||||
endif()
|
||||
|
||||
ecm_qt_declare_logging_category(KF5Archive
|
||||
HEADER loggingcategory.h
|
||||
IDENTIFIER KArchiveLog
|
||||
CATEGORY_NAME kf.archive
|
||||
OLD_CATEGORY_NAMES kf5.karchive
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "KArchive"
|
||||
EXPORT KARCHIVE
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF5Archive
|
||||
BASE_NAME KArchive
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS 5.0 5.85
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF5Archive
|
||||
INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KArchive>"
|
||||
)
|
||||
|
||||
target_link_libraries(KF5Archive
|
||||
PUBLIC
|
||||
Qt${QT_MAJOR_VERSION}::Core
|
||||
PRIVATE
|
||||
ZLIB::ZLIB
|
||||
)
|
||||
|
||||
set_target_properties(KF5Archive PROPERTIES
|
||||
VERSION ${KARCHIVE_VERSION}
|
||||
SOVERSION ${KARCHIVE_SOVERSION}
|
||||
EXPORT_NAME "Archive"
|
||||
)
|
||||
|
||||
ecm_generate_headers(KArchive_HEADERS
|
||||
HEADER_NAMES
|
||||
KArchive
|
||||
KArchiveEntry
|
||||
KArchiveFile
|
||||
KArchiveDirectory
|
||||
KAr
|
||||
KCompressionDevice
|
||||
KFilterBase
|
||||
KRcc
|
||||
KTar
|
||||
KZip
|
||||
KZipFileEntry
|
||||
${deprecated_HEADER_NAMES}
|
||||
|
||||
REQUIRED_HEADERS KArchive_HEADERS
|
||||
)
|
||||
|
||||
install(TARGETS KF5Archive
|
||||
EXPORT KF5ArchiveTargets
|
||||
${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
if(LIBLZMA_FOUND)
|
||||
ecm_generate_headers(KArchive_HEADERS
|
||||
HEADER_NAMES
|
||||
K7Zip
|
||||
REQUIRED_HEADERS KArchive_HEADERS
|
||||
)
|
||||
endif()
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/karchive_export.h
|
||||
${KArchive_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KArchive
|
||||
COMPONENT Devel)
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT KARCHIVE
|
||||
FILE karchive.categories
|
||||
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
||||
)
|
||||
|
||||
if(BUILD_QCH)
|
||||
ecm_add_qch(
|
||||
KF5Archive_QCH
|
||||
NAME KArchive
|
||||
BASE_NAME KF5Archive
|
||||
VERSION ${KF_VERSION}
|
||||
ORG_DOMAIN org.kde
|
||||
SOURCES # using only public headers, to cover only public API
|
||||
${KArchive_HEADERS}
|
||||
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
|
||||
LINK_QCHS
|
||||
Qt5Core_QCH
|
||||
INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
BLANK_MACROS
|
||||
KARCHIVE_EXPORT
|
||||
KARCHIVE_DEPRECATED
|
||||
"KARCHIVE_DEPRECATED_VERSION(x, y, t)"
|
||||
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
|
||||
include(ECMGeneratePriFile)
|
||||
ecm_generate_pri_file(BASE_NAME KArchive LIB_NAME KF5Archive DEPS "core" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF}/KArchive)
|
||||
install(FILES ${PRI_FILENAME}
|
||||
DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
#cmakedefine01 HAVE_BZIP2_SUPPORT
|
||||
|
||||
/* Set to 1 if the libbz2 functions need the BZ2_ prefix */
|
||||
#cmakedefine01 NEED_BZ2_PREFIX
|
||||
|
||||
/* Set to 1 if you have xz */
|
||||
#cmakedefine01 HAVE_XZ_SUPPORT
|
||||
|
||||
/* Set to 1 if you have zstd */
|
||||
#cmakedefine01 HAVE_ZSTD_SUPPORT
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,97 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef K7ZIP_H
|
||||
#define K7ZIP_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
/**
|
||||
* @class K7Zip k7zip.h K7Zip
|
||||
*
|
||||
* A class for reading / writing p7zip archives.
|
||||
*
|
||||
* @author Mario Bensi
|
||||
*/
|
||||
class KARCHIVE_EXPORT K7Zip : public KArchive
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(K7Zip)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename
|
||||
* using the compression filter associated to given mimetype.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/user/myfile.7z")
|
||||
*/
|
||||
explicit K7Zip(const QString &filename);
|
||||
|
||||
/**
|
||||
* Creates an instance that operates on the given device.
|
||||
* The device can be compressed (KCompressionDevice) or not (QFile, etc.).
|
||||
* @warning Do not assume that giving a QFile here will decompress the file,
|
||||
* in case it's compressed!
|
||||
* @param dev the device to read from. If the source is compressed, the
|
||||
* QIODevice must take care of decompression
|
||||
*/
|
||||
explicit K7Zip(QIODevice *dev);
|
||||
|
||||
/**
|
||||
* If the archive is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
~K7Zip() override;
|
||||
|
||||
protected:
|
||||
/// Reimplemented from KArchive
|
||||
bool doWriteSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
/// Reimplemented from KArchive
|
||||
bool doWriteDir(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
/// Reimplemented from KArchive
|
||||
bool doPrepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
/// Reimplemented from KArchive
|
||||
bool doFinishWriting(qint64 size) override;
|
||||
|
||||
/// Reimplemented from KArchive
|
||||
bool writeData(const char *data, qint64 size) override;
|
||||
|
||||
/**
|
||||
* Opens the archive for reading.
|
||||
* Parses the directory listing of the archive
|
||||
* and creates the KArchiveDirectory/KArchiveFile entries.
|
||||
* @param mode the mode of the file
|
||||
*/
|
||||
bool openArchive(QIODevice::OpenMode mode) override;
|
||||
bool closeArchive() override;
|
||||
|
||||
protected:
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
private:
|
||||
class K7ZipPrivate;
|
||||
K7ZipPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,195 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kar.h"
|
||||
#include "karchive_p.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "kcompressiondevice.h"
|
||||
//#include "klimitediodevice_p.h"
|
||||
|
||||
// As documented in QByteArray
|
||||
static constexpr int kMaxQByteArraySize = std::numeric_limits<int>::max() - 32;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////// KAr ///////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Q_DECL_HIDDEN KAr::KArPrivate
|
||||
{
|
||||
public:
|
||||
KArPrivate()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
KAr::KAr(const QString &filename)
|
||||
: KArchive(filename)
|
||||
, d(new KArPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KAr::KAr(QIODevice *dev)
|
||||
: KArchive(dev)
|
||||
, d(new KArPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KAr::~KAr()
|
||||
{
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KAr::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
||||
{
|
||||
setErrorString(tr("Cannot write to AR file"));
|
||||
qCWarning(KArchiveLog) << "doPrepareWriting not implemented for KAr";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::doFinishWriting(qint64)
|
||||
{
|
||||
setErrorString(tr("Cannot write to AR file"));
|
||||
qCWarning(KArchiveLog) << "doFinishWriting not implemented for KAr";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
||||
{
|
||||
setErrorString(tr("Cannot write to AR file"));
|
||||
qCWarning(KArchiveLog) << "doWriteDir not implemented for KAr";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
||||
{
|
||||
setErrorString(tr("Cannot write to AR file"));
|
||||
qCWarning(KArchiveLog) << "doWriteSymLink not implemented for KAr";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KAr::openArchive(QIODevice::OpenMode mode)
|
||||
{
|
||||
// Open archive
|
||||
|
||||
if (mode == QIODevice::WriteOnly) {
|
||||
return true;
|
||||
}
|
||||
if (mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite) {
|
||||
setErrorString(tr("Unsupported mode %1").arg(mode));
|
||||
return false;
|
||||
}
|
||||
|
||||
QIODevice *dev = device();
|
||||
if (!dev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray magic = dev->read(7);
|
||||
if (magic != "!<arch>") {
|
||||
setErrorString(tr("Invalid main magic"));
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray ar_longnames;
|
||||
while (!dev->atEnd()) {
|
||||
QByteArray ar_header;
|
||||
ar_header.resize(60);
|
||||
|
||||
dev->seek(dev->pos() + (2 - (dev->pos() % 2)) % 2); // Ar headers are padded to byte boundary
|
||||
|
||||
if (dev->read(ar_header.data(), 60) != 60) { // Read ar header
|
||||
qCWarning(KArchiveLog) << "Couldn't read header";
|
||||
return true; // Probably EOF / trailing junk
|
||||
}
|
||||
|
||||
if (!ar_header.endsWith("`\n")) { // Check header magic // krazy:exclude=strings
|
||||
setErrorString(tr("Invalid magic"));
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray name = ar_header.mid(0, 16); // Process header
|
||||
const int date = ar_header.mid(16, 12).trimmed().toInt();
|
||||
// const int uid = ar_header.mid( 28, 6 ).trimmed().toInt();
|
||||
// const int gid = ar_header.mid( 34, 6 ).trimmed().toInt();
|
||||
const int mode = ar_header.mid(40, 8).trimmed().toInt(nullptr, 8);
|
||||
const qint64 size = ar_header.mid(48, 10).trimmed().toInt();
|
||||
if (size < 0 || size > kMaxQByteArraySize) {
|
||||
setErrorString(tr("Invalid size"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool skip_entry = false; // Deal with special entries
|
||||
if (name.mid(0, 1) == "/") {
|
||||
if (name.mid(1, 1) == "/") { // Longfilename table entry
|
||||
ar_longnames.resize(size);
|
||||
// Read the table. Note that the QByteArray will contain NUL characters after each entry.
|
||||
dev->read(ar_longnames.data(), size);
|
||||
skip_entry = true;
|
||||
qCDebug(KArchiveLog) << "Read in longnames entry";
|
||||
} else if (name.mid(1, 1) == " ") { // Symbol table entry
|
||||
qCDebug(KArchiveLog) << "Skipped symbol entry";
|
||||
dev->seek(dev->pos() + size);
|
||||
skip_entry = true;
|
||||
} else { // Longfilename, look it up in the table
|
||||
const int ar_longnamesIndex = name.mid(1, 15).trimmed().toInt();
|
||||
qCDebug(KArchiveLog) << "Longfilename #" << ar_longnamesIndex;
|
||||
if (ar_longnames.isEmpty()) {
|
||||
setErrorString(tr("Invalid longfilename reference"));
|
||||
return false;
|
||||
}
|
||||
if (ar_longnamesIndex < 0 || ar_longnamesIndex >= ar_longnames.size()) {
|
||||
setErrorString(tr("Invalid longfilename position reference"));
|
||||
return false;
|
||||
}
|
||||
name = QByteArray(ar_longnames.constData() + ar_longnamesIndex);
|
||||
name.truncate(name.indexOf('/'));
|
||||
}
|
||||
}
|
||||
if (skip_entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process filename
|
||||
name = name.trimmed();
|
||||
name.replace('/', QByteArray());
|
||||
qCDebug(KArchiveLog) << "Filename: " << name << " Size: " << size;
|
||||
|
||||
KArchiveEntry *entry = new KArchiveFile(this,
|
||||
QString::fromLocal8Bit(name.constData()),
|
||||
mode,
|
||||
KArchivePrivate::time_tToDateTime(date),
|
||||
rootDir()->user(),
|
||||
rootDir()->group(),
|
||||
/*symlink*/ QString(),
|
||||
dev->pos(),
|
||||
size);
|
||||
rootDir()->addEntry(entry); // Ar files don't support directories, so everything in root
|
||||
|
||||
dev->seek(dev->pos() + size); // Skip contents
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KAr::closeArchive()
|
||||
{
|
||||
// Close the archive
|
||||
return true;
|
||||
}
|
||||
|
||||
void KAr::virtual_hook(int id, void *data)
|
||||
{
|
||||
KArchive::virtual_hook(id, data);
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KAR_H
|
||||
#define KAR_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
/**
|
||||
* @class KAr kar.h KAr
|
||||
*
|
||||
* KAr is a class for reading archives in ar format. Writing
|
||||
* is not supported. Reading archives that contain files bigger than
|
||||
* INT_MAX - 32 bytes is not supported.
|
||||
* @short A class for reading ar archives.
|
||||
* @author Laurence Anderson <l.d.anderson@warwick.ac.uk>
|
||||
*/
|
||||
class KARCHIVE_EXPORT KAr : public KArchive
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(KAr)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/holger/myfile.ar")
|
||||
*/
|
||||
KAr(const QString &filename);
|
||||
|
||||
/**
|
||||
* Creates an instance that operates on the given device.
|
||||
* The device can be compressed (KCompressionDevice) or not (QFile, etc.).
|
||||
* @param dev the device to read from
|
||||
*/
|
||||
KAr(QIODevice *dev);
|
||||
|
||||
/**
|
||||
* If the ar file is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
~KAr() override;
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Writing is not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
bool doPrepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
|
||||
/*
|
||||
* Writing is not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
bool doFinishWriting(qint64 size) override;
|
||||
|
||||
/*
|
||||
* Writing is not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
bool doWriteDir(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
|
||||
bool doWriteSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
|
||||
/**
|
||||
* Opens the archive for reading.
|
||||
* Parses the directory listing of the archive
|
||||
* and creates the KArchiveDirectory/KArchiveFile entries.
|
||||
*
|
||||
*/
|
||||
bool openArchive(QIODevice::OpenMode mode) override;
|
||||
bool closeArchive() override;
|
||||
|
||||
protected:
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
private:
|
||||
class KArPrivate;
|
||||
KArPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,436 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
Moved from ktar.h by Roberto Teixeira <maragato@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KARCHIVE_H
|
||||
#define KARCHIVE_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDate>
|
||||
#include <QHash>
|
||||
#include <QIODevice>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <karchive_export.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <qplatformdefs.h> // mode_t
|
||||
#endif
|
||||
|
||||
class KArchiveDirectory;
|
||||
class KArchiveFile;
|
||||
|
||||
class KArchivePrivate;
|
||||
/**
|
||||
* @class KArchive karchive.h KArchive
|
||||
*
|
||||
* KArchive is a base class for reading and writing archives.
|
||||
* @short generic class for reading/writing archives
|
||||
* @author David Faure <faure@kde.org>
|
||||
*/
|
||||
class KARCHIVE_EXPORT KArchive
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(KArchive)
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Base constructor (protected since this is a pure virtual class).
|
||||
* @param fileName is a local path (e.g. "/tmp/myfile.ext"),
|
||||
* from which the archive will be read from, or into which the archive
|
||||
* will be written, depending on the mode given to open().
|
||||
*/
|
||||
KArchive(const QString &fileName);
|
||||
|
||||
/**
|
||||
* Base constructor (protected since this is a pure virtual class).
|
||||
* @param dev the I/O device where the archive reads its data
|
||||
* Note that this can be a file, but also a data buffer, a compression filter, etc.
|
||||
* For a file in writing mode it is better to use the other constructor
|
||||
* though, to benefit from the use of QSaveFile when saving.
|
||||
*/
|
||||
KArchive(QIODevice *dev);
|
||||
|
||||
public:
|
||||
virtual ~KArchive();
|
||||
|
||||
/**
|
||||
* Opens the archive for reading or writing.
|
||||
* Inherited classes might want to reimplement openArchive instead.
|
||||
* @param mode may be QIODevice::ReadOnly or QIODevice::WriteOnly
|
||||
* @see close
|
||||
*/
|
||||
virtual bool open(QIODevice::OpenMode mode);
|
||||
|
||||
/**
|
||||
* Closes the archive.
|
||||
* Inherited classes might want to reimplement closeArchive instead.
|
||||
*
|
||||
* @return true if close succeeded without problems
|
||||
* @see open
|
||||
*/
|
||||
virtual bool close();
|
||||
|
||||
/**
|
||||
* Returns a description of the last error
|
||||
* @since 5.29
|
||||
*/
|
||||
QString errorString() const;
|
||||
|
||||
/**
|
||||
* Checks whether the archive is open.
|
||||
* @return true if the archive is opened
|
||||
*/
|
||||
bool isOpen() const;
|
||||
|
||||
/**
|
||||
* Returns the mode in which the archive was opened
|
||||
* @return the mode in which the archive was opened (QIODevice::ReadOnly or QIODevice::WriteOnly)
|
||||
* @see open()
|
||||
*/
|
||||
QIODevice::OpenMode mode() const;
|
||||
|
||||
/**
|
||||
* The underlying device.
|
||||
* @return the underlying device.
|
||||
*/
|
||||
QIODevice *device() const;
|
||||
|
||||
/**
|
||||
* The name of the archive file, as passed to the constructor that takes a
|
||||
* fileName, or an empty string if you used the QIODevice constructor.
|
||||
* @return the name of the file, or QString() if unknown
|
||||
*/
|
||||
QString fileName() const;
|
||||
|
||||
/**
|
||||
* If an archive is opened for reading, then the contents
|
||||
* of the archive can be accessed via this function.
|
||||
* @return the directory of the archive
|
||||
*/
|
||||
const KArchiveDirectory *directory() const;
|
||||
|
||||
/**
|
||||
* Writes a local file into the archive. The main difference with writeFile,
|
||||
* is that this method minimizes memory usage, by not loading the whole file
|
||||
* into memory in one go.
|
||||
*
|
||||
* If @p fileName is a symbolic link, it will be written as is, i. e.
|
||||
* it will not be resolved before.
|
||||
* @param fileName full path to an existing local file, to be added to the archive.
|
||||
* @param destName the resulting name (or relative path) of the file in the archive.
|
||||
*/
|
||||
bool addLocalFile(const QString &fileName, const QString &destName);
|
||||
|
||||
/**
|
||||
* Writes a local directory into the archive, including all its contents, recursively.
|
||||
* Calls addLocalFile for each file to be added.
|
||||
*
|
||||
* It will also add a @p path that is a symbolic link to a
|
||||
* directory. The symbolic link will be dereferenced and the content of the
|
||||
* directory it is pointing to added recursively. However, symbolic links
|
||||
* *under* @p path will be stored as is.
|
||||
* @param path full path to an existing local directory, to be added to the archive.
|
||||
* @param destName the resulting name (or relative path) of the file in the archive.
|
||||
*/
|
||||
bool addLocalDirectory(const QString &path, const QString &destName);
|
||||
|
||||
/**
|
||||
* If an archive is opened for writing then you can add new directories
|
||||
* using this function. KArchive won't write one directory twice.
|
||||
*
|
||||
* This method also allows some file metadata to be set.
|
||||
* However, depending on the archive type not all metadata might be regarded.
|
||||
*
|
||||
* @param name the name of the directory
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
bool writeDir(const QString &name,
|
||||
const QString &user = QString(),
|
||||
const QString &group = QString(),
|
||||
mode_t perm = 040755,
|
||||
const QDateTime &atime = QDateTime(),
|
||||
const QDateTime &mtime = QDateTime(),
|
||||
const QDateTime &ctime = QDateTime());
|
||||
|
||||
/**
|
||||
* Writes a symbolic link to the archive if supported.
|
||||
* The archive must be opened for writing.
|
||||
*
|
||||
* @param name name of symbolic link
|
||||
* @param target target of symbolic link
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
bool writeSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user = QString(),
|
||||
const QString &group = QString(),
|
||||
mode_t perm = 0120755,
|
||||
const QDateTime &atime = QDateTime(),
|
||||
const QDateTime &mtime = QDateTime(),
|
||||
const QDateTime &ctime = QDateTime());
|
||||
|
||||
#if KARCHIVE_ENABLE_DEPRECATED_SINCE(5, 0)
|
||||
/**
|
||||
* @deprecated since 5.0, use writeFile(const QString&, const QByteArray&, mode_t, const QString&, const QString&, const QDateTime&, const QDateTime&, const
|
||||
* QDateTime&)
|
||||
*/
|
||||
KARCHIVE_DEPRECATED_VERSION(5,
|
||||
0,
|
||||
"Use KArchive::writeFile(const QString&, const QByteArray&, mode_t, const QString&, const QString&, const QDateTime&, const "
|
||||
"QDateTime&, const QDateTime&)")
|
||||
bool writeFile(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
const char *data,
|
||||
qint64 size,
|
||||
mode_t perm = 0100644,
|
||||
const QDateTime &atime = QDateTime(),
|
||||
const QDateTime &mtime = QDateTime(),
|
||||
const QDateTime &ctime = QDateTime())
|
||||
{
|
||||
QByteArray array(data, size);
|
||||
return writeFile(name, array, perm, user, group, atime, mtime, ctime);
|
||||
}
|
||||
// The above can lead to ambiguous calls when using "..." for the first 4 arguments,
|
||||
// but that's good, better than unexpected behavior due to the signature change.
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Writes a new file into the archive.
|
||||
*
|
||||
* The archive must be opened for writing first.
|
||||
*
|
||||
* The necessary parent directories are created automatically
|
||||
* if needed. For instance, writing "mydir/test1" does not
|
||||
* require creating the directory "mydir" first.
|
||||
*
|
||||
* This method also allows some file metadata to be
|
||||
* set. However, depending on the archive type not all metadata might be
|
||||
* written out.
|
||||
*
|
||||
* @param name the name of the file
|
||||
* @param data the data to write
|
||||
* @param perm permissions of the file
|
||||
* @param user the user that owns the file
|
||||
* @param group the group that owns the file
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
bool writeFile(const QString &name,
|
||||
const QByteArray &data,
|
||||
mode_t perm = 0100644,
|
||||
const QString &user = QString(),
|
||||
const QString &group = QString(),
|
||||
const QDateTime &atime = QDateTime(),
|
||||
const QDateTime &mtime = QDateTime(),
|
||||
const QDateTime &ctime = QDateTime());
|
||||
|
||||
/**
|
||||
* Here's another way of writing a file into an archive:
|
||||
* Call prepareWriting(), then call writeData()
|
||||
* as many times as wanted then call finishWriting( totalSize ).
|
||||
* For tar.gz files, you need to know the size before hand, it is needed in the header!
|
||||
* For zip files, size isn't used.
|
||||
*
|
||||
* This method also allows some file metadata to be
|
||||
* set. However, depending on the archive type not all metadata might be
|
||||
* regarded.
|
||||
* @param name the name of the file
|
||||
* @param user the user that owns the file
|
||||
* @param group the group that owns the file
|
||||
* @param size the size of the file
|
||||
* @param perm permissions of the file
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
*/
|
||||
bool prepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm = 0100644,
|
||||
const QDateTime &atime = QDateTime(),
|
||||
const QDateTime &mtime = QDateTime(),
|
||||
const QDateTime &ctime = QDateTime());
|
||||
|
||||
/**
|
||||
* Write data into the current file - to be called after calling prepareWriting
|
||||
*/
|
||||
virtual bool writeData(const char *data, qint64 size);
|
||||
|
||||
/**
|
||||
* Call finishWriting after writing the data.
|
||||
* @param size the size of the file
|
||||
* @see prepareWriting()
|
||||
*/
|
||||
bool finishWriting(qint64 size);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Opens an archive for reading or writing.
|
||||
* Called by open.
|
||||
* @param mode may be QIODevice::ReadOnly or QIODevice::WriteOnly
|
||||
*/
|
||||
virtual bool openArchive(QIODevice::OpenMode mode) = 0;
|
||||
|
||||
/**
|
||||
* Closes the archive.
|
||||
* Called by close.
|
||||
*/
|
||||
virtual bool closeArchive() = 0;
|
||||
|
||||
/**
|
||||
* Sets error description
|
||||
* @param errorStr error description
|
||||
* @since 5.29
|
||||
*/
|
||||
void setErrorString(const QString &errorStr);
|
||||
|
||||
/**
|
||||
* Retrieves or create the root directory.
|
||||
* The default implementation assumes that openArchive() did the parsing,
|
||||
* so it creates a dummy rootdir if none was set (write mode, or no '/' in the archive).
|
||||
* Reimplement this to provide parsing/listing on demand.
|
||||
* @return the root directory
|
||||
*/
|
||||
virtual KArchiveDirectory *rootDir();
|
||||
|
||||
/**
|
||||
* Write a directory to the archive.
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* Depending on the archive type not all metadata might be used.
|
||||
*
|
||||
* @param name the name of the directory
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory. Use 040755 if you don't have any other information.
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
* @see writeDir
|
||||
*/
|
||||
virtual bool doWriteDir(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) = 0;
|
||||
|
||||
/**
|
||||
* Writes a symbolic link to the archive.
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* @param name name of symbolic link
|
||||
* @param target target of symbolic link
|
||||
* @param user the user that owns the directory
|
||||
* @param group the group that owns the directory
|
||||
* @param perm permissions of the directory
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
* @see writeSymLink
|
||||
*/
|
||||
virtual bool doWriteSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) = 0;
|
||||
|
||||
/**
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* Depending on the archive type not all metadata might be used.
|
||||
*
|
||||
* @param name the name of the file
|
||||
* @param user the user that owns the file
|
||||
* @param group the group that owns the file
|
||||
* @param size the size of the file
|
||||
* @param perm permissions of the file. Use 0100644 if you don't have any more specific permissions to set.
|
||||
* @param atime time the file was last accessed
|
||||
* @param mtime modification time of the file
|
||||
* @param ctime time of last status change
|
||||
* @see prepareWriting
|
||||
*/
|
||||
virtual bool doPrepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) = 0;
|
||||
|
||||
/**
|
||||
* Called after writing the data.
|
||||
* This virtual method must be implemented by subclasses.
|
||||
*
|
||||
* @param size the size of the file
|
||||
* @see finishWriting()
|
||||
*/
|
||||
virtual bool doFinishWriting(qint64 size) = 0;
|
||||
|
||||
/**
|
||||
* Ensures that @p path exists, create otherwise.
|
||||
* This handles e.g. tar files missing directory entries, like mico-2.3.0.tar.gz :)
|
||||
* @param path the path of the directory
|
||||
* @return the directory with the given @p path
|
||||
*/
|
||||
KArchiveDirectory *findOrCreate(const QString &path);
|
||||
|
||||
/**
|
||||
* Can be reimplemented in order to change the creation of the device
|
||||
* (when using the fileName constructor). By default this method uses
|
||||
* QSaveFile when saving, and a simple QFile on reading.
|
||||
* This method is called by open().
|
||||
*/
|
||||
virtual bool createDevice(QIODevice::OpenMode mode);
|
||||
|
||||
/**
|
||||
* Can be called by derived classes in order to set the underlying device.
|
||||
* Note that KArchive will -not- own the device, it must be deleted by the derived class.
|
||||
*/
|
||||
void setDevice(QIODevice *dev);
|
||||
|
||||
/**
|
||||
* Derived classes call setRootDir from openArchive,
|
||||
* to set the root directory after parsing an existing archive.
|
||||
*/
|
||||
void setRootDir(KArchiveDirectory *rootDir);
|
||||
|
||||
protected:
|
||||
virtual void virtual_hook(int id, void *data);
|
||||
|
||||
private:
|
||||
friend class KArchivePrivate;
|
||||
KArchivePrivate *const d;
|
||||
};
|
||||
|
||||
// for source compat
|
||||
#include "karchivedirectory.h"
|
||||
#include "karchivefile.h"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KARCHIVE_P_H
|
||||
#define KARCHIVE_P_H
|
||||
|
||||
#include "karchive.h"
|
||||
|
||||
#include <QSaveFile>
|
||||
|
||||
class KArchivePrivate
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(KArchivePrivate)
|
||||
|
||||
public:
|
||||
KArchivePrivate(KArchive *parent)
|
||||
: q(parent)
|
||||
, rootDir(nullptr)
|
||||
, saveFile(nullptr)
|
||||
, dev(nullptr)
|
||||
, fileName()
|
||||
, mode(QIODevice::NotOpen)
|
||||
, deviceOwned(false)
|
||||
{
|
||||
}
|
||||
~KArchivePrivate()
|
||||
{
|
||||
delete saveFile;
|
||||
delete rootDir;
|
||||
}
|
||||
|
||||
KArchivePrivate(const KArchivePrivate &) = delete;
|
||||
KArchivePrivate &operator=(const KArchivePrivate &) = delete;
|
||||
|
||||
static bool hasRootDir(KArchive *archive)
|
||||
{
|
||||
return archive->d->rootDir;
|
||||
}
|
||||
|
||||
void abortWriting();
|
||||
|
||||
static QDateTime time_tToDateTime(uint time_t);
|
||||
|
||||
KArchiveDirectory *findOrCreate(const QString &path, int recursionCounter);
|
||||
|
||||
KArchive *q;
|
||||
KArchiveDirectory *rootDir;
|
||||
QSaveFile *saveFile;
|
||||
QIODevice *dev;
|
||||
QString fileName;
|
||||
QIODevice::OpenMode mode;
|
||||
bool deviceOwned; // if true, we (KArchive) own dev and must delete it
|
||||
QString errorStr{tr("Unknown error")};
|
||||
};
|
||||
|
||||
#endif // KARCHIVE_P_H
|
|
@ -0,0 +1,127 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
Moved from ktar.h by Roberto Teixeira <maragato@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KARCHIVEDIRECTORY_H
|
||||
#define KARCHIVEDIRECTORY_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <QDate>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <karchiveentry.h>
|
||||
|
||||
class KArchiveDirectoryPrivate;
|
||||
class KArchiveFile;
|
||||
/**
|
||||
* @class KArchiveDirectory karchivedirectory.h KArchiveDirectory
|
||||
*
|
||||
* Represents a directory entry in a KArchive.
|
||||
* @short A directory in an archive.
|
||||
*
|
||||
* @see KArchive
|
||||
* @see KArchiveFile
|
||||
*/
|
||||
class KARCHIVE_EXPORT KArchiveDirectory : public KArchiveEntry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new directory entry.
|
||||
* @param archive the entries archive
|
||||
* @param name the name of the entry
|
||||
* @param access the permissions in unix format
|
||||
* @param date the date (in seconds since 1970)
|
||||
* @param user the user that owns the entry
|
||||
* @param group the group that owns the entry
|
||||
* @param symlink the symlink, or QString()
|
||||
*/
|
||||
KArchiveDirectory(KArchive *archive,
|
||||
const QString &name,
|
||||
int access,
|
||||
const QDateTime &date,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
const QString &symlink);
|
||||
|
||||
~KArchiveDirectory() override;
|
||||
|
||||
/**
|
||||
* Returns a list of sub-entries.
|
||||
* Note that the list is not sorted, it's even in random order (due to using a hashtable).
|
||||
* Use sort() on the result to sort the list by filename.
|
||||
*
|
||||
* @return the names of all entries in this directory (filenames, no path).
|
||||
*/
|
||||
QStringList entries() const;
|
||||
|
||||
/**
|
||||
* Returns the entry in the archive with the given name.
|
||||
* The entry could be a file or a directory, use isFile() to find out which one it is.
|
||||
* @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc.
|
||||
* @return a pointer to the entry in the directory, or a null pointer if there is no such entry.
|
||||
*/
|
||||
const KArchiveEntry *entry(const QString &name) const;
|
||||
|
||||
/**
|
||||
* Returns the file entry in the archive with the given name.
|
||||
* If the entry exists and is a file, a KArchiveFile is returned.
|
||||
* Otherwise, a null pointer is returned.
|
||||
* This is a convenience method for entry(), when we know the entry is expected to be a file.
|
||||
*
|
||||
* @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc.
|
||||
* @return a pointer to the file entry in the directory, or a null pointer if there is no such file entry.
|
||||
* @since 5.3
|
||||
*/
|
||||
const KArchiveFile *file(const QString &name) const;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Adds a new entry to the directory.
|
||||
* Note: this can delete the entry if another one with the same name is already present
|
||||
*/
|
||||
void addEntry(KArchiveEntry *); // KF6 TODO: return bool
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Adds a new entry to the directory.
|
||||
* @return whether the entry was added or not. Non added entries are deleted
|
||||
*/
|
||||
bool addEntryV2(KArchiveEntry *); // KF6 TODO: merge with the one above
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Removes an entry from the directory.
|
||||
*/
|
||||
void removeEntry(KArchiveEntry *); // KF6 TODO: return bool since it can fail
|
||||
|
||||
/**
|
||||
* Checks whether this entry is a directory.
|
||||
* @return true, since this entry is a directory
|
||||
*/
|
||||
bool isDirectory() const override;
|
||||
|
||||
/**
|
||||
* Extracts all entries in this archive directory to the directory
|
||||
* @p dest.
|
||||
* @param dest the directory to extract to
|
||||
* @param recursive if set to true, subdirectories are extracted as well
|
||||
* @return true on success, false if the directory (dest + '/' + name()) couldn't be created
|
||||
*/
|
||||
bool copyTo(const QString &dest, bool recursive = true) const;
|
||||
|
||||
protected:
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
private:
|
||||
friend class KArchiveDirectoryPrivate;
|
||||
KArchiveDirectoryPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,107 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
Moved from ktar.h by Roberto Teixeira <maragato@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KARCHIVEENTRY_H
|
||||
#define KARCHIVEENTRY_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <karchive_export.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <qplatformdefs.h> // mode_t
|
||||
#endif
|
||||
|
||||
class KArchiveDirectory;
|
||||
class KArchiveFile;
|
||||
|
||||
class KArchiveEntryPrivate;
|
||||
/**
|
||||
* @class KArchiveEntry karchiveentry.h KArchiveEntry
|
||||
*
|
||||
* A base class for entries in an KArchive.
|
||||
* @short Base class for the archive-file's directory structure.
|
||||
*
|
||||
* @see KArchiveFile
|
||||
* @see KArchiveDirectory
|
||||
*/
|
||||
class KARCHIVE_EXPORT KArchiveEntry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new entry.
|
||||
* @param archive the entries archive
|
||||
* @param name the name of the entry
|
||||
* @param access the permissions in unix format
|
||||
* @param date the date (in seconds since 1970)
|
||||
* @param user the user that owns the entry
|
||||
* @param group the group that owns the entry
|
||||
* @param symlink the symlink, or QString()
|
||||
*/
|
||||
KArchiveEntry(KArchive *archive, const QString &name, int access, const QDateTime &date, const QString &user, const QString &group, const QString &symlink);
|
||||
|
||||
virtual ~KArchiveEntry();
|
||||
|
||||
/**
|
||||
* Creation date of the file.
|
||||
* @return the creation date
|
||||
*/
|
||||
QDateTime date() const;
|
||||
|
||||
/**
|
||||
* Name of the file without path.
|
||||
* @return the file name without path
|
||||
*/
|
||||
QString name() const;
|
||||
/**
|
||||
* The permissions and mode flags as returned by the stat() function
|
||||
* in st_mode.
|
||||
* @return the permissions
|
||||
*/
|
||||
mode_t permissions() const;
|
||||
/**
|
||||
* User who created the file.
|
||||
* @return the owner of the file
|
||||
*/
|
||||
QString user() const;
|
||||
/**
|
||||
* Group of the user who created the file.
|
||||
* @return the group of the file
|
||||
*/
|
||||
QString group() const;
|
||||
|
||||
/**
|
||||
* Symlink if there is one.
|
||||
* @return the symlink, or QString()
|
||||
*/
|
||||
QString symLinkTarget() const;
|
||||
|
||||
/**
|
||||
* Checks whether the entry is a file.
|
||||
* @return true if this entry is a file
|
||||
*/
|
||||
virtual bool isFile() const;
|
||||
|
||||
/**
|
||||
* Checks whether the entry is a directory.
|
||||
* @return true if this entry is a directory
|
||||
*/
|
||||
virtual bool isDirectory() const;
|
||||
|
||||
protected:
|
||||
KArchive *archive() const;
|
||||
|
||||
protected:
|
||||
virtual void virtual_hook(int id, void *data);
|
||||
|
||||
private:
|
||||
KArchiveEntryPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,109 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
Moved from ktar.h by Roberto Teixeira <maragato@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KARCHIVEFILE_H
|
||||
#define KARCHIVEFILE_H
|
||||
|
||||
#include <karchiveentry.h>
|
||||
|
||||
class KArchiveFilePrivate;
|
||||
/**
|
||||
* @class KArchiveFile karchivefile.h KArchiveFile
|
||||
*
|
||||
* Represents a file entry in a KArchive.
|
||||
* @short A file in an archive.
|
||||
*
|
||||
* @see KArchive
|
||||
* @see KArchiveDirectory
|
||||
*/
|
||||
class KARCHIVE_EXPORT KArchiveFile : public KArchiveEntry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new file entry. Do not call this, KArchive takes care of it.
|
||||
* @param archive the entries archive
|
||||
* @param name the name of the entry
|
||||
* @param access the permissions in unix format
|
||||
* @param date the date (in seconds since 1970)
|
||||
* @param user the user that owns the entry
|
||||
* @param group the group that owns the entry
|
||||
* @param symlink the symlink, or QString()
|
||||
* @param pos the position of the file in the directory
|
||||
* @param size the size of the file
|
||||
*/
|
||||
KArchiveFile(KArchive *archive,
|
||||
const QString &name,
|
||||
int access,
|
||||
const QDateTime &date,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
const QString &symlink,
|
||||
qint64 pos,
|
||||
qint64 size);
|
||||
|
||||
/**
|
||||
* Destructor. Do not call this, KArchive takes care of it.
|
||||
*/
|
||||
~KArchiveFile() override;
|
||||
|
||||
/**
|
||||
* Position of the data in the [uncompressed] archive.
|
||||
* @return the position of the file
|
||||
*/
|
||||
qint64 position() const;
|
||||
/**
|
||||
* Size of the data.
|
||||
* @return the size of the file
|
||||
*/
|
||||
qint64 size() const;
|
||||
/**
|
||||
* Set size of data, usually after writing the file.
|
||||
* @param s the new size of the file
|
||||
*/
|
||||
void setSize(qint64 s);
|
||||
|
||||
/**
|
||||
* Returns the data of the file.
|
||||
* Call data() with care (only once per file), this data isn't cached.
|
||||
* @return the content of this file.
|
||||
*/
|
||||
virtual QByteArray data() const;
|
||||
|
||||
/**
|
||||
* This method returns QIODevice (internal class: KLimitedIODevice)
|
||||
* on top of the underlying QIODevice. This is obviously for reading only.
|
||||
*
|
||||
* WARNING: Note that the ownership of the device is being transferred to the caller,
|
||||
* who will have to delete it.
|
||||
*
|
||||
* The returned device auto-opens (in readonly mode), no need to open it.
|
||||
* @return the QIODevice of the file
|
||||
*/
|
||||
virtual QIODevice *createDevice() const;
|
||||
|
||||
/**
|
||||
* Checks whether this entry is a file.
|
||||
* @return true, since this entry is a file
|
||||
*/
|
||||
bool isFile() const override;
|
||||
|
||||
/**
|
||||
* Extracts the file to the directory @p dest
|
||||
* @param dest the directory to extract to
|
||||
* @return true on success, false if the file (dest + '/' + name()) couldn't be created
|
||||
*/
|
||||
bool copyTo(const QString &dest) const;
|
||||
|
||||
protected:
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
private:
|
||||
KArchiveFilePrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,184 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kbzip2filter.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
|
||||
// we don't need that
|
||||
#define BZ_NO_STDIO
|
||||
extern "C" {
|
||||
#include <bzlib.h>
|
||||
}
|
||||
|
||||
#if NEED_BZ2_PREFIX
|
||||
#define bzDecompressInit(x, y, z) BZ2_bzDecompressInit(x, y, z)
|
||||
#define bzDecompressEnd(x) BZ2_bzDecompressEnd(x)
|
||||
#define bzCompressEnd(x) BZ2_bzCompressEnd(x)
|
||||
#define bzDecompress(x) BZ2_bzDecompress(x)
|
||||
#define bzCompress(x, y) BZ2_bzCompress(x, y)
|
||||
#define bzCompressInit(x, y, z, a) BZ2_bzCompressInit(x, y, z, a);
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
// For docu on this, see /usr/doc/bzip2-0.9.5d/bzip2-0.9.5d/manual_3.html
|
||||
|
||||
class Q_DECL_HIDDEN KBzip2Filter::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: isInitialized(false)
|
||||
{
|
||||
memset(&zStream, 0, sizeof(zStream));
|
||||
mode = 0;
|
||||
}
|
||||
|
||||
bz_stream zStream;
|
||||
int mode;
|
||||
bool isInitialized;
|
||||
};
|
||||
|
||||
KBzip2Filter::KBzip2Filter()
|
||||
: d(new Private)
|
||||
{
|
||||
}
|
||||
|
||||
KBzip2Filter::~KBzip2Filter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KBzip2Filter::init(int mode)
|
||||
{
|
||||
if (d->isInitialized) {
|
||||
terminate();
|
||||
}
|
||||
|
||||
d->zStream.next_in = nullptr;
|
||||
d->zStream.avail_in = 0;
|
||||
if (mode == QIODevice::ReadOnly) {
|
||||
const int result = bzDecompressInit(&d->zStream, 0, 0);
|
||||
if (result != BZ_OK) {
|
||||
// qCDebug(KArchiveLog) << "bzDecompressInit returned " << result;
|
||||
return false;
|
||||
}
|
||||
} else if (mode == QIODevice::WriteOnly) {
|
||||
const int result = bzCompressInit(&d->zStream, 5, 0, 0);
|
||||
if (result != BZ_OK) {
|
||||
// qCDebug(KArchiveLog) << "bzDecompressInit returned " << result;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
|
||||
return false;
|
||||
}
|
||||
d->mode = mode;
|
||||
d->isInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int KBzip2Filter::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
bool KBzip2Filter::terminate()
|
||||
{
|
||||
if (d->mode == QIODevice::ReadOnly) {
|
||||
const int result = bzDecompressEnd(&d->zStream);
|
||||
if (result != BZ_OK) {
|
||||
// qCDebug(KArchiveLog) << "bzDecompressEnd returned " << result;
|
||||
return false;
|
||||
}
|
||||
} else if (d->mode == QIODevice::WriteOnly) {
|
||||
const int result = bzCompressEnd(&d->zStream);
|
||||
if (result != BZ_OK) {
|
||||
// qCDebug(KArchiveLog) << "bzCompressEnd returned " << result;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
|
||||
return false;
|
||||
}
|
||||
d->isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void KBzip2Filter::reset()
|
||||
{
|
||||
// bzip2 doesn't seem to have a reset call...
|
||||
terminate();
|
||||
init(d->mode);
|
||||
}
|
||||
|
||||
void KBzip2Filter::setOutBuffer(char *data, uint maxlen)
|
||||
{
|
||||
d->zStream.avail_out = maxlen;
|
||||
d->zStream.next_out = data;
|
||||
}
|
||||
|
||||
void KBzip2Filter::setInBuffer(const char *data, unsigned int size)
|
||||
{
|
||||
d->zStream.avail_in = size;
|
||||
d->zStream.next_in = const_cast<char *>(data);
|
||||
}
|
||||
|
||||
int KBzip2Filter::inBufferAvailable() const
|
||||
{
|
||||
return d->zStream.avail_in;
|
||||
}
|
||||
|
||||
int KBzip2Filter::outBufferAvailable() const
|
||||
{
|
||||
return d->zStream.avail_out;
|
||||
}
|
||||
|
||||
KBzip2Filter::Result KBzip2Filter::uncompress()
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "Calling bzDecompress with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
int result = bzDecompress(&d->zStream);
|
||||
if (result < BZ_OK) {
|
||||
qCWarning(KArchiveLog) << "bzDecompress returned" << result;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case BZ_OK:
|
||||
return KFilterBase::Ok;
|
||||
case BZ_STREAM_END:
|
||||
return KFilterBase::End;
|
||||
default:
|
||||
return KFilterBase::Error;
|
||||
}
|
||||
}
|
||||
|
||||
KBzip2Filter::Result KBzip2Filter::compress(bool finish)
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "Calling bzCompress with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
int result = bzCompress(&d->zStream, finish ? BZ_FINISH : BZ_RUN);
|
||||
|
||||
switch (result) {
|
||||
case BZ_OK:
|
||||
case BZ_FLUSH_OK:
|
||||
case BZ_RUN_OK:
|
||||
case BZ_FINISH_OK:
|
||||
return KFilterBase::Ok;
|
||||
break;
|
||||
case BZ_STREAM_END:
|
||||
// qCDebug(KArchiveLog) << " bzCompress returned " << result;
|
||||
return KFilterBase::End;
|
||||
break;
|
||||
default:
|
||||
// qCDebug(KArchiveLog) << " bzCompress returned " << result;
|
||||
return KFilterBase::Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_BZIP2_SUPPORT */
|
|
@ -0,0 +1,52 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __kbzip2filter__h
|
||||
#define __kbzip2filter__h
|
||||
|
||||
#include <config-compression.h>
|
||||
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
|
||||
#include "kfilterbase.h"
|
||||
|
||||
/**
|
||||
* Internal class used by KCompressionDevice
|
||||
* @internal
|
||||
*/
|
||||
class KBzip2Filter : public KFilterBase
|
||||
{
|
||||
public:
|
||||
KBzip2Filter();
|
||||
~KBzip2Filter() override;
|
||||
|
||||
bool init(int) override;
|
||||
int mode() const override;
|
||||
bool terminate() override;
|
||||
void reset() override;
|
||||
bool readHeader() override
|
||||
{
|
||||
return true; // bzip2 handles it by itself ! Cool !
|
||||
}
|
||||
bool writeHeader(const QByteArray &) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void setOutBuffer(char *data, uint maxlen) override;
|
||||
void setInBuffer(const char *data, uint size) override;
|
||||
int inBufferAvailable() const override;
|
||||
int outBufferAvailable() const override;
|
||||
Result uncompress() override;
|
||||
Result compress(bool finish) override;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,515 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kcompressiondevice.h"
|
||||
#include "kcompressiondevice_p.h"
|
||||
#include "kfilterbase.h"
|
||||
#include "loggingcategory.h"
|
||||
#include "kgzipfilter.h"
|
||||
#include "knonefilter.h"
|
||||
|
||||
#include "config-compression.h"
|
||||
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
#include "kbzip2filter.h"
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
#include "kxzfilter.h"
|
||||
#endif
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
#include "kzstdfilter.h"
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMimeDatabase>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h> // for EOF
|
||||
#include <stdlib.h>
|
||||
|
||||
class KCompressionDevicePrivate
|
||||
{
|
||||
public:
|
||||
KCompressionDevicePrivate(KCompressionDevice *qq)
|
||||
: bNeedHeader(true)
|
||||
, bSkipHeaders(false)
|
||||
, bOpenedUnderlyingDevice(false)
|
||||
, type(KCompressionDevice::None)
|
||||
, errorCode(QFileDevice::NoError)
|
||||
, deviceReadPos(0)
|
||||
, q(qq)
|
||||
{
|
||||
}
|
||||
|
||||
void propagateErrorCode();
|
||||
|
||||
bool bNeedHeader;
|
||||
bool bSkipHeaders;
|
||||
bool bOpenedUnderlyingDevice;
|
||||
QByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing
|
||||
QByteArray origFileName;
|
||||
KFilterBase::Result result;
|
||||
KFilterBase *filter;
|
||||
KCompressionDevice::CompressionType type;
|
||||
QFileDevice::FileError errorCode;
|
||||
qint64 deviceReadPos;
|
||||
KCompressionDevice *q;
|
||||
};
|
||||
|
||||
void KCompressionDevicePrivate::propagateErrorCode()
|
||||
{
|
||||
QIODevice *dev = filter->device();
|
||||
if (QFileDevice *fileDev = qobject_cast<QFileDevice *>(dev)) {
|
||||
if (fileDev->error() != QFileDevice::NoError) {
|
||||
errorCode = fileDev->error();
|
||||
q->setErrorString(dev->errorString());
|
||||
}
|
||||
}
|
||||
// ... we have no generic way to propagate errors from other kinds of iodevices. Sucks, heh? :(
|
||||
}
|
||||
|
||||
static KCompressionDevice::CompressionType findCompressionByFileName(const QString &fileName)
|
||||
{
|
||||
if (fileName.endsWith(QLatin1String(".gz"), Qt::CaseInsensitive)) {
|
||||
return KCompressionDevice::GZip;
|
||||
}
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
if (fileName.endsWith(QLatin1String(".bz2"), Qt::CaseInsensitive)) {
|
||||
return KCompressionDevice::BZip2;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
if (fileName.endsWith(QLatin1String(".lzma"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String(".xz"), Qt::CaseInsensitive)) {
|
||||
return KCompressionDevice::Xz;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
if (fileName.endsWith(QLatin1String(".zst"), Qt::CaseInsensitive)) {
|
||||
return KCompressionDevice::Zstd;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// not a warning, since this is called often with other MIME types (see #88574)...
|
||||
// maybe we can avoid that though?
|
||||
// qCDebug(KArchiveLog) << "findCompressionByFileName : no compression found for " << fileName;
|
||||
}
|
||||
|
||||
return KCompressionDevice::None;
|
||||
}
|
||||
|
||||
KCompressionDevice::CompressionType KCompressionDevice::compressionTypeForMimeType(const QString &mimeType)
|
||||
{
|
||||
if (mimeType == QLatin1String("application/gzip") //
|
||||
|| mimeType == QLatin1String("application/x-gzip") // legacy name, kept for compatibility
|
||||
) {
|
||||
return KCompressionDevice::GZip;
|
||||
}
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
if (mimeType == QLatin1String("application/x-bzip") //
|
||||
|| mimeType == QLatin1String("application/x-bzip2") // old name, kept for compatibility
|
||||
) {
|
||||
return KCompressionDevice::BZip2;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
if (mimeType == QLatin1String("application/x-lzma") // legacy name, still used
|
||||
|| mimeType == QLatin1String("application/x-xz") // current naming
|
||||
) {
|
||||
return KCompressionDevice::Xz;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
if (mimeType == QLatin1String("application/zstd")) {
|
||||
return KCompressionDevice::Zstd;
|
||||
}
|
||||
#endif
|
||||
QMimeDatabase db;
|
||||
const QMimeType mime = db.mimeTypeForName(mimeType);
|
||||
if (mime.isValid()) {
|
||||
// use legacy MIME type for now, see comment in impl. of KTar(const QString &, const QString &_mimetype)
|
||||
if (mime.inherits(QStringLiteral("application/x-gzip"))) {
|
||||
return KCompressionDevice::GZip;
|
||||
}
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
if (mime.inherits(QStringLiteral("application/x-bzip"))) {
|
||||
return KCompressionDevice::BZip2;
|
||||
}
|
||||
#endif
|
||||
#if HAVE_XZ_SUPPORT
|
||||
if (mime.inherits(QStringLiteral("application/x-lzma"))) {
|
||||
return KCompressionDevice::Xz;
|
||||
}
|
||||
|
||||
if (mime.inherits(QStringLiteral("application/x-xz"))) {
|
||||
return KCompressionDevice::Xz;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// not a warning, since this is called often with other MIME types (see #88574)...
|
||||
// maybe we can avoid that though?
|
||||
// qCDebug(KArchiveLog) << "no compression found for" << mimeType;
|
||||
return KCompressionDevice::None;
|
||||
}
|
||||
|
||||
KFilterBase *KCompressionDevice::filterForCompressionType(KCompressionDevice::CompressionType type)
|
||||
{
|
||||
switch (type) {
|
||||
case KCompressionDevice::GZip:
|
||||
return new KGzipFilter;
|
||||
case KCompressionDevice::BZip2:
|
||||
#if HAVE_BZIP2_SUPPORT
|
||||
return new KBzip2Filter;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
case KCompressionDevice::Xz:
|
||||
#if HAVE_XZ_SUPPORT
|
||||
return new KXzFilter;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
case KCompressionDevice::None:
|
||||
return new KNoneFilter;
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
case KCompressionDevice::Zstd:
|
||||
return new KZstdFilter;
|
||||
#endif
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KCompressionDevice::KCompressionDevice(QIODevice *inputDevice, bool autoDeleteInputDevice, CompressionType type)
|
||||
: d(new KCompressionDevicePrivate(this))
|
||||
{
|
||||
assert(inputDevice);
|
||||
d->filter = filterForCompressionType(type);
|
||||
if (d->filter) {
|
||||
d->type = type;
|
||||
d->filter->setDevice(inputDevice, autoDeleteInputDevice);
|
||||
}
|
||||
}
|
||||
|
||||
KCompressionDevice::KCompressionDevice(const QString &fileName, CompressionType type)
|
||||
: d(new KCompressionDevicePrivate(this))
|
||||
{
|
||||
QFile *f = new QFile(fileName);
|
||||
d->filter = filterForCompressionType(type);
|
||||
if (d->filter) {
|
||||
d->type = type;
|
||||
d->filter->setDevice(f, true);
|
||||
} else {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
|
||||
KCompressionDevice::KCompressionDevice(const QString &fileName)
|
||||
: KCompressionDevice(fileName, findCompressionByFileName(fileName))
|
||||
{
|
||||
}
|
||||
|
||||
KCompressionDevice::~KCompressionDevice()
|
||||
{
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
delete d->filter;
|
||||
delete d;
|
||||
}
|
||||
|
||||
KCompressionDevice::CompressionType KCompressionDevice::compressionType() const
|
||||
{
|
||||
return d->type;
|
||||
}
|
||||
|
||||
bool KCompressionDevice::open(QIODevice::OpenMode mode)
|
||||
{
|
||||
if (isOpen()) {
|
||||
// qCWarning(KArchiveLog) << "KCompressionDevice::open: device is already open";
|
||||
return true; // QFile returns false, but well, the device -is- open...
|
||||
}
|
||||
if (!d->filter) {
|
||||
return false;
|
||||
}
|
||||
d->bOpenedUnderlyingDevice = false;
|
||||
// qCDebug(KArchiveLog) << mode;
|
||||
if (mode == QIODevice::ReadOnly) {
|
||||
d->buffer.resize(0);
|
||||
} else {
|
||||
d->buffer.resize(BUFFER_SIZE);
|
||||
d->filter->setOutBuffer(d->buffer.data(), d->buffer.size());
|
||||
}
|
||||
if (!d->filter->device()->isOpen()) {
|
||||
if (!d->filter->device()->open(mode)) {
|
||||
// qCWarning(KArchiveLog) << "KCompressionDevice::open: Couldn't open underlying device";
|
||||
d->propagateErrorCode();
|
||||
return false;
|
||||
}
|
||||
d->bOpenedUnderlyingDevice = true;
|
||||
}
|
||||
d->bNeedHeader = !d->bSkipHeaders;
|
||||
d->filter->setFilterFlags(d->bSkipHeaders ? KFilterBase::NoHeaders : KFilterBase::WithHeaders);
|
||||
if (!d->filter->init(mode)) {
|
||||
return false;
|
||||
}
|
||||
d->result = KFilterBase::Ok;
|
||||
setOpenMode(mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
void KCompressionDevice::close()
|
||||
{
|
||||
if (!isOpen()) {
|
||||
return;
|
||||
}
|
||||
if (d->filter->mode() == QIODevice::WriteOnly && d->errorCode == QFileDevice::NoError) {
|
||||
write(nullptr, 0); // finish writing
|
||||
}
|
||||
// qCDebug(KArchiveLog) << "Calling terminate().";
|
||||
|
||||
if (!d->filter->terminate()) {
|
||||
// qCWarning(KArchiveLog) << "KCompressionDevice::close: terminate returned an error";
|
||||
d->errorCode = QFileDevice::UnspecifiedError;
|
||||
}
|
||||
if (d->bOpenedUnderlyingDevice) {
|
||||
QIODevice *dev = d->filter->device();
|
||||
dev->close();
|
||||
d->propagateErrorCode();
|
||||
}
|
||||
setOpenMode(QIODevice::NotOpen);
|
||||
}
|
||||
|
||||
QFileDevice::FileError KCompressionDevice::error() const
|
||||
{
|
||||
return d->errorCode;
|
||||
}
|
||||
|
||||
bool KCompressionDevice::seek(qint64 pos)
|
||||
{
|
||||
if (d->deviceReadPos == pos) {
|
||||
return QIODevice::seek(pos);
|
||||
}
|
||||
|
||||
// qCDebug(KArchiveLog) << "seek(" << pos << ") called, current pos=" << QIODevice::pos();
|
||||
|
||||
Q_ASSERT(d->filter->mode() == QIODevice::ReadOnly);
|
||||
|
||||
if (pos == 0) {
|
||||
if (!QIODevice::seek(pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can forget about the cached data
|
||||
d->bNeedHeader = !d->bSkipHeaders;
|
||||
d->result = KFilterBase::Ok;
|
||||
d->filter->setInBuffer(nullptr, 0);
|
||||
d->filter->reset();
|
||||
d->deviceReadPos = 0;
|
||||
return d->filter->device()->reset();
|
||||
}
|
||||
|
||||
qint64 bytesToRead;
|
||||
if (d->deviceReadPos < pos) { // we can start from here
|
||||
bytesToRead = pos - d->deviceReadPos;
|
||||
// Since we're going to do a read() below
|
||||
// we need to reset the internal QIODevice pos to the real position we are
|
||||
// so that after read() we are indeed pointing to the pos seek
|
||||
// asked us to be in
|
||||
if (!QIODevice::seek(d->deviceReadPos)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// we have to start from 0 ! Ugly and slow, but better than the previous
|
||||
// solution (KTarGz was allocating everything into memory)
|
||||
if (!seek(0)) { // recursive
|
||||
return false;
|
||||
}
|
||||
bytesToRead = pos;
|
||||
}
|
||||
|
||||
// qCDebug(KArchiveLog) << "reading " << bytesToRead << " dummy bytes";
|
||||
QByteArray dummy(qMin(bytesToRead, qint64(SEEK_BUFFER_SIZE)), 0);
|
||||
while (bytesToRead > 0) {
|
||||
const qint64 bytesToReadThisTime = qMin(bytesToRead, qint64(dummy.size()));
|
||||
const bool result = (read(dummy.data(), bytesToReadThisTime) == bytesToReadThisTime);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
bytesToRead -= bytesToReadThisTime;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KCompressionDevice::atEnd() const
|
||||
{
|
||||
return (d->type == KCompressionDevice::None || d->result == KFilterBase::End) //
|
||||
&& QIODevice::atEnd() // take QIODevice's internal buffer into account
|
||||
&& d->filter->device()->atEnd();
|
||||
}
|
||||
|
||||
qint64 KCompressionDevice::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_ASSERT(d->filter->mode() == QIODevice::ReadOnly);
|
||||
// qCDebug(KArchiveLog) << "maxlen=" << maxlen;
|
||||
KFilterBase *filter = d->filter;
|
||||
|
||||
uint dataReceived = 0;
|
||||
|
||||
// We came to the end of the stream
|
||||
if (d->result == KFilterBase::End) {
|
||||
return dataReceived;
|
||||
}
|
||||
|
||||
// If we had an error, return -1.
|
||||
if (d->result != KFilterBase::Ok) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
qint64 availOut = maxlen;
|
||||
filter->setOutBuffer(data, maxlen);
|
||||
|
||||
while (dataReceived < maxlen) {
|
||||
if (filter->inBufferEmpty()) {
|
||||
// Not sure about the best size to set there.
|
||||
// For sure, it should be bigger than the header size (see comment in readHeader)
|
||||
d->buffer.resize(BUFFER_SIZE);
|
||||
// Request data from underlying device
|
||||
int size = filter->device()->read(d->buffer.data(), d->buffer.size());
|
||||
// qCDebug(KArchiveLog) << "got" << size << "bytes from device";
|
||||
if (size) {
|
||||
filter->setInBuffer(d->buffer.data(), size);
|
||||
} else {
|
||||
// Not enough data available in underlying device for now
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (d->bNeedHeader) {
|
||||
(void)filter->readHeader();
|
||||
d->bNeedHeader = false;
|
||||
}
|
||||
|
||||
d->result = filter->uncompress();
|
||||
|
||||
if (d->result == KFilterBase::Error) {
|
||||
// qCWarning(KArchiveLog) << "KCompressionDevice: Error when uncompressing data";
|
||||
break;
|
||||
}
|
||||
|
||||
// We got that much data since the last time we went here
|
||||
uint outReceived = availOut - filter->outBufferAvailable();
|
||||
// qCDebug(KArchiveLog) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived;
|
||||
if (availOut < uint(filter->outBufferAvailable())) {
|
||||
// qCWarning(KArchiveLog) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !";
|
||||
}
|
||||
|
||||
dataReceived += outReceived;
|
||||
data += outReceived;
|
||||
availOut = maxlen - dataReceived;
|
||||
if (d->result == KFilterBase::End) {
|
||||
// We're actually at the end, no more data to check
|
||||
if (filter->device()->atEnd()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Still not done, re-init and try again
|
||||
filter->init(filter->mode());
|
||||
}
|
||||
filter->setOutBuffer(data, availOut);
|
||||
}
|
||||
|
||||
d->deviceReadPos += dataReceived;
|
||||
return dataReceived;
|
||||
}
|
||||
|
||||
qint64 KCompressionDevice::writeData(const char *data /*0 to finish*/, qint64 len)
|
||||
{
|
||||
KFilterBase *filter = d->filter;
|
||||
Q_ASSERT(filter->mode() == QIODevice::WriteOnly);
|
||||
// If we had an error, return 0.
|
||||
if (d->result != KFilterBase::Ok) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool finish = (data == nullptr);
|
||||
if (!finish) {
|
||||
filter->setInBuffer(data, len);
|
||||
if (d->bNeedHeader) {
|
||||
(void)filter->writeHeader(d->origFileName);
|
||||
d->bNeedHeader = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint dataWritten = 0;
|
||||
uint availIn = len;
|
||||
while (dataWritten < len || finish) {
|
||||
d->result = filter->compress(finish);
|
||||
|
||||
if (d->result == KFilterBase::Error) {
|
||||
// qCWarning(KArchiveLog) << "KCompressionDevice: Error when compressing data";
|
||||
// What to do ?
|
||||
break;
|
||||
}
|
||||
|
||||
// Wrote everything ?
|
||||
if (filter->inBufferEmpty() || (d->result == KFilterBase::End)) {
|
||||
// We got that much data since the last time we went here
|
||||
uint wrote = availIn - filter->inBufferAvailable();
|
||||
|
||||
// qCDebug(KArchiveLog) << " Wrote everything for now. avail_in=" << filter->inBufferAvailable() << "result=" << d->result << "wrote=" << wrote;
|
||||
|
||||
// Move on in the input buffer
|
||||
data += wrote;
|
||||
dataWritten += wrote;
|
||||
|
||||
availIn = len - dataWritten;
|
||||
// qCDebug(KArchiveLog) << " availIn=" << availIn << "dataWritten=" << dataWritten << "pos=" << pos();
|
||||
if (availIn > 0) {
|
||||
filter->setInBuffer(data, availIn);
|
||||
}
|
||||
}
|
||||
|
||||
if (filter->outBufferFull() || (d->result == KFilterBase::End) || finish) {
|
||||
// qCDebug(KArchiveLog) << " writing to underlying. avail_out=" << filter->outBufferAvailable();
|
||||
int towrite = d->buffer.size() - filter->outBufferAvailable();
|
||||
if (towrite > 0) {
|
||||
// Write compressed data to underlying device
|
||||
int size = filter->device()->write(d->buffer.data(), towrite);
|
||||
if (size != towrite) {
|
||||
// qCWarning(KArchiveLog) << "KCompressionDevice::write. Could only write " << size << " out of " << towrite << " bytes";
|
||||
d->errorCode = QFileDevice::WriteError;
|
||||
setErrorString(tr("Could not write. Partition full?"));
|
||||
return 0; // indicate an error
|
||||
}
|
||||
// qCDebug(KArchiveLog) << " wrote " << size << " bytes";
|
||||
}
|
||||
if (d->result == KFilterBase::End) {
|
||||
Q_ASSERT(finish); // hopefully we don't get end before finishing
|
||||
break;
|
||||
}
|
||||
d->buffer.resize(BUFFER_SIZE);
|
||||
filter->setOutBuffer(d->buffer.data(), d->buffer.size());
|
||||
}
|
||||
}
|
||||
|
||||
return dataWritten;
|
||||
}
|
||||
|
||||
void KCompressionDevice::setOrigFileName(const QByteArray &fileName)
|
||||
{
|
||||
d->origFileName = fileName;
|
||||
}
|
||||
|
||||
void KCompressionDevice::setSkipHeaders()
|
||||
{
|
||||
d->bSkipHeaders = true;
|
||||
}
|
||||
|
||||
KFilterBase *KCompressionDevice::filterBase()
|
||||
{
|
||||
return d->filter;
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef __kcompressiondevice_h
|
||||
#define __kcompressiondevice_h
|
||||
|
||||
#include <karchive_export.h>
|
||||
|
||||
#include <QFileDevice>
|
||||
#include <QIODevice>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
|
||||
class KCompressionDevicePrivate;
|
||||
|
||||
class KFilterBase;
|
||||
|
||||
/**
|
||||
* @class KCompressionDevice kcompressiondevice.h KCompressionDevice
|
||||
*
|
||||
* A class for reading and writing compressed data onto a device
|
||||
* (e.g. file, but other usages are possible, like a buffer or a socket).
|
||||
*
|
||||
* Use this class to read/write compressed files.
|
||||
*/
|
||||
|
||||
class KARCHIVE_EXPORT KCompressionDevice : public QIODevice // KF6 TODO: consider inheriting from QFileDevice, so apps can use error() generically ?
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum CompressionType {
|
||||
GZip,
|
||||
BZip2,
|
||||
Xz,
|
||||
None,
|
||||
Zstd, ///< @since 5.82
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a KCompressionDevice for a given CompressionType (e.g. GZip, BZip2 etc.).
|
||||
* @param inputDevice input device.
|
||||
* @param autoDeleteInputDevice if true, @p inputDevice will be deleted automatically
|
||||
* @param type the CompressionType to use.
|
||||
*/
|
||||
KCompressionDevice(QIODevice *inputDevice, bool autoDeleteInputDevice, CompressionType type);
|
||||
|
||||
/**
|
||||
* Constructs a KCompressionDevice for a given CompressionType (e.g. GZip, BZip2 etc.).
|
||||
* @param fileName the name of the file to filter.
|
||||
* @param type the CompressionType to use.
|
||||
*/
|
||||
KCompressionDevice(const QString &fileName, CompressionType type);
|
||||
|
||||
/**
|
||||
* Constructs a KCompressionDevice for a given @p fileName.
|
||||
* @param fileName the name of the file to filter.
|
||||
* @since 5.85
|
||||
*/
|
||||
KCompressionDevice(const QString &fileName);
|
||||
|
||||
/**
|
||||
* Destructs the KCompressionDevice.
|
||||
* Calls close() if the filter device is still open.
|
||||
*/
|
||||
~KCompressionDevice() override;
|
||||
|
||||
/**
|
||||
* The compression actually used by this device.
|
||||
* If the support for the compression requested in the constructor
|
||||
* is not available, then the device will use None.
|
||||
*/
|
||||
CompressionType compressionType() const;
|
||||
|
||||
/**
|
||||
* Open for reading or writing.
|
||||
*/
|
||||
bool open(QIODevice::OpenMode mode) override;
|
||||
|
||||
/**
|
||||
* Close after reading or writing.
|
||||
*/
|
||||
void close() override;
|
||||
|
||||
/**
|
||||
* For writing gzip compressed files only:
|
||||
* set the name of the original file, to be used in the gzip header.
|
||||
* @param fileName the name of the original file
|
||||
*/
|
||||
void setOrigFileName(const QByteArray &fileName);
|
||||
|
||||
/**
|
||||
* Call this let this device skip the gzip headers when reading/writing.
|
||||
* This way KCompressionDevice (with gzip filter) can be used as a direct wrapper
|
||||
* around zlib - this is used by KZip.
|
||||
*/
|
||||
void setSkipHeaders();
|
||||
|
||||
/**
|
||||
* That one can be quite slow, when going back. Use with care.
|
||||
*/
|
||||
bool seek(qint64) override;
|
||||
|
||||
bool atEnd() const override;
|
||||
|
||||
/**
|
||||
* Call this to create the appropriate filter for the CompressionType
|
||||
* named @p type.
|
||||
* @param type the type of the compression filter
|
||||
* @return the filter for the @p type, or 0 if not found
|
||||
*/
|
||||
static KFilterBase *filterForCompressionType(CompressionType type);
|
||||
|
||||
/**
|
||||
* Returns the compression type for the given MIME type, if possible. Otherwise returns None.
|
||||
* This handles simple cases like application/gzip, but also application/x-compressed-tar, and inheritance.
|
||||
* @since 5.85
|
||||
*/
|
||||
static CompressionType compressionTypeForMimeType(const QString &mimetype);
|
||||
|
||||
/**
|
||||
* Returns the error code from the last failing operation.
|
||||
* This is especially useful after calling close(), which unfortunately returns void
|
||||
* (see https://bugreports.qt.io/browse/QTBUG-70033), to see if the flushing done by close
|
||||
* was able to write all the data to disk.
|
||||
*/
|
||||
QFileDevice::FileError error() const;
|
||||
|
||||
protected:
|
||||
friend class K7Zip;
|
||||
|
||||
qint64 readData(char *data, qint64 maxlen) override;
|
||||
qint64 writeData(const char *data, qint64 len) override;
|
||||
|
||||
KFilterBase *filterBase();
|
||||
|
||||
private:
|
||||
friend KCompressionDevicePrivate;
|
||||
KCompressionDevicePrivate *const d;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(KCompressionDevice::CompressionType)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef __kcompressiondevice_p_h
|
||||
#define __kcompressiondevice_p_h
|
||||
|
||||
#define BUFFER_SIZE 8 * 1024
|
||||
#define SEEK_BUFFER_SIZE 3 * BUFFER_SIZE
|
||||
|
||||
#endif
|
|
@ -0,0 +1,81 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kfilterbase.h"
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
class KFilterBasePrivate
|
||||
{
|
||||
public:
|
||||
KFilterBasePrivate()
|
||||
: m_flags(KFilterBase::WithHeaders)
|
||||
, m_dev(nullptr)
|
||||
, m_bAutoDel(false)
|
||||
{
|
||||
}
|
||||
KFilterBase::FilterFlags m_flags;
|
||||
QIODevice *m_dev;
|
||||
bool m_bAutoDel;
|
||||
};
|
||||
|
||||
KFilterBase::KFilterBase()
|
||||
: d(new KFilterBasePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KFilterBase::~KFilterBase()
|
||||
{
|
||||
if (d->m_bAutoDel) {
|
||||
delete d->m_dev;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KFilterBase::setDevice(QIODevice *dev, bool autodelete)
|
||||
{
|
||||
d->m_dev = dev;
|
||||
d->m_bAutoDel = autodelete;
|
||||
}
|
||||
|
||||
QIODevice *KFilterBase::device()
|
||||
{
|
||||
return d->m_dev;
|
||||
}
|
||||
|
||||
bool KFilterBase::inBufferEmpty() const
|
||||
{
|
||||
return inBufferAvailable() == 0;
|
||||
}
|
||||
|
||||
bool KFilterBase::outBufferFull() const
|
||||
{
|
||||
return outBufferAvailable() == 0;
|
||||
}
|
||||
|
||||
bool KFilterBase::terminate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void KFilterBase::reset()
|
||||
{
|
||||
}
|
||||
|
||||
void KFilterBase::setFilterFlags(FilterFlags flags)
|
||||
{
|
||||
d->m_flags = flags;
|
||||
}
|
||||
|
||||
KFilterBase::FilterFlags KFilterBase::filterFlags() const
|
||||
{
|
||||
return d->m_flags;
|
||||
}
|
||||
|
||||
void KFilterBase::virtual_hook(int, void *)
|
||||
{
|
||||
/*BASE::virtual_hook( id, data );*/
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __kfilterbase__h
|
||||
#define __kfilterbase__h
|
||||
|
||||
#include <karchive_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
class KFilterBasePrivate;
|
||||
|
||||
class QIODevice;
|
||||
|
||||
/**
|
||||
* @class KFilterBase kfilterbase.h KFilterBase
|
||||
*
|
||||
* This is the base class for compression filters
|
||||
* such as gzip and bzip2. It's pretty much internal.
|
||||
* Don't use directly, use KCompressionDevice instead.
|
||||
* @internal
|
||||
*/
|
||||
class KARCHIVE_EXPORT KFilterBase
|
||||
{
|
||||
public:
|
||||
KFilterBase();
|
||||
virtual ~KFilterBase();
|
||||
|
||||
/**
|
||||
* Sets the device on which the filter will work
|
||||
* @param dev the device on which the filter will work
|
||||
* @param autodelete if true, @p dev is deleted when the filter is deleted
|
||||
*/
|
||||
void setDevice(QIODevice *dev, bool autodelete = false);
|
||||
// Note that this isn't in the constructor, because of KLibFactory::create,
|
||||
// but it should be called before using the filterbase !
|
||||
|
||||
/**
|
||||
* Returns the device on which the filter will work.
|
||||
* @returns the device on which the filter will work
|
||||
*/
|
||||
QIODevice *device();
|
||||
/** \internal */
|
||||
virtual bool init(int mode) = 0;
|
||||
/** \internal */
|
||||
virtual int mode() const = 0;
|
||||
/** \internal */
|
||||
virtual bool terminate();
|
||||
/** \internal */
|
||||
virtual void reset();
|
||||
/** \internal */
|
||||
virtual bool readHeader() = 0;
|
||||
/** \internal */
|
||||
virtual bool writeHeader(const QByteArray &filename) = 0;
|
||||
/** \internal */
|
||||
virtual void setOutBuffer(char *data, uint maxlen) = 0;
|
||||
/** \internal */
|
||||
virtual void setInBuffer(const char *data, uint size) = 0;
|
||||
/** \internal */
|
||||
virtual bool inBufferEmpty() const;
|
||||
/** \internal */
|
||||
virtual int inBufferAvailable() const = 0;
|
||||
/** \internal */
|
||||
virtual bool outBufferFull() const;
|
||||
/** \internal */
|
||||
virtual int outBufferAvailable() const = 0;
|
||||
|
||||
/** \internal */
|
||||
enum Result {
|
||||
Ok,
|
||||
End,
|
||||
Error,
|
||||
};
|
||||
/** \internal */
|
||||
virtual Result uncompress() = 0;
|
||||
/** \internal */
|
||||
virtual Result compress(bool finish) = 0;
|
||||
|
||||
/**
|
||||
* \internal
|
||||
* \since 4.3
|
||||
*/
|
||||
enum FilterFlags {
|
||||
NoHeaders = 0,
|
||||
WithHeaders = 1,
|
||||
ZlibHeaders = 2, // only use for gzip compression
|
||||
};
|
||||
/**
|
||||
* \internal
|
||||
* \since 4.3
|
||||
*/
|
||||
void setFilterFlags(FilterFlags flags);
|
||||
FilterFlags filterFlags() const;
|
||||
|
||||
protected:
|
||||
/** Virtual hook, used to add new "virtual" functions while maintaining
|
||||
binary compatibility. Unused in this class.
|
||||
*/
|
||||
virtual void virtual_hook(int id, void *data);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KFilterBase)
|
||||
KFilterBasePrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,21 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000, 2006 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kfilterdev.h"
|
||||
|
||||
#if KARCHIVE_BUILD_DEPRECATED_SINCE(5, 85)
|
||||
|
||||
KFilterDev::KFilterDev(const QString &fileName)
|
||||
: KCompressionDevice(fileName)
|
||||
{
|
||||
}
|
||||
|
||||
KCompressionDevice::CompressionType KFilterDev::compressionTypeForMimeType(const QString &mimeType)
|
||||
{
|
||||
return KCompressionDevice::compressionTypeForMimeType(mimeType);
|
||||
}
|
||||
|
||||
#endif // KARCHIVE_BUILD_DEPRECATED_SINCE(5, 85)
|
|
@ -0,0 +1,153 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef __kfilterdev_h
|
||||
#define __kfilterdev_h
|
||||
|
||||
#include <karchive_export.h>
|
||||
#include <kcompressiondevice.h>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#if KARCHIVE_ENABLE_DEPRECATED_SINCE(5, 85)
|
||||
|
||||
class QFile;
|
||||
class KFilterBase;
|
||||
|
||||
/**
|
||||
* @class KFilterDev kfilterdev.h KFilterDev
|
||||
*
|
||||
* A class for reading and writing compressed data onto a device
|
||||
* (e.g. file, but other usages are possible, like a buffer or a socket).
|
||||
*
|
||||
* To simply read/write compressed files, see deviceForFile.
|
||||
*
|
||||
* KFilterDev adds MIME type support to KCompressionDevice, and also
|
||||
* provides compatibility methods for KDE 4 code.
|
||||
*
|
||||
* @author David Faure <faure@kde.org>
|
||||
* @deprecated Since 5.85, use KCompressionDevice directly
|
||||
*/
|
||||
class KARCHIVE_EXPORT KFilterDev : public KCompressionDevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructs a KFilterDev for a given FileName.
|
||||
* @param fileName the name of the file to filter.
|
||||
* @since 5.0
|
||||
* @deprecated Since 5.85, use KCompressionDevice(const QString &)
|
||||
*/
|
||||
KFilterDev(const QString &fileName);
|
||||
|
||||
/**
|
||||
* Returns the compression type for the given mimetype, if possible. Otherwise returns None.
|
||||
* This handles simple cases like application/gzip, but also application/x-compressed-tar, and inheritance.
|
||||
* @deprecated Since 5.85, use KCompressionDevice::compressionTypeForMimeType(const QString &)
|
||||
*/
|
||||
KARCHIVE_DEPRECATED_VERSION(5, 85, "Use KCompressionDevice::compressionTypeForMimeType(const QString &)")
|
||||
static CompressionType compressionTypeForMimeType(const QString &mimetype);
|
||||
|
||||
#if KARCHIVE_ENABLE_DEPRECATED_SINCE(5, 0)
|
||||
/**
|
||||
* @deprecated Since 5.0, use the constructor instead (if mimetype is empty), or KCompressionDevice (if
|
||||
* the mimetype is known).
|
||||
*
|
||||
* Use:
|
||||
* KFilterDev dev(fileName)
|
||||
* instead of:
|
||||
* QIODevice * dev = KFilterDev::deviceForFile(fileName);
|
||||
*
|
||||
* If the mimetype was specified explicitly, use:
|
||||
* KCompressionDevice dev(fileName, KCompressionDevice::GZip);
|
||||
* instead of:
|
||||
* QIODevice * dev = KFilterDev::deviceForFile(fileName, "application/gzip");
|
||||
*
|
||||
* Creates an i/o device that is able to read from @p fileName,
|
||||
* whether it's compressed or not. Available compression filters
|
||||
* (gzip/bzip2 etc.) will automatically be used.
|
||||
*
|
||||
* The compression filter to be used is determined from the @p fileName
|
||||
* if @p mimetype is empty. Pass "application/gzip" or "application/x-bzip"
|
||||
* to force the corresponding decompression filter, if available.
|
||||
*
|
||||
* Warning: application/x-bzip may not be available.
|
||||
* In that case a QFile opened on the compressed data will be returned !
|
||||
* Use KFilterBase::findFilterByMimeType and code similar to what
|
||||
* deviceForFile is doing, to better control what's happening.
|
||||
*
|
||||
* The returned QIODevice has to be deleted after using.
|
||||
*
|
||||
* @param fileName the name of the file to filter
|
||||
* @param mimetype the mime type of the file to filter, or QString() if unknown
|
||||
* @param forceFilter if true, the function will either find a compression filter, or return 0.
|
||||
* If false, it will always return a QIODevice. If no
|
||||
* filter is available it will return a simple QFile.
|
||||
* This can be useful if the file is usable without a filter.
|
||||
* @return if a filter has been found, the KCompressionDevice for the filter. If the
|
||||
* filter does not exist, the return value depends on @p forceFilter.
|
||||
* The returned KCompressionDevice has to be deleted after using.
|
||||
*/
|
||||
KARCHIVE_DEPRECATED_VERSION(5, 0, "See API docs")
|
||||
static KCompressionDevice *deviceForFile(const QString &fileName, const QString &mimetype = QString(), bool forceFilter = false)
|
||||
{
|
||||
KCompressionDevice *device;
|
||||
if (mimetype.isEmpty()) {
|
||||
device = new KFilterDev(fileName);
|
||||
} else {
|
||||
device = new KCompressionDevice(fileName, compressionTypeForMimeType(mimetype));
|
||||
}
|
||||
if (device->compressionType() == KCompressionDevice::None && forceFilter) {
|
||||
delete device;
|
||||
return nullptr;
|
||||
} else {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if KARCHIVE_ENABLE_DEPRECATED_SINCE(5, 0)
|
||||
/**
|
||||
* @deprecated Since 5.0, use KCompressionDevice
|
||||
*
|
||||
* Use:
|
||||
* KCompressionDevice::CompressionType type = KFilterDev::compressionTypeForMimeType(mimeType);
|
||||
* KCompressionDevice flt(&file, false, type);
|
||||
* instead of:
|
||||
* QIODevice *flt = KFilterDev::device(&file, mimeType, false);
|
||||
*
|
||||
* Creates an i/o device that is able to read from the QIODevice @p inDevice,
|
||||
* whether the data is compressed or not. Available compression filters
|
||||
* (gzip/bzip2 etc.) will automatically be used.
|
||||
*
|
||||
* The compression filter to be used is determined @p mimetype .
|
||||
* Pass "application/gzip" or "application/x-bzip"
|
||||
* to use the corresponding decompression filter.
|
||||
*
|
||||
* Warning: application/x-bzip may not be available.
|
||||
* In that case 0 will be returned !
|
||||
*
|
||||
* The returned QIODevice has to be deleted after using.
|
||||
* @param inDevice input device. Won't be deleted if @p autoDeleteInDevice = false
|
||||
* @param mimetype the mime type for the filter
|
||||
* @param autoDeleteInDevice if true, @p inDevice will be deleted automatically
|
||||
* @return a KCompressionDevice that filters the original stream. Must be deleted after using
|
||||
*/
|
||||
KARCHIVE_DEPRECATED_VERSION(5, 0, "See API docs")
|
||||
static KCompressionDevice *device(QIODevice *inDevice, const QString &mimetype, bool autoDeleteInDevice = true)
|
||||
{
|
||||
if (inDevice == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
CompressionType type = compressionTypeForMimeType(mimetype);
|
||||
KCompressionDevice *device = new KCompressionDevice(inDevice, autoDeleteInDevice, type);
|
||||
return device;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // KARCHIVE_ENABLE_DEPRECATED_SINCE(5, 85)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,366 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kgzipfilter.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QIODevice>
|
||||
|
||||
#include <time.h>
|
||||
#include <zlib.h>
|
||||
|
||||
/* gzip flag byte */
|
||||
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
|
||||
|
||||
// #define DEBUG_GZIP
|
||||
|
||||
class Q_DECL_HIDDEN KGzipFilter::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: headerWritten(false)
|
||||
, footerWritten(false)
|
||||
, compressed(false)
|
||||
, mode(0)
|
||||
, crc(0)
|
||||
, isInitialized(false)
|
||||
{
|
||||
zStream.zalloc = static_cast<alloc_func>(nullptr);
|
||||
zStream.zfree = static_cast<free_func>(nullptr);
|
||||
zStream.opaque = static_cast<voidpf>(nullptr);
|
||||
}
|
||||
|
||||
z_stream zStream;
|
||||
bool headerWritten;
|
||||
bool footerWritten;
|
||||
bool compressed;
|
||||
int mode;
|
||||
ulong crc;
|
||||
bool isInitialized;
|
||||
};
|
||||
|
||||
KGzipFilter::KGzipFilter()
|
||||
: d(new Private)
|
||||
{
|
||||
}
|
||||
|
||||
KGzipFilter::~KGzipFilter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KGzipFilter::init(int mode)
|
||||
{
|
||||
switch (filterFlags()) {
|
||||
case NoHeaders:
|
||||
return init(mode, RawDeflate);
|
||||
case WithHeaders:
|
||||
return init(mode, GZipHeader);
|
||||
case ZlibHeaders:
|
||||
return init(mode, ZlibHeader);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KGzipFilter::init(int mode, Flag flag)
|
||||
{
|
||||
if (d->isInitialized) {
|
||||
terminate();
|
||||
}
|
||||
d->zStream.next_in = Z_NULL;
|
||||
d->zStream.avail_in = 0;
|
||||
if (mode == QIODevice::ReadOnly) {
|
||||
const int windowBits = (flag == RawDeflate) ? -MAX_WBITS /*no zlib header*/
|
||||
: (flag == GZipHeader) ? MAX_WBITS + 32 /* auto-detect and eat gzip header */
|
||||
: MAX_WBITS /*zlib header*/;
|
||||
const int result = inflateInit2(&d->zStream, windowBits);
|
||||
if (result != Z_OK) {
|
||||
// qCDebug(KArchiveLog) << "inflateInit2 returned " << result;
|
||||
return false;
|
||||
}
|
||||
} else if (mode == QIODevice::WriteOnly) {
|
||||
int result = deflateInit2(&d->zStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); // same here
|
||||
if (result != Z_OK) {
|
||||
// qCDebug(KArchiveLog) << "deflateInit returned " << result;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "KGzipFilter: Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
|
||||
return false;
|
||||
}
|
||||
d->mode = mode;
|
||||
d->compressed = true;
|
||||
d->headerWritten = false;
|
||||
d->footerWritten = false;
|
||||
d->isInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int KGzipFilter::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
bool KGzipFilter::terminate()
|
||||
{
|
||||
if (d->mode == QIODevice::ReadOnly) {
|
||||
int result = inflateEnd(&d->zStream);
|
||||
if (result != Z_OK) {
|
||||
// qCDebug(KArchiveLog) << "inflateEnd returned " << result;
|
||||
return false;
|
||||
}
|
||||
} else if (d->mode == QIODevice::WriteOnly) {
|
||||
int result = deflateEnd(&d->zStream);
|
||||
if (result != Z_OK) {
|
||||
// qCDebug(KArchiveLog) << "deflateEnd returned " << result;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
d->isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void KGzipFilter::reset()
|
||||
{
|
||||
if (d->mode == QIODevice::ReadOnly) {
|
||||
int result = inflateReset(&d->zStream);
|
||||
if (result != Z_OK) {
|
||||
// qCDebug(KArchiveLog) << "inflateReset returned " << result;
|
||||
// TODO return false
|
||||
}
|
||||
} else if (d->mode == QIODevice::WriteOnly) {
|
||||
int result = deflateReset(&d->zStream);
|
||||
if (result != Z_OK) {
|
||||
// qCDebug(KArchiveLog) << "deflateReset returned " << result;
|
||||
// TODO return false
|
||||
}
|
||||
d->headerWritten = false;
|
||||
d->footerWritten = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool KGzipFilter::readHeader()
|
||||
{
|
||||
// We now rely on zlib to read the full header (see the MAX_WBITS + 32 in init).
|
||||
// We just use this method to check if the data is actually compressed.
|
||||
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << "avail=" << d->zStream.avail_in;
|
||||
#endif
|
||||
// Assume not compressed until we see a gzip header
|
||||
d->compressed = false;
|
||||
const Bytef *p = d->zStream.next_in;
|
||||
int i = d->zStream.avail_in;
|
||||
if ((i -= 10) < 0) {
|
||||
return false; // Need at least 10 bytes
|
||||
}
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << "first byte is " << QString::number(*p, 16);
|
||||
#endif
|
||||
if (*p++ != 0x1f) {
|
||||
return false; // GZip magic
|
||||
}
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << "second byte is " << QString::number(*p, 16);
|
||||
#endif
|
||||
if (*p++ != 0x8b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->compressed = true;
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << "header OK";
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Output a 16 bit value, lsb first */
|
||||
#define put_short(w) \
|
||||
*p++ = uchar((w)&0xff); \
|
||||
*p++ = uchar(ushort(w) >> 8);
|
||||
|
||||
/* Output a 32 bit value to the bit stream, lsb first */
|
||||
#define put_long(n) \
|
||||
put_short((n)&0xffff); \
|
||||
put_short((ulong(n)) >> 16);
|
||||
|
||||
bool KGzipFilter::writeHeader(const QByteArray &fileName)
|
||||
{
|
||||
Bytef *p = d->zStream.next_out;
|
||||
int i = d->zStream.avail_out;
|
||||
*p++ = 0x1f;
|
||||
*p++ = 0x8b;
|
||||
*p++ = Z_DEFLATED;
|
||||
*p++ = ORIG_NAME;
|
||||
put_long(time(nullptr)); // Modification time (in unix format)
|
||||
*p++ = 0; // Extra flags (2=max compress, 4=fastest compress)
|
||||
*p++ = 3; // Unix
|
||||
|
||||
uint len = fileName.length();
|
||||
for (uint j = 0; j < len; ++j) {
|
||||
*p++ = fileName[j];
|
||||
}
|
||||
*p++ = 0;
|
||||
int headerSize = p - d->zStream.next_out;
|
||||
i -= headerSize;
|
||||
Q_ASSERT(i > 0);
|
||||
d->crc = crc32(0L, nullptr, 0);
|
||||
d->zStream.next_out = p;
|
||||
d->zStream.avail_out = i;
|
||||
d->headerWritten = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void KGzipFilter::writeFooter()
|
||||
{
|
||||
Q_ASSERT(d->headerWritten);
|
||||
Q_ASSERT(!d->footerWritten);
|
||||
Bytef *p = d->zStream.next_out;
|
||||
int i = d->zStream.avail_out;
|
||||
// qCDebug(KArchiveLog) << "avail_out=" << i << "writing CRC=" << QString::number(d->crc, 16) << "at p=" << p;
|
||||
put_long(d->crc);
|
||||
// qCDebug(KArchiveLog) << "writing totalin=" << d->zStream.total_in << "at p=" << p;
|
||||
put_long(d->zStream.total_in);
|
||||
i -= p - d->zStream.next_out;
|
||||
d->zStream.next_out = p;
|
||||
d->zStream.avail_out = i;
|
||||
d->footerWritten = true;
|
||||
}
|
||||
|
||||
void KGzipFilter::setOutBuffer(char *data, uint maxlen)
|
||||
{
|
||||
d->zStream.avail_out = maxlen;
|
||||
d->zStream.next_out = reinterpret_cast<Bytef *>(data);
|
||||
}
|
||||
void KGzipFilter::setInBuffer(const char *data, uint size)
|
||||
{
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << "avail_in=" << size;
|
||||
#endif
|
||||
d->zStream.avail_in = size;
|
||||
d->zStream.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(data));
|
||||
}
|
||||
int KGzipFilter::inBufferAvailable() const
|
||||
{
|
||||
return d->zStream.avail_in;
|
||||
}
|
||||
int KGzipFilter::outBufferAvailable() const
|
||||
{
|
||||
return d->zStream.avail_out;
|
||||
}
|
||||
|
||||
KGzipFilter::Result KGzipFilter::uncompress_noop()
|
||||
{
|
||||
// I'm not sure we really need support for that (uncompressed streams),
|
||||
// but why not, it can't hurt to have it. One case I can think of is someone
|
||||
// naming a tar file "blah.tar.gz" :-)
|
||||
if (d->zStream.avail_in > 0) {
|
||||
int n = (d->zStream.avail_in < d->zStream.avail_out) ? d->zStream.avail_in : d->zStream.avail_out;
|
||||
memcpy(d->zStream.next_out, d->zStream.next_in, n);
|
||||
d->zStream.avail_out -= n;
|
||||
d->zStream.next_in += n;
|
||||
d->zStream.avail_in -= n;
|
||||
return KFilterBase::Ok;
|
||||
} else {
|
||||
return KFilterBase::End;
|
||||
}
|
||||
}
|
||||
|
||||
KGzipFilter::Result KGzipFilter::uncompress()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (d->mode == 0) {
|
||||
// qCWarning(KArchiveLog) << "mode==0; KGzipFilter::init was not called!";
|
||||
return KFilterBase::Error;
|
||||
} else if (d->mode == QIODevice::WriteOnly) {
|
||||
// qCWarning(KArchiveLog) << "uncompress called but the filter was opened for writing!";
|
||||
return KFilterBase::Error;
|
||||
}
|
||||
Q_ASSERT(d->mode == QIODevice::ReadOnly);
|
||||
#endif
|
||||
|
||||
if (!d->compressed) {
|
||||
return uncompress_noop();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << "Calling inflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
qCDebug(KArchiveLog) << " next_in=" << d->zStream.next_in;
|
||||
#endif
|
||||
|
||||
while (d->zStream.avail_in > 0) {
|
||||
int result = inflate(&d->zStream, Z_SYNC_FLUSH);
|
||||
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << " -> inflate returned " << result;
|
||||
qCDebug(KArchiveLog) << " now avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
qCDebug(KArchiveLog) << " next_in=" << d->zStream.next_in;
|
||||
#endif
|
||||
|
||||
if (result == Z_OK) {
|
||||
return KFilterBase::Ok;
|
||||
}
|
||||
|
||||
// We can't handle any other results
|
||||
if (result != Z_STREAM_END) {
|
||||
return KFilterBase::Error;
|
||||
}
|
||||
|
||||
// It really was the end
|
||||
if (d->zStream.avail_in == 0) {
|
||||
return KFilterBase::End;
|
||||
}
|
||||
|
||||
// Store before resetting
|
||||
Bytef *data = d->zStream.next_in; // This is increased appropriately by zlib beforehand
|
||||
uInt size = d->zStream.avail_in;
|
||||
|
||||
// Reset the stream, if that fails we assume we're at the end
|
||||
if (!init(d->mode)) {
|
||||
return KFilterBase::End;
|
||||
}
|
||||
|
||||
// Reset the data to where we left off
|
||||
d->zStream.next_in = data;
|
||||
d->zStream.avail_in = size;
|
||||
}
|
||||
|
||||
return KFilterBase::End;
|
||||
}
|
||||
|
||||
KGzipFilter::Result KGzipFilter::compress(bool finish)
|
||||
{
|
||||
Q_ASSERT(d->compressed);
|
||||
Q_ASSERT(d->mode == QIODevice::WriteOnly);
|
||||
|
||||
const Bytef *p = d->zStream.next_in;
|
||||
ulong len = d->zStream.avail_in;
|
||||
#ifdef DEBUG_GZIP
|
||||
qCDebug(KArchiveLog) << " calling deflate with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
#endif
|
||||
const int result = deflate(&d->zStream, finish ? Z_FINISH : Z_NO_FLUSH);
|
||||
if (result != Z_OK && result != Z_STREAM_END) {
|
||||
// qCDebug(KArchiveLog) << " deflate returned " << result;
|
||||
}
|
||||
if (d->headerWritten) {
|
||||
// qCDebug(KArchiveLog) << "Computing CRC for the next " << len - d->zStream.avail_in << " bytes";
|
||||
d->crc = crc32(d->crc, p, len - d->zStream.avail_in);
|
||||
}
|
||||
KGzipFilter::Result callerResult = result == Z_OK ? KFilterBase::Ok : (Z_STREAM_END ? KFilterBase::End : KFilterBase::Error);
|
||||
|
||||
if (result == Z_STREAM_END && d->headerWritten && !d->footerWritten) {
|
||||
if (d->zStream.avail_out >= 8 /*footer size*/) {
|
||||
// qCDebug(KArchiveLog) << "finished, write footer";
|
||||
writeFooter();
|
||||
} else {
|
||||
// No room to write the footer (#157706/#188415), we'll have to do it on the next pass.
|
||||
// qCDebug(KArchiveLog) << "finished, but no room for footer yet";
|
||||
callerResult = KFilterBase::Ok;
|
||||
}
|
||||
}
|
||||
return callerResult;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000, 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __kgzipfilter__h
|
||||
#define __kgzipfilter__h
|
||||
|
||||
#include "kfilterbase.h"
|
||||
|
||||
/**
|
||||
* Internal class used by KCompressionDevice
|
||||
*
|
||||
* This header is not installed.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class KGzipFilter : public KFilterBase
|
||||
{
|
||||
public:
|
||||
KGzipFilter();
|
||||
~KGzipFilter() override;
|
||||
|
||||
bool init(int mode) override;
|
||||
|
||||
// The top of zlib.h explains it: there are three cases.
|
||||
// - Raw deflate, no header (e.g. inside a ZIP file)
|
||||
// - Thin zlib header (1) (which is normally what HTTP calls "deflate" (2))
|
||||
// - Gzip header, implemented here by readHeader
|
||||
//
|
||||
// (1) as written out by compress()/compress2()
|
||||
// (2) see http://www.zlib.net/zlib_faq.html#faq39
|
||||
enum Flag {
|
||||
RawDeflate = 0, // raw deflate data
|
||||
ZlibHeader = 1, // zlib headers (HTTP deflate)
|
||||
GZipHeader = 2,
|
||||
};
|
||||
bool init(int mode, Flag flag); // for direct users of KGzipFilter
|
||||
int mode() const override;
|
||||
bool terminate() override;
|
||||
void reset() override;
|
||||
bool readHeader() override; // this is about the GZIP header
|
||||
bool writeHeader(const QByteArray &fileName) override;
|
||||
void writeFooter();
|
||||
void setOutBuffer(char *data, uint maxlen) override;
|
||||
void setInBuffer(const char *data, uint size) override;
|
||||
int inBufferAvailable() const override;
|
||||
int outBufferAvailable() const override;
|
||||
Result uncompress() override;
|
||||
Result compress(bool finish) override;
|
||||
|
||||
private:
|
||||
Result uncompress_noop();
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001, 2002, 2007 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "klimitediodevice_p.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
KLimitedIODevice::KLimitedIODevice(QIODevice *dev, qint64 start, qint64 length)
|
||||
: m_dev(dev)
|
||||
, m_start(start)
|
||||
, m_length(length)
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "start=" << start << "length=" << length;
|
||||
open(QIODevice::ReadOnly); // krazy:exclude=syscalls
|
||||
}
|
||||
|
||||
bool KLimitedIODevice::open(QIODevice::OpenMode m)
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "m=" << m;
|
||||
if (m & QIODevice::ReadOnly) {
|
||||
/*bool ok = false;
|
||||
if ( m_dev->isOpen() )
|
||||
ok = ( m_dev->mode() == QIODevice::ReadOnly );
|
||||
else
|
||||
ok = m_dev->open( m );
|
||||
if ( ok )*/
|
||||
m_dev->seek(m_start); // No concurrent access !
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "KLimitedIODevice::open only supports QIODevice::ReadOnly!";
|
||||
}
|
||||
setOpenMode(QIODevice::ReadOnly);
|
||||
return true;
|
||||
}
|
||||
|
||||
void KLimitedIODevice::close()
|
||||
{
|
||||
}
|
||||
|
||||
qint64 KLimitedIODevice::size() const
|
||||
{
|
||||
return m_length;
|
||||
}
|
||||
|
||||
qint64 KLimitedIODevice::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
maxlen = qMin(maxlen, m_length - pos()); // Apply upper limit
|
||||
return m_dev->read(data, maxlen);
|
||||
}
|
||||
|
||||
bool KLimitedIODevice::seek(qint64 pos)
|
||||
{
|
||||
Q_ASSERT(pos <= m_length);
|
||||
pos = qMin(pos, m_length); // Apply upper limit
|
||||
bool ret = m_dev->seek(m_start + pos);
|
||||
if (ret) {
|
||||
QIODevice::seek(pos);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
qint64 KLimitedIODevice::bytesAvailable() const
|
||||
{
|
||||
return QIODevice::bytesAvailable();
|
||||
}
|
||||
|
||||
bool KLimitedIODevice::isSequential() const
|
||||
{
|
||||
return m_dev->isSequential();
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001, 2002, 2007 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef klimitediodevice_p_h
|
||||
#define klimitediodevice_p_h
|
||||
|
||||
#include <QDebug>
|
||||
#include <QIODevice>
|
||||
/**
|
||||
* A readonly device that reads from an underlying device
|
||||
* from a given point to another (e.g. to give access to a single
|
||||
* file inside an archive).
|
||||
* @author David Faure <faure@kde.org>
|
||||
* @internal - used by KArchive
|
||||
*/
|
||||
class KLimitedIODevice : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Creates a new KLimitedIODevice.
|
||||
* @param dev the underlying device, opened or not
|
||||
* This device itself auto-opens (in readonly mode), no need to open it.
|
||||
* @param start where to start reading (position in bytes)
|
||||
* @param length the length of the data to read (in bytes)
|
||||
*/
|
||||
KLimitedIODevice(QIODevice *dev, qint64 start, qint64 length);
|
||||
~KLimitedIODevice() override
|
||||
{
|
||||
}
|
||||
|
||||
bool isSequential() const override;
|
||||
|
||||
bool open(QIODevice::OpenMode m) override;
|
||||
void close() override;
|
||||
|
||||
qint64 size() const override;
|
||||
|
||||
qint64 readData(char *data, qint64 maxlen) override;
|
||||
qint64 writeData(const char *, qint64) override
|
||||
{
|
||||
return -1; // unsupported
|
||||
}
|
||||
|
||||
// virtual qint64 pos() const { return m_dev->pos() - m_start; }
|
||||
bool seek(qint64 pos) override;
|
||||
qint64 bytesAvailable() const override;
|
||||
|
||||
private:
|
||||
QIODevice *m_dev;
|
||||
qint64 m_start;
|
||||
qint64 m_length;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,127 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
Based on kbzip2filter:
|
||||
SPDX-FileCopyrightText: 2000, 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "knonefilter.h"
|
||||
|
||||
#include <QFile>
|
||||
|
||||
class Q_DECL_HIDDEN KNoneFilter::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: mode(0)
|
||||
, avail_out(0)
|
||||
, avail_in(0)
|
||||
, next_in(nullptr)
|
||||
, next_out(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
int mode;
|
||||
int avail_out;
|
||||
int avail_in;
|
||||
const char *next_in;
|
||||
char *next_out;
|
||||
};
|
||||
|
||||
KNoneFilter::KNoneFilter()
|
||||
: d(new Private)
|
||||
{
|
||||
}
|
||||
|
||||
KNoneFilter::~KNoneFilter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KNoneFilter::init(int mode)
|
||||
{
|
||||
d->mode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
int KNoneFilter::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
bool KNoneFilter::terminate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void KNoneFilter::reset()
|
||||
{
|
||||
}
|
||||
|
||||
bool KNoneFilter::readHeader()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KNoneFilter::writeHeader(const QByteArray & /*fileName*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void KNoneFilter::setOutBuffer(char *data, uint maxlen)
|
||||
{
|
||||
d->avail_out = maxlen;
|
||||
d->next_out = data;
|
||||
}
|
||||
|
||||
void KNoneFilter::setInBuffer(const char *data, uint size)
|
||||
{
|
||||
d->next_in = data;
|
||||
d->avail_in = size;
|
||||
}
|
||||
|
||||
int KNoneFilter::inBufferAvailable() const
|
||||
{
|
||||
return d->avail_in;
|
||||
}
|
||||
|
||||
int KNoneFilter::outBufferAvailable() const
|
||||
{
|
||||
return d->avail_out;
|
||||
}
|
||||
|
||||
KNoneFilter::Result KNoneFilter::uncompress()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (d->mode != QIODevice::ReadOnly) {
|
||||
return KFilterBase::Error;
|
||||
}
|
||||
#endif
|
||||
return copyData();
|
||||
}
|
||||
|
||||
KNoneFilter::Result KNoneFilter::compress(bool finish)
|
||||
{
|
||||
Q_ASSERT(d->mode == QIODevice::WriteOnly);
|
||||
Q_UNUSED(finish);
|
||||
|
||||
return copyData();
|
||||
}
|
||||
|
||||
KNoneFilter::Result KNoneFilter::copyData()
|
||||
{
|
||||
Q_ASSERT(d->avail_out > 0);
|
||||
if (d->avail_in > 0) {
|
||||
const int n = qMin(d->avail_in, d->avail_out);
|
||||
memcpy(d->next_out, d->next_in, n);
|
||||
d->avail_out -= n;
|
||||
d->next_in += n;
|
||||
d->next_out += n;
|
||||
d->avail_in -= n;
|
||||
return KFilterBase::Ok;
|
||||
} else {
|
||||
return KFilterBase::End;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
|
||||
|
||||
Based on kbzip2filter:
|
||||
SPDX-FileCopyrightText: 2000, 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __knonefilter__h
|
||||
#define __knonefilter__h
|
||||
|
||||
#include "kfilterbase.h"
|
||||
|
||||
/**
|
||||
* Internal class used by KCompressionDevice
|
||||
*
|
||||
* This header is not installed.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class KNoneFilter : public KFilterBase
|
||||
{
|
||||
public:
|
||||
KNoneFilter();
|
||||
~KNoneFilter() override;
|
||||
|
||||
bool init(int mode) override;
|
||||
int mode() const override;
|
||||
bool terminate() override;
|
||||
void reset() override;
|
||||
bool readHeader() override; // this is about the GZIP header
|
||||
bool writeHeader(const QByteArray &fileName) override;
|
||||
void setOutBuffer(char *data, uint maxlen) override;
|
||||
void setInBuffer(const char *data, uint size) override;
|
||||
int inBufferAvailable() const override;
|
||||
int outBufferAvailable() const override;
|
||||
Result uncompress() override;
|
||||
Result compress(bool finish) override;
|
||||
|
||||
private:
|
||||
Result copyData();
|
||||
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,162 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2014 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "krcc.h"
|
||||
#include "karchive_p.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QResource>
|
||||
#include <QUuid>
|
||||
|
||||
class Q_DECL_HIDDEN KRcc::KRccPrivate
|
||||
{
|
||||
public:
|
||||
KRccPrivate()
|
||||
{
|
||||
}
|
||||
void createEntries(const QDir &dir, KArchiveDirectory *parentDir, KRcc *q);
|
||||
|
||||
QString m_prefix; // '/' + uuid
|
||||
};
|
||||
|
||||
/**
|
||||
* A KRccFileEntry represents a file in a rcc archive.
|
||||
*/
|
||||
class KRccFileEntry : public KArchiveFile
|
||||
{
|
||||
public:
|
||||
KRccFileEntry(KArchive *archive,
|
||||
const QString &name,
|
||||
int access,
|
||||
const QDateTime &date,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
const QString &resourcePath)
|
||||
: KArchiveFile(archive, name, access, date, user, group, QString(), 0, size)
|
||||
, m_resourcePath(resourcePath)
|
||||
{
|
||||
}
|
||||
|
||||
QByteArray data() const override
|
||||
{
|
||||
QFile f(m_resourcePath);
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
return f.readAll();
|
||||
}
|
||||
qCWarning(KArchiveLog) << "Couldn't open" << m_resourcePath;
|
||||
return QByteArray();
|
||||
}
|
||||
QIODevice *createDevice() const override
|
||||
{
|
||||
return new QFile(m_resourcePath);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_resourcePath;
|
||||
};
|
||||
|
||||
KRcc::KRcc(const QString &filename)
|
||||
: KArchive(filename)
|
||||
, d(new KRccPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KRcc::~KRcc()
|
||||
{
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KRcc::doPrepareWriting(const QString &, const QString &, const QString &, qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
||||
{
|
||||
setErrorString(tr("Cannot write to RCC file"));
|
||||
qCWarning(KArchiveLog) << "doPrepareWriting not implemented for KRcc";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KRcc::doFinishWriting(qint64)
|
||||
{
|
||||
setErrorString(tr("Cannot write to RCC file"));
|
||||
qCWarning(KArchiveLog) << "doFinishWriting not implemented for KRcc";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KRcc::doWriteDir(const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
||||
{
|
||||
setErrorString(tr("Cannot write to RCC file"));
|
||||
qCWarning(KArchiveLog) << "doWriteDir not implemented for KRcc";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KRcc::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &)
|
||||
{
|
||||
setErrorString(tr("Cannot write to RCC file"));
|
||||
qCWarning(KArchiveLog) << "doWriteSymLink not implemented for KRcc";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KRcc::openArchive(QIODevice::OpenMode mode)
|
||||
{
|
||||
// Open archive
|
||||
|
||||
if (mode == QIODevice::WriteOnly) {
|
||||
return true;
|
||||
}
|
||||
if (mode != QIODevice::ReadOnly && mode != QIODevice::ReadWrite) {
|
||||
setErrorString(tr("Unsupported mode %1").arg(mode));
|
||||
return false;
|
||||
}
|
||||
|
||||
QUuid uuid = QUuid::createUuid();
|
||||
d->m_prefix = QLatin1Char('/') + uuid.toString();
|
||||
if (!QResource::registerResource(fileName(), d->m_prefix)) {
|
||||
setErrorString(tr("Failed to register resource %1 under prefix %2").arg(fileName(), d->m_prefix));
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir dir(QLatin1Char(':') + d->m_prefix);
|
||||
d->createEntries(dir, rootDir(), this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void KRcc::KRccPrivate::createEntries(const QDir &dir, KArchiveDirectory *parentDir, KRcc *q)
|
||||
{
|
||||
for (const QString &fileName : dir.entryList()) {
|
||||
const QString entryPath = dir.path() + QLatin1Char('/') + fileName;
|
||||
const QFileInfo info(entryPath);
|
||||
if (info.isFile()) {
|
||||
KArchiveEntry *entry = new KRccFileEntry(q, fileName, 0444, info.lastModified(), parentDir->user(), parentDir->group(), info.size(), entryPath);
|
||||
parentDir->addEntry(entry);
|
||||
} else {
|
||||
KArchiveDirectory *entry =
|
||||
new KArchiveDirectory(q, fileName, 0555, info.lastModified(), parentDir->user(), parentDir->group(), /*symlink*/ QString());
|
||||
if (parentDir->addEntryV2(entry)) {
|
||||
createEntries(QDir(entryPath), entry, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KRcc::closeArchive()
|
||||
{
|
||||
// Close the archive
|
||||
QResource::unregisterResource(fileName(), d->m_prefix);
|
||||
// ignore errors
|
||||
return true;
|
||||
}
|
||||
|
||||
void KRcc::virtual_hook(int id, void *data)
|
||||
{
|
||||
KArchive::virtual_hook(id, data);
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2014 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KRCC_H
|
||||
#define KRCC_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
/**
|
||||
* KRcc is a class for reading dynamic binary resources created by Qt's rcc tool
|
||||
* from a .qrc file and the files it points to.
|
||||
*
|
||||
* Writing is not supported.
|
||||
* @short A class for reading rcc resources.
|
||||
* @since 5.3
|
||||
*/
|
||||
class KARCHIVE_EXPORT KRcc : public KArchive
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(KRcc)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/holger/myfile.rcc")
|
||||
*/
|
||||
KRcc(const QString &filename);
|
||||
|
||||
/**
|
||||
* If the rcc file is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
~KRcc() override;
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Writing is not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
bool doPrepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
|
||||
/*
|
||||
* Writing is not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
bool doFinishWriting(qint64 size) override;
|
||||
|
||||
/*
|
||||
* Writing is not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
bool doWriteDir(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
|
||||
/*
|
||||
* Writing is not supported by this class, will always fail.
|
||||
* @return always false
|
||||
*/
|
||||
bool doWriteSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
|
||||
/**
|
||||
* Registers the .rcc resource in the QResource system under a unique identifier,
|
||||
* then lists that, and creates the KArchiveFile/KArchiveDirectory entries.
|
||||
*/
|
||||
bool openArchive(QIODevice::OpenMode mode) override;
|
||||
/**
|
||||
* Unregisters the .rcc resource from the QResource system.
|
||||
*/
|
||||
bool closeArchive() override;
|
||||
|
||||
protected:
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
private:
|
||||
class KRccPrivate;
|
||||
KRccPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,964 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "ktar.h"
|
||||
#include "karchive_p.h"
|
||||
#include "kcompressiondevice.h"
|
||||
#include "kfilterbase.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMimeDatabase>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> // strtol
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////// KTar ///////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Mime types of known filters
|
||||
static const char application_bzip[] = "application/x-bzip";
|
||||
static const char application_lzma[] = "application/x-lzma";
|
||||
static const char application_xz[] = "application/x-xz";
|
||||
static const char application_zstd[] = "application/zstd";
|
||||
|
||||
/* clang-format off */
|
||||
namespace MimeType
|
||||
{
|
||||
QString application_gzip() { return QStringLiteral("application/gzip"); }
|
||||
QString application_gzip_old() { return QStringLiteral("application/x-gzip"); }
|
||||
}
|
||||
/* clang-format on */
|
||||
|
||||
class Q_DECL_HIDDEN KTar::KTarPrivate
|
||||
{
|
||||
public:
|
||||
KTarPrivate(KTar *parent)
|
||||
: q(parent)
|
||||
, tarEnd(0)
|
||||
, tmpFile(nullptr)
|
||||
, compressionDevice(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
KTar *q;
|
||||
QStringList dirList;
|
||||
qint64 tarEnd;
|
||||
QTemporaryFile *tmpFile;
|
||||
QString mimetype;
|
||||
QByteArray origFileName;
|
||||
KCompressionDevice *compressionDevice;
|
||||
|
||||
bool fillTempFile(const QString &fileName);
|
||||
bool writeBackTempFile(const QString &fileName);
|
||||
void fillBuffer(char *buffer, const char *mode, qint64 size, const QDateTime &mtime, char typeflag, const char *uname, const char *gname);
|
||||
void writeLonglink(char *buffer, const QByteArray &name, char typeflag, const char *uname, const char *gname);
|
||||
qint64 readRawHeader(char *buffer);
|
||||
bool readLonglink(char *buffer, QByteArray &longlink);
|
||||
qint64 readHeader(char *buffer, QString &name, QString &symlink);
|
||||
};
|
||||
|
||||
KTar::KTar(const QString &fileName, const QString &_mimetype)
|
||||
: KArchive(fileName)
|
||||
, d(new KTarPrivate(this))
|
||||
{
|
||||
// shared-mime-info < 1.1 does not know about application/gzip.
|
||||
// While Qt has optionally a copy of shared-mime-info (1.10 for 5.15.0),
|
||||
// it uses the system one if it exists.
|
||||
// Once shared-mime-info 1.1 is required or can be assumed on all targeted
|
||||
// platforms (right now RHEL/CentOS 6 as target of appimage-based apps
|
||||
// bundling also karchive does not meet this requirement)
|
||||
// switch to use the new application/gzip id instead when interacting with QMimeDatabase
|
||||
// For now: map new name to legacy name and use that
|
||||
d->mimetype = (_mimetype == MimeType::application_gzip()) ? MimeType::application_gzip_old() : _mimetype;
|
||||
}
|
||||
|
||||
KTar::KTar(QIODevice *dev)
|
||||
: KArchive(dev)
|
||||
, d(new KTarPrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
// Only called when a filename was given
|
||||
bool KTar::createDevice(QIODevice::OpenMode mode)
|
||||
{
|
||||
if (d->mimetype.isEmpty()) {
|
||||
// Find out mimetype manually
|
||||
|
||||
QMimeDatabase db;
|
||||
QMimeType mime;
|
||||
if (mode != QIODevice::WriteOnly && QFile::exists(fileName())) {
|
||||
// Give priority to file contents: if someone renames a .tar.bz2 to .tar.gz,
|
||||
// we can still do the right thing here.
|
||||
QFile f(fileName());
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
mime = db.mimeTypeForData(&f);
|
||||
}
|
||||
if (!mime.isValid()) {
|
||||
// Unable to determine mimetype from contents, get it from file name
|
||||
mime = db.mimeTypeForFile(fileName(), QMimeDatabase::MatchExtension);
|
||||
}
|
||||
} else {
|
||||
mime = db.mimeTypeForFile(fileName(), QMimeDatabase::MatchExtension);
|
||||
}
|
||||
|
||||
// qCDebug(KArchiveLog) << mode << mime->name();
|
||||
|
||||
if (mime.inherits(QStringLiteral("application/x-compressed-tar")) || mime.inherits(MimeType::application_gzip_old())) {
|
||||
// gzipped tar file (with possibly invalid file name), ask for gzip filter
|
||||
d->mimetype = MimeType::application_gzip_old();
|
||||
} else if (mime.inherits(QStringLiteral("application/x-bzip-compressed-tar")) || mime.inherits(QString::fromLatin1(application_bzip))) {
|
||||
// bzipped2 tar file (with possibly invalid file name), ask for bz2 filter
|
||||
d->mimetype = QString::fromLatin1(application_bzip);
|
||||
} else if (mime.inherits(QStringLiteral("application/x-lzma-compressed-tar")) || mime.inherits(QString::fromLatin1(application_lzma))) {
|
||||
// lzma compressed tar file (with possibly invalid file name), ask for xz filter
|
||||
d->mimetype = QString::fromLatin1(application_lzma);
|
||||
} else if (mime.inherits(QStringLiteral("application/x-xz-compressed-tar")) || mime.inherits(QString::fromLatin1(application_xz))) {
|
||||
// xz compressed tar file (with possibly invalid name), ask for xz filter
|
||||
d->mimetype = QString::fromLatin1(application_xz);
|
||||
} else if (mime.inherits(QStringLiteral("application/x-zstd-compressed-tar")) || mime.inherits(QString::fromLatin1(application_zstd))) {
|
||||
// zstd compressed tar file (with possibly invalid name), ask for zstd filter
|
||||
d->mimetype = QString::fromLatin1(application_zstd);
|
||||
}
|
||||
}
|
||||
|
||||
if (d->mimetype == QLatin1String("application/x-tar")) {
|
||||
return KArchive::createDevice(mode);
|
||||
} else if (mode == QIODevice::WriteOnly) {
|
||||
if (!KArchive::createDevice(mode)) {
|
||||
return false;
|
||||
}
|
||||
if (!d->mimetype.isEmpty()) {
|
||||
// Create a compression filter on top of the QSaveFile device that KArchive created.
|
||||
// qCDebug(KArchiveLog) << "creating KCompressionDevice for" << d->mimetype;
|
||||
KCompressionDevice::CompressionType type = KCompressionDevice::compressionTypeForMimeType(d->mimetype);
|
||||
d->compressionDevice = new KCompressionDevice(device(), false, type);
|
||||
setDevice(d->compressionDevice);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// The compression filters are very slow with random access.
|
||||
// So instead of applying the filter to the device,
|
||||
// the file is completely extracted instead,
|
||||
// and we work on the extracted tar file.
|
||||
// This improves the extraction speed by the tar ioslave dramatically,
|
||||
// if the archive file contains many files.
|
||||
// This is because the tar ioslave extracts one file after the other and normally
|
||||
// has to walk through the decompression filter each time.
|
||||
// Which is in fact nearly as slow as a complete decompression for each file.
|
||||
|
||||
Q_ASSERT(!d->tmpFile);
|
||||
d->tmpFile = new QTemporaryFile();
|
||||
d->tmpFile->setFileTemplate(QDir::tempPath() + QLatin1Char('/') + QLatin1String("ktar-XXXXXX.tar"));
|
||||
d->tmpFile->open();
|
||||
// qCDebug(KArchiveLog) << "creating tempfile:" << d->tmpFile->fileName();
|
||||
|
||||
setDevice(d->tmpFile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
KTar::~KTar()
|
||||
{
|
||||
// mjarrett: Closes to prevent ~KArchive from aborting w/o device
|
||||
if (isOpen()) {
|
||||
close();
|
||||
}
|
||||
|
||||
delete d->tmpFile;
|
||||
delete d->compressionDevice;
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KTar::setOrigFileName(const QByteArray &fileName)
|
||||
{
|
||||
if (!isOpen() || !(mode() & QIODevice::WriteOnly)) {
|
||||
// qCWarning(KArchiveLog) << "KTar::setOrigFileName: File must be opened for writing first.\n";
|
||||
return;
|
||||
}
|
||||
d->origFileName = fileName;
|
||||
}
|
||||
|
||||
qint64 KTar::KTarPrivate::readRawHeader(char *buffer)
|
||||
{
|
||||
// Read header
|
||||
qint64 n = q->device()->read(buffer, 0x200);
|
||||
// we need to test if there is a prefix value because the file name can be null
|
||||
// and the prefix can have a value and in this case we don't reset n.
|
||||
if (n == 0x200 && (buffer[0] != 0 || buffer[0x159] != 0)) {
|
||||
// Make sure this is actually a tar header
|
||||
if (strncmp(buffer + 257, "ustar", 5)) {
|
||||
// The magic isn't there (broken/old tars), but maybe a correct checksum?
|
||||
|
||||
int check = 0;
|
||||
for (uint j = 0; j < 0x200; ++j) {
|
||||
check += static_cast<unsigned char>(buffer[j]);
|
||||
}
|
||||
|
||||
// adjust checksum to count the checksum fields as blanks
|
||||
for (uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++) {
|
||||
check -= static_cast<unsigned char>(buffer[148 + j]);
|
||||
}
|
||||
check += 8 * ' ';
|
||||
|
||||
QByteArray s = QByteArray::number(check, 8); // octal
|
||||
|
||||
// only compare those of the 6 checksum digits that mean something,
|
||||
// because the other digits are filled with all sorts of different chars by different tars ...
|
||||
// Some tars right-justify the checksum so it could start in one of three places - we have to check each.
|
||||
if (strncmp(buffer + 148 + 6 - s.length(), s.data(), s.length()) //
|
||||
&& strncmp(buffer + 148 + 7 - s.length(), s.data(), s.length()) //
|
||||
&& strncmp(buffer + 148 + 8 - s.length(), s.data(), s.length())) {
|
||||
/*qCWarning(KArchiveLog) << "KTar: invalid TAR file. Header is:" << QByteArray( buffer+257, 5 )
|
||||
<< "instead of ustar. Reading from wrong pos in file?"
|
||||
<< "checksum=" << QByteArray( buffer + 148 + 6 - s.length(), s.length() );*/
|
||||
return -1;
|
||||
}
|
||||
} /*end if*/
|
||||
} else {
|
||||
// reset to 0 if 0x200 because logical end of archive has been reached
|
||||
if (n == 0x200) {
|
||||
n = 0;
|
||||
}
|
||||
} /*end if*/
|
||||
return n;
|
||||
}
|
||||
|
||||
bool KTar::KTarPrivate::readLonglink(char *buffer, QByteArray &longlink)
|
||||
{
|
||||
qint64 n = 0;
|
||||
// qCDebug(KArchiveLog) << "reading longlink from pos " << q->device()->pos();
|
||||
QIODevice *dev = q->device();
|
||||
// read size of longlink from size field in header
|
||||
// size is in bytes including the trailing null (which we ignore)
|
||||
qint64 size = QByteArray(buffer + 0x7c, 12).trimmed().toLongLong(nullptr, 8 /*octal*/);
|
||||
|
||||
size--; // ignore trailing null
|
||||
if (size > std::numeric_limits<int>::max() - 32) { // QByteArray can't really be INT_MAX big, it's max size is something between INT_MAX - 32 and INT_MAX
|
||||
// depending the platform so just be safe
|
||||
qCWarning(KArchiveLog) << "Failed to allocate memory for longlink of size" << size;
|
||||
return false;
|
||||
}
|
||||
if (size < 0) {
|
||||
qCWarning(KArchiveLog) << "Invalid longlink size" << size;
|
||||
return false;
|
||||
}
|
||||
longlink.resize(size);
|
||||
qint64 offset = 0;
|
||||
while (size > 0) {
|
||||
int chunksize = qMin(size, 0x200LL);
|
||||
n = dev->read(longlink.data() + offset, chunksize);
|
||||
if (n == -1) {
|
||||
return false;
|
||||
}
|
||||
size -= chunksize;
|
||||
offset += 0x200;
|
||||
} /*wend*/
|
||||
// jump over the rest
|
||||
const int skip = 0x200 - (n % 0x200);
|
||||
if (skip <= 0x200) {
|
||||
if (dev->read(buffer, skip) != skip) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
longlink.truncate(qstrlen(longlink.constData()));
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 KTar::KTarPrivate::readHeader(char *buffer, QString &name, QString &symlink)
|
||||
{
|
||||
name.truncate(0);
|
||||
symlink.truncate(0);
|
||||
while (true) {
|
||||
qint64 n = readRawHeader(buffer);
|
||||
if (n != 0x200) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// is it a longlink?
|
||||
if (strcmp(buffer, "././@LongLink") == 0) {
|
||||
char typeflag = buffer[0x9c];
|
||||
QByteArray longlink;
|
||||
if (readLonglink(buffer, longlink)) {
|
||||
switch (typeflag) {
|
||||
case 'L':
|
||||
name = QFile::decodeName(longlink.constData());
|
||||
break;
|
||||
case 'K':
|
||||
symlink = QFile::decodeName(longlink.constData());
|
||||
break;
|
||||
} /*end switch*/
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
} /*end if*/
|
||||
} /*wend*/
|
||||
|
||||
// if not result of longlink, read names directly from the header
|
||||
if (name.isEmpty())
|
||||
// there are names that are exactly 100 bytes long
|
||||
// and neither longlink nor \0 terminated (bug:101472)
|
||||
{
|
||||
name = QFile::decodeName(QByteArray(buffer, qstrnlen(buffer, 100)));
|
||||
}
|
||||
if (symlink.isEmpty()) {
|
||||
char *symlinkBuffer = buffer + 0x9d /*?*/;
|
||||
symlink = QFile::decodeName(QByteArray(symlinkBuffer, qstrnlen(symlinkBuffer, 100)));
|
||||
}
|
||||
|
||||
return 0x200;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have created a temporary file, we have
|
||||
* to decompress the original file now and write
|
||||
* the contents to the temporary file.
|
||||
*/
|
||||
bool KTar::KTarPrivate::fillTempFile(const QString &fileName)
|
||||
{
|
||||
if (!tmpFile) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// qCDebug(KArchiveLog) << "filling tmpFile of mimetype" << mimetype;
|
||||
|
||||
KCompressionDevice::CompressionType compressionType = KCompressionDevice::compressionTypeForMimeType(mimetype);
|
||||
KCompressionDevice filterDev(fileName, compressionType);
|
||||
|
||||
QFile *file = tmpFile;
|
||||
Q_ASSERT(file->isOpen());
|
||||
Q_ASSERT(file->openMode() & QIODevice::WriteOnly);
|
||||
file->seek(0);
|
||||
QByteArray buffer;
|
||||
buffer.resize(8 * 1024);
|
||||
if (!filterDev.open(QIODevice::ReadOnly)) {
|
||||
q->setErrorString(tr("File %1 does not exist").arg(fileName));
|
||||
return false;
|
||||
}
|
||||
qint64 len = -1;
|
||||
while (!filterDev.atEnd() && len != 0) {
|
||||
len = filterDev.read(buffer.data(), buffer.size());
|
||||
if (len < 0) { // corrupted archive
|
||||
q->setErrorString(tr("Archive %1 is corrupt").arg(fileName));
|
||||
return false;
|
||||
}
|
||||
if (file->write(buffer.data(), len) != len) { // disk full
|
||||
q->setErrorString(tr("Disk full"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
filterDev.close();
|
||||
|
||||
file->flush();
|
||||
file->seek(0);
|
||||
Q_ASSERT(file->isOpen());
|
||||
Q_ASSERT(file->openMode() & QIODevice::ReadOnly);
|
||||
|
||||
// qCDebug(KArchiveLog) << "filling tmpFile finished.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KTar::openArchive(QIODevice::OpenMode mode)
|
||||
{
|
||||
if (!(mode & QIODevice::ReadOnly)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!d->fillTempFile(fileName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We'll use the permission and user/group of d->rootDir
|
||||
// for any directory we emulate (see findOrCreate)
|
||||
// struct stat buf;
|
||||
// stat( fileName(), &buf );
|
||||
|
||||
d->dirList.clear();
|
||||
QIODevice *dev = device();
|
||||
|
||||
if (!dev) {
|
||||
setErrorString(tr("Could not get underlying device"));
|
||||
qCWarning(KArchiveLog) << "Could not get underlying device";
|
||||
return false;
|
||||
}
|
||||
|
||||
// read dir information
|
||||
char buffer[0x200];
|
||||
bool ende = false;
|
||||
do {
|
||||
QString name;
|
||||
QString symlink;
|
||||
|
||||
// Read header
|
||||
qint64 n = d->readHeader(buffer, name, symlink);
|
||||
if (n < 0) {
|
||||
setErrorString(tr("Could not read tar header"));
|
||||
return false;
|
||||
}
|
||||
if (n == 0x200) {
|
||||
bool isdir = false;
|
||||
|
||||
if (name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (name.endsWith(QLatin1Char('/'))) {
|
||||
isdir = true;
|
||||
name.truncate(name.length() - 1);
|
||||
}
|
||||
|
||||
QByteArray prefix = QByteArray(buffer + 0x159, 155);
|
||||
if (prefix[0] != '\0') {
|
||||
name = (QString::fromLatin1(prefix.constData()) + QLatin1Char('/') + name);
|
||||
}
|
||||
|
||||
int pos = name.lastIndexOf(QLatin1Char('/'));
|
||||
QString nm = (pos == -1) ? name : name.mid(pos + 1);
|
||||
|
||||
// read access
|
||||
buffer[0x6b] = 0;
|
||||
char *dummy;
|
||||
const char *p = buffer + 0x64;
|
||||
while (*p == ' ') {
|
||||
++p;
|
||||
}
|
||||
int access = strtol(p, &dummy, 8);
|
||||
|
||||
// read user and group
|
||||
const int maxUserGroupLength = 32;
|
||||
const char *userStart = buffer + 0x109;
|
||||
const int userLen = qstrnlen(userStart, maxUserGroupLength);
|
||||
const QString user = QString::fromLocal8Bit(userStart, userLen);
|
||||
const char *groupStart = buffer + 0x129;
|
||||
const int groupLen = qstrnlen(groupStart, maxUserGroupLength);
|
||||
const QString group = QString::fromLocal8Bit(groupStart, groupLen);
|
||||
|
||||
// read time
|
||||
buffer[0x93] = 0;
|
||||
p = buffer + 0x88;
|
||||
while (*p == ' ') {
|
||||
++p;
|
||||
}
|
||||
uint time = strtol(p, &dummy, 8);
|
||||
|
||||
// read type flag
|
||||
char typeflag = buffer[0x9c];
|
||||
// '0' for files, '1' hard link, '2' symlink, '5' for directory
|
||||
// (and 'L' for longlink fileNames, 'K' for longlink symlink targets)
|
||||
// 'D' for GNU tar extension DUMPDIR, 'x' for Extended header referring
|
||||
// to the next file in the archive and 'g' for Global extended header
|
||||
|
||||
if (typeflag == '5') {
|
||||
isdir = true;
|
||||
}
|
||||
|
||||
bool isDumpDir = false;
|
||||
if (typeflag == 'D') {
|
||||
isdir = false;
|
||||
isDumpDir = true;
|
||||
}
|
||||
// qCDebug(KArchiveLog) << nm << "isdir=" << isdir << "pos=" << dev->pos() << "typeflag=" << typeflag << " islink=" << ( typeflag == '1' || typeflag
|
||||
// == '2' );
|
||||
|
||||
if (typeflag == 'x' || typeflag == 'g') { // pax extended header, or pax global extended header
|
||||
// Skip it for now. TODO: implement reading of extended header, as per http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html
|
||||
(void)dev->read(buffer, 0x200);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isdir) {
|
||||
access |= S_IFDIR; // broken tar files...
|
||||
}
|
||||
|
||||
KArchiveEntry *e;
|
||||
if (isdir) {
|
||||
// qCDebug(KArchiveLog) << "directory" << nm;
|
||||
e = new KArchiveDirectory(this, nm, access, KArchivePrivate::time_tToDateTime(time), user, group, symlink);
|
||||
} else {
|
||||
// read size
|
||||
QByteArray sizeBuffer(buffer + 0x7c, 12);
|
||||
qint64 size = sizeBuffer.trimmed().toLongLong(nullptr, 8 /*octal*/);
|
||||
// qCDebug(KArchiveLog) << "sizeBuffer='" << sizeBuffer << "' -> size=" << size;
|
||||
|
||||
// for isDumpDir we will skip the additional info about that dirs contents
|
||||
if (isDumpDir) {
|
||||
// qCDebug(KArchiveLog) << nm << "isDumpDir";
|
||||
e = new KArchiveDirectory(this, nm, access, KArchivePrivate::time_tToDateTime(time), user, group, symlink);
|
||||
} else {
|
||||
// Let's hack around hard links. Our classes don't support that, so make them symlinks
|
||||
if (typeflag == '1') {
|
||||
// qCDebug(KArchiveLog) << "Hard link, setting size to 0 instead of" << size;
|
||||
size = 0; // no contents
|
||||
}
|
||||
|
||||
// qCDebug(KArchiveLog) << "file" << nm << "size=" << size;
|
||||
e = new KArchiveFile(this, nm, access, KArchivePrivate::time_tToDateTime(time), user, group, symlink, dev->pos(), size);
|
||||
}
|
||||
|
||||
// Skip contents + align bytes
|
||||
qint64 rest = size % 0x200;
|
||||
qint64 skip = size + (rest ? 0x200 - rest : 0);
|
||||
// qCDebug(KArchiveLog) << "pos()=" << dev->pos() << "rest=" << rest << "skipping" << skip;
|
||||
if (!dev->seek(dev->pos() + skip)) {
|
||||
// qCWarning(KArchiveLog) << "skipping" << skip << "failed";
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == -1) {
|
||||
if (nm == QLatin1String(".")) { // special case
|
||||
if (isdir) {
|
||||
if (KArchivePrivate::hasRootDir(this)) {
|
||||
qWarning() << "Broken tar file has two root dir entries";
|
||||
delete e;
|
||||
} else {
|
||||
setRootDir(static_cast<KArchiveDirectory *>(e));
|
||||
}
|
||||
} else {
|
||||
delete e;
|
||||
}
|
||||
} else {
|
||||
rootDir()->addEntry(e);
|
||||
}
|
||||
} else {
|
||||
// In some tar files we can find dir/./file => call cleanPath
|
||||
QString path = QDir::cleanPath(name.left(pos));
|
||||
// Ensure container directory exists, create otherwise
|
||||
KArchiveDirectory *d = findOrCreate(path);
|
||||
if (d) {
|
||||
d->addEntry(e);
|
||||
} else {
|
||||
delete e;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// qCDebug(KArchiveLog) << "Terminating. Read " << n << " bytes, first one is " << buffer[0];
|
||||
d->tarEnd = dev->pos() - n; // Remember end of archive
|
||||
ende = true;
|
||||
}
|
||||
} while (!ende);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes back the changes of the temporary file
|
||||
* to the original file.
|
||||
* Must only be called if in write mode, not in read mode
|
||||
*/
|
||||
bool KTar::KTarPrivate::writeBackTempFile(const QString &fileName)
|
||||
{
|
||||
if (!tmpFile) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// qCDebug(KArchiveLog) << "Write temporary file to compressed file" << fileName << mimetype;
|
||||
|
||||
bool forced = false;
|
||||
/* clang-format off */
|
||||
if (MimeType::application_gzip_old() == mimetype ||
|
||||
QLatin1String(application_bzip) == mimetype ||
|
||||
QLatin1String(application_lzma) == mimetype ||
|
||||
QLatin1String(application_xz) == mimetype) {
|
||||
/* clang-format on */
|
||||
forced = true;
|
||||
}
|
||||
|
||||
// #### TODO this should use QSaveFile to avoid problems on disk full
|
||||
// (KArchive uses QSaveFile by default, but the temp-uncompressed-file trick
|
||||
// circumvents that).
|
||||
|
||||
KCompressionDevice dev(fileName);
|
||||
QFile *file = tmpFile;
|
||||
if (!dev.open(QIODevice::WriteOnly)) {
|
||||
file->close();
|
||||
q->setErrorString(tr("Failed to write back temp file: %1").arg(dev.errorString()));
|
||||
return false;
|
||||
}
|
||||
if (forced) {
|
||||
dev.setOrigFileName(origFileName);
|
||||
}
|
||||
file->seek(0);
|
||||
QByteArray buffer;
|
||||
buffer.resize(8 * 1024);
|
||||
qint64 len;
|
||||
while (!file->atEnd()) {
|
||||
len = file->read(buffer.data(), buffer.size());
|
||||
dev.write(buffer.data(), len); // TODO error checking
|
||||
}
|
||||
file->close();
|
||||
dev.close();
|
||||
|
||||
// qCDebug(KArchiveLog) << "Write temporary file to compressed file done.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KTar::closeArchive()
|
||||
{
|
||||
d->dirList.clear();
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// If we are in readwrite mode and had created
|
||||
// a temporary tar file, we have to write
|
||||
// back the changes to the original file
|
||||
if (d->tmpFile && (mode() & QIODevice::WriteOnly)) {
|
||||
ok = d->writeBackTempFile(fileName());
|
||||
delete d->tmpFile;
|
||||
d->tmpFile = nullptr;
|
||||
setDevice(nullptr);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool KTar::doFinishWriting(qint64 size)
|
||||
{
|
||||
// Write alignment
|
||||
int rest = size % 0x200;
|
||||
if ((mode() & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
|
||||
d->tarEnd = device()->pos() + (rest ? 0x200 - rest : 0); // Record our new end of archive
|
||||
}
|
||||
if (rest) {
|
||||
char buffer[0x201];
|
||||
for (uint i = 0; i < 0x200; ++i) {
|
||||
buffer[i] = 0;
|
||||
}
|
||||
qint64 nwritten = device()->write(buffer, 0x200 - rest);
|
||||
const bool ok = nwritten == 0x200 - rest;
|
||||
|
||||
if (!ok) {
|
||||
setErrorString(tr("Couldn't write alignment: %1").arg(device()->errorString()));
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*** Some help from the tar sources
|
||||
struct posix_header
|
||||
{ byte offset
|
||||
char name[100]; * 0 * 0x0
|
||||
char mode[8]; * 100 * 0x64
|
||||
char uid[8]; * 108 * 0x6c
|
||||
char gid[8]; * 116 * 0x74
|
||||
char size[12]; * 124 * 0x7c
|
||||
char mtime[12]; * 136 * 0x88
|
||||
char chksum[8]; * 148 * 0x94
|
||||
char typeflag; * 156 * 0x9c
|
||||
char linkname[100]; * 157 * 0x9d
|
||||
char magic[6]; * 257 * 0x101
|
||||
char version[2]; * 263 * 0x107
|
||||
char uname[32]; * 265 * 0x109
|
||||
char gname[32]; * 297 * 0x129
|
||||
char devmajor[8]; * 329 * 0x149
|
||||
char devminor[8]; * 337 * ...
|
||||
char prefix[155]; * 345 *
|
||||
* 500 *
|
||||
};
|
||||
*/
|
||||
|
||||
void KTar::KTarPrivate::fillBuffer(char *buffer, const char *mode, qint64 size, const QDateTime &mtime, char typeflag, const char *uname, const char *gname)
|
||||
{
|
||||
// mode (as in stpos())
|
||||
assert(strlen(mode) == 6);
|
||||
memcpy(buffer + 0x64, mode, 6);
|
||||
buffer[0x6a] = ' ';
|
||||
buffer[0x6b] = '\0';
|
||||
|
||||
// dummy uid
|
||||
strcpy(buffer + 0x6c, " 765 "); // 501 in decimal
|
||||
// dummy gid
|
||||
strcpy(buffer + 0x74, " 144 "); // 100 in decimal
|
||||
|
||||
// size
|
||||
QByteArray s = QByteArray::number(size, 8); // octal
|
||||
s = s.rightJustified(11, '0');
|
||||
memcpy(buffer + 0x7c, s.data(), 11);
|
||||
buffer[0x87] = ' '; // space-terminate (no null after)
|
||||
|
||||
// modification time
|
||||
const QDateTime modificationTime = mtime.isValid() ? mtime : QDateTime::currentDateTime();
|
||||
s = QByteArray::number(static_cast<qulonglong>(modificationTime.toMSecsSinceEpoch() / 1000), 8); // octal
|
||||
s = s.rightJustified(11, '0');
|
||||
memcpy(buffer + 0x88, s.data(), 11);
|
||||
buffer[0x93] = ' '; // space-terminate (no null after) -- well current tar writes a null byte
|
||||
|
||||
// spaces, replaced by the check sum later
|
||||
buffer[0x94] = 0x20;
|
||||
buffer[0x95] = 0x20;
|
||||
buffer[0x96] = 0x20;
|
||||
buffer[0x97] = 0x20;
|
||||
buffer[0x98] = 0x20;
|
||||
buffer[0x99] = 0x20;
|
||||
|
||||
/* From the tar sources :
|
||||
Fill in the checksum field. It's formatted differently from the
|
||||
other fields: it has [6] digits, a null, then a space -- rather than
|
||||
digits, a space, then a null. */
|
||||
|
||||
buffer[0x9a] = '\0';
|
||||
buffer[0x9b] = ' ';
|
||||
|
||||
// type flag (dir, file, link)
|
||||
buffer[0x9c] = typeflag;
|
||||
|
||||
// magic + version
|
||||
strcpy(buffer + 0x101, "ustar");
|
||||
strcpy(buffer + 0x107, "00");
|
||||
|
||||
// user
|
||||
strcpy(buffer + 0x109, uname);
|
||||
// group
|
||||
strcpy(buffer + 0x129, gname);
|
||||
|
||||
// Header check sum
|
||||
int check = 32;
|
||||
for (uint j = 0; j < 0x200; ++j) {
|
||||
check += static_cast<unsigned char>(buffer[j]);
|
||||
}
|
||||
s = QByteArray::number(check, 8); // octal
|
||||
s = s.rightJustified(6, '0');
|
||||
memcpy(buffer + 0x94, s.constData(), 6);
|
||||
}
|
||||
|
||||
void KTar::KTarPrivate::writeLonglink(char *buffer, const QByteArray &name, char typeflag, const char *uname, const char *gname)
|
||||
{
|
||||
strcpy(buffer, "././@LongLink");
|
||||
qint64 namelen = name.length() + 1;
|
||||
fillBuffer(buffer, " 0", namelen, QDateTime(), typeflag, uname, gname);
|
||||
q->device()->write(buffer, 0x200); // TODO error checking
|
||||
qint64 offset = 0;
|
||||
while (namelen > 0) {
|
||||
int chunksize = qMin(namelen, 0x200LL);
|
||||
memcpy(buffer, name.data() + offset, chunksize);
|
||||
// write long name
|
||||
q->device()->write(buffer, 0x200); // TODO error checking
|
||||
// not even needed to reclear the buffer, tar doesn't do it
|
||||
namelen -= chunksize;
|
||||
offset += 0x200;
|
||||
} /*wend*/
|
||||
}
|
||||
|
||||
bool KTar::doPrepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm,
|
||||
const QDateTime & /*atime*/,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime & /*ctime*/)
|
||||
{
|
||||
if (!isOpen()) {
|
||||
setErrorString(tr("Application error: TAR file must be open before being written into"));
|
||||
qCWarning(KArchiveLog) << "doPrepareWriting failed: !isOpen()";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(mode() & QIODevice::WriteOnly)) {
|
||||
setErrorString(tr("Application error: attempted to write into non-writable 7-Zip file"));
|
||||
qCWarning(KArchiveLog) << "doPrepareWriting failed: !(mode() & QIODevice::WriteOnly)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// In some tar files we can find dir/./file => call cleanPath
|
||||
QString fileName(QDir::cleanPath(name));
|
||||
|
||||
/*
|
||||
// Create toplevel dirs
|
||||
// Commented out by David since it's not necessary, and if anybody thinks it is,
|
||||
// he needs to implement a findOrCreate equivalent in writeDir.
|
||||
// But as KTar and the "tar" program both handle tar files without
|
||||
// dir entries, there's really no need for that
|
||||
QString tmp ( fileName );
|
||||
int i = tmp.lastIndexOf( '/' );
|
||||
if ( i != -1 )
|
||||
{
|
||||
QString d = tmp.left( i + 1 ); // contains trailing slash
|
||||
if ( !m_dirList.contains( d ) )
|
||||
{
|
||||
tmp = tmp.mid( i + 1 );
|
||||
writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
char buffer[0x201];
|
||||
memset(buffer, 0, 0x200);
|
||||
if ((mode() & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
|
||||
device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
|
||||
}
|
||||
|
||||
// provide converted stuff we need later on
|
||||
const QByteArray encodedFileName = QFile::encodeName(fileName);
|
||||
const QByteArray uname = user.toLocal8Bit();
|
||||
const QByteArray gname = group.toLocal8Bit();
|
||||
|
||||
// If more than 100 bytes, we need to use the LongLink trick
|
||||
if (encodedFileName.length() > 99) {
|
||||
d->writeLonglink(buffer, encodedFileName, 'L', uname.constData(), gname.constData());
|
||||
}
|
||||
|
||||
// Write (potentially truncated) name
|
||||
strncpy(buffer, encodedFileName.constData(), 99);
|
||||
buffer[99] = 0;
|
||||
// zero out the rest (except for what gets filled anyways)
|
||||
memset(buffer + 0x9d, 0, 0x200 - 0x9d);
|
||||
|
||||
QByteArray permstr = QByteArray::number(static_cast<unsigned int>(perm), 8);
|
||||
permstr = permstr.rightJustified(6, '0');
|
||||
d->fillBuffer(buffer, permstr.constData(), size, mtime, 0x30, uname.constData(), gname.constData());
|
||||
|
||||
// Write header
|
||||
if (device()->write(buffer, 0x200) != 0x200) {
|
||||
setErrorString(tr("Failed to write header: %1").arg(device()->errorString()));
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool KTar::doWriteDir(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime & /*atime*/,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime & /*ctime*/)
|
||||
{
|
||||
if (!isOpen()) {
|
||||
setErrorString(tr("Application error: TAR file must be open before being written into"));
|
||||
qCWarning(KArchiveLog) << "doWriteDir failed: !isOpen()";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(mode() & QIODevice::WriteOnly)) {
|
||||
setErrorString(tr("Application error: attempted to write into non-writable TAR file"));
|
||||
qCWarning(KArchiveLog) << "doWriteDir failed: !(mode() & QIODevice::WriteOnly)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// In some tar files we can find dir/./ => call cleanPath
|
||||
QString dirName(QDir::cleanPath(name));
|
||||
|
||||
// Need trailing '/'
|
||||
if (!dirName.endsWith(QLatin1Char('/'))) {
|
||||
dirName += QLatin1Char('/');
|
||||
}
|
||||
|
||||
if (d->dirList.contains(dirName)) {
|
||||
return true; // already there
|
||||
}
|
||||
|
||||
char buffer[0x201];
|
||||
memset(buffer, 0, 0x200);
|
||||
if ((mode() & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
|
||||
device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
|
||||
}
|
||||
|
||||
// provide converted stuff we need lateron
|
||||
QByteArray encodedDirname = QFile::encodeName(dirName);
|
||||
QByteArray uname = user.toLocal8Bit();
|
||||
QByteArray gname = group.toLocal8Bit();
|
||||
|
||||
// If more than 100 bytes, we need to use the LongLink trick
|
||||
if (encodedDirname.length() > 99) {
|
||||
d->writeLonglink(buffer, encodedDirname, 'L', uname.constData(), gname.constData());
|
||||
}
|
||||
|
||||
// Write (potentially truncated) name
|
||||
strncpy(buffer, encodedDirname.constData(), 99);
|
||||
buffer[99] = 0;
|
||||
// zero out the rest (except for what gets filled anyways)
|
||||
memset(buffer + 0x9d, 0, 0x200 - 0x9d);
|
||||
|
||||
QByteArray permstr = QByteArray::number(static_cast<unsigned int>(perm), 8);
|
||||
permstr = permstr.rightJustified(6, ' ');
|
||||
d->fillBuffer(buffer, permstr.constData(), 0, mtime, 0x35, uname.constData(), gname.constData());
|
||||
|
||||
// Write header
|
||||
device()->write(buffer, 0x200);
|
||||
if ((mode() & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
|
||||
d->tarEnd = device()->pos();
|
||||
}
|
||||
|
||||
d->dirList.append(dirName); // contains trailing slash
|
||||
return true; // TODO if wanted, better error control
|
||||
}
|
||||
|
||||
bool KTar::doWriteSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime & /*atime*/,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime & /*ctime*/)
|
||||
{
|
||||
if (!isOpen()) {
|
||||
setErrorString(tr("Application error: TAR file must be open before being written into"));
|
||||
qCWarning(KArchiveLog) << "doWriteSymLink failed: !isOpen()";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(mode() & QIODevice::WriteOnly)) {
|
||||
setErrorString(tr("Application error: attempted to write into non-writable TAR file"));
|
||||
qCWarning(KArchiveLog) << "doWriteSymLink failed: !(mode() & QIODevice::WriteOnly)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// In some tar files we can find dir/./file => call cleanPath
|
||||
QString fileName(QDir::cleanPath(name));
|
||||
|
||||
char buffer[0x201];
|
||||
memset(buffer, 0, 0x200);
|
||||
if ((mode() & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
|
||||
device()->seek(d->tarEnd); // Go to end of archive as might have moved with a read
|
||||
}
|
||||
|
||||
// provide converted stuff we need lateron
|
||||
QByteArray encodedFileName = QFile::encodeName(fileName);
|
||||
QByteArray encodedTarget = QFile::encodeName(target);
|
||||
QByteArray uname = user.toLocal8Bit();
|
||||
QByteArray gname = group.toLocal8Bit();
|
||||
|
||||
// If more than 100 bytes, we need to use the LongLink trick
|
||||
if (encodedTarget.length() > 99) {
|
||||
d->writeLonglink(buffer, encodedTarget, 'K', uname.constData(), gname.constData());
|
||||
}
|
||||
if (encodedFileName.length() > 99) {
|
||||
d->writeLonglink(buffer, encodedFileName, 'L', uname.constData(), gname.constData());
|
||||
}
|
||||
|
||||
// Write (potentially truncated) name
|
||||
strncpy(buffer, encodedFileName.constData(), 99);
|
||||
buffer[99] = 0;
|
||||
// Write (potentially truncated) symlink target
|
||||
strncpy(buffer + 0x9d, encodedTarget.constData(), 99);
|
||||
buffer[0x9d + 99] = 0;
|
||||
// zero out the rest
|
||||
memset(buffer + 0x9d + 100, 0, 0x200 - 100 - 0x9d);
|
||||
|
||||
QByteArray permstr = QByteArray::number(static_cast<unsigned int>(perm), 8);
|
||||
permstr = permstr.rightJustified(6, ' ');
|
||||
d->fillBuffer(buffer, permstr.constData(), 0, mtime, 0x32, uname.constData(), gname.constData());
|
||||
|
||||
// Write header
|
||||
bool retval = device()->write(buffer, 0x200) == 0x200;
|
||||
if ((mode() & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
|
||||
d->tarEnd = device()->pos();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void KTar::virtual_hook(int id, void *data)
|
||||
{
|
||||
KArchive::virtual_hook(id, data);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KTAR_H
|
||||
#define KTAR_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
/**
|
||||
* @class KTar ktar.h KTar
|
||||
*
|
||||
* A class for reading / writing (optionally compressed) tar archives.
|
||||
*
|
||||
* KTar allows you to read and write tar archives, including those
|
||||
* that are compressed using gzip, bzip2 or xz.
|
||||
*
|
||||
* @author Torben Weis <weis@kde.org>, David Faure <faure@kde.org>
|
||||
*/
|
||||
class KARCHIVE_EXPORT KTar : public KArchive
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(KTar)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename
|
||||
* using the compression filter associated to given mimetype.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/weis/myfile.tgz")
|
||||
* @param mimetype "application/gzip" (before 5.85: "application/x-gzip"), "application/x-bzip",
|
||||
* "application/x-xz", "application/zstd" (since 5.82)
|
||||
* Do not use application/x-compressed-tar or similar - you only need to
|
||||
* specify the compression layer ! If the mimetype is omitted, it
|
||||
* will be determined from the filename.
|
||||
*/
|
||||
explicit KTar(const QString &filename, const QString &mimetype = QString());
|
||||
|
||||
/**
|
||||
* Creates an instance that operates on the given device.
|
||||
* The device can be compressed (KCompressionDevice) or not (QFile, etc.).
|
||||
* @warning Do not assume that giving a QFile here will decompress the file,
|
||||
* in case it's compressed!
|
||||
* @param dev the device to read from. If the source is compressed, the
|
||||
* QIODevice must take care of decompression
|
||||
*/
|
||||
explicit KTar(QIODevice *dev);
|
||||
|
||||
/**
|
||||
* If the tar ball is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
~KTar() override;
|
||||
|
||||
/**
|
||||
* Special function for setting the "original file name" in the gzip header,
|
||||
* when writing a tar.gz file. It appears when using in the "file" command,
|
||||
* for instance. Should only be called if the underlying device is a KCompressionDevice!
|
||||
* @param fileName the original file name
|
||||
*/
|
||||
void setOrigFileName(const QByteArray &fileName);
|
||||
|
||||
protected:
|
||||
/// Reimplemented from KArchive
|
||||
bool doWriteSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
/// Reimplemented from KArchive
|
||||
bool doWriteDir(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
/// Reimplemented from KArchive
|
||||
bool doPrepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
/// Reimplemented from KArchive
|
||||
bool doFinishWriting(qint64 size) override;
|
||||
|
||||
/**
|
||||
* Opens the archive for reading.
|
||||
* Parses the directory listing of the archive
|
||||
* and creates the KArchiveDirectory/KArchiveFile entries.
|
||||
* @param mode the mode of the file
|
||||
*/
|
||||
bool openArchive(QIODevice::OpenMode mode) override;
|
||||
bool closeArchive() override;
|
||||
|
||||
bool createDevice(QIODevice::OpenMode mode) override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
private:
|
||||
class KTarPrivate;
|
||||
KTarPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,279 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2007-2008 Per Øyvind Karlsen <peroyvind@mandriva.org>
|
||||
|
||||
Based on kbzip2filter:
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kxzfilter.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#if HAVE_XZ_SUPPORT
|
||||
extern "C" {
|
||||
#include <lzma.h>
|
||||
}
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
class Q_DECL_HIDDEN KXzFilter::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: isInitialized(false)
|
||||
{
|
||||
memset(&zStream, 0, sizeof(zStream));
|
||||
mode = 0;
|
||||
}
|
||||
|
||||
lzma_stream zStream;
|
||||
int mode;
|
||||
bool isInitialized;
|
||||
KXzFilter::Flag flag;
|
||||
};
|
||||
|
||||
KXzFilter::KXzFilter()
|
||||
: d(new Private)
|
||||
{
|
||||
}
|
||||
|
||||
KXzFilter::~KXzFilter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KXzFilter::init(int mode)
|
||||
{
|
||||
QVector<unsigned char> props;
|
||||
return init(mode, AUTO, props);
|
||||
}
|
||||
|
||||
static void freeFilters(lzma_filter filters[])
|
||||
{
|
||||
for (int i = 0; filters[i].id != LZMA_VLI_UNKNOWN; i++) {
|
||||
free(filters[i].options);
|
||||
}
|
||||
}
|
||||
|
||||
bool KXzFilter::init(int mode, Flag flag, const QVector<unsigned char> &properties)
|
||||
{
|
||||
if (d->isInitialized) {
|
||||
terminate();
|
||||
}
|
||||
|
||||
d->flag = flag;
|
||||
lzma_ret result;
|
||||
d->zStream.next_in = nullptr;
|
||||
d->zStream.avail_in = 0;
|
||||
if (mode == QIODevice::ReadOnly) {
|
||||
// TODO when we can depend on Qt 5.12 Use a QScopeGuard to call freeFilters
|
||||
lzma_filter filters[5];
|
||||
filters[0].id = LZMA_VLI_UNKNOWN;
|
||||
|
||||
switch (flag) {
|
||||
case AUTO:
|
||||
/* We set the memlimit for decompression to 100MiB which should be
|
||||
* more than enough to be sufficient for level 9 which requires 65 MiB.
|
||||
*/
|
||||
result = lzma_auto_decoder(&d->zStream, 100 << 20, 0);
|
||||
if (result != LZMA_OK) {
|
||||
qCWarning(KArchiveLog) << "lzma_auto_decoder returned" << result;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case LZMA: {
|
||||
filters[0].id = LZMA_FILTER_LZMA1;
|
||||
filters[0].options = nullptr;
|
||||
filters[1].id = LZMA_VLI_UNKNOWN;
|
||||
filters[1].options = nullptr;
|
||||
|
||||
Q_ASSERT(properties.size() == 5);
|
||||
unsigned char props[5];
|
||||
for (int i = 0; i < properties.size(); ++i) {
|
||||
props[i] = properties[i];
|
||||
}
|
||||
|
||||
result = lzma_properties_decode(&filters[0], nullptr, props, sizeof(props));
|
||||
if (result != LZMA_OK) {
|
||||
qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
|
||||
freeFilters(filters);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LZMA2: {
|
||||
filters[0].id = LZMA_FILTER_LZMA2;
|
||||
filters[0].options = nullptr;
|
||||
filters[1].id = LZMA_VLI_UNKNOWN;
|
||||
filters[1].options = nullptr;
|
||||
|
||||
Q_ASSERT(properties.size() == 1);
|
||||
unsigned char props[1];
|
||||
props[0] = properties[0];
|
||||
|
||||
result = lzma_properties_decode(&filters[0], nullptr, props, sizeof(props));
|
||||
if (result != LZMA_OK) {
|
||||
qCWarning(KArchiveLog) << "lzma_properties_decode returned" << result;
|
||||
freeFilters(filters);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BCJ: {
|
||||
filters[0].id = LZMA_FILTER_X86;
|
||||
filters[0].options = nullptr;
|
||||
|
||||
unsigned char props[5] = {0x5d, 0x00, 0x00, 0x08, 0x00};
|
||||
filters[1].id = LZMA_FILTER_LZMA1;
|
||||
filters[1].options = nullptr;
|
||||
result = lzma_properties_decode(&filters[1], nullptr, props, sizeof(props));
|
||||
if (result != LZMA_OK) {
|
||||
qCWarning(KArchiveLog) << "lzma_properties_decode1 returned" << result;
|
||||
freeFilters(filters);
|
||||
return false;
|
||||
}
|
||||
|
||||
filters[2].id = LZMA_VLI_UNKNOWN;
|
||||
filters[2].options = nullptr;
|
||||
|
||||
break;
|
||||
}
|
||||
case POWERPC:
|
||||
case IA64:
|
||||
case ARM:
|
||||
case ARMTHUMB:
|
||||
case SPARC:
|
||||
// qCDebug(KArchiveLog) << "flag" << flag << "props size" << properties.size();
|
||||
break;
|
||||
}
|
||||
|
||||
if (flag != AUTO) {
|
||||
result = lzma_raw_decoder(&d->zStream, filters);
|
||||
if (result != LZMA_OK) {
|
||||
qCWarning(KArchiveLog) << "lzma_raw_decoder returned" << result;
|
||||
freeFilters(filters);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
freeFilters(filters);
|
||||
|
||||
} else if (mode == QIODevice::WriteOnly) {
|
||||
if (flag == AUTO) {
|
||||
result = lzma_easy_encoder(&d->zStream, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC32);
|
||||
} else {
|
||||
lzma_filter filters[5];
|
||||
if (flag == LZMA2) {
|
||||
lzma_options_lzma lzma_opt;
|
||||
lzma_lzma_preset(&lzma_opt, LZMA_PRESET_DEFAULT);
|
||||
|
||||
filters[0].id = LZMA_FILTER_LZMA2;
|
||||
filters[0].options = &lzma_opt;
|
||||
filters[1].id = LZMA_VLI_UNKNOWN;
|
||||
filters[1].options = nullptr;
|
||||
}
|
||||
result = lzma_raw_encoder(&d->zStream, filters);
|
||||
}
|
||||
if (result != LZMA_OK) {
|
||||
qCWarning(KArchiveLog) << "lzma_easy_encoder returned" << result;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
|
||||
return false;
|
||||
}
|
||||
d->mode = mode;
|
||||
d->isInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int KXzFilter::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
bool KXzFilter::terminate()
|
||||
{
|
||||
if (d->mode == QIODevice::ReadOnly || d->mode == QIODevice::WriteOnly) {
|
||||
lzma_end(&d->zStream);
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
|
||||
return false;
|
||||
}
|
||||
d->isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void KXzFilter::reset()
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "KXzFilter::reset";
|
||||
// liblzma doesn't have a reset call...
|
||||
terminate();
|
||||
init(d->mode);
|
||||
}
|
||||
|
||||
void KXzFilter::setOutBuffer(char *data, uint maxlen)
|
||||
{
|
||||
d->zStream.avail_out = maxlen;
|
||||
d->zStream.next_out = (uint8_t *)data;
|
||||
}
|
||||
|
||||
void KXzFilter::setInBuffer(const char *data, unsigned int size)
|
||||
{
|
||||
d->zStream.avail_in = size;
|
||||
d->zStream.next_in = (uint8_t *)const_cast<char *>(data);
|
||||
}
|
||||
|
||||
int KXzFilter::inBufferAvailable() const
|
||||
{
|
||||
return d->zStream.avail_in;
|
||||
}
|
||||
|
||||
int KXzFilter::outBufferAvailable() const
|
||||
{
|
||||
return d->zStream.avail_out;
|
||||
}
|
||||
|
||||
KXzFilter::Result KXzFilter::uncompress()
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out =" << outBufferAvailable();
|
||||
lzma_ret result;
|
||||
result = lzma_code(&d->zStream, LZMA_RUN);
|
||||
|
||||
/*if (result != LZMA_OK) {
|
||||
qCDebug(KArchiveLog) << "lzma_code returned " << result;
|
||||
//qCDebug(KArchiveLog) << "KXzFilter::uncompress " << ( result == LZMA_STREAM_END ? KFilterBase::End : KFilterBase::Error );
|
||||
}*/
|
||||
|
||||
switch (result) {
|
||||
case LZMA_OK:
|
||||
return KFilterBase::Ok;
|
||||
case LZMA_STREAM_END:
|
||||
return KFilterBase::End;
|
||||
default:
|
||||
return KFilterBase::Error;
|
||||
}
|
||||
}
|
||||
|
||||
KXzFilter::Result KXzFilter::compress(bool finish)
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "Calling lzma_code with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
lzma_ret result = lzma_code(&d->zStream, finish ? LZMA_FINISH : LZMA_RUN);
|
||||
switch (result) {
|
||||
case LZMA_OK:
|
||||
return KFilterBase::Ok;
|
||||
break;
|
||||
case LZMA_STREAM_END:
|
||||
// qCDebug(KArchiveLog) << " lzma_code returned " << result;
|
||||
return KFilterBase::End;
|
||||
break;
|
||||
default:
|
||||
// qCDebug(KArchiveLog) << " lzma_code returned " << result;
|
||||
return KFilterBase::Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_XZ_SUPPORT */
|
|
@ -0,0 +1,69 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2007-2008 Per Øyvind Karlsen <peroyvind@mandriva.org>
|
||||
|
||||
Based on kbzip2filter:
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KXZFILTER_H
|
||||
#define KXZFILTER_H
|
||||
|
||||
#include <config-compression.h>
|
||||
|
||||
#if HAVE_XZ_SUPPORT
|
||||
|
||||
#include "kfilterbase.h"
|
||||
|
||||
/**
|
||||
* Internal class used by KCompressionDevice
|
||||
* @internal
|
||||
*/
|
||||
class KXzFilter : public KFilterBase
|
||||
{
|
||||
public:
|
||||
KXzFilter();
|
||||
~KXzFilter() override;
|
||||
|
||||
bool init(int) override;
|
||||
|
||||
enum Flag {
|
||||
AUTO = 0,
|
||||
LZMA = 1,
|
||||
LZMA2 = 2,
|
||||
BCJ = 3, // X86
|
||||
POWERPC = 4,
|
||||
IA64 = 5,
|
||||
ARM = 6,
|
||||
ARMTHUMB = 7,
|
||||
SPARC = 8,
|
||||
};
|
||||
|
||||
virtual bool init(int, Flag flag, const QVector<unsigned char> &props);
|
||||
int mode() const override;
|
||||
bool terminate() override;
|
||||
void reset() override;
|
||||
bool readHeader() override
|
||||
{
|
||||
return true; // lzma handles it by itself ! Cool !
|
||||
}
|
||||
bool writeHeader(const QByteArray &) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void setOutBuffer(char *data, uint maxlen) override;
|
||||
void setInBuffer(const char *data, uint size) override;
|
||||
int inBufferAvailable() const override;
|
||||
int outBufferAvailable() const override;
|
||||
Result uncompress() override;
|
||||
Result compress(bool finish) override;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // KXZFILTER_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,175 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Holger Schroeder <holger-kde@holgis.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KZIP_H
|
||||
#define KZIP_H
|
||||
|
||||
#include <karchive.h>
|
||||
|
||||
#include "kzipfileentry.h" // for source compat
|
||||
|
||||
class KZipFileEntry;
|
||||
/**
|
||||
* @class KZip zip.h KZip
|
||||
*
|
||||
* A class for reading / writing zip archives.
|
||||
*
|
||||
* You can use it in QIODevice::ReadOnly or in QIODevice::WriteOnly mode, and it
|
||||
* behaves just as expected.
|
||||
* It can also be used in QIODevice::ReadWrite mode, in this case one can
|
||||
* append files to an existing zip archive. When you append new files, which
|
||||
* are not yet in the zip, it works as expected, i.e. the files are appended at the end.
|
||||
* When you append a file, which is already in the file, the reference to the
|
||||
* old file is dropped and the new one is added to the zip - but the
|
||||
* old data from the file itself is not deleted, it is still in the
|
||||
* zipfile. So when you want to have a small and garbage-free zipfile,
|
||||
* just read the contents of the appended zip file and write it to a new one
|
||||
* in QIODevice::WriteOnly mode. This is especially important when you don't want
|
||||
* to leak information of how intermediate versions of files in the zip
|
||||
* were looking.
|
||||
*
|
||||
* For more information on the zip fileformat go to
|
||||
* http://www.pkware.com/products/enterprise/white_papers/appnote.html
|
||||
* @author Holger Schroeder <holger-kde@holgis.net>
|
||||
*/
|
||||
class KARCHIVE_EXPORT KZip : public KArchive
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(KZip)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an instance that operates on the given filename.
|
||||
* using the compression filter associated to given mimetype.
|
||||
*
|
||||
* @param filename is a local path (e.g. "/home/holger/myfile.zip")
|
||||
*/
|
||||
KZip(const QString &filename);
|
||||
|
||||
/**
|
||||
* Creates an instance that operates on the given device.
|
||||
* The device can be compressed (KCompressionDevice) or not (QFile, etc.).
|
||||
* @warning Do not assume that giving a QFile here will decompress the file,
|
||||
* in case it's compressed!
|
||||
* @param dev the device to access
|
||||
*/
|
||||
KZip(QIODevice *dev);
|
||||
|
||||
/**
|
||||
* If the zip file is still opened, then it will be
|
||||
* closed automatically by the destructor.
|
||||
*/
|
||||
~KZip() override;
|
||||
|
||||
/**
|
||||
* Describes the contents of the "extra field" for a given file in the Zip archive.
|
||||
*/
|
||||
enum ExtraField {
|
||||
NoExtraField = 0, ///< No extra field
|
||||
ModificationTime = 1, ///< Modification time ("extended timestamp" header)
|
||||
DefaultExtraField = 1, // alias of ModificationTime
|
||||
};
|
||||
|
||||
/**
|
||||
* Call this before writeFile or prepareWriting, to define what the next
|
||||
* file to be written should have in its extra field.
|
||||
* @param ef the type of "extra field"
|
||||
* @see extraField()
|
||||
*/
|
||||
void setExtraField(ExtraField ef);
|
||||
|
||||
/**
|
||||
* The current type of "extra field" that will be used for new files.
|
||||
* @return the current type of "extra field"
|
||||
* @see setExtraField()
|
||||
*/
|
||||
ExtraField extraField() const;
|
||||
|
||||
/**
|
||||
* Describes the compression type for a given file in the Zip archive.
|
||||
*/
|
||||
enum Compression {
|
||||
NoCompression = 0, ///< Uncompressed.
|
||||
DeflateCompression = 1, ///< Deflate compression method.
|
||||
};
|
||||
|
||||
/**
|
||||
* Call this before writeFile or prepareWriting, to define whether the next
|
||||
* files to be written should be compressed or not.
|
||||
* @param c the new compression mode
|
||||
* @see compression()
|
||||
*/
|
||||
void setCompression(Compression c);
|
||||
|
||||
/**
|
||||
* The current compression mode that will be used for new files.
|
||||
* @return the current compression mode
|
||||
* @see setCompression()
|
||||
*/
|
||||
Compression compression() const;
|
||||
|
||||
/**
|
||||
* Write data to a file that has been created using prepareWriting().
|
||||
* @param data a pointer to the data
|
||||
* @param size the size of the chunk
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool writeData(const char *data, qint64 size) override;
|
||||
|
||||
protected:
|
||||
/// Reimplemented from KArchive
|
||||
bool doWriteSymLink(const QString &name,
|
||||
const QString &target,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
/// Reimplemented from KArchive
|
||||
bool doPrepareWriting(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
qint64 size,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &creationTime) override;
|
||||
|
||||
/**
|
||||
* Write data to a file that has been created using prepareWriting().
|
||||
* @param size the size of the file
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool doFinishWriting(qint64 size) override;
|
||||
|
||||
/**
|
||||
* Opens the archive for reading.
|
||||
* Parses the directory listing of the archive
|
||||
* and creates the KArchiveDirectory/KArchiveFile entries.
|
||||
* @param mode the mode of the file
|
||||
*/
|
||||
bool openArchive(QIODevice::OpenMode mode) override;
|
||||
|
||||
/// Closes the archive
|
||||
bool closeArchive() override;
|
||||
|
||||
/// Reimplemented from KArchive
|
||||
bool doWriteDir(const QString &name,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
mode_t perm,
|
||||
const QDateTime &atime,
|
||||
const QDateTime &mtime,
|
||||
const QDateTime &ctime) override;
|
||||
|
||||
protected:
|
||||
void virtual_hook(int id, void *data) override;
|
||||
|
||||
private:
|
||||
class KZipPrivate;
|
||||
KZipPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,79 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Holger Schroeder <holger-kde@holgis.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KZIPFILEENTRY_H
|
||||
#define KZIPFILEENTRY_H
|
||||
|
||||
#include "karchive.h"
|
||||
|
||||
class KZip;
|
||||
/**
|
||||
* @class KZipFileEntry kzipfileentry.h KZipFileEntry
|
||||
*
|
||||
* A KZipFileEntry represents a file in a zip archive.
|
||||
*/
|
||||
class KARCHIVE_EXPORT KZipFileEntry : public KArchiveFile
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new zip file entry. Do not call this, KZip takes care of it.
|
||||
*/
|
||||
KZipFileEntry(KZip *zip,
|
||||
const QString &name,
|
||||
int access,
|
||||
const QDateTime &date,
|
||||
const QString &user,
|
||||
const QString &group,
|
||||
const QString &symlink,
|
||||
const QString &path,
|
||||
qint64 start,
|
||||
qint64 uncompressedSize,
|
||||
int encoding,
|
||||
qint64 compressedSize);
|
||||
|
||||
/**
|
||||
* Destructor. Do not call this.
|
||||
*/
|
||||
~KZipFileEntry() override;
|
||||
|
||||
int encoding() const;
|
||||
qint64 compressedSize() const;
|
||||
|
||||
/// Only used when writing
|
||||
void setCompressedSize(qint64 compressedSize);
|
||||
|
||||
/// Header start: only used when writing
|
||||
void setHeaderStart(qint64 headerstart);
|
||||
qint64 headerStart() const;
|
||||
|
||||
/// CRC: only used when writing
|
||||
unsigned long crc32() const;
|
||||
void setCRC32(unsigned long crc32);
|
||||
|
||||
/// Name with complete path - KArchiveFile::name() is the filename only (no path)
|
||||
const QString &path() const;
|
||||
|
||||
/**
|
||||
* @return the content of this file.
|
||||
* Call data() with care (only once per file), this data isn't cached.
|
||||
*/
|
||||
QByteArray data() const override;
|
||||
|
||||
/**
|
||||
* This method returns a QIODevice to read the file contents.
|
||||
* This is obviously for reading only.
|
||||
* Note that the ownership of the device is being transferred to the caller,
|
||||
* who will have to delete it.
|
||||
* The returned device auto-opens (in readonly mode), no need to open it.
|
||||
*/
|
||||
QIODevice *createDevice() const override;
|
||||
|
||||
private:
|
||||
class KZipFileEntryPrivate;
|
||||
KZipFileEntryPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,134 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2021 Albert Astals Cid <aacid@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kzstdfilter.h"
|
||||
#include "loggingcategory.h"
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
|
||||
extern "C" {
|
||||
#include <zstd.h>
|
||||
}
|
||||
|
||||
class Q_DECL_HIDDEN KZstdFilter::Private
|
||||
{
|
||||
public:
|
||||
union {
|
||||
ZSTD_CStream *cStream;
|
||||
ZSTD_DStream *dStream;
|
||||
};
|
||||
int mode;
|
||||
bool isInitialized = false;
|
||||
ZSTD_inBuffer inBuffer;
|
||||
ZSTD_outBuffer outBuffer;
|
||||
};
|
||||
|
||||
KZstdFilter::KZstdFilter()
|
||||
: d(new Private)
|
||||
{
|
||||
}
|
||||
|
||||
KZstdFilter::~KZstdFilter()
|
||||
{
|
||||
}
|
||||
|
||||
bool KZstdFilter::init(int mode)
|
||||
{
|
||||
if (d->isInitialized) {
|
||||
terminate();
|
||||
}
|
||||
|
||||
d->inBuffer.size = 0;
|
||||
d->inBuffer.pos = 0;
|
||||
|
||||
if (mode == QIODevice::ReadOnly) {
|
||||
d->dStream = ZSTD_createDStream();
|
||||
} else if (mode == QIODevice::WriteOnly) {
|
||||
d->cStream = ZSTD_createCStream();
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "Unsupported mode " << mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
|
||||
return false;
|
||||
}
|
||||
d->mode = mode;
|
||||
d->isInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int KZstdFilter::mode() const
|
||||
{
|
||||
return d->mode;
|
||||
}
|
||||
|
||||
bool KZstdFilter::terminate()
|
||||
{
|
||||
if (d->mode == QIODevice::ReadOnly) {
|
||||
ZSTD_freeDStream(d->dStream);
|
||||
} else if (d->mode == QIODevice::WriteOnly) {
|
||||
ZSTD_freeCStream(d->cStream);
|
||||
} else {
|
||||
// qCWarning(KArchiveLog) << "Unsupported mode " << d->mode << ". Only QIODevice::ReadOnly and QIODevice::WriteOnly supported";
|
||||
return false;
|
||||
}
|
||||
d->isInitialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void KZstdFilter::reset()
|
||||
{
|
||||
terminate();
|
||||
init(d->mode);
|
||||
}
|
||||
|
||||
void KZstdFilter::setOutBuffer(char *data, uint maxlen)
|
||||
{
|
||||
d->outBuffer.dst = data;
|
||||
d->outBuffer.size = maxlen;
|
||||
d->outBuffer.pos = 0;
|
||||
}
|
||||
|
||||
void KZstdFilter::setInBuffer(const char *data, unsigned int size)
|
||||
{
|
||||
d->inBuffer.src = data;
|
||||
d->inBuffer.size = size;
|
||||
d->inBuffer.pos = 0;
|
||||
}
|
||||
|
||||
int KZstdFilter::inBufferAvailable() const
|
||||
{
|
||||
return d->inBuffer.size - d->inBuffer.pos;
|
||||
}
|
||||
|
||||
int KZstdFilter::outBufferAvailable() const
|
||||
{
|
||||
return d->outBuffer.size - d->outBuffer.pos;
|
||||
}
|
||||
|
||||
KZstdFilter::Result KZstdFilter::uncompress()
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "Calling ZSTD_decompressStream with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
const size_t result = ZSTD_decompressStream(d->dStream, &d->outBuffer, &d->inBuffer);
|
||||
if (ZSTD_isError(result)) {
|
||||
qCWarning(KArchiveLog) << "ZSTD_decompressStream returned" << result << ZSTD_getErrorName(result);
|
||||
return KFilterBase::Error;
|
||||
}
|
||||
|
||||
return result == 0 ? KFilterBase::End : KFilterBase::Ok;
|
||||
}
|
||||
|
||||
KZstdFilter::Result KZstdFilter::compress(bool finish)
|
||||
{
|
||||
// qCDebug(KArchiveLog) << "Calling ZSTD_compressStream2 with avail_in=" << inBufferAvailable() << " avail_out=" << outBufferAvailable();
|
||||
const size_t result = ZSTD_compressStream2(d->cStream, &d->outBuffer, &d->inBuffer, finish ? ZSTD_e_end : ZSTD_e_flush);
|
||||
if (ZSTD_isError(result)) {
|
||||
return KFilterBase::Error;
|
||||
}
|
||||
|
||||
return finish && result == 0 ? KFilterBase::End : KFilterBase::Ok;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,54 @@
|
|||
/* This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2021 Albert Astals Cid <aacid@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KZSTDFILTER_H
|
||||
#define KZSTDFILTER_H
|
||||
|
||||
#include <config-compression.h>
|
||||
|
||||
#if HAVE_ZSTD_SUPPORT
|
||||
|
||||
#include "kfilterbase.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* Internal class used by KCompressionDevice
|
||||
* @internal
|
||||
*/
|
||||
class KZstdFilter : public KFilterBase
|
||||
{
|
||||
public:
|
||||
KZstdFilter();
|
||||
~KZstdFilter() override;
|
||||
|
||||
bool init(int) override;
|
||||
int mode() const override;
|
||||
bool terminate() override;
|
||||
void reset() override;
|
||||
bool readHeader() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool writeHeader(const QByteArray &) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void setOutBuffer(char *data, uint maxlen) override;
|
||||
void setInBuffer(const char *data, uint size) override;
|
||||
int inBufferAvailable() const override;
|
||||
int outBufferAvailable() const override;
|
||||
Result uncompress() override;
|
||||
Result compress(bool finish) override;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
const std::unique_ptr<Private> d;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
project(PackageTest CXX)
|
||||
|
||||
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
|
||||
conan_basic_setup()
|
||||
|
||||
# TODO: rename to Qt6 after branching KF6
|
||||
find_package(Qt5 5.6 CONFIG REQUIRED
|
||||
Core
|
||||
)
|
||||
|
||||
# TODO: rename to Qt6 after branching KF6
|
||||
add_executable(example example.cpp)
|
||||
target_link_libraries(example ${CONAN_LIBS}
|
||||
Qt5::Core)
|
|
@ -0,0 +1,20 @@
|
|||
from conans import ConanFile, CMake
|
||||
import os
|
||||
|
||||
class KArchiveTestConan(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
generators = "cmake"
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
# Current dir is "test_package/build/<build_id>" and CMakeLists.txt is in "test_package"
|
||||
cmake.configure(source_dir=self.conanfile_directory, build_dir="./")
|
||||
cmake.build()
|
||||
|
||||
def imports(self):
|
||||
self.copy("*.dll", dst="bin", src="bin")
|
||||
self.copy("*.dylib*", dst="bin", src="lib")
|
||||
|
||||
def test(self):
|
||||
os.chdir("bin")
|
||||
self.run(".%sexample" % os.sep)
|
|
@ -0,0 +1,8 @@
|
|||
#include "kzip.h"
|
||||
#include <QIODevice>
|
||||
|
||||
int main()
|
||||
{
|
||||
KZip a("somefile");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
remove_definitions(-DQT_NO_CAST_FROM_ASCII)
|
||||
|
||||
include(ECMMarkAsTest)
|
||||
|
||||
macro(KARCHIVE_EXECUTABLE_TESTS)
|
||||
foreach(_testname ${ARGN})
|
||||
add_executable(${_testname} ${_testname}.cpp) # TODO NOGUI
|
||||
target_link_libraries(${_testname} KF5::Archive)
|
||||
ecm_mark_as_test(${_testname})
|
||||
endforeach(_testname)
|
||||
endmacro(KARCHIVE_EXECUTABLE_TESTS)
|
||||
|
||||
karchive_executable_tests(
|
||||
kartest
|
||||
ktartest
|
||||
krcctest
|
||||
kziptest
|
||||
)
|
||||
|
||||
if(LIBLZMA_FOUND)
|
||||
karchive_executable_tests(
|
||||
k7ziptest
|
||||
)
|
||||
endif()
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2011 Mario Bensi <mbensi@ipsquad.net>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "k7zip.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void recursive_print(const KArchiveDirectory *dir, const QString &path)
|
||||
{
|
||||
QStringList l = dir->entries();
|
||||
l.sort();
|
||||
QStringList::ConstIterator it = l.constBegin();
|
||||
for (; it != l.constEnd(); ++it) {
|
||||
const KArchiveEntry *entry = dir->entry((*it));
|
||||
printf("mode=%07o %s %s %s %s%s %lld isdir=%d\n",
|
||||
entry->permissions(),
|
||||
entry->date().toString(QStringLiteral("yyyy-MM-dd hh:mm:ss")).toLatin1().constData(),
|
||||
entry->user().toLatin1().constData(),
|
||||
entry->group().toLatin1().constData(),
|
||||
path.toLatin1().constData(),
|
||||
(*it).toLatin1().constData(),
|
||||
entry->isFile() ? static_cast<const KArchiveFile *>(entry)->size() : 0,
|
||||
entry->isDirectory());
|
||||
if (!entry->symLinkTarget().isEmpty()) {
|
||||
printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget()));
|
||||
}
|
||||
if (entry->isDirectory()) {
|
||||
recursive_print((KArchiveDirectory *)entry, path + (*it) + '/');
|
||||
}
|
||||
if (entry->isFile()) {
|
||||
const KArchiveFile *f = static_cast<const KArchiveFile *>(entry);
|
||||
QByteArray arr(f->data());
|
||||
qDebug() << "data" << arr;
|
||||
|
||||
QIODevice *dev = f->createDevice();
|
||||
QByteArray contents = dev->readAll();
|
||||
qDebug() << "contents" << contents;
|
||||
delete dev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See karchivetest.cpp for the unittest that covers K7Zip.
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf(
|
||||
"\n"
|
||||
" Usage :\n"
|
||||
" ./k7ziptest /path/to/existing_file.7z tests listing an existing .7z\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
K7Zip k7z(argv[1]);
|
||||
|
||||
if (!k7z.open(QIODevice::ReadOnly)) {
|
||||
printf("Could not open %s for reading\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const KArchiveDirectory *dir = k7z.directory();
|
||||
|
||||
// printf("calling recursive_print\n");
|
||||
recursive_print(dir, QLatin1String(""));
|
||||
// printf("recursive_print called\n");
|
||||
|
||||
k7z.close();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2002-2019 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kar.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void recursive_print(const KArchiveDirectory *dir, const QString &path)
|
||||
{
|
||||
QStringList l = dir->entries();
|
||||
l.sort();
|
||||
QStringList::ConstIterator it = l.constBegin();
|
||||
for (; it != l.constEnd(); ++it) {
|
||||
const KArchiveEntry *entry = dir->entry((*it));
|
||||
printf("mode=%7o path=%s type=%s size=%lld\n",
|
||||
entry->permissions(),
|
||||
qPrintable(path + (*it)),
|
||||
entry->isFile() ? "file" : "dir",
|
||||
entry->isFile() ? static_cast<const KArchiveFile *>(entry)->size() : 0);
|
||||
if (!entry->symLinkTarget().isEmpty()) {
|
||||
printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget()));
|
||||
}
|
||||
if (entry->isDirectory()) {
|
||||
recursive_print((KArchiveDirectory *)entry, path + (*it) + '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See karchivetest.cpp for the unittest that covers KAr.
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf(
|
||||
"\n"
|
||||
" Usage :\n"
|
||||
" ./kartest /path/to/existing_file.a tests listing an existing archive\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
KAr archive(argv[1]);
|
||||
|
||||
if (!archive.open(QIODevice::ReadOnly)) {
|
||||
printf("Could not open %s for reading\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const KArchiveDirectory *dir = archive.directory();
|
||||
|
||||
// printf("calling recursive_print\n");
|
||||
recursive_print(dir, QLatin1String(""));
|
||||
// printf("recursive_print called\n");
|
||||
|
||||
archive.close();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2002-2014 David Faure <faure@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "krcc.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void recursive_print(const KArchiveDirectory *dir, const QString &path)
|
||||
{
|
||||
QStringList l = dir->entries();
|
||||
l.sort();
|
||||
QStringList::ConstIterator it = l.constBegin();
|
||||
for (; it != l.constEnd(); ++it) {
|
||||
const KArchiveEntry *entry = dir->entry((*it));
|
||||
printf("mode=%07o %s %s %s%s %lld isdir=%d\n",
|
||||
entry->permissions(),
|
||||
entry->user().toLatin1().constData(),
|
||||
entry->group().toLatin1().constData(),
|
||||
path.toLatin1().constData(),
|
||||
(*it).toLatin1().constData(),
|
||||
entry->isFile() ? static_cast<const KArchiveFile *>(entry)->size() : 0,
|
||||
entry->isDirectory());
|
||||
if (!entry->symLinkTarget().isEmpty()) {
|
||||
printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget()));
|
||||
}
|
||||
if (entry->isDirectory()) {
|
||||
recursive_print((KArchiveDirectory *)entry, path + (*it) + '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See karchivetest.cpp for the unittest that covers KTar.
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf(
|
||||
"\n"
|
||||
" Usage :\n"
|
||||
" ./ktartest /path/to/existing_file.tar.gz tests listing an existing tar.gz\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
KRcc rcc(argv[1]);
|
||||
|
||||
if (!rcc.open(QIODevice::ReadOnly)) {
|
||||
printf("Could not open %s for reading\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const KArchiveDirectory *dir = rcc.directory();
|
||||
|
||||
// printf("calling recursive_print\n");
|
||||
recursive_print(dir, QLatin1String(""));
|
||||
// printf("recursive_print called\n");
|
||||
|
||||
rcc.close();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2002-2005 David Faure <faure@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "ktar.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void recursive_print(const KArchiveDirectory *dir, const QString &path)
|
||||
{
|
||||
QStringList l = dir->entries();
|
||||
l.sort();
|
||||
QStringList::ConstIterator it = l.constBegin();
|
||||
for (; it != l.constEnd(); ++it) {
|
||||
const KArchiveEntry *entry = dir->entry((*it));
|
||||
printf("mode=%07o %s %s %s%s %lld isdir=%d\n",
|
||||
entry->permissions(),
|
||||
entry->user().toLatin1().constData(),
|
||||
entry->group().toLatin1().constData(),
|
||||
path.toLatin1().constData(),
|
||||
(*it).toLatin1().constData(),
|
||||
entry->isFile() ? static_cast<const KArchiveFile *>(entry)->size() : 0,
|
||||
entry->isDirectory());
|
||||
if (!entry->symLinkTarget().isEmpty()) {
|
||||
printf(" (symlink to %s)\n", qPrintable(entry->symLinkTarget()));
|
||||
}
|
||||
if (entry->isDirectory()) {
|
||||
recursive_print((KArchiveDirectory *)entry, path + (*it) + '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See karchivetest.cpp for the unittest that covers KTar.
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf(
|
||||
"\n"
|
||||
" Usage :\n"
|
||||
" ./ktartest /path/to/existing_file.tar.gz tests listing an existing tar.gz\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
KTar tar(argv[1]);
|
||||
|
||||
if (!tar.open(QIODevice::ReadOnly)) {
|
||||
printf("Could not open %s for reading\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const KArchiveDirectory *dir = tar.directory();
|
||||
|
||||
// printf("calling recursive_print\n");
|
||||
recursive_print(dir, QLatin1String(""));
|
||||
// printf("recursive_print called\n");
|
||||
|
||||
tar.close();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2002-2013 David Faure <faure@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kzip.h"
|
||||
#include "kcompressiondevice.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void recursive_print(const KArchiveDirectory *dir, const QString &path)
|
||||
{
|
||||
const QStringList lst = dir->entries();
|
||||
for (const QString &it : lst) {
|
||||
const KArchiveEntry *entry = dir->entry(it);
|
||||
printf("mode=%07o %s %s \"%s%s\" size: %lld pos: %lld isdir=%d%s",
|
||||
entry->permissions(),
|
||||
entry->user().toLatin1().constData(),
|
||||
entry->group().toLatin1().constData(),
|
||||
path.toLatin1().constData(),
|
||||
it.toLatin1().constData(),
|
||||
entry->isDirectory() ? 0 : (static_cast<const KArchiveFile *>(entry))->size(),
|
||||
entry->isDirectory() ? 0 : (static_cast<const KArchiveFile *>(entry))->position(),
|
||||
entry->isDirectory(),
|
||||
entry->symLinkTarget().isEmpty() ? "" : QStringLiteral(" symlink: %1").arg(entry->symLinkTarget()).toLatin1().constData());
|
||||
|
||||
// if (!entry->isDirectory()) printf("%d", (static_cast<const KArchiveFile *>(entry))->size());
|
||||
printf("\n");
|
||||
if (entry->isDirectory()) {
|
||||
recursive_print(static_cast<const KArchiveDirectory *>(entry), path + it + '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recursive_transfer(const KArchiveDirectory *dir, const QString &path, KZip *zip)
|
||||
{
|
||||
const QStringList lst = dir->entries();
|
||||
for (const QString &it : lst) {
|
||||
const KArchiveEntry *e = dir->entry(it);
|
||||
qDebug() << "actual file: " << e->name();
|
||||
if (e->isFile()) {
|
||||
Q_ASSERT(e && e->isFile());
|
||||
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
|
||||
printf("FILE=%s\n", qPrintable(e->name()));
|
||||
|
||||
QByteArray arr(f->data());
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
printf("SIZE=%lli\n", arr.size());
|
||||
#else
|
||||
printf("SIZE=%i\n", arr.size());
|
||||
#endif
|
||||
QString str(arr);
|
||||
printf("DATA=%s\n", qPrintable(str));
|
||||
|
||||
if (e->symLinkTarget().isEmpty()) {
|
||||
zip->writeFile(path + e->name(), arr);
|
||||
} else {
|
||||
zip->writeSymLink(path + e->name(), e->symLinkTarget());
|
||||
}
|
||||
} else if (e->isDirectory()) {
|
||||
recursive_transfer(static_cast<const KArchiveDirectory *>(e), path + e->name() + '/', zip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int doList(const QString &fileName)
|
||||
{
|
||||
KZip zip(fileName);
|
||||
if (!zip.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for reading. ZIP file doesn't exist or is invalid:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory *dir = zip.directory();
|
||||
recursive_print(dir, QString());
|
||||
zip.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doPrintAll(const QString &fileName)
|
||||
{
|
||||
KZip zip(fileName);
|
||||
qDebug() << "Opening zip file";
|
||||
if (!zip.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for reading. ZIP file doesn't exist or is invalid:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory *dir = zip.directory();
|
||||
qDebug() << "Listing toplevel of zip file";
|
||||
const QStringList lst = dir->entries();
|
||||
for (const QString &it : lst) {
|
||||
const KArchiveEntry *e = dir->entry(it);
|
||||
qDebug() << "Printing" << it;
|
||||
if (e->isFile()) {
|
||||
Q_ASSERT(e && e->isFile());
|
||||
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
|
||||
const QByteArray data(f->data());
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
printf("SIZE=%lli\n", data.size());
|
||||
#else
|
||||
printf("SIZE=%i\n", data.size());
|
||||
#endif
|
||||
QString str = QString::fromUtf8(data);
|
||||
printf("DATA=%s\n", qPrintable(str));
|
||||
}
|
||||
}
|
||||
zip.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doSave(const QString &fileName)
|
||||
{
|
||||
KZip zip(fileName);
|
||||
if (!zip.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for writing:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const QByteArray data = "This is the data for the main file";
|
||||
bool writeOk = zip.writeFile(QStringLiteral("maindoc.txt"), data);
|
||||
if (!writeOk) {
|
||||
qWarning() << "Write error (main file)";
|
||||
return 1;
|
||||
}
|
||||
const QByteArray data2 = "This is the data for the other file";
|
||||
writeOk = zip.writeFile(QStringLiteral("subdir/other.txt"), data2);
|
||||
if (!writeOk) {
|
||||
qWarning() << "Write error (other file)";
|
||||
return 1;
|
||||
}
|
||||
// writeOk = zip.addLocalFile("David.jpg", "picture.jpg");
|
||||
// if (!writeOk) {
|
||||
// qWarning() << "Write error (picture)";
|
||||
// return 1;
|
||||
//}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doLoad(const QString &fileName)
|
||||
{
|
||||
KZip zip(fileName);
|
||||
if (!zip.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for reading. ZIP file doesn't exist or is invalid:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory *dir = zip.directory();
|
||||
const KArchiveEntry *mainEntry = dir->entry(QStringLiteral("maindoc.txt"));
|
||||
Q_ASSERT(mainEntry && mainEntry->isFile());
|
||||
const KArchiveFile *mainFile = static_cast<const KArchiveFile *>(mainEntry);
|
||||
qDebug() << "maindoc.txt:" << mainFile->data();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doPrint(const QString &fileName, const QString &entryName)
|
||||
{
|
||||
KZip zip(fileName);
|
||||
if (!zip.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for reading. ZIP file doesn't exist or is invalid:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory *dir = zip.directory();
|
||||
const KArchiveEntry *e = dir->entry(entryName);
|
||||
Q_ASSERT(e && e->isFile());
|
||||
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
|
||||
|
||||
const QByteArray arr(f->data());
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
printf("SIZE=%lli\n", arr.size());
|
||||
#else
|
||||
printf("SIZE=%i\n", arr.size());
|
||||
#endif
|
||||
|
||||
QString str = QString::fromUtf8(arr);
|
||||
printf("%s", qPrintable(str));
|
||||
return zip.close() ? 0 : 1 /*error*/;
|
||||
}
|
||||
|
||||
static int doCreate(const QString &archiveName, const QStringList &fileNames)
|
||||
{
|
||||
KZip zip(archiveName);
|
||||
if (!zip.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Could not open" << archiveName << "for writing:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
for (const QString &fileName : fileNames) {
|
||||
QFile f(fileName);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for reading:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
zip.writeFile(fileName, f.readAll());
|
||||
}
|
||||
return zip.close() ? 0 : 1 /*error*/;
|
||||
}
|
||||
|
||||
static int doUpdate(const QString &archiveName, const QString &fileName)
|
||||
{
|
||||
KZip zip(archiveName);
|
||||
if (!zip.open(QIODevice::ReadWrite)) {
|
||||
qWarning() << "Could not open" << archiveName << "for read/write:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
|
||||
QFile f(fileName);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for reading:" << zip.errorString();
|
||||
return 1;
|
||||
}
|
||||
|
||||
zip.writeFile(fileName, f.readAll());
|
||||
return zip.close() ? 0 : 1 /*error*/;
|
||||
}
|
||||
|
||||
static int doTransfer(const QString &sourceFile, const QString &destFile)
|
||||
{
|
||||
KZip zip1(sourceFile);
|
||||
KZip zip2(destFile);
|
||||
if (!zip1.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << sourceFile << "for reading. ZIP file doesn't exist or is invalid:" << zip1.errorString();
|
||||
return 1;
|
||||
}
|
||||
if (!zip2.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Could not open" << destFile << "for writing:" << zip2.errorString();
|
||||
return 1;
|
||||
}
|
||||
const KArchiveDirectory *dir1 = zip1.directory();
|
||||
|
||||
recursive_transfer(dir1, QLatin1String(""), &zip2);
|
||||
|
||||
zip1.close();
|
||||
zip2.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool save(QIODevice *device)
|
||||
{
|
||||
const QByteArray data = "This is some text that will be compressed.\n";
|
||||
const int written = device->write(data);
|
||||
if (written != data.size()) {
|
||||
qWarning() << "Error writing data";
|
||||
return 1;
|
||||
}
|
||||
// success
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doCompress(const QString &fileName)
|
||||
{
|
||||
KCompressionDevice device(fileName, KCompressionDevice::BZip2);
|
||||
if (!device.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for writing:" << device.errorString();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return save(&device);
|
||||
}
|
||||
|
||||
static bool load(QIODevice *device)
|
||||
{
|
||||
const QByteArray data = device->readAll();
|
||||
printf("%s", data.constData());
|
||||
return true;
|
||||
}
|
||||
|
||||
static int doUncompress(const QString &fileName)
|
||||
{
|
||||
KCompressionDevice device(fileName, KCompressionDevice::BZip2);
|
||||
if (!device.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open" << fileName << "for reading:" << device.errorString();
|
||||
return 1;
|
||||
}
|
||||
return load(&device);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
// ###### Note: please consider adding new tests to karchivetest (so that they can be automated)
|
||||
// rather than here (interactive)
|
||||
printf(
|
||||
"\n"
|
||||
" Usage :\n"
|
||||
" ./kziptest list /path/to/existing_file.zip tests listing an existing zip\n"
|
||||
" ./kziptest print-all file.zip prints contents of all files.\n"
|
||||
" ./kziptest print file.zip filename prints contents of one file.\n"
|
||||
" ./kziptest create file.zip filenames create a new zip file with these files.\n"
|
||||
" ./kziptest update file.zip filename update filename in file.zip.\n"
|
||||
" ./kziptest save file.zip save file.\n"
|
||||
" ./kziptest load file.zip load file.\n"
|
||||
" ./kziptest write file.bz2 write compressed file.\n"
|
||||
" ./kziptest read file.bz2 read uncompressed file.\n");
|
||||
return 1;
|
||||
}
|
||||
QCoreApplication app(argc, argv);
|
||||
QString command = argv[1];
|
||||
if (command == QLatin1String("list")) {
|
||||
return doList(QFile::decodeName(argv[2]));
|
||||
} else if (command == QLatin1String("print-all")) {
|
||||
return doPrintAll(QFile::decodeName(argv[2]));
|
||||
} else if (command == QLatin1String("print")) {
|
||||
if (argc != 4) {
|
||||
printf("usage: kziptest print archivename filename");
|
||||
return 1;
|
||||
}
|
||||
return doPrint(QFile::decodeName(argv[2]), argv[3]);
|
||||
} else if (command == QLatin1String("save")) {
|
||||
return doSave(QFile::decodeName(argv[2]));
|
||||
} else if (command == QLatin1String("load")) {
|
||||
return doLoad(QFile::decodeName(argv[2]));
|
||||
} else if (command == QLatin1String("write")) {
|
||||
return doCompress(QFile::decodeName(argv[2]));
|
||||
} else if (command == QLatin1String("read")) {
|
||||
return doUncompress(QFile::decodeName(argv[2]));
|
||||
} else if (command == QLatin1String("create")) {
|
||||
if (argc < 4) {
|
||||
printf("usage: kziptest create archivename filenames");
|
||||
return 1;
|
||||
}
|
||||
const QStringList fileNames = app.arguments().mid(3);
|
||||
return doCreate(QFile::decodeName(argv[2]), fileNames);
|
||||
} else if (command == QLatin1String("update")) {
|
||||
if (argc != 4) {
|
||||
printf("usage: kziptest update archivename filename");
|
||||
return 1;
|
||||
}
|
||||
return doUpdate(QFile::decodeName(argv[2]), QFile::decodeName(argv[3]));
|
||||
} else if (command == QLatin1String("transfer")) {
|
||||
if (argc != 4) {
|
||||
printf("usage: kziptest transfer sourcefile destfile");
|
||||
return 1;
|
||||
}
|
||||
return doTransfer(QFile::decodeName(argv[2]), QFile::decodeName(argv[3]));
|
||||
} else {
|
||||
printf("Unknown command\n");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue