1 /* minigzip.c -- simulate gzip using the zlib compression library 2 * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly 3 * For conditions of distribution and use, see copyright notice in zlib.h 4 */ 5 6 /* 7 * minigzip is a minimal implementation of the gzip utility. This is 8 * only an example of using zlib and isn't meant to replace the 9 * full-featured gzip. No attempt is made to deal with file systems 10 * limiting names to 14 or 8+3 characters, etc... Error checking is 11 * very limited. So use minigzip only for testing; use gzip for the 12 * real thing. On MSDOS, use only on file names without extension 13 * or in pipe mode. 14 */ 15 16 /* @(#) $Id$ */ 17 18 #include "zlib.h" 19 #include <stdio.h> 20 21 #ifdef STDC 22 # include <string.h> 23 # include <stdlib.h> 24 #endif 25 26 #ifdef USE_MMAP 27 # include <sys/types.h> 28 # include <sys/mman.h> 29 # include <sys/stat.h> 30 #endif 31 32 #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) 33 # include <fcntl.h> 34 # include <io.h> 35 # ifdef UNDER_CE 36 # include <stdlib.h> 37 # endif 38 # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) 39 #else 40 # define SET_BINARY_MODE(file) 41 #endif 42 43 #if defined(_MSC_VER) && _MSC_VER < 1900 44 # define snprintf _snprintf 45 #endif 46 47 #ifdef VMS 48 # define unlink delete 49 # define GZ_SUFFIX "-gz" 50 #endif 51 #ifdef RISCOS 52 # define unlink remove 53 # define GZ_SUFFIX "-gz" 54 # define fileno(file) file->__file 55 #endif 56 #if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os 57 # include <unix.h> /* for fileno */ 58 #endif 59 60 #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE) 61 #ifndef WIN32 /* unlink already in stdio.h for WIN32 */ 62 extern int unlink OF((const char *)); 63 #endif 64 #endif 65 66 #if defined(UNDER_CE) 67 # include <windows.h> 68 # define perror(s) pwinerror(s) 69 70 /* Map the Windows error number in ERROR to a locale-dependent error 71 message string and return a pointer to it. Typically, the values 72 for ERROR come from GetLastError. 73 74 The string pointed to shall not be modified by the application, 75 but may be overwritten by a subsequent call to strwinerror 76 77 The strwinerror function does not change the current setting 78 of GetLastError. */ 79 80 static char *strwinerror (error) 81 DWORD error; 82 { 83 static char buf[1024]; 84 85 wchar_t *msgbuf; 86 DWORD lasterr = GetLastError(); 87 DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM 88 | FORMAT_MESSAGE_ALLOCATE_BUFFER, 89 NULL, 90 error, 91 0, /* Default language */ 92 (LPVOID)&msgbuf, 93 0, 94 NULL); 95 if (chars != 0) { 96 /* If there is an \r\n appended, zap it. */ 97 if (chars >= 2 98 && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { 99 chars -= 2; 100 msgbuf[chars] = 0; 101 } 102 103 if (chars > sizeof (buf) - 1) { 104 chars = sizeof (buf) - 1; 105 msgbuf[chars] = 0; 106 } 107 108 wcstombs(buf, msgbuf, chars + 1); 109 LocalFree(msgbuf); 110 } 111 else { 112 sprintf(buf, "unknown win32 error (%ld)", error); 113 } 114 115 SetLastError(lasterr); 116 return buf; 117 } 118 119 static void pwinerror (s) 120 const char *s; 121 { 122 if (s && *s) 123 fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ())); 124 else 125 fprintf(stderr, "%s\n", strwinerror(GetLastError ())); 126 } 127 128 #endif /* UNDER_CE */ 129 130 #ifndef GZ_SUFFIX 131 # define GZ_SUFFIX ".gz" 132 #endif 133 #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) 134 135 #define BUFLEN 16384 136 #define MAX_NAME_LEN 1024 137 138 #ifdef MAXSEG_64K 139 # define local static 140 /* Needed for systems with limitation on stack size. */ 141 #else 142 # define local 143 #endif 144 145 #ifdef Z_SOLO 146 /* for Z_SOLO, create simplified gz* functions using deflate and inflate */ 147 148 #if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE) 149 # include <unistd.h> /* for unlink() */ 150 #endif 151 152 void *myalloc OF((void *, unsigned, unsigned)); 153 void myfree OF((void *, void *)); 154 155 void *myalloc(q, n, m) 156 void *q; 157 unsigned n, m; 158 { 159 (void)q; 160 return calloc(n, m); 161 } 162 163 void myfree(q, p) 164 void *q, *p; 165 { 166 (void)q; 167 free(p); 168 } 169 170 typedef struct gzFile_s { 171 FILE *file; 172 int write; 173 int err; 174 char *msg; 175 z_stream strm; 176 } *gzFile; 177 178 gzFile gzopen OF((const char *, const char *)); 179 gzFile gzdopen OF((int, const char *)); 180 gzFile gz_open OF((const char *, int, const char *)); 181 182 gzFile gzopen(path, mode) 183 const char *path; 184 const char *mode; 185 { 186 return gz_open(path, -1, mode); 187 } 188 189 gzFile gzdopen(fd, mode) 190 int fd; 191 const char *mode; 192 { 193 return gz_open(NULL, fd, mode); 194 } 195 196 gzFile gz_open(path, fd, mode) 197 const char *path; 198 int fd; 199 const char *mode; 200 { 201 gzFile gz; 202 int ret; 203 204 gz = malloc(sizeof(struct gzFile_s)); 205 if (gz == NULL) 206 return NULL; 207 gz->write = strchr(mode, 'w') != NULL; 208 gz->strm.zalloc = myalloc; 209 gz->strm.zfree = myfree; 210 gz->strm.opaque = Z_NULL; 211 if (gz->write) 212 ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0); 213 else { 214 gz->strm.next_in = 0; 215 gz->strm.avail_in = Z_NULL; 216 ret = inflateInit2(&(gz->strm), 15 + 16); 217 } 218 if (ret != Z_OK) { 219 free(gz); 220 return NULL; 221 } 222 gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") : 223 fopen(path, gz->write ? "wb" : "rb"); 224 if (gz->file == NULL) { 225 gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm)); 226 free(gz); 227 return NULL; 228 } 229 gz->err = 0; 230 gz->msg = ""; 231 return gz; 232 } 233 234 int gzwrite OF((gzFile, const void *, unsigned)); 235 236 int gzwrite(gz, buf, len) 237 gzFile gz; 238 const void *buf; 239 unsigned len; 240 { 241 z_stream *strm; 242 unsigned char out[BUFLEN]; 243 244 if (gz == NULL || !gz->write) 245 return 0; 246 strm = &(gz->strm); 247 strm->next_in = (void *)buf; 248 strm->avail_in = len; 249 do { 250 strm->next_out = out; 251 strm->avail_out = BUFLEN; 252 (void)deflate(strm, Z_NO_FLUSH); 253 fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); 254 } while (strm->avail_out == 0); 255 return len; 256 } 257 258 int gzread OF((gzFile, void *, unsigned)); 259 260 int gzread(gz, buf, len) 261 gzFile gz; 262 void *buf; 263 unsigned len; 264 { 265 int ret; 266 unsigned got; 267 unsigned char in[1]; 268 z_stream *strm; 269 270 if (gz == NULL || gz->write) 271 return 0; 272 if (gz->err) 273 return 0; 274 strm = &(gz->strm); 275 strm->next_out = (void *)buf; 276 strm->avail_out = len; 277 do { 278 got = fread(in, 1, 1, gz->file); 279 if (got == 0) 280 break; 281 strm->next_in = in; 282 strm->avail_in = 1; 283 ret = inflate(strm, Z_NO_FLUSH); 284 if (ret == Z_DATA_ERROR) { 285 gz->err = Z_DATA_ERROR; 286 gz->msg = strm->msg; 287 return 0; 288 } 289 if (ret == Z_STREAM_END) 290 inflateReset(strm); 291 } while (strm->avail_out); 292 return len - strm->avail_out; 293 } 294 295 int gzclose OF((gzFile)); 296 297 int gzclose(gz) 298 gzFile gz; 299 { 300 z_stream *strm; 301 unsigned char out[BUFLEN]; 302 303 if (gz == NULL) 304 return Z_STREAM_ERROR; 305 strm = &(gz->strm); 306 if (gz->write) { 307 strm->next_in = Z_NULL; 308 strm->avail_in = 0; 309 do { 310 strm->next_out = out; 311 strm->avail_out = BUFLEN; 312 (void)deflate(strm, Z_FINISH); 313 fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); 314 } while (strm->avail_out == 0); 315 deflateEnd(strm); 316 } 317 else 318 inflateEnd(strm); 319 fclose(gz->file); 320 free(gz); 321 return Z_OK; 322 } 323 324 const char *gzerror OF((gzFile, int *)); 325 326 const char *gzerror(gz, err) 327 gzFile gz; 328 int *err; 329 { 330 *err = gz->err; 331 return gz->msg; 332 } 333 334 #endif 335 336 static char *prog; 337 338 void error OF((const char *msg)); 339 void gz_compress OF((FILE *in, gzFile out)); 340 #ifdef USE_MMAP 341 int gz_compress_mmap OF((FILE *in, gzFile out)); 342 #endif 343 void gz_uncompress OF((gzFile in, FILE *out)); 344 void file_compress OF((char *file, char *mode)); 345 void file_uncompress OF((char *file)); 346 int main OF((int argc, char *argv[])); 347 348 /* =========================================================================== 349 * Display error message and exit 350 */ 351 void error(msg) 352 const char *msg; 353 { 354 fprintf(stderr, "%s: %s\n", prog, msg); 355 exit(1); 356 } 357 358 /* =========================================================================== 359 * Compress input to output then close both files. 360 */ 361 362 void gz_compress(in, out) 363 FILE *in; 364 gzFile out; 365 { 366 local char buf[BUFLEN]; 367 int len; 368 int err; 369 370 #ifdef USE_MMAP 371 /* Try first compressing with mmap. If mmap fails (minigzip used in a 372 * pipe), use the normal fread loop. 373 */ 374 if (gz_compress_mmap(in, out) == Z_OK) return; 375 #endif 376 for (;;) { 377 len = (int)fread(buf, 1, sizeof(buf), in); 378 if (ferror(in)) { 379 perror("fread"); 380 exit(1); 381 } 382 if (len == 0) break; 383 384 if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); 385 } 386 fclose(in); 387 if (gzclose(out) != Z_OK) error("failed gzclose"); 388 } 389 390 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */ 391 392 /* Try compressing the input file at once using mmap. Return Z_OK if 393 * if success, Z_ERRNO otherwise. 394 */ 395 int gz_compress_mmap(in, out) 396 FILE *in; 397 gzFile out; 398 { 399 int len; 400 int err; 401 int ifd = fileno(in); 402 caddr_t buf; /* mmap'ed buffer for the entire input file */ 403 off_t buf_len; /* length of the input file */ 404 struct stat sb; 405 406 /* Determine the size of the file, needed for mmap: */ 407 if (fstat(ifd, &sb) < 0) return Z_ERRNO; 408 buf_len = sb.st_size; 409 if (buf_len <= 0) return Z_ERRNO; 410 411 /* Now do the actual mmap: */ 412 buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); 413 if (buf == (caddr_t)(-1)) return Z_ERRNO; 414 415 /* Compress the whole file at once: */ 416 len = gzwrite(out, (char *)buf, (unsigned)buf_len); 417 418 if (len != (int)buf_len) error(gzerror(out, &err)); 419 420 munmap(buf, buf_len); 421 fclose(in); 422 if (gzclose(out) != Z_OK) error("failed gzclose"); 423 return Z_OK; 424 } 425 #endif /* USE_MMAP */ 426 427 /* =========================================================================== 428 * Uncompress input to output then close both files. 429 */ 430 void gz_uncompress(in, out) 431 gzFile in; 432 FILE *out; 433 { 434 local char buf[BUFLEN]; 435 int len; 436 int err; 437 438 for (;;) { 439 len = gzread(in, buf, sizeof(buf)); 440 if (len < 0) error (gzerror(in, &err)); 441 if (len == 0) break; 442 443 if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { 444 error("failed fwrite"); 445 } 446 } 447 if (fclose(out)) error("failed fclose"); 448 449 if (gzclose(in) != Z_OK) error("failed gzclose"); 450 } 451 452 453 /* =========================================================================== 454 * Compress the given file: create a corresponding .gz file and remove the 455 * original. 456 */ 457 void file_compress(file, mode) 458 char *file; 459 char *mode; 460 { 461 local char outfile[MAX_NAME_LEN]; 462 FILE *in; 463 gzFile out; 464 465 if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) { 466 fprintf(stderr, "%s: filename too long\n", prog); 467 exit(1); 468 } 469 470 #if !defined(NO_snprintf) && !defined(NO_vsnprintf) 471 snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX); 472 #else 473 strcpy(outfile, file); 474 strcat(outfile, GZ_SUFFIX); 475 #endif 476 477 in = fopen(file, "rb"); 478 if (in == NULL) { 479 perror(file); 480 exit(1); 481 } 482 out = gzopen(outfile, mode); 483 if (out == NULL) { 484 fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); 485 exit(1); 486 } 487 gz_compress(in, out); 488 489 unlink(file); 490 } 491 492 493 /* =========================================================================== 494 * Uncompress the given file and remove the original. 495 */ 496 void file_uncompress(file) 497 char *file; 498 { 499 local char buf[MAX_NAME_LEN]; 500 char *infile, *outfile; 501 FILE *out; 502 gzFile in; 503 z_size_t len = strlen(file); 504 505 if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) { 506 fprintf(stderr, "%s: filename too long\n", prog); 507 exit(1); 508 } 509 510 #if !defined(NO_snprintf) && !defined(NO_vsnprintf) 511 snprintf(buf, sizeof(buf), "%s", file); 512 #else 513 strcpy(buf, file); 514 #endif 515 516 if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { 517 infile = file; 518 outfile = buf; 519 outfile[len-3] = '\0'; 520 } else { 521 outfile = file; 522 infile = buf; 523 #if !defined(NO_snprintf) && !defined(NO_vsnprintf) 524 snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX); 525 #else 526 strcat(infile, GZ_SUFFIX); 527 #endif 528 } 529 in = gzopen(infile, "rb"); 530 if (in == NULL) { 531 fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); 532 exit(1); 533 } 534 out = fopen(outfile, "wb"); 535 if (out == NULL) { 536 perror(file); 537 exit(1); 538 } 539 540 gz_uncompress(in, out); 541 542 unlink(infile); 543 } 544 545 546 /* =========================================================================== 547 * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...] 548 * -c : write to standard output 549 * -d : decompress 550 * -f : compress with Z_FILTERED 551 * -h : compress with Z_HUFFMAN_ONLY 552 * -r : compress with Z_RLE 553 * -1 to -9 : compression level 554 */ 555 556 int main(argc, argv) 557 int argc; 558 char *argv[]; 559 { 560 int copyout = 0; 561 int uncompr = 0; 562 gzFile file; 563 char *bname, outmode[20]; 564 565 #if !defined(NO_snprintf) && !defined(NO_vsnprintf) 566 snprintf(outmode, sizeof(outmode), "%s", "wb6 "); 567 #else 568 strcpy(outmode, "wb6 "); 569 #endif 570 571 prog = argv[0]; 572 bname = strrchr(argv[0], '/'); 573 if (bname) 574 bname++; 575 else 576 bname = argv[0]; 577 argc--, argv++; 578 579 if (!strcmp(bname, "gunzip")) 580 uncompr = 1; 581 else if (!strcmp(bname, "zcat")) 582 copyout = uncompr = 1; 583 584 while (argc > 0) { 585 if (strcmp(*argv, "-c") == 0) 586 copyout = 1; 587 else if (strcmp(*argv, "-d") == 0) 588 uncompr = 1; 589 else if (strcmp(*argv, "-f") == 0) 590 outmode[3] = 'f'; 591 else if (strcmp(*argv, "-h") == 0) 592 outmode[3] = 'h'; 593 else if (strcmp(*argv, "-r") == 0) 594 outmode[3] = 'R'; 595 else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && 596 (*argv)[2] == 0) 597 outmode[2] = (*argv)[1]; 598 else 599 break; 600 argc--, argv++; 601 } 602 if (outmode[3] == ' ') 603 outmode[3] = 0; 604 if (argc == 0) { 605 SET_BINARY_MODE(stdin); 606 SET_BINARY_MODE(stdout); 607 if (uncompr) { 608 file = gzdopen(fileno(stdin), "rb"); 609 if (file == NULL) error("can't gzdopen stdin"); 610 gz_uncompress(file, stdout); 611 } else { 612 file = gzdopen(fileno(stdout), outmode); 613 if (file == NULL) error("can't gzdopen stdout"); 614 gz_compress(stdin, file); 615 } 616 } else { 617 if (copyout) { 618 SET_BINARY_MODE(stdout); 619 } 620 do { 621 if (uncompr) { 622 if (copyout) { 623 file = gzopen(*argv, "rb"); 624 if (file == NULL) 625 fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv); 626 else 627 gz_uncompress(file, stdout); 628 } else { 629 file_uncompress(*argv); 630 } 631 } else { 632 if (copyout) { 633 FILE * in = fopen(*argv, "rb"); 634 635 if (in == NULL) { 636 perror(*argv); 637 } else { 638 file = gzdopen(fileno(stdout), outmode); 639 if (file == NULL) error("can't gzdopen stdout"); 640 641 gz_compress(in, file); 642 } 643 644 } else { 645 file_compress(*argv, outmode); 646 } 647 } 648 } while (argv++, --argc); 649 } 650 return 0; 651 } 652