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