17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
480ee5cbfSDavid du Colombier #include <flate.h>
57dd7cddfSDavid du Colombier #include "gzip.h"
67dd7cddfSDavid du Colombier
77dd7cddfSDavid du Colombier typedef struct GZHead GZHead;
87dd7cddfSDavid du Colombier
97dd7cddfSDavid du Colombier struct GZHead
107dd7cddfSDavid du Colombier {
117dd7cddfSDavid du Colombier ulong mtime;
127dd7cddfSDavid du Colombier char *file;
137dd7cddfSDavid du Colombier };
147dd7cddfSDavid du Colombier
157dd7cddfSDavid du Colombier static int crcwrite(void *bout, void *buf, int n);
167dd7cddfSDavid du Colombier static int get1(Biobuf *b);
177dd7cddfSDavid du Colombier static ulong get4(Biobuf *b);
187dd7cddfSDavid du Colombier static int gunzipf(char *file, int stdout);
197dd7cddfSDavid du Colombier static int gunzip(int ofd, char *ofile, Biobuf *bin);
207dd7cddfSDavid du Colombier static void header(Biobuf *bin, GZHead *h);
217dd7cddfSDavid du Colombier static void trailer(Biobuf *bin, long wlen);
227dd7cddfSDavid du Colombier static void error(char*, ...);
237dd7cddfSDavid du Colombier #pragma varargck argpos error 1
247dd7cddfSDavid du Colombier
257dd7cddfSDavid du Colombier static Biobuf bin;
267dd7cddfSDavid du Colombier static ulong crc;
2780ee5cbfSDavid du Colombier static ulong *crctab;
287dd7cddfSDavid du Colombier static int debug;
297dd7cddfSDavid du Colombier static char *delfile;
307dd7cddfSDavid du Colombier static vlong gzok;
317dd7cddfSDavid du Colombier static char *infile;
327dd7cddfSDavid du Colombier static int settimes;
337dd7cddfSDavid du Colombier static int table;
347dd7cddfSDavid du Colombier static int verbose;
357dd7cddfSDavid du Colombier static int wbad;
367dd7cddfSDavid du Colombier static ulong wlen;
377dd7cddfSDavid du Colombier static jmp_buf zjmp;
387dd7cddfSDavid du Colombier
397dd7cddfSDavid du Colombier void
usage(void)407dd7cddfSDavid du Colombier usage(void)
417dd7cddfSDavid du Colombier {
427dd7cddfSDavid du Colombier fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");
437dd7cddfSDavid du Colombier exits("usage");
447dd7cddfSDavid du Colombier }
457dd7cddfSDavid du Colombier
467dd7cddfSDavid du Colombier void
main(int argc,char * argv[])477dd7cddfSDavid du Colombier main(int argc, char *argv[])
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier int i, ok, stdout;
507dd7cddfSDavid du Colombier
517dd7cddfSDavid du Colombier stdout = 0;
527dd7cddfSDavid du Colombier ARGBEGIN{
537dd7cddfSDavid du Colombier case 'D':
547dd7cddfSDavid du Colombier debug++;
557dd7cddfSDavid du Colombier break;
567dd7cddfSDavid du Colombier case 'c':
577dd7cddfSDavid du Colombier stdout++;
587dd7cddfSDavid du Colombier break;
597dd7cddfSDavid du Colombier case 't':
607dd7cddfSDavid du Colombier table++;
617dd7cddfSDavid du Colombier break;
627dd7cddfSDavid du Colombier case 'T':
637dd7cddfSDavid du Colombier settimes++;
647dd7cddfSDavid du Colombier break;
657dd7cddfSDavid du Colombier case 'v':
667dd7cddfSDavid du Colombier verbose++;
677dd7cddfSDavid du Colombier break;
687dd7cddfSDavid du Colombier default:
697dd7cddfSDavid du Colombier usage();
707dd7cddfSDavid du Colombier break;
717dd7cddfSDavid du Colombier }ARGEND
727dd7cddfSDavid du Colombier
7380ee5cbfSDavid du Colombier crctab = mkcrctab(GZCRCPOLY);
7480ee5cbfSDavid du Colombier ok = inflateinit();
7580ee5cbfSDavid du Colombier if(ok != FlateOk)
76*14cc0f53SDavid du Colombier sysfatal("inflateinit failed: %s", flateerr(ok));
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier if(argc == 0){
797dd7cddfSDavid du Colombier Binit(&bin, 0, OREAD);
807dd7cddfSDavid du Colombier settimes = 0;
817dd7cddfSDavid du Colombier infile = "<stdin>";
827dd7cddfSDavid du Colombier ok = gunzip(1, "<stdout>", &bin);
837dd7cddfSDavid du Colombier }else{
847dd7cddfSDavid du Colombier ok = 1;
857dd7cddfSDavid du Colombier if(stdout)
867dd7cddfSDavid du Colombier settimes = 0;
877dd7cddfSDavid du Colombier for(i = 0; i < argc; i++)
887dd7cddfSDavid du Colombier ok &= gunzipf(argv[i], stdout);
897dd7cddfSDavid du Colombier }
907dd7cddfSDavid du Colombier
917dd7cddfSDavid du Colombier exits(ok ? nil: "errors");
927dd7cddfSDavid du Colombier }
937dd7cddfSDavid du Colombier
947dd7cddfSDavid du Colombier static int
gunzipf(char * file,int stdout)957dd7cddfSDavid du Colombier gunzipf(char *file, int stdout)
967dd7cddfSDavid du Colombier {
979a747e4fSDavid du Colombier char ofile[256], *s;
987dd7cddfSDavid du Colombier int ofd, ifd, ok;
997dd7cddfSDavid du Colombier
1007dd7cddfSDavid du Colombier infile = file;
1017dd7cddfSDavid du Colombier ifd = open(file, OREAD);
1027dd7cddfSDavid du Colombier if(ifd < 0){
1037dd7cddfSDavid du Colombier fprint(2, "gunzip: can't open %s: %r\n", file);
1047dd7cddfSDavid du Colombier return 0;
1057dd7cddfSDavid du Colombier }
1067dd7cddfSDavid du Colombier
1077dd7cddfSDavid du Colombier Binit(&bin, ifd, OREAD);
1087dd7cddfSDavid du Colombier if(Bgetc(&bin) != GZMAGIC1 || Bgetc(&bin) != GZMAGIC2 || Bgetc(&bin) != GZDEFLATE){
1097dd7cddfSDavid du Colombier fprint(2, "gunzip: %s is not a gzip deflate file\n", file);
1107dd7cddfSDavid du Colombier Bterm(&bin);
1117dd7cddfSDavid du Colombier close(ifd);
1127dd7cddfSDavid du Colombier return 0;
1137dd7cddfSDavid du Colombier }
1147dd7cddfSDavid du Colombier Bungetc(&bin);
1157dd7cddfSDavid du Colombier Bungetc(&bin);
1167dd7cddfSDavid du Colombier Bungetc(&bin);
1177dd7cddfSDavid du Colombier
1187dd7cddfSDavid du Colombier if(table)
1197dd7cddfSDavid du Colombier ofd = -1;
1207dd7cddfSDavid du Colombier else if(stdout){
1217dd7cddfSDavid du Colombier ofd = 1;
1227dd7cddfSDavid du Colombier strcpy(ofile, "<stdout>");
1237dd7cddfSDavid du Colombier }else{
1247dd7cddfSDavid du Colombier s = strrchr(file, '/');
1257dd7cddfSDavid du Colombier if(s != nil)
1267dd7cddfSDavid du Colombier s++;
1277dd7cddfSDavid du Colombier else
1287dd7cddfSDavid du Colombier s = file;
1299a747e4fSDavid du Colombier strecpy(ofile, ofile+sizeof ofile, s);
1307dd7cddfSDavid du Colombier s = strrchr(ofile, '.');
1317dd7cddfSDavid du Colombier if(s != nil && s != ofile && strcmp(s, ".gz") == 0)
1327dd7cddfSDavid du Colombier *s = '\0';
1337dd7cddfSDavid du Colombier else if(s != nil && strcmp(s, ".tgz") == 0)
1347dd7cddfSDavid du Colombier strcpy(s, ".tar");
1357dd7cddfSDavid du Colombier else if(strcmp(file, ofile) == 0){
1367dd7cddfSDavid du Colombier fprint(2, "gunzip: can't overwrite %s\n", file);
1377dd7cddfSDavid du Colombier Bterm(&bin);
1387dd7cddfSDavid du Colombier close(ifd);
1397dd7cddfSDavid du Colombier return 0;
1407dd7cddfSDavid du Colombier }
1417dd7cddfSDavid du Colombier
1427dd7cddfSDavid du Colombier ofd = create(ofile, OWRITE, 0666);
1437dd7cddfSDavid du Colombier if(ofd < 0){
1447dd7cddfSDavid du Colombier fprint(2, "gunzip: can't create %s: %r\n", ofile);
1457dd7cddfSDavid du Colombier Bterm(&bin);
1467dd7cddfSDavid du Colombier close(ifd);
1477dd7cddfSDavid du Colombier return 0;
1487dd7cddfSDavid du Colombier }
1497dd7cddfSDavid du Colombier delfile = ofile;
1507dd7cddfSDavid du Colombier }
1517dd7cddfSDavid du Colombier
1527dd7cddfSDavid du Colombier wbad = 0;
1537dd7cddfSDavid du Colombier ok = gunzip(ofd, ofile, &bin);
1547dd7cddfSDavid du Colombier Bterm(&bin);
1557dd7cddfSDavid du Colombier close(ifd);
1567dd7cddfSDavid du Colombier if(wbad){
1577dd7cddfSDavid du Colombier fprint(2, "gunzip: can't write %s: %r\n", ofile);
1587dd7cddfSDavid du Colombier if(delfile)
1597dd7cddfSDavid du Colombier remove(delfile);
1607dd7cddfSDavid du Colombier }
1617dd7cddfSDavid du Colombier delfile = nil;
1627dd7cddfSDavid du Colombier if(!stdout && ofd >= 0)
1637dd7cddfSDavid du Colombier close(ofd);
1647dd7cddfSDavid du Colombier return ok;
1657dd7cddfSDavid du Colombier }
1667dd7cddfSDavid du Colombier
1677dd7cddfSDavid du Colombier static int
gunzip(int ofd,char * ofile,Biobuf * bin)1687dd7cddfSDavid du Colombier gunzip(int ofd, char *ofile, Biobuf *bin)
1697dd7cddfSDavid du Colombier {
1709a747e4fSDavid du Colombier Dir *d;
1717dd7cddfSDavid du Colombier GZHead h;
17280ee5cbfSDavid du Colombier int err;
1737dd7cddfSDavid du Colombier
1747dd7cddfSDavid du Colombier h.file = nil;
1757dd7cddfSDavid du Colombier gzok = 0;
1767dd7cddfSDavid du Colombier for(;;){
1777dd7cddfSDavid du Colombier if(Bgetc(bin) < 0)
1787dd7cddfSDavid du Colombier return 1;
1797dd7cddfSDavid du Colombier Bungetc(bin);
1807dd7cddfSDavid du Colombier
1817dd7cddfSDavid du Colombier if(setjmp(zjmp))
1827dd7cddfSDavid du Colombier return 0;
1837dd7cddfSDavid du Colombier header(bin, &h);
1847dd7cddfSDavid du Colombier gzok = 0;
1857dd7cddfSDavid du Colombier
1867dd7cddfSDavid du Colombier wlen = 0;
1877dd7cddfSDavid du Colombier crc = 0;
1887dd7cddfSDavid du Colombier
1897dd7cddfSDavid du Colombier if(!table && verbose)
1907dd7cddfSDavid du Colombier fprint(2, "extracting %s to %s\n", h.file, ofile);
1917dd7cddfSDavid du Colombier
19280ee5cbfSDavid du Colombier err = inflate((void*)ofd, crcwrite, bin, (int(*)(void*))Bgetc);
19380ee5cbfSDavid du Colombier if(err != FlateOk)
19480ee5cbfSDavid du Colombier error("inflate failed: %s", flateerr(err));
1957dd7cddfSDavid du Colombier
1967dd7cddfSDavid du Colombier trailer(bin, wlen);
1977dd7cddfSDavid du Colombier
1987dd7cddfSDavid du Colombier if(table){
1997dd7cddfSDavid du Colombier if(verbose)
2007dd7cddfSDavid du Colombier print("%-32s %10ld %s", h.file, wlen, ctime(h.mtime));
2017dd7cddfSDavid du Colombier else
2027dd7cddfSDavid du Colombier print("%s\n", h.file);
2039a747e4fSDavid du Colombier }else if(settimes && h.mtime && (d=dirfstat(ofd)) != nil){
2049a747e4fSDavid du Colombier d->mtime = h.mtime;
2059a747e4fSDavid du Colombier dirfwstat(ofd, d);
2069a747e4fSDavid du Colombier free(d);
2077dd7cddfSDavid du Colombier }
2087dd7cddfSDavid du Colombier
2097dd7cddfSDavid du Colombier free(h.file);
2107dd7cddfSDavid du Colombier h.file = nil;
2117dd7cddfSDavid du Colombier gzok = Boffset(bin);
2127dd7cddfSDavid du Colombier }
2137dd7cddfSDavid du Colombier }
2147dd7cddfSDavid du Colombier
2157dd7cddfSDavid du Colombier static void
header(Biobuf * bin,GZHead * h)2167dd7cddfSDavid du Colombier header(Biobuf *bin, GZHead *h)
2177dd7cddfSDavid du Colombier {
2187dd7cddfSDavid du Colombier char *s;
2197dd7cddfSDavid du Colombier int i, c, flag, ns, nsa;
2207dd7cddfSDavid du Colombier
2217dd7cddfSDavid du Colombier if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)
2227dd7cddfSDavid du Colombier error("bad gzip file magic");
2237dd7cddfSDavid du Colombier if(get1(bin) != GZDEFLATE)
2247dd7cddfSDavid du Colombier error("unknown compression type");
2257dd7cddfSDavid du Colombier
2267dd7cddfSDavid du Colombier flag = get1(bin);
2277dd7cddfSDavid du Colombier if(flag & ~(GZFTEXT|GZFEXTRA|GZFNAME|GZFCOMMENT|GZFHCRC))
2287dd7cddfSDavid du Colombier fprint(2, "gunzip: reserved flags set, data may not be decompressed correctly\n");
2297dd7cddfSDavid du Colombier
2307dd7cddfSDavid du Colombier /* mod time */
2317dd7cddfSDavid du Colombier h->mtime = get4(bin);
2327dd7cddfSDavid du Colombier
2337dd7cddfSDavid du Colombier /* extra flags */
2347dd7cddfSDavid du Colombier get1(bin);
2357dd7cddfSDavid du Colombier
2367dd7cddfSDavid du Colombier /* OS type */
2377dd7cddfSDavid du Colombier get1(bin);
2387dd7cddfSDavid du Colombier
2397dd7cddfSDavid du Colombier if(flag & GZFEXTRA)
2407dd7cddfSDavid du Colombier for(i=get1(bin); i>0; i--)
2417dd7cddfSDavid du Colombier get1(bin);
2427dd7cddfSDavid du Colombier
2437dd7cddfSDavid du Colombier /* name */
2447dd7cddfSDavid du Colombier if(flag & GZFNAME){
2457dd7cddfSDavid du Colombier nsa = 32;
2467dd7cddfSDavid du Colombier ns = 0;
2477dd7cddfSDavid du Colombier s = malloc(nsa);
2487dd7cddfSDavid du Colombier if(s == nil)
2497dd7cddfSDavid du Colombier error("out of memory");
2507dd7cddfSDavid du Colombier while((c = get1(bin)) != 0){
2517dd7cddfSDavid du Colombier s[ns++] = c;
2527dd7cddfSDavid du Colombier if(ns >= nsa){
2537dd7cddfSDavid du Colombier nsa += 32;
2547dd7cddfSDavid du Colombier s = realloc(s, nsa);
2557dd7cddfSDavid du Colombier if(s == nil)
2567dd7cddfSDavid du Colombier error("out of memory");
2577dd7cddfSDavid du Colombier }
2587dd7cddfSDavid du Colombier }
2597dd7cddfSDavid du Colombier s[ns] = '\0';
2607dd7cddfSDavid du Colombier h->file = s;
2617dd7cddfSDavid du Colombier }else
2627dd7cddfSDavid du Colombier h->file = strdup("<unnamed file>");
2637dd7cddfSDavid du Colombier
2647dd7cddfSDavid du Colombier /* comment */
2657dd7cddfSDavid du Colombier if(flag & GZFCOMMENT)
2667dd7cddfSDavid du Colombier while(get1(bin) != 0)
2677dd7cddfSDavid du Colombier ;
2687dd7cddfSDavid du Colombier
2697dd7cddfSDavid du Colombier /* crc16 */
2707dd7cddfSDavid du Colombier if(flag & GZFHCRC){
2717dd7cddfSDavid du Colombier get1(bin);
2727dd7cddfSDavid du Colombier get1(bin);
2737dd7cddfSDavid du Colombier }
2747dd7cddfSDavid du Colombier }
2757dd7cddfSDavid du Colombier
2767dd7cddfSDavid du Colombier static void
trailer(Biobuf * bin,long wlen)2777dd7cddfSDavid du Colombier trailer(Biobuf *bin, long wlen)
2787dd7cddfSDavid du Colombier {
2797dd7cddfSDavid du Colombier ulong tcrc;
2807dd7cddfSDavid du Colombier long len;
2817dd7cddfSDavid du Colombier
2827dd7cddfSDavid du Colombier tcrc = get4(bin);
2837dd7cddfSDavid du Colombier if(tcrc != crc)
2847dd7cddfSDavid du Colombier error("crc mismatch");
2857dd7cddfSDavid du Colombier
2867dd7cddfSDavid du Colombier len = get4(bin);
2877dd7cddfSDavid du Colombier
2887dd7cddfSDavid du Colombier if(len != wlen)
2897dd7cddfSDavid du Colombier error("bad output length: expected %lud got %lud", wlen, len);
2907dd7cddfSDavid du Colombier }
2917dd7cddfSDavid du Colombier
2927dd7cddfSDavid du Colombier static ulong
get4(Biobuf * b)2937dd7cddfSDavid du Colombier get4(Biobuf *b)
2947dd7cddfSDavid du Colombier {
2957dd7cddfSDavid du Colombier ulong v;
2967dd7cddfSDavid du Colombier int i, c;
2977dd7cddfSDavid du Colombier
2987dd7cddfSDavid du Colombier v = 0;
2997dd7cddfSDavid du Colombier for(i = 0; i < 4; i++){
3007dd7cddfSDavid du Colombier c = Bgetc(b);
3017dd7cddfSDavid du Colombier if(c < 0)
3027dd7cddfSDavid du Colombier error("unexpected eof reading file information");
3037dd7cddfSDavid du Colombier v |= c << (i * 8);
3047dd7cddfSDavid du Colombier }
3057dd7cddfSDavid du Colombier return v;
3067dd7cddfSDavid du Colombier }
3077dd7cddfSDavid du Colombier
3087dd7cddfSDavid du Colombier static int
get1(Biobuf * b)3097dd7cddfSDavid du Colombier get1(Biobuf *b)
3107dd7cddfSDavid du Colombier {
3117dd7cddfSDavid du Colombier int c;
3127dd7cddfSDavid du Colombier
3137dd7cddfSDavid du Colombier c = Bgetc(b);
3147dd7cddfSDavid du Colombier if(c < 0)
3157dd7cddfSDavid du Colombier error("unexpected eof reading file information");
3167dd7cddfSDavid du Colombier return c;
3177dd7cddfSDavid du Colombier }
3187dd7cddfSDavid du Colombier
3197dd7cddfSDavid du Colombier static int
crcwrite(void * out,void * buf,int n)3207dd7cddfSDavid du Colombier crcwrite(void *out, void *buf, int n)
3217dd7cddfSDavid du Colombier {
3227dd7cddfSDavid du Colombier int fd, nw;
3237dd7cddfSDavid du Colombier
3247dd7cddfSDavid du Colombier wlen += n;
32580ee5cbfSDavid du Colombier crc = blockcrc(crctab, crc, buf, n);
32674f16c81SDavid du Colombier fd = (int)(uintptr)out;
3277dd7cddfSDavid du Colombier if(fd < 0)
3287dd7cddfSDavid du Colombier return n;
3297dd7cddfSDavid du Colombier nw = write(fd, buf, n);
3307dd7cddfSDavid du Colombier if(nw != n)
3317dd7cddfSDavid du Colombier wbad = 1;
3327dd7cddfSDavid du Colombier return nw;
3337dd7cddfSDavid du Colombier }
3347dd7cddfSDavid du Colombier
3357dd7cddfSDavid du Colombier static void
error(char * fmt,...)3367dd7cddfSDavid du Colombier error(char *fmt, ...)
3377dd7cddfSDavid du Colombier {
3387dd7cddfSDavid du Colombier va_list arg;
3397dd7cddfSDavid du Colombier
3407dd7cddfSDavid du Colombier if(gzok)
3417dd7cddfSDavid du Colombier fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok);
3427dd7cddfSDavid du Colombier else{
3439a747e4fSDavid du Colombier fprint(2, "gunzip: ");
3449a747e4fSDavid du Colombier if(infile)
3459a747e4fSDavid du Colombier fprint(2, "%s: ", infile);
3467dd7cddfSDavid du Colombier va_start(arg, fmt);
3479a747e4fSDavid du Colombier vfprint(2, fmt, arg);
3487dd7cddfSDavid du Colombier va_end(arg);
3499a747e4fSDavid du Colombier fprint(2, "\n");
3507dd7cddfSDavid du Colombier
3517dd7cddfSDavid du Colombier if(delfile != nil){
3527dd7cddfSDavid du Colombier fprint(2, "gunzip: removing output file %s\n", delfile);
3537dd7cddfSDavid du Colombier remove(delfile);
3547dd7cddfSDavid du Colombier delfile = nil;
3557dd7cddfSDavid du Colombier }
3567dd7cddfSDavid du Colombier }
3577dd7cddfSDavid du Colombier
3587dd7cddfSDavid du Colombier longjmp(zjmp, 1);
3597dd7cddfSDavid du Colombier }
360