// // aegis - project change supervisor // Copyright (C) 2006-2008, 2012, 2014 Peter Miller // Copyright (C) 2007, 2009 Walter Franzini // // 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, see // . // #include #include #include #include #include void input_bunzip2::bzlib_fatal_error(int err) { if (err >= 0) return; sub_context_ty sc; sc.var_set_charstar("ERRNO", BZ2_strerror(err)); sc.var_override("ERRNO"); sc.var_set_string("File_Name", deeper->name()); sc.fatal_intl(i18n("gunzip $filename: $errno")); } input_bunzip2::~input_bunzip2() { // // Ask bzlib to free any stream resources it may be using. // int err = BZ2_bzDecompressEnd(&stream); if (err < 0) bzlib_fatal_error(err); // // Return unused input to the deeper stream. // if (stream.avail_in) { deeper->unread(stream.next_in, stream.avail_in); stream.next_in = 0; stream.avail_in = 0; } delete [] buf; buf = 0; } input_bunzip2::input_bunzip2(const input::pointer &arg) : deeper(arg), end_of_file(false), pos(0), buf(new char [BUFFER_SIZE]) { stream.bzalloc = 0; stream.bzfree = 0; stream.opaque = 0; int verbosity = 0; // // If we are running with limited resources we use a slower // algorithm that use less memory. // int small = 0; struct rlimit memory_limit; getrlimit(RLIMIT_AS, &memory_limit); if (memory_limit.rlim_cur != RLIM_INFINITY) small = 1; int err = BZ2_bzDecompressInit(&stream, verbosity, small); if (err != BZ_OK) bzlib_fatal_error(err); stream.avail_in = 0; stream.next_in = 0; } input_bunzip2::pointer input_bunzip2::create(const input::pointer &a_deeper) { return pointer(new input_bunzip2(a_deeper)); } ssize_t input_bunzip2::read_inner(void *data, size_t len) { if (end_of_file) return 0; if (!data) { bzlib_fatal_error(BZ_PARAM_ERROR); //NOTREACHED } if (len == 0) return 0; stream.avail_out = len; stream.next_out = (char *)data; for (;;) { if (stream.avail_in == 0) { long n = deeper->read(buf, BUFFER_SIZE); stream.avail_in = n; stream.next_in = buf; } int err = BZ2_bzDecompress(&stream); switch (err) { case BZ_OK: if ( deeper->at_end() && stream.avail_in == 0 && stream.avail_out > 0 ) { bzlib_fatal_error(BZ_UNEXPECTED_EOF); //NOTREACHED } break; case BZ_STREAM_END: { end_of_file = true; int nbytes = (len - stream.avail_out); pos += nbytes; return nbytes; } default: bzlib_fatal_error(err); //NOTREACHED } if (stream.avail_out == 0) { pos += len; return len; } } } off_t input_bunzip2::ftell_inner(void) { return pos; } nstring input_bunzip2::name(void) { if (filename.empty()) { nstring s = deeper->name(); if (s.ends_with_nocase(".bz")) filename = nstring(s.c_str(), s.size() - 3); else if (s.ends_with_nocase(".bz2")) filename = nstring(s.c_str(), s.size() - 4); else if (s.ends_with_nocase(".tbz")) { filename = nstring::format("%.*s.tar", (int)s.size() - 4, s.c_str()); } else filename = s; } return filename; } off_t input_bunzip2::length(void) { // // We have no idea how long the decompressed stream will be. // return -1; } bool input_bunzip2::candidate(const input::pointer &ip) { // // Check for the magic number. // unsigned char magic[4]; ssize_t n = ip->read(magic, 4); ip->unread(magic, n); return ( n == 4 && magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' && magic[3] >= '1' && magic[3] <= '9' ); } void input_bunzip2::keepalive(void) { deeper->keepalive(); } input_bunzip2::pointer input_bunzip2::create_if_candidate(const input::pointer &ip) { if (!input_bunzip2::candidate(ip)) { // // If it is not actually a compressed file, // simply return the deeper file. This will // give much better performance. // return ip; } return create(ip); } bool input_bunzip2::is_remote(void) const { return deeper->is_remote(); } // vim: set ts=8 sw=4 et :