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