summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Fankhauser (@tux-six)2022-07-07 02:26:29 +0200
committerAndreas Fankhauser (@tux-six)2022-07-07 02:29:04 +0200
commitbe5a1fe4a8fe8e79214cb87a870d7e744ea9f200 (patch)
tree09b9f577d185d65858d22875bf8f0aacb8874e80
downloadDeflateAndInflate-be5a1fe4a8fe8e79214cb87a870d7e744ea9f200.zip
DeflateAndInflate-be5a1fe4a8fe8e79214cb87a870d7e744ea9f200.tar.gz
Extract from existing sources
I tried this to be a grafted history because MUST NOT include all the other, unrelated crap from the mess where I did extract this code from. But it seems a hard thing to do as all sorts of git commands (eg push, clone, ...) did not work as epxected. So I keep the commit metadata here where this got split-off. commit 196ff8beda7a23aee6e1e7388cfc24f6247a63c9 Author: Andreas Fankhauser (@tux-six) <https://hiddenalpha.ch/slnk/id/1:c0n74ct> Date: 2022-06-10 02:52:11 +0200 (scriptlee) Fix bug in http
-rw-r--r--.gitignore10
-rw-r--r--LICENCE.txt15
-rw-r--r--README.txt5
-rwxr-xr-xconfigure47
-rw-r--r--src/main/c/deflate.c204
-rw-r--r--src/main/c/inflate.c228
6 files changed, 509 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..140bac1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+
+/build
+/dist
+/external
+
+# Eclipse
+.cproject
+.project
+.settings
+
diff --git a/LICENCE.txt b/LICENCE.txt
new file mode 100644
index 0000000..f0308ca
--- /dev/null
+++ b/LICENCE.txt
@@ -0,0 +1,15 @@
+
+Copyright 2020, Andreas Fankhauser. All rights reserved.
+
+ THIS WORK IS PROVIDED BY THE AUTHOR(S) "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGE.
+
+
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..7b64d36
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,5 @@
+
+Pure DeflateInflate executables
+===============================
+
+
diff --git a/configure b/configure
new file mode 100755
index 0000000..cc38eac
--- /dev/null
+++ b/configure
@@ -0,0 +1,47 @@
+
+set -e
+if test -n "$BASH_VERSION"; then set -o posix; fi
+
+
+# See https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
+prefix=/usr/local
+exec_prefix='$(PREFIX)'
+bindir='$(EXEC_PREFIX)/bin'
+host=
+
+
+main() {
+ parseArgs "$@"
+ echo TODO create Makefile
+}
+
+
+printHelp() {
+ echo ''
+ echo "Example usage: $0 --prefix /usr/local"
+ echo ''
+ echo 'Options:'
+ echo ' --prefix <path> Default "/usr/local"'
+ echo ' --bindir <path> Default "$(EXEC_PREFIX)/bin"'
+ echo ' --host <name> Default <empty> (Eg "x86_64-w64-mingw32")'
+ echo ''
+}
+
+
+parseArgs() {
+ # See: https://stackoverflow.com/a/14203146/4415884
+ while test $# -gt 0 ; do
+ case $1 in
+ --help) printHelp; exit 1; ;;
+ --prefix) prefix="$2" shift; shift; ;;
+ --exec_prefix) exec_prefix="$2" shift; shift; ;;
+ --bindir) bindir="$2" shift; shift; ;;
+ --host) host="$2" shift; shift; ;;
+ *) echo "[ERROR] Unexpected argument: $1" exit 1 ;;
+ esac
+ done
+}
+
+
+main "$@"
+
diff --git a/src/main/c/deflate.c b/src/main/c/deflate.c
new file mode 100644
index 0000000..9043c10
--- /dev/null
+++ b/src/main/c/deflate.c
@@ -0,0 +1,204 @@
+/*
+
+ See "./inflate.c" for bash helpers.
+
+*/
+
+#include "commonbase.h"
+
+/* system */
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/* libs */
+#include "zlib.h"
+
+/* project */
+#include "string-util.h"
+#include "log.h"
+
+
+typedef struct Deflate Deflate;
+
+
+struct Deflate {
+ int level;
+ int useZlibHdr;
+};
+
+
+static void printHelp( void ){
+ printf("\n %s%s", strrchr(__FILE__,'/')+1, " @ " STR_QUOT(PROJECT_VERSION) "\n"
+ "\n"
+ "deflates stdin to stdout\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " --level\n"
+ " Compression level 0-9 (0 fastest, 9 smallest).\n"
+ "\n"
+ " --zlib\n"
+ " Prepend output with a zlib header.\n"
+ "\n");
+}
+
+
+static int parseArgs( int argc, char**argv, Deflate*deflateCls ){
+ deflateCls->level = Z_DEFAULT_COMPRESSION;
+ deflateCls->useZlibHdr = 0;
+ for( uint i=1 ; i<argc ; ++i ){
+ char *arg = argv[i];
+ if( !strcmp(arg,"--help") ){
+ printHelp(); return -1;
+ }else if( !strcmp(arg,"--level") ){
+ arg = argv[++i];
+ if( arg == NULL ){ fprintf(stderr, "Arg level needs a value\n"); return -1; }
+ char *end;
+ deflateCls->level = strtol(arg, &end, 10);
+ if( arg == end ){ fprintf(stderr, "Failed to parse '%s'\n", arg); return -1; }
+ if( deflateCls->level < 0 || deflateCls->level > 9 ){
+ fprintf(stderr, "OutOfRange: %s %s\n", argv[i-1], arg); return -1;
+ }
+ }else if( !strcmp(arg,"--zlib") ){
+ deflateCls->useZlibHdr = !0;
+ }else{
+ fprintf(stderr, "Unknown arg: %s\n", arg);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static int doDeflate( Deflate*deflateCls ){
+ int err;
+ z_stream strm;
+ long tmpLong;
+ uchar innBuf[65536];
+ const int innBuf_cap = sizeof innBuf;
+ int innBuf_len = 0, innBuf_off = 0;
+ uchar outBuf[65536];
+ const int outBuf_cap = sizeof outBuf;
+ int outBuf_len = 0;
+ int zlibHdrWritten = deflateCls->useZlibHdr ? 2 : 0;
+ int inputIsEOF = 0;
+ int outputIsEOF = 0;
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.next_in = innBuf;
+ strm.avail_in = innBuf_len;
+
+ err = deflateInit(&strm, deflateCls->level);
+ if( err != Z_OK ){
+ LOG_ERROR("deflateInit(): (ret=%d): %s", err, strerror(errno));
+ return -1;
+ }
+
+ for(; !outputIsEOF ;){
+
+ /* input */ {
+ int space = innBuf_cap - innBuf_len;
+ /* look if we can shift */
+ //LOG_DEBUG("space=%d\n", space);
+ if( space <= 0 && innBuf_off > 0 ){
+ //LOG_DEBUG("do shifting\n");
+ int wr = 0, rd = innBuf_off;
+ for(;;){
+ err = rd - wr; /* chunk length to copy */
+ if( rd + err > innBuf_cap) err = innBuf_cap - rd;
+ if( err <= 0 ) break;
+ memcpy(innBuf + wr, innBuf + rd, err);
+ rd += err;
+ wr += err;
+ }
+ innBuf_len -= innBuf_off;
+ innBuf_off = 0;
+ space = innBuf_cap - innBuf_len;
+ //LOG_DEBUG("space=%d (after shift)\n", space);
+ }
+ if( space > 0 && !inputIsEOF ){
+ err = fread(innBuf + innBuf_len, 1, space, stdin);
+ if(unlikely( err <= 0 )){
+ if( feof(stdin) ){
+ inputIsEOF = !0;
+ }else{
+ LOG_ERROR("fread(): %s", strerror(errno));
+ return -1;
+ }
+ }
+ assert(err <= space);
+ innBuf_len += err;
+ }
+ assert(innBuf_len - innBuf_off >= 0);
+ }
+
+ /* process */ {
+ strm.next_in = innBuf + innBuf_off;
+ strm.avail_in = innBuf_len - innBuf_off;
+ strm.next_out = outBuf;
+ strm.avail_out = outBuf_cap;
+ assert(strm.avail_out > 0);
+ //LOG_DEBUG("avail_in=%d, inputIsEOF=%d\n", strm.avail_in, inputIsEOF);
+ assert(strm.avail_in > 0 || inputIsEOF);
+ err = deflate(&strm, inputIsEOF ? Z_FINISH : Z_NO_FLUSH);
+ if(unlikely( err != Z_OK )){
+ if( err == Z_STREAM_END ){
+ outputIsEOF = !0;
+ }else{
+ LOG_ERROR("deflate(): %s\n", strm.msg);
+ return -1;
+ }
+ }
+ innBuf_off = strm.next_in - innBuf;
+ outBuf_len = strm.next_out - outBuf;
+ }
+
+ /* output */ {
+ int outBuf_off = 0;
+ if( zlibHdrWritten < 2 ){
+ /* try to skip up to 2 bytes */
+ err = 2 - zlibHdrWritten;
+ if( outBuf_len < err ){ err = outBuf_len; }
+ zlibHdrWritten += err;
+ outBuf_off += err;
+ }
+ err = fwrite(outBuf + outBuf_off, 1, outBuf_len - outBuf_off, stdout);
+ if(unlikely( err != outBuf_len - outBuf_off )){
+ LOG_ERROR("fwrite(): %s\n", strerror(errno));
+ return -1;
+ }
+ outBuf_len = 0;
+ }
+ }
+
+ err = deflateEnd(&strm);
+ if( err != Z_OK ){
+ LOG_WARN("deflateEnd() ret=%d: %s\n", err, strm.msg);
+ }
+
+ return 0;
+}
+
+
+int deflate_main( int argc, char**argv ){
+ int err;
+ Deflate deflateCls;
+ #define deflateCls (&deflateCls)
+
+ err = parseArgs(argc, argv, deflateCls);
+ if( err < 0 ){ return err; }
+
+ stdinModeBinary();
+ stdoutModeBinary();
+
+ err = doDeflate(deflateCls);
+ if( err < 0 ){ return err; }
+
+ return 0;
+ #undef deflate
+}
+
diff --git a/src/main/c/inflate.c b/src/main/c/inflate.c
new file mode 100644
index 0000000..67ddb71
--- /dev/null
+++ b/src/main/c/inflate.c
@@ -0,0 +1,228 @@
+/*
+
+ bash-deflate(){ gzip -c | tail -c +10; }
+ bash-inflate(){ (printf "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00"&&cat -)|gzip -dc; }
+
+*/
+
+#include "commonbase.h"
+
+/* system */
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/* libs */
+#include "zlib.h"
+
+/* project */
+#include "log.h"
+#include "string-util.h"
+
+
+typedef struct MyInflate MyInflate;
+
+
+struct MyInflate {
+ int tryParseHeaders;
+};
+
+
+static void printHelp( void ){
+ printf("\n %s%s", strrchr(__FILE__,'/')+1, " @ " STR_QUOT(PROJECT_VERSION) "\n"
+ "\n"
+ "inflates stdin to stdout\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " --raw\n"
+ " By default, we'll try to decode leading headers. Using this\n"
+ " option, no header parsing is done and the input is expected to\n"
+ " be a pure deflate stream.\n"
+ "\n"
+ "\n");
+}
+
+
+static int parseArgs( int argc, char**argv, MyInflate*myInflate ){
+ myInflate->tryParseHeaders = !0; /* enabled by default */
+ for( uint i=1 ; i<argc ; ++i ){
+ char *arg = argv[i];
+ if( !strcmp(arg,"--help") ){
+ printHelp(); return -1;
+ }else if( !strcmp(arg,"--raw") ){
+ myInflate->tryParseHeaders = 0;
+ }else{
+ fprintf(stderr, "Unknown arg: %s\n", arg);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static int doInflate( MyInflate*myInflate ){
+ #define MAX(a, b) ((a) > (b) ? (a) : (b))
+ int err;
+ z_stream strm;
+ uchar innBuf[65536];
+ const int innBuf_cap = sizeof innBuf;
+ int innBuf_len = 0, innBuf_off = 0;
+ uchar outBuf[65536];
+ const int outBuf_cap = sizeof outBuf;
+ int outBuf_len = 0;
+ int inputIsEOF = 0;
+ int outputIsEOF = 0;
+ int noProgressSince = 0;
+ /* only counts while reading through possible file headers. Later, the
+ * counter will not be updated anymore, to prevent overflows */
+ int headerIsPassed = 0;
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.next_in = innBuf;
+ strm.avail_in = innBuf_len;
+
+ // Pass special 2nd arg. See "https://codeandlife.com/2014/01/01/unzip-library-for-c/"
+ err = inflateInit2(&strm, -MAX_WBITS);
+ if( err != Z_OK ){
+ LOG_ERROR("inflateInit() ret=%d: %s", err, strerror(errno));
+ return -1;
+ }
+
+ for(; !inputIsEOF || !outputIsEOF ;){
+
+ /* input */ {
+ int space = innBuf_cap - innBuf_len;
+ /* look if we can shift */
+ if( space <= 0 && innBuf_off > 0 ){
+ //LOG_DEBUG("try-shift space=%d, innBuf_off=%d\n", space, innBuf_off);
+ int wr = 0, rd = innBuf_off;
+ for(;;){
+ err = rd - wr; /* chunk length to copy */
+ if( rd + err > innBuf_cap) err = innBuf_cap - rd;
+ if( err <= 0 ) break;
+ memcpy(innBuf + wr, innBuf + rd, err);
+ rd += err;
+ wr += err;
+ }
+ innBuf_len -= innBuf_off;
+ innBuf_off = 0;
+ space = innBuf_cap - innBuf_len;
+ //LOG_DEBUG("after shift space=%d, innBuf_off=%d, innBuf_len=%d\n", space, innBuf_off, innBuf_len);
+ }
+ if( space > 0 && !inputIsEOF ){
+ //LOG_TRACE("go read space=%d, inputIsEOF=%d\n", space, inputIsEOF);
+ err = fread(innBuf + innBuf_len, 1, space, stdin);
+ //LOG_TRACE("fread() -> %d\n", err);
+ if(unlikely( err <= 0 )){
+ if( feof(stdin) ){
+ inputIsEOF = !0;
+ }else{
+ LOG_ERROR("fread(): %s", strerror(errno));
+ return -1;
+ }
+ }
+ assert(err <= space);
+ innBuf_len += err;
+ }
+ }
+
+ if(unlikely( !headerIsPassed )){
+ #define avail (innBuf_len - innBuf_off)
+ /* Ensure we have at least two bytes for zlib header detection */
+ if( avail < 2 && !inputIsEOF ){ LOG_DEBUG("Not enough data for header detection. have"
+ " %d\n", avail); continue; }
+ headerIsPassed = !0;
+ if( myInflate->tryParseHeaders ){
+ int b0 = innBuf[0], b1 = innBuf[1];
+ if( b0 == 0x78 && (b1 == 0x01 || b1 == 0x5E || b1 == 0x9C || b1 == 0xDA) ){
+ //LOG_DEBUG("looks like a zlib header. Skip it\n");
+ innBuf_off += 2;
+ }
+ }
+ #undef avail
+ }
+
+ /* process */ {
+ strm.next_in = innBuf + innBuf_off;
+ strm.avail_in = MAX(0, innBuf_len - innBuf_off);
+ strm.next_out = outBuf;
+ strm.avail_out = outBuf_cap;
+ // TODO remove assert(strm.avail_in >= 0 || inputIsEOF);
+ assert(strm.avail_out > 0);
+ int flush = inputIsEOF ? Z_FINISH : Z_NO_FLUSH;
+ //LOG_DEBUG("inflate(in=0x%p, inLen=%d, %s)\n", strm.next_in, strm.avail_in,
+ // (flush==Z_FINISH) ? "Z_FINISH" : (flush==Z_FULL_FLUSH) ? "Z_FULL_FLUSH" : "Z_NO_FLUSH");
+ errno = 0;
+ err = inflate(&strm, flush);
+ if(unlikely( err != Z_OK )){
+ if( err == Z_STREAM_END ){
+ //LOG_DEBUG("inflate() -> Z_STREAM_END\n");
+ outputIsEOF = !0;
+ }else if( err == Z_BUF_ERROR ){
+ /* Could not make any progress. Have to call again with updated buffers */
+ noProgressSince += 1;
+ if( noProgressSince > 42 ){
+ LOG_WARN("inflate() -> Z_BUF_ERROR: %s (maybe EOF came too early?)\n",
+ (strm.msg == NULL) ? "Could not make any progress" : strm.msg);
+ return -1;
+ }
+ }else if( strm.msg != NULL || errno ){
+ LOG_ERROR("inflate(): %s\n", (strm.msg != NULL) ? strm.msg : strerror(errno));
+ return -1;
+ }else{
+ LOG_ERROR("inflate(): -> %d\n", err);
+ return -1;
+ }
+ }else{
+ noProgressSince = 0; /* reset as we just made progress */
+ }
+ innBuf_off += strm.next_in - innBuf;
+ outBuf_len = strm.next_out - outBuf;
+ if( innBuf_off > innBuf_len ){ innBuf_off = innBuf_len; }
+ //LOG_DEBUG("strm.remain=%d, outputIsEOF=%d\n", innBuf_len - innBuf_off, outputIsEOF);
+ }
+
+ /* output */ {
+ err = fwrite(outBuf, 1, outBuf_len, stdout);
+ //LOG_DEBUG("fwrite(0x%p, 1, %d, stdout) -> %d\n", outBuf, outBuf_len, err);
+ if(unlikely( err != outBuf_len )){
+ LOG_ERROR("fwrite(): %s\n", strerror(errno));
+ return -1;
+ }
+ outBuf_len = 0;
+ }
+ }
+
+ err = inflateEnd(&strm);
+ if( err != Z_OK ){
+ LOG_WARN("inflateEnd() ret=%d: %s\n", err, strm.msg);
+ }
+
+ return 0;
+ #undef MAX
+}
+
+
+int inflate_main( int argc, char**argv ){
+ int err;
+ MyInflate myInflate;
+ #define myInflate (&myInflate)
+
+ err = parseArgs(argc, argv, myInflate);
+ if( err < 0 ){ return err; }
+
+ stdinModeBinary();
+ stdoutModeBinary();
+
+ err = doInflate(myInflate);
+ if( err < 0 ){ return err; }
+
+ return 0;
+ #undef myInflate
+}
+
+