summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2019-05-24 14:41:43 (GMT)
committerKitware Robot <kwrobot@kitware.com>2019-05-24 14:43:14 (GMT)
commit4ec2b999414495ae58871755307ea17f391f7910 (patch)
tree3d69527e6968df26a444888fbf09c8c8e7e35bb6
parent2b8e5a3ba772d6df6e56a28d8645127008636b03 (diff)
parentc8e217e0a70f8f445e2c593f44e1105c959fb9d7 (diff)
downloadCMake-4ec2b999414495ae58871755307ea17f391f7910.zip
CMake-4ec2b999414495ae58871755307ea17f391f7910.tar.gz
CMake-4ec2b999414495ae58871755307ea17f391f7910.tar.bz2
Merge topic 'selective-tar-extracting-and-listing'
c8e217e0a7 cmake: tar: Allow selective extracting and listing of archives Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !3342
-rw-r--r--Help/manual/cmake.1.rst12
-rw-r--r--Help/release/dev/cmake-e-tar-extract-specific-files.rst8
-rw-r--r--Source/cmSystemTools.cxx59
-rw-r--r--Source/cmSystemTools.h6
-rw-r--r--Source/cmcmd.cxx4
-rw-r--r--Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake3
-rw-r--r--Tests/RunCMake/CommandLineTar/roundtrip.cmake14
-rw-r--r--Tests/RunCMake/CommandLineTar/zip-filtered.cmake28
8 files changed, 121 insertions, 13 deletions
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index df0d4c5..97c65e7 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -528,16 +528,22 @@ Available commands are:
``sleep <number>...``
Sleep for given number of seconds.
-``tar [cxt][vf][zjJ] file.tar [<options>] [--] [<file>...]``
+``tar [cxt][vf][zjJ] file.tar [<options>] [--] [<pathname>...]``
Create or extract a tar or zip archive. Options are:
``c``
Create a new archive containing the specified files.
- If used, the <file> argument is mandatory.
+ If used, the ``<pathname>...`` argument is mandatory.
``x``
Extract to disk from the archive.
+ The ``<pathname>...`` argument could be used to extract only selected files
+ or directories.
+ When extracting selected files or directories, you must provide their exact
+ names including the path, as printed by list (``-t``).
``t``
- List archive contents to stdout.
+ List archive contents.
+ The ``<pathname>...`` argument could be used to list only selected files
+ or directories.
``v``
Produce verbose output.
``z``
diff --git a/Help/release/dev/cmake-e-tar-extract-specific-files.rst b/Help/release/dev/cmake-e-tar-extract-specific-files.rst
new file mode 100644
index 0000000..4ab3eab
--- /dev/null
+++ b/Help/release/dev/cmake-e-tar-extract-specific-files.rst
@@ -0,0 +1,8 @@
+cmake-e-tar-extract-specific-files
+----------------------------------
+
+* The :manual:`cmake(1)` ``-E tar`` tool allow for extract (``-x``) or list
+ (``-t``) only specific files or directories. To select pathnames append
+ a space-separated list of file names or directories.
+ When extracting selected files or directories, you must provide their exact
+ pathname, as printed by list (``-t``)
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 612284a..2453aea 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -1757,7 +1757,9 @@ bool copy_data(struct archive* ar, struct archive* aw)
# endif
}
-bool extract_tar(const char* outFileName, bool verbose, bool extract)
+bool extract_tar(const char* outFileName,
+ const std::vector<std::string>& files, bool verbose,
+ bool extract)
{
cmLocaleRAII localeRAII;
static_cast<void>(localeRAII);
@@ -1766,6 +1768,21 @@ bool extract_tar(const char* outFileName, bool verbose, bool extract)
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
struct archive_entry* entry;
+
+ struct archive* matching = archive_match_new();
+ if (matching == nullptr) {
+ cmSystemTools::Error("Out of memory");
+ return false;
+ }
+
+ for (const auto& filename : files) {
+ if (archive_match_include_pattern(matching, filename.c_str()) !=
+ ARCHIVE_OK) {
+ cmSystemTools::Error("Failed to add to inclusion list: " + filename);
+ return false;
+ }
+ }
+
int r = cm_archive_read_open_file(a, outFileName, 10240);
if (r) {
ArchiveError("Problem with archive_read_open_file(): ", a);
@@ -1782,6 +1799,11 @@ bool extract_tar(const char* outFileName, bool verbose, bool extract)
ArchiveError("Problem with archive_read_next_header(): ", a);
break;
}
+
+ if (archive_match_excluded(matching, entry)) {
+ continue;
+ }
+
if (verbose) {
if (extract) {
cmSystemTools::Stdout("x ");
@@ -1827,6 +1849,27 @@ bool extract_tar(const char* outFileName, bool verbose, bool extract)
}
}
}
+
+ bool error_occured = false;
+ if (matching != nullptr) {
+ const char* p;
+ int ar;
+
+ while ((ar = archive_match_path_unmatched_inclusions_next(matching, &p)) ==
+ ARCHIVE_OK) {
+ cmSystemTools::Error("tar: " + std::string(p) +
+ ": Not found in archive");
+ error_occured = true;
+ }
+ if (error_occured) {
+ return false;
+ }
+ if (ar == ARCHIVE_FATAL) {
+ cmSystemTools::Error("tar: Out of memory");
+ return false;
+ }
+ }
+ archive_match_free(matching);
archive_write_free(ext);
archive_read_close(a);
archive_read_free(a);
@@ -1835,23 +1878,29 @@ bool extract_tar(const char* outFileName, bool verbose, bool extract)
}
#endif
-bool cmSystemTools::ExtractTar(const char* outFileName, bool verbose)
+bool cmSystemTools::ExtractTar(const char* outFileName,
+ const std::vector<std::string>& files,
+ bool verbose)
{
#if defined(CMAKE_BUILD_WITH_CMAKE)
- return extract_tar(outFileName, verbose, true);
+ return extract_tar(outFileName, files, verbose, true);
#else
(void)outFileName;
+ (void)files;
(void)verbose;
return false;
#endif
}
-bool cmSystemTools::ListTar(const char* outFileName, bool verbose)
+bool cmSystemTools::ListTar(const char* outFileName,
+ const std::vector<std::string>& files,
+ bool verbose)
{
#if defined(CMAKE_BUILD_WITH_CMAKE)
- return extract_tar(outFileName, verbose, false);
+ return extract_tar(outFileName, files, verbose, false);
#else
(void)outFileName;
+ (void)files;
(void)verbose;
return false;
#endif
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 80c6ee3..09a4d13 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -450,13 +450,15 @@ public:
TarCompressNone
};
- static bool ListTar(const char* outFileName, bool verbose);
+ static bool ListTar(const char* outFileName,
+ const std::vector<std::string>& files, bool verbose);
static bool CreateTar(const char* outFileName,
const std::vector<std::string>& files,
cmTarCompression compressType, bool verbose,
std::string const& mtime = std::string(),
std::string const& format = std::string());
- static bool ExtractTar(const char* inFileName, bool verbose);
+ static bool ExtractTar(const char* inFileName,
+ const std::vector<std::string>& files, bool verbose);
// This should be called first thing in main
// it will keep child processes from inheriting the
// stdin and stdout of this process. This is important
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index ad34107..4ddfddd 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -1127,7 +1127,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
return 1;
}
if (action == cmSystemTools::TarActionList) {
- if (!cmSystemTools::ListTar(outFile.c_str(), verbose)) {
+ if (!cmSystemTools::ListTar(outFile.c_str(), files, verbose)) {
cmSystemTools::Error("Problem listing tar: " + outFile);
return 1;
}
@@ -1142,7 +1142,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
return 1;
}
} else if (action == cmSystemTools::TarActionExtract) {
- if (!cmSystemTools::ExtractTar(outFile.c_str(), verbose)) {
+ if (!cmSystemTools::ExtractTar(outFile.c_str(), files, verbose)) {
cmSystemTools::Error("Problem extracting tar: " + outFile);
return 1;
}
diff --git a/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake b/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake
index 5deb110..4d1b396 100644
--- a/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake
@@ -30,3 +30,6 @@ run_cmake(pax-xz)
run_cmake(paxr)
run_cmake(paxr-bz2)
run_cmake(zip)
+
+# Extracting only selected files or directories
+run_cmake(zip-filtered)
diff --git a/Tests/RunCMake/CommandLineTar/roundtrip.cmake b/Tests/RunCMake/CommandLineTar/roundtrip.cmake
index dc1c885..fa63d12 100644
--- a/Tests/RunCMake/CommandLineTar/roundtrip.cmake
+++ b/Tests/RunCMake/CommandLineTar/roundtrip.cmake
@@ -47,7 +47,11 @@ file(REMOVE_RECURSE ${FULL_DECOMPRESS_DIR})
file(MAKE_DIRECTORY ${FULL_DECOMPRESS_DIR})
run_tar(${CMAKE_CURRENT_BINARY_DIR} ${COMPRESSION_FLAGS} ${FULL_OUTPUT_NAME} ${COMPRESSION_OPTIONS} ${COMPRESS_DIR})
-run_tar(${FULL_DECOMPRESS_DIR} ${DECOMPRESSION_FLAGS} ${FULL_OUTPUT_NAME} ${DECOMPRESSION_OPTIONS})
+run_tar(${FULL_DECOMPRESS_DIR} ${DECOMPRESSION_FLAGS} ${FULL_OUTPUT_NAME} ${DECOMPRESSION_OPTIONS} -- ${DECOMPRESSION_PATHNAMES})
+
+if(CUSTOM_CHECK_FILES)
+ set(CHECK_FILES ${CUSTOM_CHECK_FILES})
+endif()
foreach(file ${CHECK_FILES})
set(input ${FULL_COMPRESS_DIR}/${file})
@@ -69,6 +73,14 @@ foreach(file ${CHECK_FILES})
endif()
endforeach()
+foreach(file ${NOT_EXISTING_FILES_CHECK})
+ set(output ${FULL_DECOMPRESS_DIR}/${COMPRESS_DIR}/${file})
+
+ if(EXISTS ${output})
+ message(SEND_ERROR "File ${output} exists but it shouldn't")
+ endif()
+endforeach()
+
function(check_magic EXPECTED)
file(READ ${FULL_OUTPUT_NAME} ACTUAL
${ARGN}
diff --git a/Tests/RunCMake/CommandLineTar/zip-filtered.cmake b/Tests/RunCMake/CommandLineTar/zip-filtered.cmake
new file mode 100644
index 0000000..c13210e
--- /dev/null
+++ b/Tests/RunCMake/CommandLineTar/zip-filtered.cmake
@@ -0,0 +1,28 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --format=zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+set(LIST_ARCHIVE TRUE)
+set(DECOMPRESSION_PATHNAMES
+ compress_dir/f1.txt # Decompress only file
+ compress_dir/d1 # and whole directory
+)
+
+set(CUSTOM_CHECK_FILES
+ "f1.txt"
+ "d1/f1.txt"
+)
+
+# This files shouldn't exists
+set(NOT_EXISTING_FILES_CHECK
+ "d 2/f1.txt"
+ "d + 3/f1.txt"
+ "d_4/f1.txt"
+ "d-4/f1.txt"
+)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)