xref: /freebsd/sys/contrib/zlib/test/minigzip.c (revision e6bfd18d21b225af6a0ed67ceeaf1293b7b9eba5)
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 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 
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 
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 
155 void *myalloc(q, n, m)
156     void *q;
157     unsigned n, m;
158 {
159     (void)q;
160     return calloc(n, m);
161 }
162 
163 void myfree(q, p)
164     void *q, *p;
165 {
166     (void)q;
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 
182 gzFile gzopen(path, mode)
183 const char *path;
184 const char *mode;
185 {
186     return gz_open(path, -1, mode);
187 }
188 
189 gzFile gzdopen(fd, mode)
190 int fd;
191 const char *mode;
192 {
193     return gz_open(NULL, fd, mode);
194 }
195 
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 
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 
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 
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 
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 static 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  */
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 
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  */
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  */
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  */
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  */
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     z_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 
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