zip.c 6.9 KB

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