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
strwinerror(error)76 static 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
pwinerror(s)115 static 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
148 void *myalloc OF((void *, unsigned, unsigned));
149 void myfree OF((void *, void *));
150
myalloc(q,n,m)151 void *myalloc(q, n, m)
152 void *q;
153 unsigned n, m;
154 {
155 q = Z_NULL;
156 return calloc(n, m);
157 }
158
myfree(q,p)159 void myfree(q, p)
160 void *q, *p;
161 {
162 q = Z_NULL;
163 free(p);
164 }
165
166 typedef struct gzFile_s {
167 FILE *file;
168 int write;
169 int err;
170 char *msg;
171 z_stream strm;
172 } *gzFile;
173
174 gzFile gzopen OF((const char *, const char *));
175 gzFile gzdopen OF((int, const char *));
176 gzFile gz_open OF((const char *, int, const char *));
177
gzopen(path,mode)178 gzFile gzopen(path, mode)
179 const char *path;
180 const char *mode;
181 {
182 return gz_open(path, -1, mode);
183 }
184
gzdopen(fd,mode)185 gzFile gzdopen(fd, mode)
186 int fd;
187 const char *mode;
188 {
189 return gz_open(NULL, fd, mode);
190 }
191
gz_open(path,fd,mode)192 gzFile 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(gzFile));
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
230 int gzwrite OF((gzFile, const void *, unsigned));
231
gzwrite(gz,buf,len)232 int 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
254 int gzread OF((gzFile, void *, unsigned));
255
gzread(gz,buf,len)256 int 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
291 int gzclose OF((gzFile));
292
gzclose(gz)293 int 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
320 const char *gzerror OF((gzFile, int *));
321
gzerror(gz,err)322 const char *gzerror(gz, err)
323 gzFile gz;
324 int *err;
325 {
326 *err = gz->err;
327 return gz->msg;
328 }
329
330 #endif
331
332 char *prog;
333
334 void error OF((const char *msg));
335 void gz_compress OF((FILE *in, gzFile out));
336 #ifdef USE_MMAP
337 int gz_compress_mmap OF((FILE *in, gzFile out));
338 #endif
339 void gz_uncompress OF((gzFile in, FILE *out));
340 void file_compress OF((char *file, char *mode));
341 void file_uncompress OF((char *file));
342 int main OF((int argc, char *argv[]));
343
344 /* ===========================================================================
345 * Display error message and exit
346 */
error(msg)347 void 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
gz_compress(in,out)358 void 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 */
gz_compress_mmap(in,out)391 int 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 */
gz_uncompress(in,out)426 void 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 */
file_compress(file,mode)453 void 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 */
file_uncompress(file)488 void 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
main(argc,argv)540 int 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