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(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
strwinerror(error)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
pwinerror(s)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
myalloc(void * q,unsigned n,unsigned m)152 static void *myalloc(void *q, unsigned n, unsigned m)
153 {
154 (void)q;
155 return calloc(n, m);
156 }
157
myfree(void * q,void * p)158 static void myfree(void *q, void *p)
159 {
160 (void)q;
161 free(p);
162 }
163
164 typedef struct gzFile_s {
165 FILE *file;
166 int write;
167 int err;
168 char *msg;
169 z_stream strm;
170 } *gzFile;
171
gz_open(const char * path,int fd,const char * mode)172 static gzFile gz_open(const char *path, int fd, const char *mode)
173 {
174 gzFile gz;
175 int ret;
176
177 gz = malloc(sizeof(struct gzFile_s));
178 if (gz == NULL)
179 return NULL;
180 gz->write = strchr(mode, 'w') != NULL;
181 gz->strm.zalloc = myalloc;
182 gz->strm.zfree = myfree;
183 gz->strm.opaque = Z_NULL;
184 if (gz->write)
185 ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0);
186 else {
187 gz->strm.next_in = 0;
188 gz->strm.avail_in = Z_NULL;
189 ret = inflateInit2(&(gz->strm), 15 + 16);
190 }
191 if (ret != Z_OK) {
192 free(gz);
193 return NULL;
194 }
195 gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
196 fopen(path, gz->write ? "wb" : "rb");
197 if (gz->file == NULL) {
198 gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
199 free(gz);
200 return NULL;
201 }
202 gz->err = 0;
203 gz->msg = "";
204 return gz;
205 }
206
gzopen(const char * path,const char * mode)207 static gzFile gzopen(const char *path, const char *mode)
208 {
209 return gz_open(path, -1, mode);
210 }
211
gzdopen(int fd,const char * mode)212 static gzFile gzdopen(int fd, const char *mode)
213 {
214 return gz_open(NULL, fd, mode);
215 }
216
gzwrite(gzFile gz,const void * buf,unsigned len)217 static int gzwrite(gzFile gz, const void *buf, unsigned len)
218 {
219 z_stream *strm;
220 unsigned char out[BUFLEN];
221
222 if (gz == NULL || !gz->write)
223 return 0;
224 strm = &(gz->strm);
225 strm->next_in = (void *)buf;
226 strm->avail_in = len;
227 do {
228 strm->next_out = out;
229 strm->avail_out = BUFLEN;
230 (void)deflate(strm, Z_NO_FLUSH);
231 fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
232 } while (strm->avail_out == 0);
233 return len;
234 }
235
gzread(gzFile gz,void * buf,unsigned len)236 static int gzread(gzFile gz, void *buf, unsigned len)
237 {
238 int ret;
239 unsigned got;
240 unsigned char in[1];
241 z_stream *strm;
242
243 if (gz == NULL || gz->write)
244 return 0;
245 if (gz->err)
246 return 0;
247 strm = &(gz->strm);
248 strm->next_out = (void *)buf;
249 strm->avail_out = len;
250 do {
251 got = fread(in, 1, 1, gz->file);
252 if (got == 0)
253 break;
254 strm->next_in = in;
255 strm->avail_in = 1;
256 ret = inflate(strm, Z_NO_FLUSH);
257 if (ret == Z_DATA_ERROR) {
258 gz->err = Z_DATA_ERROR;
259 gz->msg = strm->msg;
260 return 0;
261 }
262 if (ret == Z_STREAM_END)
263 inflateReset(strm);
264 } while (strm->avail_out);
265 return len - strm->avail_out;
266 }
267
gzclose(gzFile gz)268 static int gzclose(gzFile gz)
269 {
270 z_stream *strm;
271 unsigned char out[BUFLEN];
272
273 if (gz == NULL)
274 return Z_STREAM_ERROR;
275 strm = &(gz->strm);
276 if (gz->write) {
277 strm->next_in = Z_NULL;
278 strm->avail_in = 0;
279 do {
280 strm->next_out = out;
281 strm->avail_out = BUFLEN;
282 (void)deflate(strm, Z_FINISH);
283 fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
284 } while (strm->avail_out == 0);
285 deflateEnd(strm);
286 }
287 else
288 inflateEnd(strm);
289 fclose(gz->file);
290 free(gz);
291 return Z_OK;
292 }
293
gzerror(gzFile gz,int * err)294 static const char *gzerror(gzFile gz, int *err)
295 {
296 *err = gz->err;
297 return gz->msg;
298 }
299
300 #endif
301
302 static char *prog;
303
304 /* ===========================================================================
305 * Display error message and exit
306 */
error(const char * msg)307 static void error(const char *msg)
308 {
309 fprintf(stderr, "%s: %s\n", prog, msg);
310 exit(1);
311 }
312
313 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
314
315 /* Try compressing the input file at once using mmap. Return Z_OK if
316 * success, Z_ERRNO otherwise.
317 */
gz_compress_mmap(FILE * in,gzFile out)318 static int gz_compress_mmap(FILE *in, gzFile out)
319 {
320 int len;
321 int err;
322 int ifd = fileno(in);
323 caddr_t buf; /* mmap'ed buffer for the entire input file */
324 off_t buf_len; /* length of the input file */
325 struct stat sb;
326
327 /* Determine the size of the file, needed for mmap: */
328 if (fstat(ifd, &sb) < 0)
329 {
330 return Z_ERRNO;
331 }
332 buf_len = sb.st_size;
333 if (buf_len <= 0)
334 {
335 return Z_ERRNO;
336 }
337
338 /* Now do the actual mmap: */
339 buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
340 if (buf == (caddr_t)(-1))
341 {
342 return Z_ERRNO;
343 }
344
345 /* Compress the whole file at once: */
346 len = gzwrite(out, (char *)buf, (unsigned)buf_len);
347
348 if (len != (int)buf_len)
349 {
350 error(gzerror(out, &err));
351 }
352
353 munmap(buf, buf_len);
354 fclose(in);
355 if (gzclose(out) != Z_OK)
356 {
357 error("failed gzclose");
358 }
359 return Z_OK;
360 }
361 #endif /* USE_MMAP */
362
363 /* ===========================================================================
364 * Compress input to output then close both files.
365 */
366
gz_compress(FILE * in,gzFile out)367 static void gz_compress(FILE *in, gzFile out)
368 {
369 local char buf[BUFLEN];
370 int len;
371 int err;
372
373 #ifdef USE_MMAP
374 /* Try first compressing with mmap. If mmap fails (minigzip used in a
375 * pipe), use the normal fread loop.
376 */
377 if (gz_compress_mmap(in, out) == Z_OK) return;
378 #endif
379 for (;;) {
380 len = (int)fread(buf, 1, sizeof(buf), in);
381 if (ferror(in)) {
382 perror("fread");
383 exit(1);
384 }
385 if (len == 0)
386 {
387 break;
388 }
389
390 if (gzwrite(out, buf, (unsigned)len) != len)
391 {
392 error(gzerror(out, &err));
393 }
394 }
395 fclose(in);
396 if (gzclose(out) != Z_OK)
397 {
398 error("failed gzclose");
399 }
400 }
401
402 /* ===========================================================================
403 * Uncompress input to output then close both files.
404 */
gz_uncompress(gzFile in,FILE * out)405 static void gz_uncompress(gzFile in, FILE *out)
406 {
407 local char buf[BUFLEN];
408 int len;
409 int err;
410
411 for (;;) {
412 len = gzread(in, buf, sizeof(buf));
413 if (len < 0)
414 {
415 error (gzerror(in, &err));
416 }
417 if (len == 0)
418 {
419 break;
420 }
421
422 if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
423 error("failed fwrite");
424 }
425 }
426 if (fclose(out))
427 {
428 error("failed fclose");
429 }
430
431 if (gzclose(in) != Z_OK)
432 {
433 error("failed gzclose");
434 }
435 }
436
437
438 /* ===========================================================================
439 * Compress the given file: create a corresponding .gz file and remove the
440 * original.
441 */
file_compress(char * file,char * mode)442 static void file_compress(char *file, char *mode)
443 {
444 local char outfile[MAX_NAME_LEN];
445 FILE *in;
446 gzFile out;
447
448 if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
449 fprintf(stderr, "%s: filename too long\n", prog);
450 exit(1);
451 }
452
453 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
454 snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
455 #else
456 strcpy(outfile, file);
457 strcat(outfile, GZ_SUFFIX);
458 #endif
459
460 in = fopen(file, "rb");
461 if (in == NULL) {
462 perror(file);
463 exit(1);
464 }
465 out = gzopen(outfile, mode);
466 if (out == NULL) {
467 fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
468 exit(1);
469 }
470 gz_compress(in, out);
471
472 unlink(file);
473 }
474
475
476 /* ===========================================================================
477 * Uncompress the given file and remove the original.
478 */
file_uncompress(char * file)479 static void file_uncompress(char *file)
480 {
481 local char buf[MAX_NAME_LEN];
482 char *infile, *outfile;
483 FILE *out;
484 gzFile in;
485 z_size_t len = strlen(file);
486
487 if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
488 fprintf(stderr, "%s: filename too long\n", prog);
489 exit(1);
490 }
491
492 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
493 snprintf(buf, sizeof(buf), "%s", file);
494 #else
495 strcpy(buf, file);
496 #endif
497
498 if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
499 infile = file;
500 outfile = buf;
501 outfile[len-3] = '\0';
502 } else {
503 outfile = file;
504 infile = buf;
505 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
506 snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
507 #else
508 strcat(infile, GZ_SUFFIX);
509 #endif
510 }
511 in = gzopen(infile, "rb");
512 if (in == NULL) {
513 fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
514 exit(1);
515 }
516 out = fopen(outfile, "wb");
517 if (out == NULL) {
518 perror(file);
519 exit(1);
520 }
521
522 gz_uncompress(in, out);
523
524 unlink(infile);
525 }
526
527
528 /* ===========================================================================
529 * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
530 * -c : write to standard output
531 * -d : decompress
532 * -f : compress with Z_FILTERED
533 * -h : compress with Z_HUFFMAN_ONLY
534 * -r : compress with Z_RLE
535 * -1 to -9 : compression level
536 */
537
main(int argc,char * argv[])538 int main(int argc, char *argv[])
539 {
540 int copyout = 0;
541 int uncompr = 0;
542 gzFile file;
543 char *bname, outmode[20];
544
545 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
546 snprintf(outmode, sizeof(outmode), "%s", "wb6 ");
547 #else
548 strcpy(outmode, "wb6 ");
549 #endif
550
551 prog = argv[0];
552 bname = strrchr(argv[0], '/');
553 if (bname)
554 {
555 bname++;
556 }
557 else
558 {
559 bname = argv[0];
560 }
561 argc--, argv++;
562
563 if (!strcmp(bname, "gunzip"))
564 {
565 uncompr = 1;
566 }
567 else if (!strcmp(bname, "zcat"))
568 {
569 copyout = uncompr = 1;
570 }
571
572 while (argc > 0) {
573 if (strcmp(*argv, "-c") == 0)
574 {
575 copyout = 1;
576 }
577 else if (strcmp(*argv, "-d") == 0)
578 {
579 uncompr = 1;
580 }
581 else if (strcmp(*argv, "-f") == 0)
582 {
583 outmode[3] = 'f';
584 }
585 else if (strcmp(*argv, "-h") == 0)
586 {
587 outmode[3] = 'h';
588 }
589 else if (strcmp(*argv, "-r") == 0)
590 {
591 outmode[3] = 'R';
592 }
593 else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
594 (*argv)[2] == 0)
595 {
596 outmode[2] = (*argv)[1];
597 }
598 else
599 {
600 break;
601 }
602 argc--, argv++;
603 }
604 if (outmode[3] == ' ')
605 {
606 outmode[3] = 0;
607 }
608 if (argc == 0) {
609 SET_BINARY_MODE(stdin);
610 SET_BINARY_MODE(stdout);
611 if (uncompr) {
612 file = gzdopen(fileno(stdin), "rb");
613 if (file == NULL)
614 {
615 error("can't gzdopen stdin");
616 }
617 gz_uncompress(file, stdout);
618 } else {
619 file = gzdopen(fileno(stdout), outmode);
620 if (file == NULL)
621 {
622 error("can't gzdopen stdout");
623 }
624 gz_compress(stdin, file);
625 }
626 } else {
627 if (copyout) {
628 SET_BINARY_MODE(stdout);
629 }
630 do {
631 if (uncompr) {
632 if (copyout) {
633 file = gzopen(*argv, "rb");
634 if (file == NULL)
635 {
636 fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
637 }
638 else
639 {
640 gz_uncompress(file, stdout);
641 }
642 } else {
643 file_uncompress(*argv);
644 }
645 } else {
646 if (copyout) {
647 FILE * in = fopen(*argv, "rb");
648
649 if (in == NULL) {
650 perror(*argv);
651 } else {
652 file = gzdopen(fileno(stdout), outmode);
653 if (file == NULL)
654 {
655 error("can't gzdopen stdout");
656 }
657
658 gz_compress(in, file);
659 }
660
661 } else {
662 file_compress(*argv, outmode);
663 }
664 }
665 } while (argv++, --argc);
666 }
667 return 0;
668 }
669