URI:
       tgunzip.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tgunzip.c (6075B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <flate.h>
            5 #include "gzip.h"
            6 
            7 typedef struct        GZHead        GZHead;
            8 
            9 struct GZHead
           10 {
           11         u32int        mtime;
           12         char        *file;
           13 };
           14 
           15 static        int        crcwrite(void *bout, void *buf, int n);
           16 static        int        get1(Biobuf *b);
           17 static        u32int        get4(Biobuf *b);
           18 static        int        gunzipf(char *file, int stdout);
           19 static        int        gunzip(int ofd, char *ofile, Biobuf *bin);
           20 static        void        header(Biobuf *bin, GZHead *h);
           21 static        void        trailer(Biobuf *bin, long wlen);
           22 static        void        error(char*, ...);
           23 /* #pragma        varargck        argpos        error        1 */
           24 
           25 static        Biobuf        bin;
           26 static        u32int        crc;
           27 static        u32int        *crctab;
           28 static        int        debug;
           29 static        char        *delfile;
           30 static        vlong        gzok;
           31 static        char        *infile;
           32 static        int        settimes;
           33 static        int        table;
           34 static        int        verbose;
           35 static        int        wbad;
           36 static        u32int        wlen;
           37 static        jmp_buf        zjmp;
           38 
           39 void
           40 usage(void)
           41 {
           42         fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");
           43         exits("usage");
           44 }
           45 
           46 void
           47 main(int argc, char *argv[])
           48 {
           49         int i, ok, stdout;
           50 
           51         stdout = 0;
           52         ARGBEGIN{
           53         case 'D':
           54                 debug++;
           55                 break;
           56         case 'c':
           57                 stdout++;
           58                 break;
           59         case 't':
           60                 table++;
           61                 break;
           62         case 'T':
           63                 settimes++;
           64                 break;
           65         case 'v':
           66                 verbose++;
           67                 break;
           68         default:
           69                 usage();
           70                 break;
           71         }ARGEND
           72 
           73         crctab = mkcrctab(GZCRCPOLY);
           74         ok = inflateinit();
           75         if(ok != FlateOk)
           76                 sysfatal("inflateinit failed: %s\n", flateerr(ok));
           77 
           78         if(argc == 0){
           79                 Binit(&bin, 0, OREAD);
           80                 settimes = 0;
           81                 infile = "<stdin>";
           82                 ok = gunzip(1, "<stdout>", &bin);
           83         }else{
           84                 ok = 1;
           85                 if(stdout)
           86                         settimes = 0;
           87                 for(i = 0; i < argc; i++)
           88                         ok &= gunzipf(argv[i], stdout);
           89         }
           90 
           91         exits(ok ? nil: "errors");
           92 }
           93 
           94 static int
           95 gunzipf(char *file, int stdout)
           96 {
           97         char ofile[256], *s;
           98         int ofd, ifd, ok;
           99 
          100         infile = file;
          101         ifd = open(file, OREAD);
          102         if(ifd < 0){
          103                 fprint(2, "gunzip: can't open %s: %r\n", file);
          104                 return 0;
          105         }
          106 
          107         Binit(&bin, ifd, OREAD);
          108         if(Bgetc(&bin) != GZMAGIC1 || Bgetc(&bin) != GZMAGIC2 || Bgetc(&bin) != GZDEFLATE){
          109                 fprint(2, "gunzip: %s is not a gzip deflate file\n", file);
          110                 Bterm(&bin);
          111                 close(ifd);
          112                 return 0;
          113         }
          114         Bungetc(&bin);
          115         Bungetc(&bin);
          116         Bungetc(&bin);
          117 
          118         if(table)
          119                 ofd = -1;
          120         else if(stdout){
          121                 ofd = 1;
          122                 strcpy(ofile, "<stdout>");
          123         }else{
          124                 s = strrchr(file, '/');
          125                 if(s != nil)
          126                         s++;
          127                 else
          128                         s = file;
          129                 strecpy(ofile, ofile+sizeof ofile, s);
          130                 s = strrchr(ofile, '.');
          131                 if(s != nil && s != ofile && strcmp(s, ".gz") == 0)
          132                         *s = '\0';
          133                 else if(s != nil && strcmp(s, ".tgz") == 0)
          134                         strcpy(s, ".tar");
          135                 else if(strcmp(file, ofile) == 0){
          136                         fprint(2, "gunzip: can't overwrite %s\n", file);
          137                         Bterm(&bin);
          138                         close(ifd);
          139                         return 0;
          140                 }
          141 
          142                 ofd = create(ofile, OWRITE, 0666);
          143                 if(ofd < 0){
          144                         fprint(2, "gunzip: can't create %s: %r\n", ofile);
          145                         Bterm(&bin);
          146                         close(ifd);
          147                         return 0;
          148                 }
          149                 delfile = ofile;
          150         }
          151 
          152         wbad = 0;
          153         ok = gunzip(ofd, ofile, &bin);
          154         Bterm(&bin);
          155         close(ifd);
          156         if(wbad){
          157                 fprint(2, "gunzip: can't write %s: %r\n", ofile);
          158                 if(delfile)
          159                         remove(delfile);
          160         }
          161         delfile = nil;
          162         if(!stdout && ofd >= 0)
          163                 close(ofd);
          164         return ok;
          165 }
          166 
          167 static int
          168 gunzip(int ofd, char *ofile, Biobuf *bin)
          169 {
          170         Dir *d;
          171         GZHead h;
          172         int err;
          173 
          174         h.file = nil;
          175         gzok = 0;
          176         for(;;){
          177                 if(Bgetc(bin) < 0)
          178                         return 1;
          179                 Bungetc(bin);
          180 
          181                 if(setjmp(zjmp))
          182                         return 0;
          183                 header(bin, &h);
          184                 gzok = 0;
          185 
          186                 wlen = 0;
          187                 crc = 0;
          188 
          189                 if(!table && verbose)
          190                         fprint(2, "extracting %s to %s\n", h.file, ofile);
          191 
          192                 err = inflate((void*)(uintptr)ofd, crcwrite, bin, (int(*)(void*))Bgetc);
          193                 if(err != FlateOk)
          194                         error("inflate failed: %s", flateerr(err));
          195 
          196                 trailer(bin, wlen);
          197 
          198                 if(table){
          199                         if(verbose)
          200                                 print("%-32s %10ld %s", h.file, wlen, ctime(h.mtime));
          201                         else
          202                                 print("%s\n", h.file);
          203                 }else if(settimes && h.mtime && (d=dirfstat(ofd)) != nil){
          204                         d->mtime = h.mtime;
          205                         dirfwstat(ofd, d);
          206                         free(d);
          207                 }
          208 
          209                 free(h.file);
          210                 h.file = nil;
          211                 gzok = Boffset(bin);
          212         }
          213 /*        return 0; */
          214 }
          215 
          216 static void
          217 header(Biobuf *bin, GZHead *h)
          218 {
          219         char *s;
          220         int i, c, flag, ns, nsa;
          221 
          222         if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)
          223                 error("bad gzip file magic");
          224         if(get1(bin) != GZDEFLATE)
          225                 error("unknown compression type");
          226 
          227         flag = get1(bin);
          228         if(flag & ~(GZFTEXT|GZFEXTRA|GZFNAME|GZFCOMMENT|GZFHCRC))
          229                 fprint(2, "gunzip: reserved flags set, data may not be decompressed correctly\n");
          230 
          231         /* mod time */
          232         h->mtime = get4(bin);
          233 
          234         /* extra flags */
          235         get1(bin);
          236 
          237         /* OS type */
          238         get1(bin);
          239 
          240         if(flag & GZFEXTRA)
          241                 for(i=get1(bin); i>0; i--)
          242                         get1(bin);
          243 
          244         /* name */
          245         if(flag & GZFNAME){
          246                 nsa = 32;
          247                 ns = 0;
          248                 s = malloc(nsa);
          249                 if(s == nil)
          250                         error("out of memory");
          251                 while((c = get1(bin)) != 0){
          252                         s[ns++] = c;
          253                         if(ns >= nsa){
          254                                 nsa += 32;
          255                                 s = realloc(s, nsa);
          256                                 if(s == nil)
          257                                         error("out of memory");
          258                         }
          259                 }
          260                 s[ns] = '\0';
          261                 h->file = s;
          262         }else
          263                 h->file = strdup("<unnamed file>");
          264 
          265         /* comment */
          266         if(flag & GZFCOMMENT)
          267                 while(get1(bin) != 0)
          268                         ;
          269 
          270         /* crc16 */
          271         if(flag & GZFHCRC){
          272                 get1(bin);
          273                 get1(bin);
          274         }
          275 }
          276 
          277 static void
          278 trailer(Biobuf *bin, long wlen)
          279 {
          280         u32int tcrc;
          281         long len;
          282 
          283         tcrc = get4(bin);
          284         if(tcrc != crc)
          285                 error("crc mismatch");
          286 
          287         len = get4(bin);
          288 
          289         if(len != wlen)
          290                 error("bad output length: expected %lud got %lud", wlen, len);
          291 }
          292 
          293 static u32int
          294 get4(Biobuf *b)
          295 {
          296         u32int v;
          297         int i, c;
          298 
          299         v = 0;
          300         for(i = 0; i < 4; i++){
          301                 c = Bgetc(b);
          302                 if(c < 0)
          303                         error("unexpected eof reading file information");
          304                 v |= c << (i * 8);
          305         }
          306         return v;
          307 }
          308 
          309 static int
          310 get1(Biobuf *b)
          311 {
          312         int c;
          313 
          314         c = Bgetc(b);
          315         if(c < 0)
          316                 error("unexpected eof reading file information");
          317         return c;
          318 }
          319 
          320 static int
          321 crcwrite(void *out, void *buf, int n)
          322 {
          323         int fd, nw;
          324 
          325         wlen += n;
          326         crc = blockcrc(crctab, crc, buf, n);
          327         fd = (int)(uintptr)out;
          328         if(fd < 0)
          329                 return n;
          330         nw = write(fd, buf, n);
          331         if(nw != n)
          332                 wbad = 1;
          333         return nw;
          334 }
          335 
          336 static void
          337 error(char *fmt, ...)
          338 {
          339         va_list arg;
          340 
          341         if(gzok)
          342                 fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok);
          343         else{
          344                 fprint(2, "gunzip: ");
          345                 if(infile)
          346                         fprint(2, "%s: ", infile);
          347                 va_start(arg, fmt);
          348                 vfprint(2, fmt, arg);
          349                 va_end(arg);
          350                 fprint(2, "\n");
          351 
          352                 if(delfile != nil){
          353                         fprint(2, "gunzip: removing output file %s\n", delfile);
          354                         remove(delfile);
          355                         delfile = nil;
          356                 }
          357         }
          358 
          359         longjmp(zjmp, 1);
          360 }