blob: bb348bc8837b31d155d549d0b80180efda6e8bf0 [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <flate.h>
#include "gzip.h"
typedef struct GZHead GZHead;
struct GZHead
{
u32int mtime;
char *file;
};
static int crcwrite(void *bout, void *buf, int n);
static int get1(Biobuf *b);
static u32int get4(Biobuf *b);
static int gunzipf(char *file, int stdout);
static int gunzip(int ofd, char *ofile, Biobuf *bin);
static void header(Biobuf *bin, GZHead *h);
static void trailer(Biobuf *bin, long wlen);
static void error(char*, ...);
/* #pragma varargck argpos error 1 */
static Biobuf bin;
static u32int crc;
static u32int *crctab;
static int debug;
static char *delfile;
static vlong gzok;
static char *infile;
static int settimes;
static int table;
static int verbose;
static int wbad;
static u32int wlen;
static jmp_buf zjmp;
void
usage(void)
{
fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");
exits("usage");
}
void
main(int argc, char *argv[])
{
int i, ok, stdout;
stdout = 0;
ARGBEGIN{
case 'D':
debug++;
break;
case 'c':
stdout++;
break;
case 't':
table++;
break;
case 'T':
settimes++;
break;
case 'v':
verbose++;
break;
default:
usage();
break;
}ARGEND
crctab = mkcrctab(GZCRCPOLY);
ok = inflateinit();
if(ok != FlateOk)
sysfatal("inflateinit failed: %s\n", flateerr(ok));
if(argc == 0){
Binit(&bin, 0, OREAD);
settimes = 0;
infile = "<stdin>";
ok = gunzip(1, "<stdout>", &bin);
}else{
ok = 1;
if(stdout)
settimes = 0;
for(i = 0; i < argc; i++)
ok &= gunzipf(argv[i], stdout);
}
exits(ok ? nil: "errors");
}
static int
gunzipf(char *file, int stdout)
{
char ofile[256], *s;
int ofd, ifd, ok;
infile = file;
ifd = open(file, OREAD);
if(ifd < 0){
fprint(2, "gunzip: can't open %s: %r\n", file);
return 0;
}
Binit(&bin, ifd, OREAD);
if(Bgetc(&bin) != GZMAGIC1 || Bgetc(&bin) != GZMAGIC2 || Bgetc(&bin) != GZDEFLATE){
fprint(2, "gunzip: %s is not a gzip deflate file\n", file);
Bterm(&bin);
close(ifd);
return 0;
}
Bungetc(&bin);
Bungetc(&bin);
Bungetc(&bin);
if(table)
ofd = -1;
else if(stdout){
ofd = 1;
strcpy(ofile, "<stdout>");
}else{
s = strrchr(file, '/');
if(s != nil)
s++;
else
s = file;
strecpy(ofile, ofile+sizeof ofile, s);
s = strrchr(ofile, '.');
if(s != nil && s != ofile && strcmp(s, ".gz") == 0)
*s = '\0';
else if(s != nil && strcmp(s, ".tgz") == 0)
strcpy(s, ".tar");
else if(strcmp(file, ofile) == 0){
fprint(2, "gunzip: can't overwrite %s\n", file);
Bterm(&bin);
close(ifd);
return 0;
}
ofd = create(ofile, OWRITE, 0666);
if(ofd < 0){
fprint(2, "gunzip: can't create %s: %r\n", ofile);
Bterm(&bin);
close(ifd);
return 0;
}
delfile = ofile;
}
wbad = 0;
ok = gunzip(ofd, ofile, &bin);
Bterm(&bin);
close(ifd);
if(wbad){
fprint(2, "gunzip: can't write %s: %r\n", ofile);
if(delfile)
remove(delfile);
}
delfile = nil;
if(!stdout && ofd >= 0)
close(ofd);
return ok;
}
static int
gunzip(int ofd, char *ofile, Biobuf *bin)
{
Dir *d;
GZHead h;
int err;
h.file = nil;
gzok = 0;
for(;;){
if(Bgetc(bin) < 0)
return 1;
Bungetc(bin);
if(setjmp(zjmp))
return 0;
header(bin, &h);
gzok = 0;
wlen = 0;
crc = 0;
if(!table && verbose)
fprint(2, "extracting %s to %s\n", h.file, ofile);
err = inflate((void*)(uintptr)ofd, crcwrite, bin, (int(*)(void*))Bgetc);
if(err != FlateOk)
error("inflate failed: %s", flateerr(err));
trailer(bin, wlen);
if(table){
if(verbose)
print("%-32s %10ld %s", h.file, wlen, ctime(h.mtime));
else
print("%s\n", h.file);
}else if(settimes && h.mtime && (d=dirfstat(ofd)) != nil){
d->mtime = h.mtime;
dirfwstat(ofd, d);
free(d);
}
free(h.file);
h.file = nil;
gzok = Boffset(bin);
}
/* return 0; */
}
static void
header(Biobuf *bin, GZHead *h)
{
char *s;
int i, c, flag, ns, nsa;
if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)
error("bad gzip file magic");
if(get1(bin) != GZDEFLATE)
error("unknown compression type");
flag = get1(bin);
if(flag & ~(GZFTEXT|GZFEXTRA|GZFNAME|GZFCOMMENT|GZFHCRC))
fprint(2, "gunzip: reserved flags set, data may not be decompressed correctly\n");
/* mod time */
h->mtime = get4(bin);
/* extra flags */
get1(bin);
/* OS type */
get1(bin);
if(flag & GZFEXTRA)
for(i=get1(bin); i>0; i--)
get1(bin);
/* name */
if(flag & GZFNAME){
nsa = 32;
ns = 0;
s = malloc(nsa);
if(s == nil)
error("out of memory");
while((c = get1(bin)) != 0){
s[ns++] = c;
if(ns >= nsa){
nsa += 32;
s = realloc(s, nsa);
if(s == nil)
error("out of memory");
}
}
s[ns] = '\0';
h->file = s;
}else
h->file = strdup("<unnamed file>");
/* comment */
if(flag & GZFCOMMENT)
while(get1(bin) != 0)
;
/* crc16 */
if(flag & GZFHCRC){
get1(bin);
get1(bin);
}
}
static void
trailer(Biobuf *bin, long wlen)
{
u32int tcrc;
long len;
tcrc = get4(bin);
if(tcrc != crc)
error("crc mismatch");
len = get4(bin);
if(len != wlen)
error("bad output length: expected %lud got %lud", wlen, len);
}
static u32int
get4(Biobuf *b)
{
u32int v;
int i, c;
v = 0;
for(i = 0; i < 4; i++){
c = Bgetc(b);
if(c < 0)
error("unexpected eof reading file information");
v |= c << (i * 8);
}
return v;
}
static int
get1(Biobuf *b)
{
int c;
c = Bgetc(b);
if(c < 0)
error("unexpected eof reading file information");
return c;
}
static int
crcwrite(void *out, void *buf, int n)
{
int fd, nw;
wlen += n;
crc = blockcrc(crctab, crc, buf, n);
fd = (int)(uintptr)out;
if(fd < 0)
return n;
nw = write(fd, buf, n);
if(nw != n)
wbad = 1;
return nw;
}
static void
error(char *fmt, ...)
{
va_list arg;
if(gzok)
fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok);
else{
fprint(2, "gunzip: ");
if(infile)
fprint(2, "%s: ", infile);
va_start(arg, fmt);
vfprint(2, fmt, arg);
va_end(arg);
fprint(2, "\n");
if(delfile != nil){
fprint(2, "gunzip: removing output file %s\n", delfile);
remove(delfile);
delfile = nil;
}
}
longjmp(zjmp, 1);
}