andry81/tacklelib

View on GitHub
src/utility/arc/libarchive/libarchive.cpp

Summary

Maintainability
Test Coverage
#include <src/utility/arc/libarchive/libarchive.hpp>

#if ERROR_IF_EMPTY_PP_DEF(USE_UTILITY_ARC_LIBARCHIVE)

#include <tacklelib/utility/platform.hpp>
#include <tacklelib/utility/debug.hpp>
#include <tacklelib/utility/assert.hpp>
#include <tacklelib/utility/memory.hpp>
#include <tacklelib/utility/locale.hpp>
#include <tacklelib/utility/utility.hpp>

#include <tacklelib/tackle/file_handle.hpp>
#include <tacklelib/tackle/path_string.hpp>

#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
#  include <fmt/format.h>
#endif

#include <libarchive/archive_entry.h>

#include <cstdio>
#include <cstdlib>
#include <stdexcept>

#include <fcntl.h>
#include <sys/stat.h>
#ifdef UTILITY_COMPILER_CXX_MSC
#include <io.h>
#else
#include <unistd.h>
#endif


namespace utility {
namespace arc {
namespace libarchive {

namespace {

    FORCE_INLINE int _archive_write_set_options(archive * a, const std::string & options)
    {
        return archive_write_set_options(a, options.c_str());
    }

    FORCE_INLINE int _archive_write_set_options(archive * a, const std::wstring & options)
    {
        return archive_write_set_options(a, utility::convert_utf16_to_utf8_string(options).c_str());
    }

    FORCE_INLINE int _archive_write_open_filename(archive * a, const std::string & filename)
    {
        return archive_write_open_filename(a, filename.c_str());
    }

    FORCE_INLINE int _archive_write_open_filename(archive * a, const std::wstring & filename)
    {
        return archive_write_open_filename_w(a, filename.c_str());
    }

    void _archive_entry_set_pathname(archive_entry * entry, const std::string & name, utility::tag_string)
    {
        return archive_entry_set_pathname(entry, name.c_str());
    }

    void _archive_entry_set_pathname(archive_entry * entry, const std::string & name_utf8, utility::tag_wstring)
    {
        return archive_entry_set_pathname_utf8(entry, name_utf8.c_str());
    }

