sflate.c (9978B)
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <string.h> 4 #include "flate.h" 5 6 static void set32(uchar *p, uint n) { 7 p[0] = n >> 24; 8 p[1] = n >> 16; 9 p[2] = n >> 8; 10 p[3] = n; 11 } 12 13 static void set32le(uchar *p, uint n) { 14 p[0] = n; 15 p[1] = n >> 8; 16 p[2] = n >> 16; 17 p[3] = n >> 24; 18 } 19 20 static int check32(uchar *p, uint n) { 21 return n == ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); 22 } 23 24 static int check32le(uchar *p, uint n) { 25 return n == (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)); 26 } 27 28 enum { 29 ZlibCM = 7 << 4, 30 ZlibCINFO = 8, 31 ZlibFLEV = 3 << 6, 32 ZlibFDICT = 1 << 5, 33 ZlibFCHK = 31 - (((ZlibCM | ZlibCINFO) << 8) | ZlibFLEV) % 31 34 }; 35 36 int deflate_zlib_header(uchar *p, int n) { 37 if (n < 2) 38 return FlateErr; 39 p[0] = ZlibCM | ZlibCINFO; /* deflate method, 32K window size */ 40 p[1] = ZlibFLEV | ZlibFCHK; /* highest compression */ 41 return 2; 42 } 43 44 int deflate_zlib_footer(uchar *p, int n, uint sum, uint len, uint zlen) { 45 if (n < 4) 46 return FlateErr; 47 set32(p, sum); 48 return 4; 49 } 50 51 int inflate_zlib_header(uchar *p, int n) { 52 if (n < 2) 53 return FlateErr; 54 if (((p[0] << 8) | p[1]) % 31) 55 return FlateErr; 56 if ((p[0] & 0xf0) != ZlibCM || (p[0] & 0x0f) > ZlibCINFO) 57 return FlateErr; 58 if (p[1] & ZlibFDICT) 59 return FlateErr; 60 return 2; 61 } 62 63 int inflate_zlib_footer(uchar *p, int n, uint sum, uint len, uint zlen) { 64 if (n < 4 || !check32(p, sum)) 65 return FlateErr; 66 return 4; 67 } 68 69 70 enum { 71 GZipID1 = 0x1f, 72 GZipID2 = 0x8b, 73 GZipCM = 8, 74 GZipFHCRC = 1 << 1, 75 GZipFEXTRA = 1 << 2, 76 GZipFNAME = 1 << 3, 77 GZipFCOMM = 1 << 4, 78 GZipXFL = 2, 79 GZipOS = 255 80 }; 81 82 int deflate_gzip_header(uchar *p, int n) { 83 if (n < 10) 84 return FlateErr; 85 memset(p, 0, 10); 86 p[0] = GZipID1; 87 p[1] = GZipID2; 88 p[2] = GZipCM; 89 p[8] = GZipXFL; 90 p[9] = GZipOS; 91 return 10; 92 } 93 94 int deflate_gzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) { 95 if (n < 8) 96 return FlateErr; 97 set32le(p, sum); 98 set32le(p+4, len); 99 return 8; 100 } 101 102 int inflate_gzip_header(uchar *p, int n) { 103 int k = 10; 104 105 if (k > n) 106 return FlateErr; 107 if (p[0] != GZipID1 || p[1] != GZipID2 || p[2] != GZipCM) 108 return FlateErr; 109 if (p[3] & GZipFEXTRA) { 110 k += 2 + ((p[k] << 8) | p[k+1]); 111 if (k > n) 112 return FlateErr; 113 } 114 if (p[3] & GZipFNAME) { 115 for (; k < n; k++) 116 if (p[k] == 0) 117 break; 118 k++; 119 if (k > n) 120 return FlateErr; 121 } 122 if (p[3] & GZipFCOMM) { 123 for (; k < n; k++) 124 if (p[k] == 0) 125 break; 126 k++; 127 if (k > n) 128 return FlateErr; 129 } 130 if (p[3] & GZipFHCRC) { 131 k += 2; 132 if (k > n) 133 return FlateErr; 134 } 135 return k; 136 } 137 138 int inflate_gzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) { 139 if (n < 8 || !check32le(p, sum) || !check32le(p+4, len)) 140 return FlateErr; 141 return 8; 142 } 143 144 145 static char pkname[] = "sflate_stream"; 146 147 enum { 148 PKHeadID = 0x04034b50, 149 PKDataID = 0x08074b50, 150 PKDirID = 0x02014b50, 151 PKFootID = 0x06054b50, 152 PKVersion = 20, 153 PKFlag = 1 << 3, 154 PKMethod = 8, 155 PKDate = ((2009 - 1980) << 25) | (1 << 21) | (1 << 16), 156 PKHeadSize = 30, 157 PKDirSize = 46, 158 PKNameLen = sizeof(pkname) - 1 159 }; 160 161 int deflate_pkzip_header(uchar *p, int n) { 162 if (n < PKHeadSize + PKNameLen) 163 return FlateErr; 164 memset(p, 0, PKHeadSize); 165 set32le(p, PKHeadID); 166 set32le(p+4, PKVersion); 167 set32le(p+6, PKFlag); 168 set32le(p+8, PKMethod); 169 set32le(p+10, PKDate); 170 set32le(p+26, PKNameLen); 171 memcpy(p + PKHeadSize, pkname, PKNameLen); 172 return PKHeadSize + PKNameLen; 173 } 174 175 int deflate_pkzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) { 176 if (n < PKDirSize + PKNameLen + 22) 177 return FlateErr; 178 /* unzip bug */ 179 /* 180 if (n < 16 + PKDirSize + PKNameLen + 22) 181 return FlateErr; 182 set32le(p, PKDataID); 183 set32le(p+4, sum); 184 set32le(p+8, zlen); 185 set32le(p+12, len); 186 p += 16; 187 */ 188 memset(p, 0, PKDirSize); 189 set32le(p, PKDirID); 190 set32le(p+4, PKVersion | (PKVersion << 16)); 191 set32le(p+8, PKFlag); 192 set32le(p+10, PKMethod); 193 set32le(p+12, PKDate); 194 set32le(p+16, sum); 195 set32le(p+20, zlen); 196 set32le(p+24, len); 197 set32le(p+28, PKNameLen); 198 memcpy(p + PKDirSize, pkname, PKNameLen); 199 p += PKDirSize + PKNameLen; 200 memset(p, 0, 22); 201 set32le(p, PKFootID); 202 p[8] = p[10] = 1; 203 set32le(p+12, PKDirSize + PKNameLen); 204 set32le(p+16, zlen + PKHeadSize + PKNameLen); 205 return PKDirSize + PKNameLen + 22; 206 /* 207 set32le(p+12, 16 + PKDirSize + PKNameLen); 208 set32le(p+16, zlen + PKHeadSize + PKNameLen); 209 return 16 + PKDirSize + PKNameLen + 22; 210 */ 211 } 212 213 int inflate_pkzip_header(uchar *p, int n) { 214 int k = 30; 215 216 if (k > n) 217 return FlateErr; 218 if (!check32le(p, PKHeadID)) 219 return FlateErr; 220 if ((p[4] | (p[5] << 8)) > PKVersion) 221 return FlateErr; 222 if ((p[8] | (p[9] << 8)) != PKMethod) 223 return FlateErr; 224 k += p[26] | (p[27] << 8); 225 k += p[28] | (p[29] << 8); 226 if (k > n) 227 return FlateErr; 228 return k; 229 } 230 231 int inflate_pkzip_footer(uchar *p, int n, uint sum, uint len, uint zlen) { 232 int k = PKDirSize + 22; 233 234 if (k > n) 235 return FlateErr; 236 if (check32le(p, PKDataID)) { 237 p += 16; 238 k += 16; 239 if (k > n) 240 return FlateErr; 241 } 242 if (!check32le(p, PKDirID)) 243 return FlateErr; 244 if (!check32le(p+16, sum)) 245 return FlateErr; 246 if (!check32le(p+20, zlen)) 247 return FlateErr; 248 if (!check32le(p+24, len)) 249 return FlateErr; 250 return k; 251 } 252 253 254 /* example usage */ 255 256 static int (*header)(uchar *, int); 257 static int (*footer)(uchar *, int, uint, uint, uint); 258 static uint (*checksum)(uchar *, int, uint); 259 static char *err; 260 static uint sum; 261 static uint nin; 262 static uint nout; 263 static uint headerlen; 264 static uint footerlen; 265 static uint extralen; 266 267 static int dummyheader(uchar *p, int n) { 268 return 0; 269 } 270 static int dummyfooter(uchar *p, int n, uint sum, uint len, uint zlen) { 271 return 0; 272 } 273 static uint dummysum(uchar *p, int n, uint sum) { 274 return 0; 275 } 276 277 /* compress, using FlateStream interface */ 278 int compress_stream(FILE *in, FILE *out) { 279 FlateStream s; 280 int k, n; 281 enum {Nin = 1<<15, Nout = 1<<15}; 282 283 s.in = malloc(Nin); 284 s.out = malloc(Nout); 285 s.nin = 0; 286 s.nout = Nout; 287 s.err = 0; 288 s.state = 0; 289 290 k = header(s.out, s.nout); 291 if (k == FlateErr) { 292 s.err = "header error."; 293 n = FlateErr; 294 } else { 295 headerlen = s.nout = k; 296 n = FlateOut; 297 } 298 for (;; n = deflate(&s)) 299 switch (n) { 300 case FlateOk: 301 k = footer(s.out, s.nout, sum, nin, nout - headerlen); 302 if (k == FlateErr) { 303 s.err = "footer error."; 304 n = FlateErr; 305 } else if (k != fwrite(s.out, 1, k, out)) { 306 s.err = "write error."; 307 n = FlateErr; 308 } else { 309 footerlen = k; 310 nout += k; 311 } 312 case FlateErr: 313 free(s.in); 314 free(s.out); 315 err = s.err; 316 return n; 317 case FlateIn: 318 s.nin = fread(s.in, 1, Nin, in); 319 nin += s.nin; 320 sum = checksum(s.in, s.nin, sum); 321 break; 322 case FlateOut: 323 k = fwrite(s.out, 1, s.nout, out); 324 if (k != s.nout) 325 s.err = "write error."; 326 nout += k; 327 s.nout = Nout; 328 break; 329 } 330 } 331 332 /* decompress, using FlateStream interface */ 333 int decompress_stream(FILE *in, FILE *out) { 334 FlateStream s; 335 uchar *begin; 336 int k, n; 337 enum {Nin = 1<<15, Nout = 1<<15}; 338 339 s.in = begin = malloc(Nin); 340 s.out = malloc(Nout); 341 s.nout = Nout; 342 s.err = 0; 343 s.state = 0; 344 345 s.nin = fread(s.in, 1, Nin, in); 346 nin += s.nin; 347 k = header(s.in, s.nin); 348 if (k == FlateErr) { 349 s.err = "header error."; 350 n = FlateErr; 351 } else { 352 headerlen = k; 353 s.nin -= k; 354 s.in += k; 355 n = inflate(&s); 356 } 357 for (;; n = inflate(&s)) 358 switch (n) { 359 case FlateOk: 360 memmove(begin, s.in, s.nin); 361 k = fread(begin, 1, Nin-s.nin, in); 362 nin += k; 363 s.nin += k; 364 k = footer(begin, s.nin, sum, nout, nin - s.nin - headerlen); 365 if (k == FlateErr) { 366 s.err = "footer error."; 367 n = FlateErr; 368 } else { 369 footerlen = k; 370 extralen = s.nin - k; 371 } 372 case FlateErr: 373 free(begin); 374 free(s.out); 375 err = s.err; 376 return n; 377 case FlateIn: 378 s.in = begin; 379 s.nin = fread(s.in, 1, Nin, in); 380 nin += s.nin; 381 break; 382 case FlateOut: 383 k = fwrite(s.out, 1, s.nout, out); 384 if (k != s.nout) 385 s.err = "write error."; 386 sum = checksum(s.out, k, sum); 387 nout += k; 388 s.nout = Nout; 389 break; 390 } 391 } 392 393 int main(int argc, char *argv[]) { 394 char comp = 'c'; 395 char fmt = 'r'; 396 char verbose = 'q'; 397 int (*call)(FILE *, FILE*); 398 int n, i; 399 400 for (i = 1; i < argc; i++) { 401 if (argv[i][0] == '-' && argv[i][1] && argv[i][2] == 0) 402 switch (argv[i][1]) { 403 case 'q': 404 case 'v': 405 verbose = argv[i][1]; 406 continue; 407 case 'c': 408 case 'd': 409 comp = argv[i][1]; 410 continue; 411 case 'r': 412 case 'g': 413 case 'z': 414 case 'p': 415 fmt = argv[i][1]; 416 continue; 417 } 418 fprintf(stderr, "usage: %s [-q|-v] [-c|-d] [-r|-g|-z|-p]\n\n" 419 "deflate stream compression\n" 420 " -q quiet (default)\n" 421 " -v verbose\n" 422 " -c compress (default)\n" 423 " -d decompress\n" 424 " -r raw (default)\n" 425 " -g gzip\n" 426 " -z zlib\n" 427 " -p pkzip\n", argv[0]); 428 return -1; 429 } 430 call = comp == 'c' ? compress_stream : decompress_stream; 431 switch (fmt) { 432 case 'r': 433 header = dummyheader; 434 footer = dummyfooter; 435 checksum = dummysum; 436 n = call(stdin, stdout); 437 break; 438 case 'g': 439 if (comp == 'c') { 440 header = deflate_gzip_header; 441 footer = deflate_gzip_footer; 442 } else { 443 header = inflate_gzip_header; 444 footer = inflate_gzip_footer; 445 } 446 checksum = crc32; 447 crc32init(); 448 n = call(stdin, stdout); 449 break; 450 case 'z': 451 if (comp == 'c') { 452 header = deflate_zlib_header; 453 footer = deflate_zlib_footer; 454 } else { 455 header = inflate_zlib_header; 456 footer = inflate_zlib_footer; 457 } 458 checksum = adler32; 459 n = call(stdin, stdout); 460 break; 461 case 'p': 462 if (comp == 'c') { 463 header = deflate_pkzip_header; 464 footer = deflate_pkzip_footer; 465 } else { 466 header = inflate_pkzip_header; 467 footer = inflate_pkzip_footer; 468 } 469 checksum = crc32; 470 crc32init(); 471 n = call(stdin, stdout); 472 break; 473 default: 474 err = "uninplemented."; 475 n = FlateErr; 476 break; 477 } 478 if (verbose == 'v') 479 fprintf(stderr, "in:%d out:%d checksum: 0x%08x (header:%d data:%d footer:%d extra input:%s)\n", 480 nin, nout, sum, headerlen, (comp == 'c' ? nout : nin) - headerlen - footerlen - extralen, 481 footerlen, extralen ? "yes" : "no"); 482 if (n != FlateOk) 483 fprintf(stderr, "error: %s\n", err); 484 return n; 485 }