Skip to content

Commit 5d7de99

Browse files
committed
Utilities: Add karchive library
1 parent 03b8b34 commit 5d7de99

File tree

3 files changed

+211
-0
lines changed

3 files changed

+211
-0
lines changed

src/Utilities/Compression/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
target_sources(${CMAKE_PROJECT_NAME}
22
PRIVATE
3+
karchive.cc
4+
karchive.h
35
QGCLZMA.cc
46
QGCLZMA.h
57
QGCZip.cc
@@ -75,3 +77,34 @@ target_compile_definitions(xz
7577
)
7678

7779
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE xz)
80+
81+
#===========================================================================#
82+
83+
CPMAddPackage(
84+
NAME ECM
85+
GITHUB_REPOSITORY KDE/extra-cmake-modules
86+
VERSION 6.15.0
87+
DOWNLOAD_ONLY
88+
OPTIONS
89+
"BUILD_DOC OFF"
90+
"BUILD_TESTING OFF"
91+
)
92+
93+
list(APPEND CMAKE_MODULE_PATH
94+
"${extra-cmake-modules_SOURCE_DIR}/modules"
95+
)
96+
97+
#===========================================================================#
98+
99+
CPMAddPackage(
100+
NAME karchive
101+
GITHUB_REPOSITORY KDE/karchive
102+
VERSION 6.15.0
103+
OPTIONS
104+
"WITH_BZIP2 ON"
105+
"WITH_LIBLZMA ON"
106+
"WITH_OPENSSL ON"
107+
"WITH_LIBZSTD ON"
108+
)
109+
110+
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE KF6::Archive)

