ArchiveWriter.cc 4.36 KB
/*
 * ArchiveWriter.cc
 *
 *  Created on: 26 sept. 2013
 *      Author: CS
 */

#include "ArchiveWriter.hh"
#include "archive.h"

#include <archive.h>
#include <archive_entry.h>

#include <boost/filesystem.hpp>
#include <boost/scope_exit.hpp>
#include <boost/filesystem/path.hpp>

#include <stdexcept>
#include <fstream>
#include <sstream>

#include <sys/stat.h>

namespace postprocessing {

log4cxx::LoggerPtr ArchiveWriter::_logger = log4cxx::Logger::getLogger(
		"AMDA-Kernel.ArchiveWriter");

ArchiveWriter::ArchiveWriter(const std::string& pArchiveFileName,
		const Formats& pFormat) :
		_open(true), _archive(archive_write_new()), _entry(archive_entry_new()), _archiveFileName(
				pArchiveFileName), _format(pFormat) {
	switch (pFormat) {
	case TAR:
		checkError(archive_write_set_format_gnutar(_archive), true);
		break;
	case ZIP:
		checkError(archive_write_set_format_zip(_archive), true);
		break;
	}

	checkError(archive_write_open_filename(_archive, pArchiveFileName.c_str()),
			true);
}

ArchiveWriter::~ArchiveWriter() {
	close();
}

void ArchiveWriter::checkError(const int pErrCode,
		const bool pCloseBeforeThrow) {
	if (pCloseBeforeThrow && pErrCode == ARCHIVE_FATAL)
		close();
	if (pErrCode != ARCHIVE_OK && pErrCode != ARCHIVE_WARN)
		throw std::runtime_error(archive_error_string(_archive));
}

void ArchiveWriter::addHeader(const std::string& pFilePath,
		const std::string& pEntryName) {
	struct archive* a = archive_read_disk_new();
	BOOST_SCOPE_EXIT(&a) {
			if (a != NULL) {
				archive_read_close(a);
				archive_read_free(a);
			}
		}
	BOOST_SCOPE_EXIT_END

	_entry = archive_entry_clear(_entry);
	archive_entry_set_pathname(_entry, pEntryName.c_str());
	archive_entry_copy_sourcepath(_entry, pFilePath.c_str());
	checkError(archive_read_disk_entry_from_file(a, _entry, -1, 0));
	checkError(archive_write_header(_archive, _entry));
}

void ArchiveWriter::addFinish() {
	archive_write_finish_entry(_archive);
}

void ArchiveWriter::addFile(const std::string& pFilePath,
		const std::string& pEntryName) {
	if (boost::filesystem::exists(pFilePath)) {
		boost::filesystem::file_status file_stat = boost::filesystem::status(
				pFilePath);

		addHeader(pFilePath, pEntryName.empty() ? pFilePath : pEntryName);

		if (file_stat.type() == boost::filesystem::regular_file) {
			std::fstream entry_file(pFilePath.c_str(), std::ios::in);
			char buff[8192];
			while (entry_file.good()) {
				entry_file.read(buff, 8192);
				archive_write_data(_archive, buff,
						static_cast<size_t>(entry_file.gcount()));
			}
			entry_file.close();
		} else
			throw std::runtime_error("Entry file type not yet supported.");

		addFinish();
	} else
		throw std::runtime_error("Entry file not found.");
}

void ArchiveWriter::close() {
	if (_open) {
		if (_archive != NULL) {
			archive_write_close(_archive);
			archive_write_free(_archive);
			LOG4CXX_INFO(_logger, "Written archive " + _archiveFileName);
		}
		if (_entry != NULL)
			archive_entry_free(_entry);

		_open = false;
	}
}

std::string ArchiveWriter::createArchive(const Formats& pFormat,
		const std::string& pExtension, const std::vector<std::string>& pFiles,
		 const std::string& pPrefix, const std::string& pOutDir) {
	if (pFiles.empty()) {
		LOG4CXX_WARN(_logger, "No file to archive or compress.");
		return std::string();
	}
	// calculate out file name (timestamp)
	std::ostringstream archiveFileName;

	if (!pPrefix.empty()) {
		archiveFileName << pPrefix << "_";
	}
	archiveFileName << std::time(0) << pExtension;
	std::string archiveCompletePath;
	// calculate out file path
	if (!pOutDir.empty()) {
		// user path
		archiveCompletePath = pOutDir + "/" + archiveFileName.str();
	} else {
		// default path is first file parent path
		std::string firstFile = pFiles.front();
		if(boost::filesystem::path(firstFile).parent_path().string().empty()){
			archiveCompletePath = archiveFileName.str();

		}
		else {
			archiveCompletePath =
					boost::filesystem::path(firstFile).parent_path().string()
							+ "/" + archiveFileName.str();
		}

	}

	// create archive
	ArchiveWriter writer(archiveCompletePath, pFormat);
	// add each file to the archive
	for (auto file : pFiles) {
		// Remove "./" at the begining of the filename
		if (file.compare (0, 2, "./") == 0)
			file = file.substr(2);

		boost::filesystem::path path(file);
		writer.addFile(file, path.c_str());
	}
	// close the archive
	writer.close();

	return archiveCompletePath;
}

} /* namespace postprocessing */