From e64f361e407264681f7b97381fd393ea1ee6014b Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Wed, 10 Jan 2024 15:01:20 -0600 Subject: [PATCH] Compress help text with gzip. --- Config.in | 8 ++++++ lib/deflate.c | 65 +++++++++++++++++++++++++++++++++-------------- lib/lib.h | 3 +-- main.c | 58 +++++++++++++++++++++++++----------------- scripts/install.c | 20 +++++++++++++++ scripts/make.sh | 17 ++++++++++--- 6 files changed, 124 insertions(+), 47 deletions(-) diff --git a/Config.in b/Config.in index 62185dfbd..98add31b9 100644 --- a/Config.in +++ b/Config.in @@ -106,6 +106,14 @@ config TOYBOX_HELP_DASHDASH optstring. (Use TOYFLAG_NOHELP to disable.) Produces the same output as "help command". --version shows toybox version. +config TOYBOX_ZHELP + bool "compress help text" + default y + depends on TOYBOX_HELP + help + Compress help with gzip -9, deflating when displayed. This makes the + binary smaller but can increase runtime memory usage. + config TOYBOX_FREE bool "Free memory unnecessarily" default n diff --git a/lib/deflate.c b/lib/deflate.c index a418c21c5..12263f7a9 100644 --- a/lib/deflate.c +++ b/lib/deflate.c @@ -26,14 +26,14 @@ struct deflate { // Compressed data buffer (extra space malloced at end) unsigned pos, len; - int infd, outfd; - char data[]; + int infd, outfd, outbuflen; + char *outbuf, data[]; }; // little endian bit buffer struct bitbuf { int fd, bitpos, len, max; - char buf[]; + char *buf, data[]; }; // malloc a struct bitbuf @@ -43,6 +43,7 @@ static struct bitbuf *bitbuf_init(int fd, int size) bb->max = size; bb->fd = fd; + bb->buf = bb->data; return bb; } @@ -57,7 +58,7 @@ static int bitbuf_skip(struct bitbuf *bb, int bits) while (pos >= (len = bb->len<<3)) { pos -= len; if (1 > (bb->len = read(bb->fd, bb->buf, bb->max))) { - if (!bb->len && !bits) break; + if (!bits) break; error_exit("inflate EOF"); } } @@ -135,16 +136,26 @@ static void bitbuf_put(struct bitbuf *bb, int data, int len) } } +// Output inflated data +static void inflate_out(struct deflate *dd, int len) +{ + if (!len) return; + if (dd->outfd!=-1) xwrite(dd->outfd, dd->data, len); + else if (len>dd->outbuflen) error_exit("inflate too big"); + else { + memcpy(dd->outbuf, dd->data, len); + dd->outbuf += len; + dd->outbuflen -= len; + } + if (dd->crcfunc) dd->crcfunc(dd, dd->data, len); +} + static void output_byte(struct deflate *dd, char sym) { int pos = dd->pos++ & 32767; dd->data[pos] = sym; - - if (pos == 32767) { - xwrite(dd->outfd, dd->data, 32768); - if (dd->crcfunc) dd->crcfunc(dd, dd->data, 32768); - } + if (pos == 32767) inflate_out(dd, 32768); } // Huffman coding uses bits to traverse a binary tree to a leaf node, @@ -313,10 +324,7 @@ static void inflate(struct deflate *dd, struct bitbuf *bb) if (final) break; } - if (dd->pos & 32767) { - xwrite(dd->outfd, dd->data, dd->pos&32767); - if (dd->crcfunc) dd->crcfunc(dd, dd->data, dd->pos&32767); - } + if (dd->pos & 32767) inflate_out(dd, dd->pos&32767); } // Deflate from dd->infd to bitbuf @@ -337,6 +345,7 @@ static void deflate(struct deflate *dd, struct bitbuf *bb) // dd->len += len; crcfunc advances len TODO // store block as literal +// TODO: actually compress! bitbuf_put(bb, final, 1); bitbuf_put(bb, 0, 1); @@ -481,28 +490,23 @@ long long gzip_fd(int infd, int outfd) return rc; } -long long gunzip_fd(int infd, int outfd) +long long gunzip_common(struct bitbuf *bb, struct deflate *dd) { - struct bitbuf *bb = bitbuf_init(infd, 4096); - struct deflate *dd = init_deflate(0); long long rc = 0; // Little endian crc table crc_init(dd->crctable, 1); dd->crcfunc = gzip_crc; - dd->outfd = outfd; do { if (!is_gzip(bb)) error_exit("not gzip"); inflate(dd, bb); - // tail: crc32, len32 bitbuf_skip(bb, (8-bb->bitpos)&7); if (~dd->crc != bitbuf_get(bb, 32) || dd->len != bitbuf_get(bb, 32)) error_exit("bad crc"); rc += dd->len; - bitbuf_skip(bb, (8-bb->bitpos)&7); dd->pos = dd->len = 0; } while (bitbuf_skip(bb, 0)); @@ -511,3 +515,26 @@ long long gunzip_fd(int infd, int outfd) return rc; } + +long long gunzip_mem(char *inbuf, int inlen, char *outbuf, int outlen) +{ + struct bitbuf *bb = bitbuf_init(-1, 0); + struct deflate *dd = init_deflate(0); + + bb->buf = inbuf; + bb->max = bb->len = inlen; + dd->outfd = -1; + dd->outbuf = outbuf; + dd->outbuflen = outlen; + + return gunzip_common(bb, dd); +} + +long long gunzip_fd(int infd, int outfd) +{ + struct bitbuf *bb = bitbuf_init(infd, 4096); + struct deflate *dd = init_deflate(0); + + dd->outfd = outfd; + return gunzip_common(bb, dd); +} diff --git a/lib/lib.h b/lib/lib.h index 1ea16b6cc..89e15f994 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -383,8 +383,7 @@ int comma_remove(char *optlist, char *opt); long long gzip_fd(int infd, int outfd); long long gunzip_fd(int infd, int outfd); -long long gunzip_fd_preload(int infd, int outfd, char *buf, unsigned len); - +long long gunzip_mem(char *inbuf, int inlen, char *outbuf, int outlen); // getmountlist.c struct mtab_list { diff --git a/main.c b/main.c index 3d9f612e0..bb8377dbc 100644 --- a/main.c +++ b/main.c @@ -76,37 +76,49 @@ static const char help_data[] = #include "generated/newtoys.h" ; +#if CFG_TOYBOX_ZHELP +#include "generated/zhelp.h" +#else +static char *zhelp_data = 0; +#define ZHELP_LEN 0 +#endif + void show_help(FILE *out, int flags) { int i = toys.which-toy_list; - char *s, *ss; + char *s, *ss, *hd; - if (CFG_TOYBOX_HELP) { - if (flags & HELP_HEADER) - fprintf(out, "Toybox %s"USE_TOYBOX(" multicall binary")"%s\n\n", - toybox_version, (CFG_TOYBOX && i) ? " (see toybox --help)" - : " (see https://landley.net/toybox)"); + if (!CFG_TOYBOX_HELP) return; - for (;;) { - s = (void *)help_data; - while (i--) s += strlen(s) + 1; - // If it's an alias, restart search for real name - if (*s != 255) break; - i = toy_find(++s)-toy_list; - if ((flags & HELP_SEE) && toy_list[i].flags) { - if (flags & HELP_HTML) fprintf(out, "See %s\n", s, s); - else fprintf(out, "%s see %s\n", toys.which->name, s); + if (CFG_TOYBOX_ZHELP) + gunzip_mem(zhelp_data, sizeof(zhelp_data), hd = xmalloc(ZHELP_LEN), + ZHELP_LEN); + else hd = (void *)help_data; - return; - } + if (flags & HELP_HEADER) + fprintf(out, "Toybox %s"USE_TOYBOX(" multicall binary")"%s\n\n", + toybox_version, (CFG_TOYBOX && i) ? " (see toybox --help)" + : " (see https://landley.net/toybox)"); + + for (;;) { + s = (void *)help_data; + while (i--) s += strlen(s) + 1; + // If it's an alias, restart search for real name + if (*s != 255) break; + i = toy_find(++s)-toy_list; + if ((flags & HELP_SEE) && toy_list[i].flags) { + if (flags & HELP_HTML) fprintf(out, "See %s\n", s, s); + else fprintf(out, "%s see %s\n", toys.which->name, s); + + return; } + } - if (!(flags & HELP_USAGE)) fprintf(out, "%s\n", s); - else { - strstart(&s, "usage: "); - for (ss = s; *ss && *ss!='\n'; ss++); - fprintf(out, "%.*s\n", (int)(ss-s), s); - } + if (!(flags & HELP_USAGE)) fprintf(out, "%s\n", s); + else { + strstart(&s, "usage: "); + for (ss = s; *ss && *ss!='\n'; ss++); + fprintf(out, "%.*s\n", (int)(ss-s), s); } } diff --git a/scripts/install.c b/scripts/install.c index 3ffb867d1..2ee20fe55 100644 --- a/scripts/install.c +++ b/scripts/install.c @@ -4,6 +4,9 @@ */ #include +#include +#include +#include #include "generated/config.h" #include "lib/toyflags.h" @@ -16,11 +19,28 @@ struct {char *name; int flags;} toy_list[] = { #include "generated/newtoys.h" }; +#undef NEWTOY +#undef OLDTOY +#define NEWTOY(name,opt,flags) HELP_##name "\0" +#if CFG_TOYBOX +#define OLDTOY(name,oldname,flags) "\xff" #oldname "\0" +#else +#define OLDTOY(name, oldname, flags) HELP_##oldname "\0" +#endif + +#include "generated/help.h" +static char help_data[] = +#include "generated/newtoys.h" +; + int main(int argc, char *argv[]) { static char *toy_paths[]={"usr/","bin/","sbin/",0}; int i, len = 0; + if (argc>1 && !strcmp(argv[1], "--help")) + exit(sizeof(help_data)!=write(1, help_data, sizeof(help_data))); + // Output list of applets. for (i=1; i "$GENDIR"/tags.h fi +# Create help.h, and zhelp.h if zcat enabled hostcomp config2help if isnewer help.h "$GENDIR"/Config.in then - "$UNSTRIPPED"/config2help Config.in $KCONFIG_CONFIG > "$GENDIR"/help.h || exit 1 + "$UNSTRIPPED"/config2help Config.in $KCONFIG_CONFIG > "$GENDIR"/help.h||exit 1 fi -[ -z "$DIDNEWER" ] || echo } +if grep -qx 'CONFIG_TOYBOX_ZHELP=y' "$KCONFIG_CONFIG" +then + do_loudly $HOSTCC -I . scripts/install.c -o "$UNSTRIPPED"/instlist || exit 1 + { echo "#define ZHELP_LEN $("$UNSTRIPPED"/instlist --help | wc -c)" && + "$UNSTRIPPED"/instlist --help | gzip -9 | od -Anone -vtx1 | \ + sed 's/ /,0x/g;1s/^,/static char zhelp_data[] = {\n /;$s/.*/&};/' + } > "$GENDIR"/zhelp.h || exit 1 +else + rm -f "$GENDIR"/zhelp.h +fi + +[ -z "$DIDNEWER" ] || echo } [ -n "$NOBUILD" ] && exit 0 echo "Compile $OUTNAME"