src/Utilities/Compression/karchive.cc

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#include "karchive.h"
2+
#include "QGCLoggingCategory.h"
3+
4+
#include <QtCore/QDir>
5+
#include <QtCore/QFileInfo>
6+
#include <QtCore/QFile>
7+
#include <QtCore/QDebug>
8+
9+
#include <memory>
10+
11+
#include <KCompressionDevice>
12+
#include <ktar.h>
13+
#include <kzip.h>
14+
15+
#if __has_include(<k7zip.h>)
16+
#include <k7zip.h>
17+
#define KARCHIVE_HAS_K7ZIP 1
18+
#else
19+
#define KARCHIVE_HAS_K7ZIP 0
20+
#endif
21+
22+
QGC_LOGGING_CATEGORY(karchiveLog, "qgc.utilities.compression.karchive")
23+
24+
namespace
25+
{
26+
27+
/// Returns only the file/folder name component of a path ("/a/b/c" -> "c")
28+
QString _archiveRootName(const QString &path)
29+
{
30+
return QFileInfo(path).fileName();
31+
}
32+
33+
/// Factory: choose the correct *writer* class based on the file extension.
34+
std::unique_ptr<KArchive> _makeWriter(const QString &archivePath)
35+
{
36+
const QString lower = archivePath.toLower();
37+
38+
if (lower.endsWith(".zip")) {
39+
return std::make_unique<KZip>(archivePath);
40+
}
41+
42+
// Tar family (plain, gzip, bzip2, xz)
43+
if (lower.endsWith(".tar") || lower.endsWith(".tar.gz") || lower.endsWith(".tgz") ||
44+
lower.endsWith(".tar.bz2")|| lower.endsWith(".tbz2") || lower.endsWith(".tbz") ||
45+
lower.endsWith(".tar.xz") || lower.endsWith(".txz")) {
46+
return std::make_unique<KTar>(archivePath);
47+
}
48+
49+
#if KARCHIVE_HAS_K7ZIP
50+
// NOTE: K7Zip (at least in KF5/KF6) is **read‑only**. We therefore return nullptr
51+
// to tell the caller this archive type cannot be *created*.
52+
if (lower.endsWith(".7z")) {
53+
return nullptr;
54+
}
55+
#endif
56+
57+
return nullptr; // Unknown format
58+
}
59+
60+
/// Factory: choose the correct *reader* class based on the file extension.
61+
std::unique_ptr<KArchive> _makeReader(const QString &archivePath)
62+
{
63+
const QString lower = archivePath.toLower();
64+
65+
if (lower.endsWith(".zip")) {
66+
return std::make_unique<KZip>(archivePath);
67+
}
68+
69+
if (lower.endsWith(".tar") || lower.endsWith(".tar.gz") || lower.endsWith(".tgz") ||
70+
lower.endsWith(".tar.bz2")|| lower.endsWith(".tbz2") || lower.endsWith(".tbz") ||
71+
lower.endsWith(".tar.xz") || lower.endsWith(".txz")) {
72+
return std::make_unique<KTar>(archivePath);
73+
}
74+
75+
#if KARCHIVE_HAS_K7ZIP
76+
if (lower.endsWith(".7z")) {
77+
return std::make_unique<K7Zip>(archivePath);
78+
}
79+
#endif
80+
81+
return nullptr; // Unknown format
82+
}
83+
84+
}
85+
86+
namespace karchive
87+
{
88+
89+
bool createArchive(const QString &directoryPath, const QString &archivePath)
90+
{
91+
// Sanity checks --------------------------------------------------
92+
if (!QDir(directoryPath).exists()) {
93+
qCWarning(karchiveLog) << "Directory does not exist:" << directoryPath;
94+
return false;
95+
}
96+
97+
const QFileInfo info(archivePath);
98+
if (!QDir().mkpath(info.absolutePath())) {
99+
qCWarning(karchiveLog) << "Unable to create directory for archive:" << info.absolutePath();
100+
return false;
101+
}
102+
103+
// Select writer ---------------------------------------------------
104+
auto archive = _makeWriter(archivePath);
105+
if (!archive) {
106+
qCWarning(karchiveLog) << "Unsupported or read‑only archive type for writing:" << archivePath;
107+
return false;
108+
}
109+
110+
if (!archive->open(QIODevice::WriteOnly)) {
111+
qCWarning(karchiveLog) << "Cannot create archive:" << archivePath << "error:" << archive->errorString();
112+
return false;
113+
}
114+
115+
// Add contents ----------------------------------------------------
116+
if (!archive->addLocalDirectory(directoryPath, _archiveRootName(directoryPath))) {
117+
qCWarning(karchiveLog) << "Failed to add directory" << directoryPath << "to archive";
118+
archive->close();
119+
return false;
120+
}
121+
122+
archive->close();
123+
return true;
124+
}
125+
126+
bool extractArchive(const QString &archivePath, const QString &outputDirectoryPath)
127+
{
128+
// Sanity checks --------------------------------------------------
129+
if (!QFile::exists(archivePath)) {
130+
qCWarning(karchiveLog) << "Archive does not exist:" << archivePath;
131+
return false;
132+
}
133+
134+
if (!QDir().mkpath(outputDirectoryPath)) {
135+
qCWarning(karchiveLog) << "Cannot create output directory:" << outputDirectoryPath;
136+
return false;
137+
}
138+
139+
// Select reader ---------------------------------------------------
140+
auto archive = _makeReader(archivePath);
141+
if (!archive) {
142+
qCWarning(karchiveLog) << "Unsupported archive type:" << archivePath;
143+
return false;
144+
}
145+
146+
if (!archive->open(QIODevice::ReadOnly)) {
147+
qCWarning(karchiveLog) << "Cannot open" << archivePath << "error:" << archive->errorString();
148+
return false;
149+
}
150+
151+
// Extract ---------------------------------------------------------
152+
const KArchiveDirectory *root = archive->directory();
153+
if (!root) {
154+
qCWarning(karchiveLog) << "Failed to obtain root directory from archive";
155+
archive->close();
156+
return false;
157+
}
158+
159+
root->copyTo(outputDirectoryPath, true);
160+
archive->close();
161+
return true;
162+
}
163+
164+
} // namespace karchive

src/Utilities/Compression/karchive.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
3+
#include <QtCore/QLoggingCategory>
4+
5+
Q_DECLARE_LOGGING_CATEGORY(karchiveLog)
6+
7+
namespace karchive
8+
{
9+
/// Method to zip files in a given directory
10+
bool createArchive(const QString &directoryPath, const QString &archivePath);
11+
12+
/// Method to unzip files to a given directory
13+
bool extractArchive(const QString &archivePath, const QString &outputDirectoryPath);
14+
}

0 commit comments

Comments
 (0)