zip.c (7108B)
1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <flate.h> 5 #include "zip.h" 6 7 enum 8 { 9 HeadAlloc = 64 10 }; 11 12 static void zip(Biobuf *bout, char *file, int stdout); 13 static void zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout); 14 static int crcread(void *fd, void *buf, int n); 15 static int zwrite(void *bout, void *buf, int n); 16 static void put4(Biobuf *b, u32int v); 17 static void put2(Biobuf *b, int v); 18 static void put1(Biobuf *b, int v); 19 static void header(Biobuf *bout, ZipHead *zh); 20 static void trailer(Biobuf *bout, ZipHead *zh, vlong off); 21 static void putCDir(Biobuf *bout); 22 23 static void error(char*, ...); 24 /* #pragma varargck argpos error 1 */ 25 26 static Biobuf bout; 27 static u32int crc; 28 static u32int *crctab; 29 static int debug; 30 static int eof; 31 static int level; 32 static int nzheads; 33 static u32int totr; 34 static u32int totw; 35 static int verbose; 36 static int zhalloc; 37 static ZipHead *zheads; 38 static jmp_buf zjmp; 39 40 void 41 usage(void) 42 { 43 fprint(2, "usage: zip [-vD] [-1-9] [-f zipfile] file ...\n"); 44 exits("usage"); 45 } 46 47 void 48 main(int volatile argc, char **volatile argv) 49 { 50 char *volatile zfile; 51 int i, fd, err; 52 53 zfile = nil; 54 level = 6; 55 ARGBEGIN{ 56 case 'D': 57 debug++; 58 break; 59 case 'f': 60 zfile = ARGF(); 61 if(zfile == nil) 62 usage(); 63 break; 64 case 'v': 65 verbose++; 66 break; 67 case '1': case '2': case '3': case '4': 68 case '5': case '6': case '7': case '8': case '9': 69 level = ARGC() - '0'; 70 break; 71 default: 72 usage(); 73 break; 74 }ARGEND 75 76 if(argc == 0) 77 usage(); 78 79 crctab = mkcrctab(ZCrcPoly); 80 err = deflateinit(); 81 if(err != FlateOk) 82 sysfatal("deflateinit failed: %s\n", flateerr(err)); 83 84 if(zfile == nil) 85 fd = 1; 86 else{ 87 fd = create(zfile, OWRITE, 0664); 88 if(fd < 0) 89 sysfatal("can't create %s: %r\n", zfile); 90 } 91 Binit(&bout, fd, OWRITE); 92 93 if(setjmp(zjmp)){ 94 if(zfile != nil){ 95 fprint(2, "zip: removing output file %s\n", zfile); 96 remove(zfile); 97 } 98 exits("errors"); 99 } 100 101 for(i = 0; i < argc; i++) 102 zip(&bout, argv[i], zfile == nil); 103 104 putCDir(&bout); 105 106 exits(nil); 107 } 108 109 static void 110 zip(Biobuf *bout, char *file, int stdout) 111 { 112 Tm *t; 113 ZipHead *zh; 114 Dir *dir; 115 vlong off; 116 int fd, err; 117 118 fd = open(file, OREAD); 119 if(fd < 0) 120 error("can't open %s: %r", file); 121 dir = dirfstat(fd); 122 if(dir == nil) 123 error("can't stat %s: %r", file); 124 125 /* 126 * create the header 127 */ 128 if(nzheads >= zhalloc){ 129 zhalloc += HeadAlloc; 130 zheads = realloc(zheads, zhalloc * sizeof(ZipHead)); 131 if(zheads == nil) 132 error("out of memory"); 133 } 134 zh = &zheads[nzheads++]; 135 zh->madeos = ZDos; 136 zh->madevers = (2 * 10) + 0; 137 zh->extos = ZDos; 138 zh->extvers = (2 * 10) + 0; 139 140 t = localtime(dir->mtime); 141 zh->modtime = (t->hour<<11) | (t->min<<5) | (t->sec>>1); 142 zh->moddate = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; 143 144 zh->flags = 0; 145 zh->crc = 0; 146 zh->csize = 0; 147 zh->uncsize = 0; 148 zh->file = strdup(file); 149 if(zh->file == nil) 150 error("out of memory"); 151 zh->iattr = 0; 152 zh->eattr = ZDArch; 153 if((dir->mode & 0700) == 0) 154 zh->eattr |= ZDROnly; 155 zh->off = Boffset(bout); 156 157 if(dir->mode & DMDIR){ 158 zh->eattr |= ZDDir; 159 zh->meth = 0; 160 zipDir(bout, fd, zh, stdout); 161 }else{ 162 zh->meth = 8; 163 if(stdout) 164 zh->flags |= ZTrailInfo; 165 off = Boffset(bout); 166 header(bout, zh); 167 168 crc = 0; 169 eof = 0; 170 totr = 0; 171 totw = 0; 172 err = deflate(bout, zwrite, (void*)(uintptr)fd, crcread, level, debug); 173 if(err != FlateOk) 174 error("deflate failed: %s: %r", flateerr(err)); 175 176 zh->csize = totw; 177 zh->uncsize = totr; 178 zh->crc = crc; 179 trailer(bout, zh, off); 180 } 181 close(fd); 182 free(dir); 183 } 184 185 static void 186 zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout) 187 { 188 Dir *dirs; 189 char *file, *pfile; 190 int i, nf, nd; 191 192 nf = strlen(zh->file) + 1; 193 if(strcmp(zh->file, ".") == 0){ 194 nzheads--; 195 free(zh->file); 196 pfile = ""; 197 nf = 1; 198 }else{ 199 nf++; 200 pfile = malloc(nf); 201 if(pfile == nil) 202 error("out of memory"); 203 snprint(pfile, nf, "%s/", zh->file); 204 free(zh->file); 205 zh->file = pfile; 206 header(bout, zh); 207 } 208 209 nf += 256; /* plenty of room */ 210 file = malloc(nf); 211 if(file == nil) 212 error("out of memory"); 213 while((nd = dirread(fd, &dirs)) > 0){ 214 for(i = 0; i < nd; i++){ 215 snprint(file, nf, "%s%s", pfile, dirs[i].name); 216 zip(bout, file, stdout); 217 } 218 free(dirs); 219 } 220 } 221 222 static void 223 header(Biobuf *bout, ZipHead *zh) 224 { 225 int flen; 226 227 if(verbose) 228 fprint(2, "adding %s\n", zh->file); 229 put4(bout, ZHeader); 230 put1(bout, zh->extvers); 231 put1(bout, zh->extos); 232 put2(bout, zh->flags); 233 put2(bout, zh->meth); 234 put2(bout, zh->modtime); 235 put2(bout, zh->moddate); 236 put4(bout, zh->crc); 237 put4(bout, zh->csize); 238 put4(bout, zh->uncsize); 239 flen = strlen(zh->file); 240 put2(bout, flen); 241 put2(bout, 0); 242 if(Bwrite(bout, zh->file, flen) != flen) 243 error("write error"); 244 } 245 246 static void 247 trailer(Biobuf *bout, ZipHead *zh, vlong off) 248 { 249 vlong coff; 250 251 coff = -1; 252 if(!(zh->flags & ZTrailInfo)){ 253 coff = Boffset(bout); 254 if(Bseek(bout, off + ZHeadCrc, 0) < 0) 255 error("can't seek in archive"); 256 } 257 put4(bout, zh->crc); 258 put4(bout, zh->csize); 259 put4(bout, zh->uncsize); 260 if(!(zh->flags & ZTrailInfo)){ 261 if(Bseek(bout, coff, 0) < 0) 262 error("can't seek in archive"); 263 } 264 } 265 266 static void 267 cheader(Biobuf *bout, ZipHead *zh) 268 { 269 int flen; 270 271 put4(bout, ZCHeader); 272 put1(bout, zh->madevers); 273 put1(bout, zh->madeos); 274 put1(bout, zh->extvers); 275 put1(bout, zh->extos); 276 put2(bout, zh->flags & ~ZTrailInfo); 277 put2(bout, zh->meth); 278 put2(bout, zh->modtime); 279 put2(bout, zh->moddate); 280 put4(bout, zh->crc); 281 put4(bout, zh->csize); 282 put4(bout, zh->uncsize); 283 flen = strlen(zh->file); 284 put2(bout, flen); 285 put2(bout, 0); 286 put2(bout, 0); 287 put2(bout, 0); 288 put2(bout, zh->iattr); 289 put4(bout, zh->eattr); 290 put4(bout, zh->off); 291 if(Bwrite(bout, zh->file, flen) != flen) 292 error("write error"); 293 } 294 295 static void 296 putCDir(Biobuf *bout) 297 { 298 vlong hoff, ecoff; 299 int i; 300 301 hoff = Boffset(bout); 302 303 for(i = 0; i < nzheads; i++) 304 cheader(bout, &zheads[i]); 305 306 ecoff = Boffset(bout); 307 308 if(nzheads >= (1 << 16)) 309 error("too many entries in zip file: max %d", (1 << 16) - 1); 310 put4(bout, ZECHeader); 311 put2(bout, 0); 312 put2(bout, 0); 313 put2(bout, nzheads); 314 put2(bout, nzheads); 315 put4(bout, ecoff - hoff); 316 put4(bout, hoff); 317 put2(bout, 0); 318 } 319 320 static int 321 crcread(void *fd, void *buf, int n) 322 { 323 int nr, m; 324 325 nr = 0; 326 for(; !eof && n > 0; n -= m){ 327 m = read((int)(uintptr)fd, (char*)buf+nr, n); 328 if(m <= 0){ 329 eof = 1; 330 if(m < 0) 331 { 332 fprint(2, "input error %r\n"); 333 return -1; 334 } 335 break; 336 } 337 nr += m; 338 } 339 crc = blockcrc(crctab, crc, buf, nr); 340 totr += nr; 341 return nr; 342 } 343 344 static int 345 zwrite(void *bout, void *buf, int n) 346 { 347 if(n != Bwrite(bout, buf, n)){ 348 eof = 1; 349 return -1; 350 } 351 totw += n; 352 return n; 353 } 354 355 static void 356 put4(Biobuf *b, u32int v) 357 { 358 int i; 359 360 for(i = 0; i < 4; i++){ 361 if(Bputc(b, v) < 0) 362 error("write error"); 363 v >>= 8; 364 } 365 } 366 367 static void 368 put2(Biobuf *b, int v) 369 { 370 int i; 371 372 for(i = 0; i < 2; i++){ 373 if(Bputc(b, v) < 0) 374 error("write error"); 375 v >>= 8; 376 } 377 } 378 379 static void 380 put1(Biobuf *b, int v) 381 { 382 if(Bputc(b, v)< 0) 383 error("unexpected eof reading file information"); 384 } 385 386 static void 387 error(char *fmt, ...) 388 { 389 va_list arg; 390 391 fprint(2, "zip: "); 392 va_start(arg, fmt); 393 vfprint(2, fmt, arg); 394 va_end(arg); 395 fprint(2, "\n"); 396 397 longjmp(zjmp, 1); 398 }