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