Skip to content

Instantly share code, notes, and snippets.

@clalancette
Last active December 20, 2023 10:26
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save clalancette/bb5069a09c609e2d33c9858fcc6e170e to your computer and use it in GitHub Desktop.
Save clalancette/bb5069a09c609e2d33c9858fcc6e170e to your computer and use it in GitHub Desktop.
libzip example to create archive
// Copyright (c) 2018, Chris Lalancette
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * 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.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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.
#include <iostream>
#include <string>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <zip.h>
static bool is_dir(const std::string& dir)
{
struct stat st;
::stat(dir.c_str(), &st);
return S_ISDIR(st.st_mode);
}
static void walk_directory(const std::string& startdir, const std::string& inputdir, zip_t *zipper)
{
DIR *dp = ::opendir(inputdir.c_str());
if (dp == nullptr) {
throw std::runtime_error("Failed to open input directory: " + std::string(::strerror(errno)));
}
struct dirent *dirp;
while ((dirp = readdir(dp)) != NULL) {
if (dirp->d_name != std::string(".") && dirp->d_name != std::string("..")) {
std::string fullname = inputdir + "/" + dirp->d_name;
if (is_dir(fullname)) {
if (zip_dir_add(zipper, fullname.substr(startdir.length() + 1).c_str(), ZIP_FL_ENC_UTF_8) < 0) {
throw std::runtime_error("Failed to add directory to zip: " + std::string(zip_strerror(zipper)));
}
walk_directory(startdir, fullname, zipper);
} else {
zip_source_t *source = zip_source_file(zipper, fullname.c_str(), 0, 0);
if (source == nullptr) {
throw std::runtime_error("Failed to add file to zip: " + std::string(zip_strerror(zipper)));
}
if (zip_file_add(zipper, fullname.substr(startdir.length() + 1).c_str(), source, ZIP_FL_ENC_UTF_8) < 0) {
zip_source_free(source);
throw std::runtime_error("Failed to add file to zip: " + std::string(zip_strerror(zipper)));
}
}
}
}
::closedir(dp);
}
static void zip_directory(const std::string& inputdir, const std::string& output_filename)
{
int errorp;
zip_t *zipper = zip_open(output_filename.c_str(), ZIP_CREATE | ZIP_EXCL, &errorp);
if (zipper == nullptr) {
zip_error_t ziperror;
zip_error_init_with_code(&ziperror, errorp);
throw std::runtime_error("Failed to open output file " + output_filename + ": " + zip_error_strerror(&ziperror));
}
try {
walk_directory(inputdir, inputdir, zipper);
} catch(...) {
zip_close(zipper);
throw;
}
zip_close(zipper);
}
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr, "Usage: %s <input_dir> <output_file>\n", argv[0]);
return 1;
}
zip_directory(argv[1], argv[2]);
return 0;
}
@AntonioCS
Copy link

Why isn't something like this in the official docs? Or at least on the codes github source page...
Thanks! I will be using but this is a good start!

@pabloariasal
Copy link

Great example! I was also wodering why this isn't part of the official docs, the examples there are just so bad.

@OscarKro
Copy link

You are, a pure hero! You saved my ass big time. Couldn't figure out a way to do it recursively and placeall files in their respective directories. your solution with the substraction of the string is just briljant! My man (or woman)!

@goshante
Copy link

Bad example, why not using std::filesystem? This directory walking looks ugly

@ikelaiah
Copy link

Thanks for the example!

@theVerySharpFlat
Copy link

@clalancette Can you please provide a license for your code that I can use? I would like to give you credit for this major help. Thanks!

@clalancette
Copy link
Author

@clalancette Can you please provide a license for your code that I can use? I would like to give you credit for this major help. Thanks!

I went ahead and added a BSD license header to the code. Thanks for asking before using it!

@shinsuke-makino-vacan
Copy link

For MacOS apps with App Sandbox, you need the write permission for the destination folder not just the destination file. It failed silently on MacOS Ventura when I only had the file permission without the folder permission.

@SirHesti
Copy link

thanks for these snippets

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment