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 _MSC_VER
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
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
152 void *myalloc OF((void *, unsigned, unsigned));
153 void myfree OF((void *, void *));
154
myalloc(q,n,m)155 void *myalloc(q, n, m)
156 void *q;
157 unsigned n, m;
158 {
159 q = Z_NULL;
160 return calloc(n, m);
161 }
162
myfree(q,p)163 void myfree(q, p)
164 void *q, *p;
165 {
166 q = Z_NULL;
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
gzopen(path,mode)182 gzFile gzopen(path, mode)
183 const char *path;
184 const char *mode;
185 {
186 return gz_open(path, -1, mode);
187 }
188
gzdopen(fd,mode)189 gzFile gzdopen(fd, mode)
190 int fd;
191 const char *mode;
192 {
193 return gz_open(NULL, fd, mode);
194 }
195
gz_open(path,fd,mode)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
gzwrite(gz,buf,len)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
gzread(gz,buf,len)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
gzclose(gz)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
gzerror(gz,err)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 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 */
error(msg)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
gz_compress(in,out)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 */
gz_compress_mmap(in,out)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 */
gz_uncompress(in,out)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 */
file_compress(file,mode)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 */
file_uncompress(file)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 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
main(argc,argv)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