From a2385047cc00d434a69f8ddc2eb313585a543380 Mon Sep 17 00:00:00 2001 From: nick black Date: Sat, 4 Dec 2021 12:07:55 -0500 Subject: [PATCH] [ncman] add support for inflation using libdeflate --- CMakeLists.txt | 10 +++++++++ src/man/main.c | 57 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64f0139e0..b4d1f00eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -628,6 +628,10 @@ endif() # ncman file(GLOB NCMANSRCS CONFIGURE_DEPENDS src/man/*.c) add_executable(ncman ${NCMANSRCS} ${COMPATSRC}) +target_compile_definitions(ncman + PRIVATE + _GNU_SOURCE +) target_include_directories(ncman BEFORE PRIVATE @@ -636,9 +640,15 @@ target_include_directories(ncman "${CMAKE_REQUIRED_INCLUDES}" "${PROJECT_BINARY_DIR}/include" ) +target_include_directories(ncman + BEFORE + PRIVATE + "${libdeflate_INCLUDE_DIRS}" +) target_link_libraries(ncman PRIVATE notcurses-core + "${libdeflate}" ) ############################################################################ diff --git a/src/man/main.c b/src/man/main.c index 881289c5d..8c4e32472 100644 --- a/src/man/main.c +++ b/src/man/main.c @@ -7,6 +7,7 @@ #include #include #include +#include "builddef.h" static void usage(const char* argv0, FILE* o){ @@ -44,26 +45,68 @@ parse_args(int argc, char** argv){ return optind; } +#ifdef USE_DEFLATE // libdeflate implementation +#include +// assume that |buf| is |*len| bytes of deflated data, and try to inflate +// it. if successful, the inflated map will be returned. either way, the +// input map will be unmapped (we take ownership). |*len| will be updated +// if an inflated map is successfully returned. +static unsigned char* +map_gzipped_data(unsigned char* buf, size_t* len, unsigned char* ubuf, uint32_t ulen){ + struct libdeflate_decompressor* inflate = libdeflate_alloc_decompressor(); + size_t outbytes; + enum libdeflate_result r; + r = libdeflate_gzip_decompress(inflate, buf, *len, ubuf, ulen, &outbytes); + munmap(buf, *len); + libdeflate_free_decompressor(inflate); + if(r != LIBDEFLATE_SUCCESS){ + return NULL; + } + *len = ulen; + return ubuf; +} +#else // libz implementation +static unsigned char* +map_gzipped_data(unsigned char* buf, size_t* len, unsigned char* ubuf, uint32_t ulen){ + munmap(buf, *len); + (void)ulen; // FIXME + return NULL; +} +#endif + static unsigned char* map_troff_data(int fd, size_t* len){ struct stat sbuf; if(fstat(fd, &sbuf)){ return NULL; } - // gzip has a 10-byte mandatory header - if(sbuf.st_size < 10){ + // gzip has a 10-byte mandatory header and an 8-byte mandatory footer + if(sbuf.st_size < 18){ return NULL; } - unsigned char* buf = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + *len = sbuf.st_size; + unsigned char* buf = mmap(NULL, *len, PROT_READ, + MAP_PRIVATE | MAP_POPULATE, fd, 0); if(buf == MAP_FAILED){ return NULL; } if(buf[0] == 0x1f && buf[1] == 0x8b && buf[2] == 0x08){ - // FIXME gzipped! - munmap(buf, sbuf.st_size); - return NULL; + // the last four bytes have the uncompressed length + uint32_t ulen; + memcpy(&ulen, buf + *len - 4, 4); + size_t pgsize = 4096; // FIXME + void* ubuf = mmap(NULL, (ulen + pgsize - 1) / pgsize * pgsize, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if(ubuf == MAP_FAILED){ + munmap(buf, *len); + return NULL; + } + if(map_gzipped_data(buf, len, ubuf, ulen) == NULL){ + munmap(ubuf, ulen); + return NULL; + } + return ubuf; } - *len = sbuf.st_size; return buf; }