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