rsc | ff3adf6 | 2004-04-14 20:09:21 +0000 | [diff] [blame] | 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, ulong 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 ulong crc; |
| 28 | static ulong *crctab; |
| 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, 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*)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)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, ulong 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 | } |