    template <class t_elem, class t_traits, class t_alloc, t_elem separator_char>
    FORCE_INLINE void _write_archive(const std::vector<int> & input_filter_ids, int format_code,
        const std::basic_string<t_elem, t_traits, t_alloc> & options,
        const tackle::path_basic_string<t_elem, t_traits, t_alloc, separator_char> & out_file_path,
        const tackle::path_basic_string<t_elem, t_traits, t_alloc, separator_char> & in_dir,
        const std::vector<tackle::path_basic_string<t_elem, t_traits, t_alloc, separator_char> > & in_file_paths,
        size_t read_block_size)
    {
        using native_basic_path_string_t = tackle::native_basic_path_string<t_elem, t_traits, t_alloc>;
        using basic_string_identity_t = utility::basic_string_identity<t_elem, t_traits, t_alloc>;
        using FileHandleT = tackle::FileHandle<t_elem, t_traits, t_alloc>;

        struct archive *a;
        struct archive_entry *entry;
        struct stat st;
        utility::Buffer buf{ read_block_size };
        int len;

        a = archive_write_new();

        for (auto filter_id : input_filter_ids) {
            switch (filter_id) {
            case ARCHIVE_FILTER_NONE:
                break;
            case ARCHIVE_FILTER_GZIP:
                archive_write_add_filter_gzip(a);
                break;
            case ARCHIVE_FILTER_BZIP2:
                archive_write_add_filter_bzip2(a);
                break;
            case ARCHIVE_FILTER_COMPRESS:
                archive_write_add_filter_compress(a);
                break;
            case ARCHIVE_FILTER_LZMA:
                archive_write_add_filter_lzma(a);
                break;
            case ARCHIVE_FILTER_XZ:
                archive_write_add_filter_xz(a);
                break;
            case ARCHIVE_FILTER_UU:
                archive_write_add_filter_uuencode(a);
                break;
            case ARCHIVE_FILTER_LZIP:
                archive_write_add_filter_lzip(a);
                break;
            case ARCHIVE_FILTER_LRZIP:
                archive_write_add_filter_lrzip(a);
                break;
            case ARCHIVE_FILTER_LZOP:
                archive_write_add_filter_lzop(a);
                break;
            case ARCHIVE_FILTER_GRZIP:
                archive_write_add_filter_grzip(a);
                break;
            case ARCHIVE_FILTER_LZ4:
                archive_write_add_filter_lz4(a);
                break;
            case ARCHIVE_FILTER_ZSTD:
                archive_write_add_filter_zstd(a);
                break;

                // not supported
            default:
                DEBUG_BREAK_THROW(true) std::runtime_error(
#if ERROR_IF_EMPTY_PP_DEF(USE_FMT_LIBRARY_FORMAT_INSTEAD_UTILITY_STRING_FORMAT)
                    fmt::format("{:s}({:d}): archive filter does not supported: filter_id={:d}",
                        UTILITY_PP_FUNCSIG, UTILITY_PP_LINE, filter_id)
#else
                    utility::string_format(256, "%s(%d): archive filter does not supported: filter_id=%d",
                        UTILITY_PP_FUNCSIG, UTILITY_PP_LINE, filter_id)
#endif
                );
            }
        }

        if (format_code) {
            archive_write_set_format(a, format_code);
        }

        if (!options.empty()) {
            _archive_write_set_options(a, options);
        }

        _archive_write_open_filename(a, out_file_path);

        native_basic_path_string_t fixed_in_file;
        tackle::native_path_string fixed_in_file_ansi_or_utf8;
        tackle::path_string in_file_path_ansi_or_utf8;

        for (auto in_file_path : in_file_paths) {
            if (in_dir.empty() || utility::is_absolute_path(in_file_path)) {
                fixed_in_file = utility::fix_long_path(in_file_path, true); // must be fixed in case of the windows
                in_file_path_ansi_or_utf8 = fixed_in_file_ansi_or_utf8 = utility::convert_utf16_to_utf8_string(fixed_in_file);
            }
            else {
                fixed_in_file = utility::fix_long_path(in_dir / in_file_path, true); // must be fixed in case of the windows
                in_file_path_ansi_or_utf8 = utility::convert_utf16_to_utf8_string(in_file_path);
                fixed_in_file_ansi_or_utf8 = utility::convert_utf16_to_utf8_string(fixed_in_file);
            }

            stat(fixed_in_file_ansi_or_utf8.c_str(), &st);

            entry = archive_entry_new();

            _archive_entry_set_pathname(entry, in_file_path_ansi_or_utf8, utility::tag_string_by_elem<t_elem>{});
            archive_entry_set_size(entry, st.st_size);
            archive_entry_set_filetype(entry, AE_IFREG);
            archive_entry_set_ctime(entry, st.st_ctime, 0);
            archive_entry_set_atime(entry, st.st_atime, 0);
            archive_entry_set_mtime(entry, st.st_mtime, 0);
            archive_entry_set_uid(entry, st.st_uid);
            archive_entry_set_dev(entry, st.st_dev);
            archive_entry_set_gid(entry, st.st_gid);
            archive_entry_set_ino(entry, st.st_ino);
            archive_entry_set_nlink(entry, st.st_nlink);
            archive_entry_set_rdev(entry, st.st_rdev);
            archive_entry_set_perm(entry, 0644);
            archive_entry_set_mode(entry, st.st_mode);
            archive_write_header(a, entry);

            const FileHandleT in_file_handle =
                utility::open_file(fixed_in_file, UTILITY_LITERAL_STRING("rb", t_elem), utility::SharedAccess_DenyWrite); // should not be opened for writing

            const int in_file_desc = in_file_handle.fileno();
            len = read(in_file_desc, buf.get(), size_t(buf.size())); // buffer size can not be greater than max of size_t type here
            while (len > 0) {
                archive_write_data(a, buf.get(), len);
                len = read(in_file_desc, buf.get(), size_t(buf.size())); // buffer size can not be greater than max of size_t type here
            }

            archive_entry_free(entry);
        }

        archive_write_close(a);
        archive_write_free(a);
    }

}

    void write_archive(const std::vector<int> & input_filter_ids, int format_code,
        const std::string & options, const tackle::path_string & out_file_path,
        const tackle::path_string & in_dir, const std::vector<tackle::path_string> & in_file_paths,
        size_t read_block_size)
    {
        return _write_archive(input_filter_ids, format_code, options, out_file_path, in_dir, in_file_paths, read_block_size);
    }

    void write_archive(const std::vector<int> & input_filter_ids, int format_code,
        const std::wstring & options, const tackle::path_wstring & out_file_path,
        const tackle::path_wstring & in_dir, const std::vector<tackle::path_wstring> & in_file_paths,
        size_t read_block_size)
    {
        return _write_archive(input_filter_ids, format_code, options, out_file_path, in_dir, in_file_paths, read_block_size);
    }

}
}
}

#endif