diff options
Diffstat (limited to 'src/cmd/gzip/zip.c')
-rw-r--r-- | src/cmd/gzip/zip.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/src/cmd/gzip/zip.c b/src/cmd/gzip/zip.c new file mode 100644 index 00000000..0e0d6baa --- /dev/null +++ b/src/cmd/gzip/zip.c @@ -0,0 +1,398 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <flate.h> +#include "zip.h" + +enum +{ + HeadAlloc = 64, +}; + +static void zip(Biobuf *bout, char *file, int stdout); +static void zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout); +static int crcread(void *fd, void *buf, int n); +static int zwrite(void *bout, void *buf, int n); +static void put4(Biobuf *b, ulong v); +static void put2(Biobuf *b, int v); +static void put1(Biobuf *b, int v); +static void header(Biobuf *bout, ZipHead *zh); +static void trailer(Biobuf *bout, ZipHead *zh, vlong off); +static void putCDir(Biobuf *bout); + +static void error(char*, ...); +/* #pragma varargck argpos error 1 */ + +static Biobuf bout; +static ulong crc; +static ulong *crctab; +static int debug; +static int eof; +static int level; +static int nzheads; +static ulong totr; +static ulong totw; +static int verbose; +static int zhalloc; +static ZipHead *zheads; +static jmp_buf zjmp; + +void +usage(void) +{ + fprint(2, "usage: zip [-vD] [-1-9] [-f zipfile] file ...\n"); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + char *zfile; + int i, fd, err; + + zfile = nil; + level = 6; + ARGBEGIN{ + case 'D': + debug++; + break; + case 'f': + zfile = ARGF(); + if(zfile == nil) + usage(); + break; + case 'v': + verbose++; + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + level = ARGC() - '0'; + break; + default: + usage(); + break; + }ARGEND + + if(argc == 0) + usage(); + + crctab = mkcrctab(ZCrcPoly); + err = deflateinit(); + if(err != FlateOk) + sysfatal("deflateinit failed: %s\n", flateerr(err)); + + if(zfile == nil) + fd = 1; + else{ + fd = create(zfile, OWRITE, 0664); + if(fd < 0) + sysfatal("can't create %s: %r\n", zfile); + } + Binit(&bout, fd, OWRITE); + + if(setjmp(zjmp)){ + if(zfile != nil){ + fprint(2, "zip: removing output file %s\n", zfile); + remove(zfile); + } + exits("errors"); + } + + for(i = 0; i < argc; i++) + zip(&bout, argv[i], zfile == nil); + + putCDir(&bout); + + exits(nil); +} + +static void +zip(Biobuf *bout, char *file, int stdout) +{ + Tm *t; + ZipHead *zh; + Dir *dir; + vlong off; + int fd, err; + + fd = open(file, OREAD); + if(fd < 0) + error("can't open %s: %r", file); + dir = dirfstat(fd); + if(dir == nil) + error("can't stat %s: %r", file); + + /* + * create the header + */ + if(nzheads >= zhalloc){ + zhalloc += HeadAlloc; + zheads = realloc(zheads, zhalloc * sizeof(ZipHead)); + if(zheads == nil) + error("out of memory"); + } + zh = &zheads[nzheads++]; + zh->madeos = ZDos; + zh->madevers = (2 * 10) + 0; + zh->extos = ZDos; + zh->extvers = (2 * 10) + 0; + + t = localtime(dir->mtime); + zh->modtime = (t->hour<<11) | (t->min<<5) | (t->sec>>1); + zh->moddate = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; + + zh->flags = 0; + zh->crc = 0; + zh->csize = 0; + zh->uncsize = 0; + zh->file = strdup(file); + if(zh->file == nil) + error("out of memory"); + zh->iattr = 0; + zh->eattr = ZDArch; + if((dir->mode & 0700) == 0) + zh->eattr |= ZDROnly; + zh->off = Boffset(bout); + + if(dir->mode & DMDIR){ + zh->eattr |= ZDDir; + zh->meth = 0; + zipDir(bout, fd, zh, stdout); + }else{ + zh->meth = 8; + if(stdout) + zh->flags |= ZTrailInfo; + off = Boffset(bout); + header(bout, zh); + + crc = 0; + eof = 0; + totr = 0; + totw = 0; + err = deflate(bout, zwrite, (void*)fd, crcread, level, debug); + if(err != FlateOk) + error("deflate failed: %s: %r", flateerr(err)); + + zh->csize = totw; + zh->uncsize = totr; + zh->crc = crc; + trailer(bout, zh, off); + } + close(fd); + free(dir); +} + +static void +zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout) +{ + Dir *dirs; + char *file, *pfile; + int i, nf, nd; + + nf = strlen(zh->file) + 1; + if(strcmp(zh->file, ".") == 0){ + nzheads--; + free(zh->file); + pfile = ""; + nf = 1; + }else{ + nf++; + pfile = malloc(nf); + if(pfile == nil) + error("out of memory"); + snprint(pfile, nf, "%s/", zh->file); + free(zh->file); + zh->file = pfile; + header(bout, zh); + } + + nf += 256; /* plenty of room */ + file = malloc(nf); + if(file == nil) + error("out of memory"); + while((nd = dirread(fd, &dirs)) > 0){ + for(i = 0; i < nd; i++){ + snprint(file, nf, "%s%s", pfile, dirs[i].name); + zip(bout, file, stdout); + } + free(dirs); + } +} + +static void +header(Biobuf *bout, ZipHead *zh) +{ + int flen; + + if(verbose) + fprint(2, "adding %s\n", zh->file); + put4(bout, ZHeader); + put1(bout, zh->extvers); + put1(bout, zh->extos); + put2(bout, zh->flags); + put2(bout, zh->meth); + put2(bout, zh->modtime); + put2(bout, zh->moddate); + put4(bout, zh->crc); + put4(bout, zh->csize); + put4(bout, zh->uncsize); + flen = strlen(zh->file); + put2(bout, flen); + put2(bout, 0); + if(Bwrite(bout, zh->file, flen) != flen) + error("write error"); +} + +static void +trailer(Biobuf *bout, ZipHead *zh, vlong off) +{ + vlong coff; + + coff = -1; + if(!(zh->flags & ZTrailInfo)){ + coff = Boffset(bout); + if(Bseek(bout, off + ZHeadCrc, 0) < 0) + error("can't seek in archive"); + } + put4(bout, zh->crc); + put4(bout, zh->csize); + put4(bout, zh->uncsize); + if(!(zh->flags & ZTrailInfo)){ + if(Bseek(bout, coff, 0) < 0) + error("can't seek in archive"); + } +} + +static void +cheader(Biobuf *bout, ZipHead *zh) +{ + int flen; + + put4(bout, ZCHeader); + put1(bout, zh->madevers); + put1(bout, zh->madeos); + put1(bout, zh->extvers); + put1(bout, zh->extos); + put2(bout, zh->flags & ~ZTrailInfo); + put2(bout, zh->meth); + put2(bout, zh->modtime); + put2(bout, zh->moddate); + put4(bout, zh->crc); + put4(bout, zh->csize); + put4(bout, zh->uncsize); + flen = strlen(zh->file); + put2(bout, flen); + put2(bout, 0); + put2(bout, 0); + put2(bout, 0); + put2(bout, zh->iattr); + put4(bout, zh->eattr); + put4(bout, zh->off); + if(Bwrite(bout, zh->file, flen) != flen) + error("write error"); +} + +static void +putCDir(Biobuf *bout) +{ + vlong hoff, ecoff; + int i; + + hoff = Boffset(bout); + + for(i = 0; i < nzheads; i++) + cheader(bout, &zheads[i]); + + ecoff = Boffset(bout); + + if(nzheads >= (1 << 16)) + error("too many entries in zip file: max %d", (1 << 16) - 1); + put4(bout, ZECHeader); + put2(bout, 0); + put2(bout, 0); + put2(bout, nzheads); + put2(bout, nzheads); + put4(bout, ecoff - hoff); + put4(bout, hoff); + put2(bout, 0); +} + +static int +crcread(void *fd, void *buf, int n) +{ + int nr, m; + + nr = 0; + for(; !eof && n > 0; n -= m){ + m = read((int)fd, (char*)buf+nr, n); + if(m <= 0){ + eof = 1; + if(m < 0) +{ +fprint(2, "input error %r\n"); + return -1; +} + break; + } + nr += m; + } + crc = blockcrc(crctab, crc, buf, nr); + totr += nr; + return nr; +} + +static int +zwrite(void *bout, void *buf, int n) +{ + if(n != Bwrite(bout, buf, n)){ + eof = 1; + return -1; + } + totw += n; + return n; +} + +static void +put4(Biobuf *b, ulong v) +{ + int i; + + for(i = 0; i < 4; i++){ + if(Bputc(b, v) < 0) + error("write error"); + v >>= 8; + } +} + +static void +put2(Biobuf *b, int v) +{ + int i; + + for(i = 0; i < 2; i++){ + if(Bputc(b, v) < 0) + error("write error"); + v >>= 8; + } +} + +static void +put1(Biobuf *b, int v) +{ + if(Bputc(b, v)< 0) + error("unexpected eof reading file information"); +} + +static void +error(char *fmt, ...) +{ + va_list arg; + + fprint(2, "zip: "); + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); + fprint(2, "\n"); + + longjmp(zjmp, 1); +} |