• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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