Rev 446 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2019 to 2022 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <iostream>
#include <fstream>
#include <assert.h>
#include "expand.h"
#if __cplusplus < 201402L
# error "This module requires at least C++14 standard!"
#else
# if __cplusplus < 201703L
# include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
# warning "Support for C++14 and experimental filesystem will be removed in a future version!"
# else
# include <filesystem>
# ifdef __ANDROID__
namespace fs = std::__fs::filesystem;
# else
namespace fs = std::filesystem;
# endif
# endif
#endif
using namespace std;
void Expand::setFileName (const string &fn)
{
fname.assign(fn);
}
int Expand::unzip()
{
if (fname.empty())
return -1;
string target(fname+".temp");
FILE *source, *dest;
source = fopen(fname.c_str(), "rb");
if (!source)
{
cerr << "Error opening file" << fname << "!" << endl;
return -1;
}
dest = fopen(target.c_str(), "wb");
if (!dest)
{
fclose(source);
cerr << "Error creating the temporary file " << target << "!" << endl;
return -1;
}
int ret;
unsigned have;
z_stream strm;
unsigned char in[CHUNK];
unsigned char out[CHUNK];
// allocate inflate state
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit2(&strm, 32+MAX_WBITS);
if (ret != Z_OK)
{
zerr(ret);
fclose(source);
fclose(dest);
fs::remove(target);
return ret;
}
do
{
strm.avail_in = static_cast<uint>(fread(in, 1, CHUNK, source));
if (ferror(source))
{
(void)inflateEnd(&strm);
cerr << "Error reading from file " << fname << "!" << endl;
fclose(source);
fclose(dest);
fs::remove(target);
return Z_ERRNO;
}
if (strm.avail_in == 0)
break;
strm.next_in = in;
// run inflate() on input until output buffer not full
do
{
strm.avail_out = CHUNK;
strm.next_out = out;
ret = inflate(&strm, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR); // state not clobbered
switch (ret)
{
case Z_NEED_DICT:
ret = Z_DATA_ERROR; // and fall through
// fall through
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
fclose(source);
fclose(dest);
fs::remove(target);
zerr(ret);
return ret;
}
have = CHUNK - strm.avail_out;
if (fwrite(out, 1, have, dest) != have || ferror(dest))
{
(void)inflateEnd(&strm);
cerr << "Error on writing to file " << target << "!" << endl;
fclose(source);
fclose(dest);
fs::remove(target);
return Z_ERRNO;
}
}
while (strm.avail_out == 0);
// done when inflate() says it's done
}
while (ret != Z_STREAM_END);
// clean up and return
(void)inflateEnd(&strm);
fclose(source);
fclose(dest);
fs::remove(fname);
fs::rename(target, fname);
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
void Expand::zerr(int ret)
{
switch (ret)
{
case Z_ERRNO:
cerr << "Error reading or writing a file!" << endl;
break;
case Z_STREAM_ERROR:
cerr << "invalid compression level" << endl;
break;
case Z_DATA_ERROR:
cerr << "invalid or incomplete deflate data" << endl;
break;
case Z_MEM_ERROR:
cerr << "out of memory" << endl;
break;
case Z_VERSION_ERROR:
cerr << "zlib version mismatch!" << endl;
break;
default:
if (ret != Z_OK)
cerr << "Unknown error " << to_string(ret) << "!" << endl;
}
}