summaryrefslogtreecommitdiff
path: root/utils/sqz
diff options
context:
space:
mode:
Diffstat (limited to 'utils/sqz')
-rw-r--r--utils/sqz/NOTICE27
-rw-r--r--utils/sqz/mkfile19
-rw-r--r--utils/sqz/squeeze.h34
-rw-r--r--utils/sqz/sqz.c528
-rw-r--r--utils/sqz/zqs.c243
5 files changed, 851 insertions, 0 deletions
diff --git a/utils/sqz/NOTICE b/utils/sqz/NOTICE
new file mode 100644
index 00000000..724003a1
--- /dev/null
+++ b/utils/sqz/NOTICE
@@ -0,0 +1,27 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 1998 C H Forsyth (forsyth@terzarima.net).
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/utils/sqz/mkfile b/utils/sqz/mkfile
new file mode 100644
index 00000000..430bbce5
--- /dev/null
+++ b/utils/sqz/mkfile
@@ -0,0 +1,19 @@
+<../../mkconfig
+CFLAGS=$CFLAGS -I../include
+
+TARG=sqz
+
+OFILES= sqz.$O\
+
+HFILES= \
+ squeeze.h\
+ ../include/a.out.h\
+ ../include/mach.h\
+
+LIBS= mach bio 9
+
+BIN=$ROOT/$OBJDIR/bin
+
+<$ROOT/mkfiles/mkone-$SHELLTYPE
+
+CFLAGS= $CFLAGS -I../include
diff --git a/utils/sqz/squeeze.h b/utils/sqz/squeeze.h
new file mode 100644
index 00000000..b06c1b79
--- /dev/null
+++ b/utils/sqz/squeeze.h
@@ -0,0 +1,34 @@
+
+/*
+ * squeezed file format:
+ * Sqhdr
+ * original Exec header
+ * two Squeeze tables
+ * squeezed segment
+ * unsqueezed segment, if any
+ */
+#define SQMAGIC (ulong)0xFEEF0F1E
+
+typedef struct Sqhdr Sqhdr;
+struct Sqhdr {
+ uchar magic[4]; /* SQMAGIC */
+ uchar text[4]; /* squeezed length of text (excluding tables) */
+ uchar data[4]; /* squeezed length of data (excluding tables) */
+ uchar asis[4]; /* length of unsqueezed segment */
+ uchar toptxt[4]; /* value for 0 encoding in text */
+ uchar topdat[4]; /* value for 0 encoding in data */
+ uchar sum[4]; /* simple checksum of unsqueezed data */
+ uchar flags[4];
+};
+#define SQHDRLEN (8*4)
+
+/*
+ * certain power instruction types are rearranged by sqz
+ * so as to move the variable part of the instruction word to the
+ * low order bits. note that the mapping is its own inverse.
+ */
+#define QREMAP(X)\
+ switch((X)>>26){\
+ case 19: case 31: case 59: case 63:\
+ (X) = (((X) & 0xFC00F801) | (((X)>>15)&0x7FE) | (((X)&0x7FE)<<15));\
+ }
diff --git a/utils/sqz/sqz.c b/utils/sqz/sqz.c
new file mode 100644
index 00000000..cf399e45
--- /dev/null
+++ b/utils/sqz/sqz.c
@@ -0,0 +1,528 @@
+#include <lib9.h>
+#include <a.out.h>
+#include "squeeze.h"
+
+/*
+ * forsyth@vitanuova.com
+ */
+
+typedef struct Word Word;
+struct Word {
+ ulong v;
+ ushort freq;
+ ushort code;
+ Word* next;
+};
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+ int n;
+ /*union {*/
+ ulong tab[7*256];
+ Word* rep[7*256];
+ /*};*/
+};
+
+enum {
+ HMASK = 0xFFFF,
+ HSIZE = HMASK+1,
+
+ Codebufsize = 3*1024*1024
+};
+
+#define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+#define GET4L(p) (((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])
+#define PUT4(p,v) (((p)[0]=(v)>>24),((p)[1]=(v)>>16),((p)[2]=(v)>>8),((p)[3]=(v)))
+
+static uchar prog[Codebufsize];
+static uchar outbuf[Codebufsize];
+static Word* hash1[HSIZE];
+static Word* hash2[HSIZE];
+static Sqhdr sqhdr;
+static ulong chksum;
+
+static int aflag; /* all: both text (squeezed) and data (not) */
+static int dflag; /* squeeze data, not text */
+static int tflag; /* squeeze text, leave data as-is */
+static int qflag = 1; /* enable powerpc option */
+static int wflag; /* write output */
+static int zflag; /* use top==0 for data segment */
+static int islittle; /* object code uses little-endian byte order */
+static int debug;
+static char* fname;
+
+static void analyse(ulong*, int, Squeeze*, Squeeze*, Word**);
+static Word** collate(Word**, int);
+static void dumpsq(Squeeze*, int);
+static void freehash(Word**);
+static long Read(int, void*, long);
+static void remap(Squeeze*);
+static int squeeze(ulong*, int, uchar*, ulong);
+static int squeezetab(int, int, Squeeze*, Word**, int);
+static void squirt(int, Squeeze*);
+static void Write(int, void*, long);
+
+static void
+usage(void)
+{
+ fprint(2, "Usage: sqz [-w] [-t] [-d] [-q] q.out\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int fd, n, ns, nst, nsd;
+ long txtlen, datlen, asis;
+ ulong topdat, toptxt;
+ Exec ex;
+ Squeeze sq3, sq4, sq5, sq6;
+ Word *top;
+
+ setbinmode();
+/* fmtinstall('f', gfltconv); */
+ ARGBEGIN{
+ case 'D':
+ debug++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'q':
+ qflag = 0;
+ break;
+ case 't':
+ tflag++;
+ break;
+ case 'w':
+ wflag++;
+ break;
+ default:
+ usage();
+ }ARGEND
+ fname = *argv;
+ if(fname == nil)
+ usage();
+ fd = open(fname, OREAD);
+ if(fd < 0){
+ fprint(2, "sqz: can't open %s: %r\n", fname);
+ exits("open");
+ }
+ Read(fd, &ex, sizeof(Exec));
+ txtlen = GET4((uchar*)&ex.text);
+ datlen = GET4((uchar*)&ex.data);
+ switch(GET4((uchar*)&ex.magic)){
+ case Q_MAGIC: /* powerpc */
+ islittle = 0;
+ break;
+ case E_MAGIC: /* arm */
+ islittle = 1;
+ qflag = 0;
+ break;
+ case 0xA0E1: /* arm AIF */
+ islittle = 1;
+ qflag = 0;
+ txtlen = GET4L((uchar*)&ex+(5*4))-sizeof(Exec);
+ datlen = GET4L((uchar*)&ex+(6*4));
+ break;
+ default:
+ fprint(2, "sqz: unknown magic for sqz: %8.8ux\n", GET4((uchar*)&ex.magic));
+ exits("bad magic");
+ }
+ if(qflag)
+ fprint(2, "PowerPC rules\n");
+ if(islittle)
+ fprint(2, "Little endian\n");
+ if(txtlen > sizeof(prog) || datlen > sizeof(prog) || txtlen+datlen > sizeof(prog)){
+ fprint(2, "sqz: executable too big: %lud+%lud; increase Codebufsize in sqz.c\n", txtlen, datlen);
+ exits("size");
+ }
+ if(dflag){
+ seek(fd, txtlen, 1);
+ Read(fd, prog, datlen);
+ }else{
+ Read(fd, prog, txtlen);
+ Read(fd, prog+txtlen, datlen);
+ }
+ close(fd);
+ asis = 0;
+ if(dflag)
+ n = datlen;
+ else if(tflag){
+ n = txtlen;
+ asis = datlen;
+ }else
+ n = txtlen+datlen;
+ if(dflag || tflag){
+ analyse((ulong*)prog, n/4, &sq3, &sq4, &top);
+ nst = squeeze((ulong*)prog, n/4, outbuf, top->v);
+ if(nst < 0)
+ exits("sqz");
+ nsd = 0;
+ remap(&sq3);
+ remap(&sq4);
+ toptxt = topdat = top->v;
+ }else{
+ analyse((ulong*)prog, txtlen/4, &sq3, &sq4, &top);
+ nst = squeeze((ulong*)prog, txtlen/4, outbuf, top->v);
+ if(nst < 0)
+ exits("sqz");
+ toptxt = top->v;
+ remap(&sq3);
+ remap(&sq4);
+ if(datlen/4){
+ freehash(hash1);
+ freehash(hash2);
+ analyse((ulong*)(prog+txtlen), datlen/4, &sq5, &sq6, &top);
+ nsd = squeeze((ulong*)(prog+txtlen), datlen/4, outbuf+nst, top->v);
+ if(nsd < 0)
+ exits("sqz");
+ topdat = top->v;
+ remap(&sq5);
+ remap(&sq6);
+ }else{
+ nsd = 0;
+ topdat = 0;
+ }
+ }
+ ns = nst+nsd;
+ fprint(2, "%d/%d bytes\n", ns, n);
+ fprint(2, "%8.8lux csum\n", chksum);
+ if(!wflag)
+ exits(0);
+ PUT4(sqhdr.magic, SQMAGIC);
+ PUT4(sqhdr.toptxt, toptxt);
+ PUT4(sqhdr.sum, chksum);
+ PUT4(sqhdr.text, nst);
+ PUT4(sqhdr.topdat, topdat);
+ PUT4(sqhdr.data, nsd);
+ PUT4(sqhdr.asis, asis);
+ PUT4(sqhdr.flags, 0);
+ Write(1, &sqhdr, SQHDRLEN);
+ Write(1, &ex, sizeof(Exec));
+ squirt(1, &sq3);
+ squirt(1, &sq4);
+ Write(1, outbuf, nst);
+ if(nsd){
+ squirt(1, &sq5);
+ squirt(1, &sq6);
+ Write(1, outbuf+nst, nsd);
+ }
+ if(asis)
+ Write(1, prog+txtlen, asis);
+ exits(0);
+}
+
+static void
+analyse(ulong *prog, int nw, Squeeze *sq3, Squeeze *sq4, Word **top)
+{
+ Word *w, **hp, **sorts, **resorts;
+ ulong *rp, *ep;
+ ulong v;
+ int i, nv1, nv2, nv, nz;
+
+ rp = prog;
+ ep = prog+nw;
+ nv = 0;
+ nz = 0;
+ while(rp < ep){
+ if(islittle){
+ v = GET4L((uchar*)rp);
+ }else{
+ v = GET4((uchar*)rp);
+ }
+ rp++;
+ chksum += v;
+ if(v == 0){
+ nz++;
+ if(0)
+ continue;
+ }
+ if(qflag){
+ QREMAP(v);
+ }
+ for(hp = &hash1[v&HMASK]; (w = *hp) != nil; hp = &w->next)
+ if(w->v == v)
+ break;
+ if(w == nil){
+ w = (Word*)malloc(sizeof(*w));
+ w->v = v;
+ w->freq = 0;
+ w->code = 0;
+ w->next = nil;
+ *hp = w;
+ nv++;
+ }
+ w->freq++;
+ }
+ sorts = collate(hash1, nv);
+ fprint(2, "phase 1: %d/%d words (%d zero), %d top (%8.8lux)\n", nv, nw, nz, sorts[0]->freq, sorts[0]->v);
+ *top = sorts[0];
+ nv1 = squeezetab(1, 0x900, sq3, sorts+1, nv-1)+1;
+ nv2 = 0;
+ for(i=nv1; i<nv; i++){
+ v = sorts[i]->v >> 8;
+ for(hp = &hash2[v&HMASK]; (w = *hp) != nil; hp = &w->next)
+ if(w->v == v)
+ break;
+ if(w == nil){
+ w = (Word*)malloc(sizeof(*w));
+ w->v = v;
+ w->freq = 0;
+ w->code = 0;
+ w->next = nil;
+ *hp = w;
+ nv2++;
+ }
+ w->freq++;
+ }
+ free(sorts);
+ resorts = collate(hash2, nv2);
+ fprint(2, "phase 2: %d/%d\n", nv2, nv-nv1);
+ squeezetab(2, 0x200, sq4, resorts, nv2);
+ free(resorts);
+ fprint(2, "phase 3: 1 4-code, %d 12-codes, %d 20-codes, %d uncoded\n",
+ sq3->n, sq4->n, nv-(sq3->n+sq4->n+1));
+}
+
+static int
+wdcmp(const void *a, const void *b)
+{
+ return (*(Word**)b)->freq - (*(Word**)a)->freq;
+}
+
+static Word **
+collate(Word **tab, int nv)
+{
+ Word *w, **hp, **sorts;
+ int i;
+
+ sorts = (Word**)malloc(nv*sizeof(Word**));
+ i = 0;
+ for(hp = &tab[0]; hp < &tab[HSIZE]; hp++)
+ for(w = *hp; w != nil; w = w->next)
+ sorts[i++] = w;
+ qsort(sorts, nv, sizeof(*sorts), wdcmp);
+ if(debug > 1)
+ for(i=0; i<nv; i++)
+ fprint(2, "%d\t%d\t%8.8lux\n", i, sorts[i]->freq, sorts[i]->v);
+ return sorts;
+}
+
+static int
+tabcmp(const void *a, const void *b)
+{
+ ulong av, bv;
+
+ av = (*(Word**)a)->v;
+ bv = (*(Word**)b)->v;
+ if(av > bv)
+ return 1;
+ if(av < bv)
+ return -1;
+ return 0;
+}
+
+static int
+squeezetab(int tabno, int base, Squeeze *sq, Word **sorts, int nv)
+{
+ int i;
+
+ if(nv >= 7*256)
+ nv = 7*256;
+ memset(sq, 0, sizeof(*sq));
+ for(i=0; i<nv; i++)
+ sq->rep[sq->n++] = sorts[i];
+ qsort(sq->rep, sq->n, sizeof(*sq->rep), tabcmp);
+ for(i=0; i<sq->n; i++)
+ sq->rep[i]->code = base + i;
+ if(debug)
+ dumpsq(sq, tabno);
+ return sq->n;
+}
+
+static void
+dumpsq(Squeeze *sq, int n)
+{
+ int i;
+
+ fprint(2, "table %d: %d entries\n", n, sq->n);
+ for(i=0; i<sq->n; i++)
+ fprint(2, "%.3x\t%8.8lux\t%lux\n", sq->rep[i]->code, sq->rep[i]->v, i? sq->rep[i]->v - sq->rep[i-1]->v: 0);
+}
+
+static void
+remap(Squeeze *sq)
+{
+ int i;
+ ulong v;
+
+ if(sq->n){
+ v = 0;
+ for(i=0; i<sq->n; i++){
+ sq->tab[i] = sq->rep[i]->v - v;
+ v += sq->tab[i];
+ }
+ }
+}
+
+static Word *
+squash(Word **tab, ulong v)
+{
+ Word *w, **hp;
+
+ for(hp = &tab[v&0xFFFF]; (w = *hp) != nil; hp = &w->next)
+ if(w->v == v)
+ return w;
+ return nil;
+}
+
+static void
+freehash(Word **tab)
+{
+ Word *w, **hp;
+
+ for(hp = &tab[0]; hp < &tab[HSIZE]; hp++)
+ while((w = *hp) != nil){
+ *hp = w->next;
+ free(w);
+ }
+}
+
+static int
+squeeze(ulong *prog, int nw, uchar *out, ulong top)
+{
+ ulong *rp, *ep;
+ ulong v, bits;
+ ulong e1, e2, e3, e4;
+ Word *w;
+ uchar bytes[8], *bp, *wp;
+ int ctl, n;
+
+ rp = prog;
+ ep = prog+nw;
+ bits = 0;
+ e1 = e2 = e3 = e4 = 0;
+ wp = out;
+ n = 0;
+ ctl = 0;
+ bp = bytes;
+ for(;;){
+ if(n == 2){
+ *wp++ = ctl;
+ if(0)
+ fprint(2, "%x\n", ctl);
+ memmove(wp, bytes, bp-bytes);
+ wp += bp-bytes;
+ bp = bytes;
+ ctl = 0;
+ n = 0;
+ }
+ ctl <<= 4;
+ n++;
+ if(rp >= ep){
+ if(n == 1)
+ break;
+ continue;
+ }
+ if(islittle){
+ v = GET4L((uchar*)rp);
+ }else{
+ v = GET4((uchar*)rp);
+ }
+ rp++;
+ if(qflag){
+ QREMAP(v);
+ }
+ if(v == top){
+ e1++;
+ bits += 4;
+ ctl |= 0;
+ continue;
+ }
+ w = squash(hash1, v);
+ if(w && w->code){
+ e2++;
+ bits += 4+8;
+ ctl |= w->code>>8;
+ *bp++ = w->code;
+ continue;
+ }
+ w = squash(hash2, v>>8);
+ if(w && w->code){
+ e3++;
+ bits += 4+8+8;
+ ctl |= w->code>>8;
+ *bp++ = w->code;
+ *bp++ = v & 0xFF;
+ if(debug > 2)
+ fprint(2, "%x %8.8lux %8.8lux\n", w->code, w->v, v);
+ continue;
+ }
+ e4++;
+ bits += 4+32;
+ ctl |= 0x1;
+ bp[0] = v;
+ bp[1] = v>>8;
+ bp[2] = v>>16;
+ bp[3] = v>>24;
+ bp += 4;
+ }
+ fprint(2, "enc: %lud 4-bits, %lud 12-bits %lud 20-bits %lud 36-bits -- %ld bytes\n",
+ e1, e2, e3, e4, wp-out);
+ return wp-out;
+}
+
+static void
+squirt(int fd, Squeeze *sq)
+{
+ uchar b[7*256*5 + 2], rep[5], *p, *q;
+ ulong v;
+ int i;
+
+ p = b+2;
+ for(i=0; i<sq->n; i++){
+ v = sq->tab[i];
+ q = rep;
+ do {
+ *q++ = v & 0x7F;
+ }while((v >>= 7) != 0);
+ do {
+ *p++ = *--q | 0x80;
+ }while(q != rep);
+ p[-1] &= ~0x80;
+ }
+ if(p > b+sizeof(b))
+ abort();
+ i = p-b;
+ b[0] = i>>8;
+ b[1] = i;
+ Write(fd, b, i);
+ fprint(2, "table: %d/%d\n", i, (sq->n+1)*4);
+}
+
+static long
+Read(int fd, void *buf, long nb)
+{
+ long n;
+
+ n = read(fd, buf, nb);
+ if(n < 0){
+ fprint(2, "sqz: %s: read error: %r\n", fname);
+ exits("read");
+ }
+ if(n < nb){
+ fprint(2, "sqz: %s: unexpected end-of-file\n", fname);
+ exits("read");
+ }
+ return n;
+}
+
+static void
+Write(int fd, void *buf, long nb)
+{
+ if(write(fd, buf, nb) != nb){
+ fprint(2, "sqz: write error: %r\n");
+ exits("write err");
+ }
+}
diff --git a/utils/sqz/zqs.c b/utils/sqz/zqs.c
new file mode 100644
index 00000000..b9b2a163
--- /dev/null
+++ b/utils/sqz/zqs.c
@@ -0,0 +1,243 @@
+#include <lib9.h>
+#include <a.out.h>
+#include "squeeze.h"
+
+/*
+ * forsyth@vitanuova.com
+ */
+
+/*
+ * for details of `unsqueeze' see:
+ *
+ * %A Mark Taunton
+ * %T Compressed Executables: An Exercise in Thinking Small
+ * %P 385-404
+ * %I USENIX
+ * %B USENIX Conference Proceedings
+ * %D Summer 1991
+ * %C Nashville, TN
+ *
+ * several of the unimplemented improvements described in the paper
+ * have been implemented here
+ *
+ * there is a further transformation on the powerpc (QFLAG!=0) to shuffle bits
+ * in certain instructions so as to push the fixed bits to the top of the word.
+ */
+
+typedef struct Squeeze Squeeze;
+struct Squeeze {
+ int n;
+ ulong tab[7*256];
+};
+
+enum {
+ CHECK = 1 /* check precise bounds in Squeeze array */
+};
+
+#define GET4(p) (((((((p)[0]<<8)|(p)[1])<<8)|(p)[2])<<8)|(p)[3])
+
+static uchar out[3*1024*1024];
+static uchar bigb[1024*1024];
+static ulong top;
+static int qflag = 1;
+static int islittle = 0;
+static ulong chksum, oldsum;
+static int rdtab(int, Squeeze*, int);
+static long unsqueezefd(int, void*);
+static uchar* unsqueeze(uchar*, uchar*, uchar*, Squeeze*, Squeeze*, ulong);
+static uchar* unsqzseg(int, uchar*, long, ulong);
+
+void
+main(int argc, char **argv)
+{
+ int fd;
+ long n;
+
+ if(argc < 2)
+ exits("args");
+ fd = open(argv[1], OREAD);
+ if(fd < 0)
+ exits("open");
+ n = unsqueezefd(fd, out);
+ if(n < 0){
+ fprint(2, "zqs: can't unsqueeze\n");
+ exits("err");
+ }
+ if(write(1, out, n) != n){
+ fprint(2, "zqs: write error: %r\n");
+ exits("err");
+ }
+ fprint(2, "%ld bytes, %8.8lux csum\n", n, chksum);
+ exits(0);
+}
+
+static long
+unsqueezefd(int fd, void *v)
+{
+ uchar *wp, *out;
+ ulong toptxt, topdat;
+ long asis, nst, nsd;
+ Sqhdr sqh;
+ Exec ex;
+
+ out = (uchar*)v;
+ if(read(fd, &sqh, SQHDRLEN) != SQHDRLEN)
+ return -1;
+ if(GET4(sqh.magic) != SQMAGIC)
+ return -1;
+ if(read(fd, &ex, sizeof(Exec)) != sizeof(Exec))
+ return -1;
+ toptxt = GET4(sqh.toptxt);
+ topdat = GET4(sqh.topdat);
+ oldsum = GET4(sqh.sum);
+ asis = GET4(sqh.asis);
+ if(asis < 0)
+ asis = 0;
+ nst = GET4(sqh.text);
+ nsd = GET4(sqh.data);
+ switch(GET4((uchar*)&ex.magic)){
+ case Q_MAGIC:
+ if(qflag)
+ fprint(2, "PowerPC mode\n");
+ islittle = 0;
+ break;
+ case E_MAGIC:
+ case 0xA0E1: /* arm AIF */
+ islittle = 1;
+ qflag = 0;
+ break;
+ default:
+ fprint(2, "Unknown magic: %8.8ux\n", GET4((uchar*)&ex.magic));
+ qflag = 0;
+ break;
+ }
+ memmove(out, &ex, sizeof(ex));
+ wp = unsqzseg(fd, out + sizeof(ex), nst, toptxt);
+ if(wp == nil)
+ return -1;
+ wp = unsqzseg(fd, wp, nsd, topdat);
+ if(wp == nil)
+ return -1;
+ if(asis){
+ if(read(fd, wp, asis) != asis)
+ return -1;
+ wp += asis;
+ }
+ return wp-out;
+}
+
+static uchar*
+unsqzseg(int fd, uchar *wp, long ns, ulong top)
+{
+ Squeeze sq3, sq4;
+
+ if(ns == 0)
+ return wp;
+ if(rdtab(fd, &sq3, 0) < 0)
+ return nil;
+ if(rdtab(fd, &sq4, 8) < 0)
+ return nil;
+ fprint(2, "tables: %d %d\n", sq3.n, sq4.n);
+ if(read(fd, bigb, ns) != ns)
+ return nil;
+ return unsqueeze(wp, bigb, bigb+ns, &sq3, &sq4, top);
+}
+
+static uchar*
+unsqueeze(uchar *wp, uchar *rp, uchar *ep, Squeeze *sq3, Squeeze *sq4, ulong top)
+{
+ ulong nx;
+ int code, n;
+
+ if(qflag){
+ QREMAP(top); /* adjust top just once, outside the loop */
+ }
+ while(rp < ep){
+ code = *rp++;
+ n = 0;
+ nx = code>>4;
+ do{
+ if(nx == 0){
+ nx = top;
+ }else{
+ if(nx==1){
+ if(rp+3 >= ep)
+ return nil;
+ nx = (((((rp[3]<<8)|rp[2])<<8)|rp[1])<<8)|rp[0];
+ rp += 4;
+ }else if(nx <= 8){ /* 2 to 8 */
+ if(rp+1 >= ep)
+ return nil;
+ nx = ((nx-2)<<8) | rp[0];
+ if(CHECK && nx >= sq4->n)
+ return nil; /* corrupted file */
+ nx = sq4->tab[nx] | rp[1];
+ rp += 2;
+ }else{ /* 9 to 15 */
+ if(rp >= ep)
+ return nil; /* corrupted file */
+ nx = ((nx-9)<<8) | rp[0];
+ if(CHECK && nx >= sq3->n)
+ return nil; /* corrupted file */
+ nx = sq3->tab[nx];
+ rp++;
+ }
+ if(rp > ep)
+ return nil; /* corrupted file */
+ if(qflag){
+ QREMAP(nx);
+ }
+ }
+ if(islittle){
+ wp[0] = nx;
+ wp[1] = nx>>8;
+ wp[2] = nx>>16;
+ wp[3] = nx>>24;
+ }else{
+ wp[0] = nx>>24;
+ wp[1] = nx>>16;
+ wp[2] = nx>>8;
+ wp[3] = nx;
+ }
+ wp += 4;
+ chksum += nx;
+ nx = code & 0xF;
+ }while(++n == 1);
+ }
+ return wp;
+}
+
+static int
+rdtab(int fd, Squeeze *sq, int shift)
+{
+ uchar b[7*256*5], *p, *ep;
+ ulong v, w;
+ int i;
+
+ if(read(fd, b, 2) != 2)
+ return -1;
+ i = (b[0]<<8) | b[1];
+ if(1)
+ fprint(2, "table: %d\n", i);
+ if((i -= 2) > 0){
+ if(read(fd, b, i) != i)
+ return -1;
+ }
+ sq->n = 0;
+ p = b;
+ ep = b+i;
+ v = 0;
+ while(p < ep){
+ w = 0;
+ do{
+ if(p >= ep)
+ return -1;
+ w = (w<<7) | (*p & 0x7F);
+ }while(*p++ & 0x80);
+ v += w;
+ if(0)
+ fprint(2, "%d %8.8lux %8.8lux\n", sq->n, v, w);
+ sq->tab[sq->n++] = v << shift;
+ }
+ return 0;
+}