diff --git a/CMakeLists.txt b/CMakeLists.txt index c38ccfc..64f095d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(CompileCheck) include(VersionString) # TODO use this +include(CheckSymbolExists) # Force re-checking libraries if the compiler or compiler flags change. if((NOT LAST_CMAKE_CXX_FLAGS STREQUAL CMAKE_CXX_FLAGS) @@ -34,6 +35,7 @@ include_directories(SYSTEM "${LZMA_INCLUDE_DIR}") # TODO debugging-only add_cxxflag("-ggdb") add_cxxflag("-O3") +add_definitions(-D_DEBUG) # TODO should probably be supplied by the user add_cxxflag("-march=native") @@ -70,6 +72,9 @@ if(UNITY_BUILD) add_cxxflag("-fwhole-program") endif() +check_symbol_exists(isatty "unistd.h" HAVE_ISATTY) +check_symbol_exists(ioctl "sys/ioctl.h" HAVE_IOCTL) + set(INNOEXTRACT_SOURCES src/crypto/Adler-32.cpp @@ -106,15 +111,18 @@ set(INNOEXTRACT_SOURCES src/stream/LzmaFilter.cpp src/stream/SliceReader.cpp + src/util/console.cpp src/util/load.cpp - src/util/color.cpp + src/util/log.cpp src/InnoExtract.cpp ) file(GLOB_RECURSE ALL_INCLUDES "${CMAKE_SOURCE_DIR}/src/*.hpp") -include_directories(src) +include_directories(src ${CMAKE_CURRENT_BINARY_DIR}) + +configure_file("src/configure.hpp.in" "configure.hpp") add_executable(innoextract ${INNOEXTRACT_SOURCES} ${ALL_INCLUDES}) target_link_libraries(innoextract ${LIBRARIES}) diff --git a/src/InnoExtract.cpp b/src/InnoExtract.cpp index d3a8119..2a3f972 100644 --- a/src/InnoExtract.cpp +++ b/src/InnoExtract.cpp @@ -11,8 +11,6 @@ #include #include -#include - #include #include #include @@ -56,75 +54,17 @@ #include "stream/ChecksumFilter.hpp" #include "stream/InstructionFilter.hpp" -#include "util/color.hpp" +#include "util/console.hpp" #include "util/load.hpp" +#include "util/log.hpp" #include "util/output.hpp" -class progress { - -public: - - static void show(float value, const std::string & label) { - - struct winsize w; - ioctl(0, TIOCGWINSZ, &w); - - clear(); - - std::ios_base::fmtflags flags = std::cout.flags(); - - size_t progress_length = w.ws_col - label.length() - 6 - 2 - 2 - 1; - - if(progress_length > 10) { - - size_t progress = size_t(ceil(float(progress_length) * value)); - - std::cout << '['; - for(size_t i = 0; i < progress; i++) { - std::cout << '='; - } - std::cout << '>'; - for(size_t i = progress; i < progress_length; i++) { - std::cout << ' '; - } - std::cout << ']'; - - } - - std::cout << std::right << std::fixed << std::setprecision(1) << std::setfill(' ') - << std::setw(5) << (value * 100) << "% " << label; - std::cout.flush(); - - std::cout.flags(flags); - - } - - static void clear() { - std::cout << "\33[2K\r"; - } - -}; - using std::cout; using std::string; using std::endl; using std::setw; using std::setfill; -template -void discard(T & is, uint64_t bytes) { - - std::cout << "discarding " << print_bytes(bytes) << std::endl; - - char buf[1024]; - while(bytes) { - std::streamsize n = std::streamsize(std::min(bytes, ARRAY_SIZE(buf))); - is.read(buf, n); - bytes -= uint64_t(n); - } - -} - namespace io = boost::iostreams; namespace fs = boost::filesystem; @@ -274,13 +214,17 @@ static void readWizardImageAndDecompressor(std::istream & is, const InnoVersion } if(is.fail()) { - LogError << "error reading misc setup data"; + log_error << "error reading misc setup data"; } } int main(int argc, char * argv[]) { + color::init(); + + logger::debug = true; + if(argc <= 1) { std::cout << "usage: innoextract " << endl; return 1; @@ -289,7 +233,7 @@ int main(int argc, char * argv[]) { std::ifstream ifs(argv[1], std::ios_base::in | std::ios_base::binary | std::ios_base::ate); if(!ifs.is_open()) { - LogError << "error opening file"; + log_error << "error opening file"; return 1; } @@ -319,12 +263,12 @@ int main(int argc, char * argv[]) { InnoVersion version; version.load(ifs); if(ifs.fail()) { - LogError << "error reading setup data version!"; + log_error << "error reading setup data version!"; return 1; } if(!version.known) { - LogError << "unknown version!"; + log_error << "unknown version!"; return 1; // TODO } @@ -332,7 +276,7 @@ int main(int argc, char * argv[]) { boost::shared_ptr is(BlockReader::get(ifs, version)); if(!is) { - LogError << "error reading block"; + log_error << "error reading block"; return 1; } @@ -341,7 +285,7 @@ int main(int argc, char * argv[]) { SetupHeader header; header.load(*is, version); if(is->fail()) { - LogError << "error reading setup data header!"; + log_error << "error reading setup data header!"; return 1; } @@ -459,7 +403,7 @@ int main(int argc, char * argv[]) { LanguageEntry & entry = languages[i]; entry.load(*is, version); if(is->fail()) { - LogError << "error reading language entry #" << i; + log_error << "error reading language entry #" << i; } cout << " - " << quoted(entry.name) << ':' << endl; @@ -498,11 +442,11 @@ int main(int argc, char * argv[]) { MessageEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading custom message entry #" << i; + log_error << "error reading custom message entry #" << i; } if(entry.language >= 0 ? size_t(entry.language) >= languages.size() : entry.language != -1) { - LogWarning << "unexpected language index: " << entry.language; + log_warning << "unexpected language index: " << entry.language; } uint32_t codepage; @@ -534,7 +478,7 @@ int main(int argc, char * argv[]) { PermissionEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading permission entry #" << i; + log_error << "error reading permission entry #" << i; } cout << " - " << entry.permissions.length() << " bytes"; @@ -549,7 +493,7 @@ int main(int argc, char * argv[]) { SetupTypeEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading type entry #" << i; + log_error << "error reading type entry #" << i; } cout << " - " << quoted(entry.name) << ':' << endl; @@ -574,7 +518,7 @@ int main(int argc, char * argv[]) { SetupComponentEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading component entry #" << i; + log_error << "error reading component entry #" << i; } cout << " - " << quoted(entry.name) << ':' << endl; @@ -603,7 +547,7 @@ int main(int argc, char * argv[]) { SetupTaskEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading task entry #" << i; + log_error << "error reading task entry #" << i; } cout << " - " << quoted(entry.name) << ':' << endl; @@ -633,7 +577,7 @@ int main(int argc, char * argv[]) { DirectoryEntry & entry = directories[i]; entry.load(*is, version); if(is->fail()) { - LogError << "error reading directory entry #" << i; + log_error << "error reading directory entry #" << i; } cout << " - " << quoted(entry.name) << ':' << endl; @@ -663,7 +607,7 @@ int main(int argc, char * argv[]) { FileEntry & entry = files[i]; entry.load(*is, version); if(is->fail()) { - LogError << "error reading file entry #" << i; + log_error << "error reading file entry #" << i; } if(entry.destination.empty()) { @@ -701,7 +645,7 @@ int main(int argc, char * argv[]) { IconEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading icon entry #" << i; + log_error << "error reading icon entry #" << i; } cout << " - " << quoted(entry.name) << " -> " << quoted(entry.filename) << endl; @@ -731,7 +675,7 @@ int main(int argc, char * argv[]) { IniEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading ini entry #" << i; + log_error << "error reading ini entry #" << i; } cout << " - in " << quoted(entry.inifile); @@ -752,7 +696,7 @@ int main(int argc, char * argv[]) { RegistryEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading registry entry #" << i; + log_error << "error reading registry entry #" << i; } cout << " - "; @@ -793,7 +737,7 @@ int main(int argc, char * argv[]) { DeleteEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading install delete entry #" << i; + log_error << "error reading install delete entry #" << i; } cout << " - " << quoted(entry.name) @@ -811,7 +755,7 @@ int main(int argc, char * argv[]) { DeleteEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading uninstall delete entry #" << i; + log_error << "error reading uninstall delete entry #" << i; } cout << " - " << quoted(entry.name) @@ -829,7 +773,7 @@ int main(int argc, char * argv[]) { RunEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading install run entry #" << i; + log_error << "error reading install run entry #" << i; } print(cout, entry, header); @@ -844,7 +788,7 @@ int main(int argc, char * argv[]) { RunEntry entry; entry.load(*is, version); if(is->fail()) { - LogError << "error reading uninstall run entry #" << i; + log_error << "error reading uninstall run entry #" << i; } print(cout, entry, header); @@ -859,7 +803,7 @@ int main(int argc, char * argv[]) { is->exceptions(std::ios_base::goodbit); char dummy; if(!is->get(dummy).eof()) { - LogWarning << "expected end of stream"; + log_warning << "expected end of stream"; } } @@ -867,7 +811,7 @@ int main(int argc, char * argv[]) { is.reset(BlockReader::get(ifs, version)); if(!is) { - LogError << "error reading block"; + log_error << "error reading block"; return 1; } @@ -883,7 +827,7 @@ int main(int argc, char * argv[]) { FileLocationEntry & entry = locations[i]; entry.load(*is, version); if(is->fail()) { - LogError << "error reading file location entry #" << i; + log_error << "error reading file location entry #" << i; } cout << " - " << "File location #" << i << ':' << endl; @@ -927,7 +871,7 @@ int main(int argc, char * argv[]) { is->exceptions(std::ios_base::goodbit); char dummy; if(!is->get(dummy).eof()) { - LogWarning << "expected end of stream"; + log_warning << "expected end of stream"; } } @@ -975,7 +919,7 @@ int main(int argc, char * argv[]) { std::sort(chunk.second.begin(), chunk.second.end(), FileLocationComparer(locations)); if(!slice_reader->seek(chunk.first.firstSlice, chunk.first.chunkOffset)) { - LogError << "error seeking" << std::endl; + log_error << "error seeking"; return 1; } @@ -983,7 +927,7 @@ int main(int argc, char * argv[]) { char magic[4]; if(slice_reader->read(magic, 4) != 4 || memcmp(magic, chunkId, 4)) { - LogError << "bad chunk id"; + log_error << "bad chunk id"; return 1; } @@ -997,7 +941,7 @@ int main(int argc, char * argv[]) { case SetupHeader::BZip2: chunk_source.push(io::bzip2_decompressor(), 8192); break; case SetupHeader::LZMA1: chunk_source.push(inno_lzma1_decompressor(), 8192); break; case SetupHeader::LZMA2: chunk_source.push(inno_lzma2_decompressor(), 8192); break; - default: LogError << "unknown compression"; + default: log_error << "unknown compression"; } } @@ -1010,11 +954,12 @@ int main(int argc, char * argv[]) { const FileLocationEntry & location = locations[location_i]; if(location.fileOffset < offset) { - LogError << "bad offset"; + log_error << "bad offset"; return 1; } if(location.fileOffset > offset) { + std::cout << "discarding " << print_bytes(location.fileOffset - offset) << std::endl; discard(chunk_source, location.fileOffset - offset); } offset = location.fileOffset + location.fileSize; @@ -1115,30 +1060,30 @@ int main(int argc, char * argv[]) { Checksum actual; hasher.finalize(actual); if(actual != location.checksum) { - LogWarning << "checksum mismatch:"; - LogWarning << "actual: " << actual; - LogWarning << "expected: " << location.checksum; + log_warning << "checksum mismatch:"; + log_warning << "actual: " << actual; + log_warning << "expected: " << location.checksum; } } } } catch(io::bzip2_error e) { - LogError << e.what() << ": " << + log_error << e.what() << ": " << e.error(); } std::cout << color::green << "Done" << color::reset << std::dec; - if(total_errors || total_warnings) { + if(logger::total_errors || logger::total_warnings) { std::cout << " with "; - if(total_errors) { - std::cout << color::red << total_errors << " errors" << color::reset; + if(logger::total_errors) { + std::cout << color::red << logger::total_errors << " errors" << color::reset; } - if(total_errors && total_warnings) { + if(logger::total_errors && logger::total_warnings) { std::cout << " and "; } - if(total_warnings) { - std::cout << color::yellow << total_warnings << " warnings" << color::reset; + if(logger::total_warnings) { + std::cout << color::yellow << logger::total_warnings << " warnings" << color::reset; } } diff --git a/src/configure.hpp.in b/src/configure.hpp.in new file mode 100644 index 0000000..eae275d --- /dev/null +++ b/src/configure.hpp.in @@ -0,0 +1,8 @@ + +#ifndef INNOEXTRACT_CONFIGURE_HPP +#define INNOEXTRACT_CONFIGURE_HPP + +#cmakedefine HAVE_ISATTY +#cmakedefine HAVE_IOCTL + +#endif // INNOEXTRACT_CONFIGURE_HPP diff --git a/src/loader/SetupLoader.cpp b/src/loader/SetupLoader.cpp index d865a39..fe80975 100644 --- a/src/loader/SetupLoader.cpp +++ b/src/loader/SetupLoader.cpp @@ -9,8 +9,8 @@ #include "crypto/CRC32.hpp" #include "loader/ExeReader.hpp" #include "setup/Version.hpp" -#include "util/color.hpp" #include "util/load.hpp" +#include "util/log.hpp" namespace { @@ -147,7 +147,7 @@ bool SetupLoader::loadOffsetsAt(std::istream & is, uint32_t pos) { return false; } if(checksum.finalize() != expected) { - LogError << "[loader] CRC32 mismatch"; + log_error << "[loader] CRC32 mismatch"; return false; } } diff --git a/src/setup/FileEntry.cpp b/src/setup/FileEntry.cpp index b64b78d..30902ff 100644 --- a/src/setup/FileEntry.cpp +++ b/src/setup/FileEntry.cpp @@ -2,6 +2,7 @@ #include "setup/FileEntry.hpp" #include "util/load.hpp" +#include "util/log.hpp" #include "util/storedenum.hpp" namespace { @@ -161,7 +162,7 @@ void FileEntry::load(std::istream & is, const InnoVersion & version) { // TODO find out where this byte comes from int byte = is.get(); if(byte) { - LogWarning << "unknown byte: " << byte; + log_warning << "[file] unknown byte: " << byte; } } diff --git a/src/setup/FileLocationEntry.cpp b/src/setup/FileLocationEntry.cpp index f47bebe..304beca 100644 --- a/src/setup/FileLocationEntry.cpp +++ b/src/setup/FileLocationEntry.cpp @@ -1,8 +1,8 @@ #include "setup/FileLocationEntry.hpp" -#include "util/color.hpp" #include "util/load.hpp" +#include "util/log.hpp" #include "util/storedenum.hpp" void FileLocationEntry::load(std::istream & is, const InnoVersion & version) { @@ -11,9 +11,11 @@ void FileLocationEntry::load(std::istream & is, const InnoVersion & version) { lastSlice = load_number(is, version.bits); if(version < INNO_VERSION(4, 0, 0)) { if(firstSlice < 1 || lastSlice < 1) { - LogWarning << "unexpected disk number: " << firstSlice << " / " << lastSlice; + log_warning << "[file location] unexpected disk number: " << firstSlice << " / " + << lastSlice; + } else { + firstSlice--, lastSlice--; } - firstSlice--, lastSlice--; } chunkOffset = load_number(is); @@ -58,7 +60,7 @@ void FileLocationEntry::load(std::istream & is, const InnoVersion & version) { static const int64_t FILETIME_OFFSET = 0x19DB1DED53E8000l; if(filetime < FILETIME_OFFSET) { - LogWarning << "unexpected filetime: " << filetime; + log_warning << "[file location] unexpected filetime: " << filetime; } filetime -= FILETIME_OFFSET; diff --git a/src/setup/SetupHeader.cpp b/src/setup/SetupHeader.cpp index 27468a9..3931a40 100644 --- a/src/setup/SetupHeader.cpp +++ b/src/setup/SetupHeader.cpp @@ -422,7 +422,6 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { flags.add(Password); flags.add(AllowRootDirectory); flags.add(DisableFinishedPage); - if(version.bits != 16) { if(version < INNO_VERSION(3, 0, 4)) { flags.add(AdminPrivilegesRequired); @@ -435,7 +434,6 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { } flags.add(ChangesAssociations); } - if(version >= INNO_VERSION(1, 3, 21)) { if(version < INNO_VERSION(5, 3, 8)) { flags.add(CreateUninstallRegKey); @@ -445,7 +443,6 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { flags.add(UsePreviousGroup); flags.add(UpdateUninstallLogAppName); } - if(version >= INNO_VERSION(2, 0, 0)) { flags.add(UsePreviousSetupType); flags.add(DisableReadyMemo); @@ -455,37 +452,29 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { flags.add(UsePreviousTasks); flags.add(DisableReadyPage); } - if(version >= INNO_VERSION(2, 0, 7)) { flags.add(AlwaysShowDirOnReadyPage); flags.add(AlwaysShowGroupOnReadyPage); } - if(version >= INNO_VERSION(2, 0, 17) && version < INNO_VERSION(4, 1, 5)) { flags.add(BzipUsed); } - if(version >= INNO_VERSION(2, 0, 18)) { flags.add(AllowUNCPath); } - if(version >= INNO_VERSION(3, 0, 0)) { flags.add(UserInfoPage); flags.add(UsePreviousUserInfo); } - if(version >= INNO_VERSION(3, 0, 1)) { flags.add(UninstallRestartComputer); } - if(version >= INNO_VERSION(3, 0, 3)) { flags.add(RestartIfNeededByRun); } - if(version >= INNO_VERSION_EXT(3, 0, 6, 1)) { flags.add(ShowTasksTreeLines); } - if(version >= INNO_VERSION(4, 0, 0) && version < INNO_VERSION(4, 0, 10)) { flags.add(ShowLanguageDialog); } @@ -493,46 +482,36 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { if(version >= INNO_VERSION(4, 0, 1) && version < INNO_VERSION(4, 0, 10)) { flags.add(DetectLanguageUsingLocale); } - if(version >= INNO_VERSION(4, 0, 9)) { flags.add(AllowCancelDuringInstall); } else { options |= AllowCancelDuringInstall; } - if(version >= INNO_VERSION(4, 1, 3)) { flags.add(WizardImageStretch); } - if(version >= INNO_VERSION(4, 1, 8)) { flags.add(AppendDefaultDirName); flags.add(AppendDefaultGroupName); } - if(version >= INNO_VERSION(4, 2, 2)) { flags.add(EncryptionUsed); } - if(version >= INNO_VERSION(5, 0, 4)) { flags.add(ChangesEnvironment); } - if(version >= INNO_VERSION(5, 1, 7) && !version.unicode) { flags.add(ShowUndisplayableLanguages); } - if(version >= INNO_VERSION(5, 1, 13)) { flags.add(SetupLogging); } - if(version >= INNO_VERSION(5, 2, 1)) { flags.add(SignedUninstaller); } - if(version >= INNO_VERSION(5, 3, 8)) { flags.add(UsePreviousLanguage); } - if(version >= INNO_VERSION(5, 3, 9)) { flags.add(DisableWelcomePage); } diff --git a/src/stream/BlockReader.cpp b/src/stream/BlockReader.cpp index d12a217..c7e2ad9 100644 --- a/src/stream/BlockReader.cpp +++ b/src/stream/BlockReader.cpp @@ -14,11 +14,9 @@ #include "stream/LzmaFilter.hpp" #include "util/enum.hpp" #include "util/load.hpp" +#include "util/log.hpp" #include "util/util.hpp" -using std::cout; -using std::endl; - namespace io = boost::iostreams; namespace { @@ -65,11 +63,11 @@ std::istream * BlockReader::get(std::istream & base, const InnoVersion & version } if(actualCrc.finalize() != expectedCrc) { - LogError << "block CRC32 mismatch"; + log_error << "block CRC32 mismatch"; return NULL; } - cout << "[block] size: " << storedSize << " compression: " << compression << endl; + debug("[block] size: " << storedSize << " compression: " << compression); io::filtering_istream * fis; fis = new io::filtering_istream; diff --git a/src/stream/SliceReader.cpp b/src/stream/SliceReader.cpp index 57a2304..7774c84 100644 --- a/src/stream/SliceReader.cpp +++ b/src/stream/SliceReader.cpp @@ -6,8 +6,9 @@ #include #include -#include "util/color.hpp" +#include "util/console.hpp" #include "util/load.hpp" +#include "util/log.hpp" #include "util/util.hpp" using std::string; @@ -45,7 +46,7 @@ bool SliceReader::seek(size_t slice) { } if(dataOffset != 0) { - LogError << "[slice] cannot change slices in single-file setup"; + log_error << "[slice] cannot change slices in single-file setup"; return false; } @@ -54,7 +55,7 @@ bool SliceReader::seek(size_t slice) { bool SliceReader::openFile(const std::string & file) { - std::cout << color::cyan << "\33[2K\r[slice] opening " << file << color::reset << std::endl; + log_info << "[slice] opening " << file; ifs.close(); @@ -69,7 +70,7 @@ bool SliceReader::openFile(const std::string & file) { char magic[8]; if(ifs.read(magic, 8).fail()) { ifs.close(); - LogError << "[slice] error reading magic number"; + log_error << "[slice] error reading magic number"; return false; } bool found = false; @@ -80,14 +81,14 @@ bool SliceReader::openFile(const std::string & file) { } } if(!found) { - LogError << "[slice] bad magic number"; + log_error << "[slice] bad magic number"; ifs.close(); return false; } sliceSize = load_number(ifs); if(ifs.fail() || std::streampos(sliceSize) > fileSize) { - LogError << "[slice] bad slice size: " << sliceSize << " > " << fileSize; + log_error << "[slice] bad slice size: " << sliceSize << " > " << fileSize; ifs.close(); return false; } @@ -110,7 +111,7 @@ bool SliceReader::open(size_t slice, const std::string & file) { ifs.close(); if(slicesPerDisk == 0) { - LogError << "[slice] slices per disk must not be zero"; + log_error << "[slice] slices per disk must not be zero"; return false; } diff --git a/src/util/color.cpp b/src/util/color.cpp deleted file mode 100644 index d68fd14..0000000 --- a/src/util/color.cpp +++ /dev/null @@ -1,11 +0,0 @@ - -#include "util/color.hpp" - -namespace color { - -shell_command current = color::reset; - -} - -size_t total_warnings = 0; -size_t total_errors = 0; diff --git a/src/util/color.hpp b/src/util/color.hpp deleted file mode 100644 index ba03932..0000000 --- a/src/util/color.hpp +++ /dev/null @@ -1,69 +0,0 @@ - -#ifndef INNOEXTRACT_UTIL_COLOR_HPP -#define INNOEXTRACT_UTIL_COLOR_HPP - -#include -#include - -namespace color { - - struct shell_command { - - int c0; - int c1; - - }; - - const shell_command reset = { 0, 0 }; - - const shell_command black = { 1, 30 }; - const shell_command red = { 1, 31 }; - const shell_command green = { 1, 32 }; - const shell_command yellow = { 1, 33 }; - const shell_command blue = { 1, 34 }; - const shell_command magenta = { 1, 35 }; - const shell_command cyan = { 1, 36 }; - const shell_command white = { 1, 37 }; - - const shell_command dim_black = { 0, 30 }; - const shell_command dim_red = { 0, 31 }; - const shell_command dim_green = { 0, 32 }; - const shell_command dim_yellow = { 0, 33 }; - const shell_command dim_blue = { 0, 34 }; - const shell_command dim_magenta = { 0, 35 }; - const shell_command dim_cyan = { 0, 36 }; - const shell_command dim_white = { 0, 37 }; - - extern shell_command current; - -} // namespace color - -inline std::ostream & operator<<(std::ostream & os, const color::shell_command command) { - color::current = command; - std::ios_base::fmtflags old = os.flags(); - os << "\x1B[" << std::dec << command.c0 << ';' << command.c1 << 'm'; - os.setf(old, std::ios_base::basefield); - return os; -} - -struct error_base { - - color::shell_command previous; - - inline error_base(color::shell_command type) : previous(color::current) { - std::cerr << type << "!! "; - } - - inline ~error_base() { - std::cerr << previous << std::endl; - } - -}; - -extern size_t total_warnings; -extern size_t total_errors; - -#define LogError (error_base(color::red), total_errors++, std::cerr) -#define LogWarning (error_base(color::yellow), total_warnings++, std::cerr) - -#endif // INNOEXTRACT_UTIL_COLOR_HPP diff --git a/src/util/console.cpp b/src/util/console.cpp new file mode 100644 index 0000000..14d9460 --- /dev/null +++ b/src/util/console.cpp @@ -0,0 +1,138 @@ + +#include "util/console.hpp" + +#include +#include + +#include "configure.hpp" + +#ifdef HAVE_ISATTY +#include +#endif + +#ifdef HAVE_IOCTL +#include +#endif + +static bool native_console = true; + +namespace color { + +shell_command reset = { "\x1b[0m" }; + +shell_command black = { "\x1b[1;30m" }; +shell_command red = { "\x1b[1;31m" }; +shell_command green = { "\x1b[1;32m" }; +shell_command yellow = { "\x1b[1;33m" }; +shell_command blue = { "\x1b[1;34m" }; +shell_command magenta = { "\x1b[1;35m" }; +shell_command cyan = { "\x1b[1;36m" }; +shell_command white = { "\x1b[1;37m" }; + +shell_command dim_black = { "\x1b[0;30m" }; +shell_command dim_red = { "\x1b[0;31m" }; +shell_command dim_green = { "\x1b[0;32m" }; +shell_command dim_yellow = { "\x1b[0;33m" }; +shell_command dim_blue = { "\x1b[0;34m" }; +shell_command dim_magenta = { "\x1b[0;35m" }; +shell_command dim_cyan = { "\x1b[0;36m" }; +shell_command dim_white = { "\x1b[0;37m" }; + +shell_command current = reset; + +void init() { + +#ifdef HAVE_ISATTY + if(isatty(1) && isatty(2)) { + return; + } +#endif + + native_console = false; + + reset.command = ""; + + black.command = ""; + red.command = ""; + green.command = ""; + yellow.command = ""; + blue.command = ""; + magenta.command = ""; + cyan.command = ""; + white.command = ""; + + dim_black.command = ""; + dim_red.command = ""; + dim_green.command = ""; + dim_yellow.command = ""; + dim_blue.command = ""; + dim_magenta.command = ""; + dim_cyan.command = ""; + dim_white.command = ""; + + current = reset; + +} + +} + +namespace progress { + +void show(float value, const std::string & label) { + + if(!native_console) { + return; + } + +#if defined(HAVE_IOCTL) && defined(TIOCGWINSZ) + + struct winsize w; + ioctl(0, TIOCGWINSZ, &w); + + clear(); + + std::ios_base::fmtflags flags = std::cout.flags(); + + size_t progress_length = w.ws_col - label.length() - 6 - 2 - 2 - 1; + + if(progress_length > 10) { + + size_t progress = size_t(std::ceil(float(progress_length) * value)); + + std::cout << '['; + for(size_t i = 0; i < progress; i++) { + std::cout << '='; + } + std::cout << '>'; + for(size_t i = progress; i < progress_length; i++) { + std::cout << ' '; + } + std::cout << ']'; + + } + + std::cout << std::right << std::fixed << std::setprecision(1) << std::setfill(' ') + << std::setw(5) << (value * 100) << "% " << label; + std::cout.flush(); + + std::cout.flags(flags); + +#endif + +} + +void clear() { + + if(!native_console) { + return; + } + +#ifdef HAVE_IOCTL + + std::cout << "\33[2K\r"; + +#endif + +} + +} // namespace progress diff --git a/src/util/console.hpp b/src/util/console.hpp new file mode 100644 index 0000000..113c107 --- /dev/null +++ b/src/util/console.hpp @@ -0,0 +1,53 @@ + +#ifndef INNOEXTRACT_UTIL_CONSOLE_HPP +#define INNOEXTRACT_UTIL_CONSOLE_HPP + +#include +#include + +namespace color { + +struct shell_command { + const char * command; +}; + +extern shell_command reset; + +extern shell_command black; +extern shell_command red; +extern shell_command green; +extern shell_command yellow; +extern shell_command blue; +extern shell_command magenta; +extern shell_command cyan; +extern shell_command white; + +extern shell_command dim_black; +extern shell_command dim_red; +extern shell_command dim_green; +extern shell_command dim_yellow; +extern shell_command dim_blue; +extern shell_command dim_magenta; +extern shell_command dim_cyan; +extern shell_command dim_white; + +extern shell_command current; + +void init(); + +} // namespace color + +inline std::ostream & operator<<(std::ostream & os, const color::shell_command command) { + color::current = command; + return os << command.command; +} + +namespace progress { + +void show(float value, const std::string & label); + +void clear(); + +} // namespace + +#endif // INNOEXTRACT_UTIL_CONSOLE_HPP diff --git a/src/util/enum.hpp b/src/util/enum.hpp index e875992..de4372b 100644 --- a/src/util/enum.hpp +++ b/src/util/enum.hpp @@ -5,7 +5,7 @@ #include #include -#include "util/color.hpp" +#include "util/console.hpp" #include "util/flags.hpp" #include "util/util.hpp" diff --git a/src/util/load.cpp b/src/util/load.cpp index 85484ce..bd0e1ff 100644 --- a/src/util/load.cpp +++ b/src/util/load.cpp @@ -8,7 +8,7 @@ #include #include -#include "util/color.hpp" +#include "util/log.hpp" namespace { @@ -79,7 +79,7 @@ void to_utf8(const std::string & from, std::string & to, uint32_t codepage) { size_t ret = iconv(converter, const_cast(&inbuf), &insize, &outbuf, &outsize); if(ret == size_t(-1) && errno != E2BIG) { - LogError << "iconv error while converting from CP" << codepage << ": " << errno; + log_error << "iconv error while converting from CP" << codepage << ": " << errno; to.clear(); return; } diff --git a/src/util/load.hpp b/src/util/load.hpp index 7d30105..b759263 100644 --- a/src/util/load.hpp +++ b/src/util/load.hpp @@ -1,6 +1,6 @@ -#ifndef INNOEXTRACT_UTIL_LOADINGUTILS_HPP -#define INNOEXTRACT_UTIL_LOADINGUTILS_HPP +#ifndef INNOEXTRACT_UTIL_LOAD_HPP +#define INNOEXTRACT_UTIL_LOAD_HPP #include #include @@ -9,6 +9,7 @@ #include "util/endian.hpp" #include "util/types.hpp" +#include "util/util.hpp" struct binary_string { @@ -77,4 +78,14 @@ T load_number(std::istream & is, size_t bits) { } } -#endif // INNOEXTRACT_UTIL_LOADINGUTILS_HPP +template +void discard(T & is, uint64_t bytes) { + char buf[1024]; + while(bytes) { + std::streamsize n = std::streamsize(std::min(bytes, ARRAY_SIZE(buf))); + is.read(buf, n); + bytes -= uint64_t(n); + } +} + +#endif // INNOEXTRACT_UTIL_LOAD_HPP diff --git a/src/util/log.cpp b/src/util/log.cpp new file mode 100644 index 0000000..e4df4d6 --- /dev/null +++ b/src/util/log.cpp @@ -0,0 +1,26 @@ + +#include "util/log.hpp" + +#include + +#include "util/console.hpp" + +bool logger::debug = false; + +size_t logger::total_errors = 0; +size_t logger::total_warnings = 0; + +logger::~logger() { + + color::shell_command previous = color::current; + progress::clear(); + + switch(level) { + case Debug: std::cout << color::cyan << buffer.str() << std::endl; break; + case Info: std::cout << color::white << buffer.str() << std::endl; break; + case Warning: std::cerr << color::yellow << buffer.str() << std::endl; break; + case Error: std::cerr << color::red << buffer.str() << std::endl; break; + } + + std::cout << previous; +} diff --git a/src/util/log.hpp b/src/util/log.hpp new file mode 100644 index 0000000..b16a966 --- /dev/null +++ b/src/util/log.hpp @@ -0,0 +1,71 @@ + +#ifndef INNOEXTRACT_UTIL_LOG_HPP +#define INNOEXTRACT_UTIL_LOG_HPP + +#include +#include + +#ifdef _DEBUG +#define debug(...) \ + if(::logger::debug) \ + ::logger(__FILE__, __LINE__, ::logger::Debug) << __VA_ARGS__ +#define log_info ::logger(__FILE__, __LINE__, ::logger::Info) +#define log_warning ::logger(__FILE__, __LINE__, ::logger::Warning) +#define log_error ::logger(__FILE__, __LINE__, ::logger::Error) +#else +#define debug(...) +#define log_info ::logger(::logger::Info) +#define log_warning ::logger(::logger::Warning) +#define log_error ::logger(::logger::Error) +#endif + +/*! + * logger class that allows longging via the stream operator. + */ +class logger { + +public: + + enum log_level { + Debug, + Info, + Warning, + Error + }; + +private: + +#ifdef _DEBUG + const char * const file; + const int line; +#endif + + const log_level level; + + std::ostringstream buffer; //! Buffer for the log message excluding level, file and line. + +public: + + static size_t total_warnings; + static size_t total_errors; + + static bool debug; + +#ifdef _DEBUG + inline logger(const char * _file, int _line, log_level _level) + : file(_file), line(_line), level(_level) { } +#else + inline logger(log_level _level) : level(_level) { } +#endif + + template + inline logger & operator<<(const T & i) { + buffer << i; + return *this; + } + + ~logger(); + +}; + +#endif // INNOEXTRACT_UTIL_LOG_HPP diff --git a/src/util/output.hpp b/src/util/output.hpp index 5f87296..15f9b4b 100644 --- a/src/util/output.hpp +++ b/src/util/output.hpp @@ -6,7 +6,7 @@ #include #include -#include "util/color.hpp" +#include "util/console.hpp" #include "util/util.hpp" struct quoted { diff --git a/src/util/storedenum.hpp b/src/util/storedenum.hpp new file mode 100644 index 0000000..3780325 --- /dev/null +++ b/src/util/storedenum.hpp @@ -0,0 +1,213 @@ + +#ifndef INNOEXTRACT_UTIL_STOREDENUM_HPP +#define INNOEXTRACT_UTIL_STOREDENUM_HPP + +#include +#include +#include + +#include +#include + +#include "util/load.hpp" +#include "util/enum.hpp" +#include "util/load.hpp" +#include "util/log.hpp" +#include "util/util.hpp" + +template +struct enum_value_map { + + typedef Enum enum_type; + typedef Enum flag_type; + +}; + +#define STORED_ENUM_MAP(MapName, Default, ...) \ +struct MapName : public enum_value_map { \ + static const flag_type default_value; \ + static const flag_type values[]; \ + static const size_t count; \ +}; \ +const MapName::flag_type MapName::default_value = Default; \ +const MapName::flag_type MapName::values[] = { __VA_ARGS__ }; \ +const size_t MapName::count = ARRAY_SIZE(MapName::values) + +#define STORED_FLAGS_MAP(MapName, Flag0, ...) \ + STORED_ENUM_MAP(MapName, Flag0, Flag0, ## __VA_ARGS__) + +template +struct stored_enum { + + size_t value; + +public: + + typedef Mapping mapping_type; + typedef typename Mapping::enum_type enum_type; + + static const size_t size = Mapping::count; + + inline stored_enum(std::istream & is) { + // TODO use larger types for larger enums + BOOST_STATIC_ASSERT(size <= (1 << 8)); + value = load_number(is); + } + + enum_type get() { + + if(value < size) { + return Mapping::values[value]; + } + + log_warning << "warning: unexpected " << enum_names::name << " value: " << value; + + return Mapping::default_value; + } + +}; + +template +class stored_bitfield { + + typedef uint8_t base_type; + + static const size_t base_size = sizeof(base_type) * 8; + static const size_t count = (Bits + (base_size - 1)) / base_size; // ceildiv + + base_type bits[count]; + +public: + + static const size_t size = Bits; + + inline stored_bitfield(std::istream & is) { + for(size_t i = 0; i < count; i++) { + bits[i] = load_number(is); + } + } + + inline uint64_t lower_bits() const { + + BOOST_STATIC_ASSERT(sizeof(uint64_t) % sizeof(base_type) == 0); + + uint64_t result = 0; + + for(size_t i = 0; i < std::min(sizeof(uint64_t) / sizeof(base_type), size_t(count)); i++) { + result |= (uint64_t(bits[i]) << (i * base_size)); + } + + return result; + } + + inline operator std::bitset() const { + + // Make `make style` shut up since we really need unsigned long here. + #define stored_enum_concat_(a, b, c, d) a##b c##d + typedef stored_enum_concat_(unsi, gned, lo, ng) ulong_type; + #undef stored_enum_concat_ + + static const size_t ulong_size = sizeof(ulong_type) * 8; + + BOOST_STATIC_ASSERT(base_size % ulong_size == 0 || base_size < ulong_size); + + std::bitset result(0); + for(size_t i = 0; i < count; i++) { + for(size_t j = 0; j < ceildiv(base_size, ulong_size); j++) { + result |= std::bitset(static_cast(bits[i] >> (j * ulong_size))) + << ((i * base_size) + (j * ulong_size)); + } + } + return result; + } + +}; + +template +class stored_flags : private stored_bitfield { + +public: + + typedef Mapping mapping_type; + typedef typename Mapping::enum_type enum_type; + typedef flags flag_type; + + inline stored_flags(std::istream & is) : stored_bitfield(is) { } + + flag_type get() { + + uint64_t bits = this->lower_bits(); + flag_type result = 0; + + for(size_t i = 0; i < this->size; i++) { + if(bits & (uint64_t(1) << i)) { + result |= Mapping::values[i]; + bits &= ~(uint64_t(1) << i); + } + } + + if(bits) { + log_warning << "unexpected " << enum_names::name << " flags: " + << std::hex << bits << std::dec; + } + + return result; + } + +}; + +template +class stored_flag_reader { + +public: + + typedef Enum enum_type; + typedef flags flag_type; + + std::istream & is; + + typedef uint8_t stored_type; + static const size_t stored_bits = sizeof(stored_type) * 8; + + size_t pos; + stored_type buffer; + + flag_type result; + + size_t bits; + + explicit stored_flag_reader(std::istream & _is) : is(_is), pos(0), result(0), bits(0) { } + + void add(enum_type flag) { + + if(pos == 0) { + buffer = load_number(is); + } + + if(buffer & (stored_type(1) << pos)) { + result |= flag; + } + + pos = (pos + 1) % stored_bits; + + bits++; + } + + operator flag_type() const { + return result; + } + +}; + +template +class stored_flag_reader > : public stored_flag_reader { + +public: + + explicit stored_flag_reader(std::istream & is) : stored_flag_reader(is) { } + +}; + +typedef stored_bitfield<256> stored_char_set; + +#endif // INNOEXTRACT_UTIL_STOREDENUM_HPP diff --git a/src/util/util.hpp b/src/util/util.hpp index 495e015..7dd9d95 100644 --- a/src/util/util.hpp +++ b/src/util/util.hpp @@ -1,6 +1,6 @@ -#ifndef INNOEXTRACT_UTIL_ARRAY_HPP -#define INNOEXTRACT_UTIL_ARRAY_HPP +#ifndef INNOEXTRACT_UTIL_UTIL_HPP +#define INNOEXTRACT_UTIL_UTIL_HPP #define ARRAY_SIZE(array) (sizeof(array)/sizeof(*(array))) @@ -63,4 +63,4 @@ inline T safe_left_shift(T value) { return detail::safe_shifter<(bits >= (8 * sizeof(T)))>::left_shift(value, bits); } -#endif // INNOEXTRACT_UTIL_ARRAY_HPP +#endif // INNOEXTRACT_UTIL_UTIL_HPP