mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
360 lines
5.9 KiB
C
360 lines
5.9 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <bio.h>
|
|
#include <flate.h>
|
|
#include "gzip.h"
|
|
|
|
typedef struct GZHead GZHead;
|
|
|
|
struct GZHead
|
|
{
|
|
ulong mtime;
|
|
char *file;
|
|
};
|
|
|
|
static int crcwrite(void *bout, void *buf, int n);
|
|
static int get1(Biobuf *b);
|
|
static ulong 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 ulong crc;
|
|
static ulong *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 ulong 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*)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)
|
|
{
|
|
ulong 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 ulong
|
|
get4(Biobuf *b)
|
|
{
|
|
ulong 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)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);
|
|
}
|