xref: /freebsd/sys/contrib/zstd/zlibWrapper/examples/minigzip.c (revision d3d381b2b194b4d24853e92eecef55f262688d1a)
1 /* minigzip.c contains minimal changes required to be compiled with zlibWrapper:
2  * - #include "zlib.h" was changed to #include "zstd_zlibwrapper.h"        */
3 
4 /* minigzip.c -- simulate gzip using the zlib compression library
5  * Copyright (C) 1995-2006, 2010, 2011 Jean-loup Gailly.
6  * For conditions of distribution and use, see http://www.zlib.net/zlib_license.html
7  */
8 
9 /*
10  * minigzip is a minimal implementation of the gzip utility. This is
11  * only an example of using zlib and isn't meant to replace the
12  * full-featured gzip. No attempt is made to deal with file systems
13  * limiting names to 14 or 8+3 characters, etc... Error checking is
14  * very limited. So use minigzip only for testing; use gzip for the
15  * real thing. On MSDOS, use only on file names without extension
16  * or in pipe mode.
17  */
18 
19 /* @(#) $Id$ */
20 
21 #include "zstd_zlibwrapper.h"
22 #include <stdio.h>
23 
24 #ifdef STDC
25 #  include <string.h>
26 #  include <stdlib.h>
27 #endif
28 
29 #ifdef USE_MMAP
30 #  include <sys/types.h>
31 #  include <sys/mman.h>
32 #  include <sys/stat.h>
33 #endif
34 
35 #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
36 #  include <fcntl.h>
37 #  include <io.h>
38 #  ifdef UNDER_CE
39 #    include <stdlib.h>
40 #  endif
41 #  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
42 #else
43 #  define SET_BINARY_MODE(file)
44 #endif
45 
46 #ifdef _MSC_VER
47 #  define snprintf _snprintf
48 #endif
49 
50 #ifdef VMS
51 #  define unlink delete
52 #  define GZ_SUFFIX "-gz"
53 #endif
54 #ifdef RISCOS
55 #  define unlink remove
56 #  define GZ_SUFFIX "-gz"
57 #  define fileno(file) file->__file
58 #endif
59 #if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
60 #  include <unix.h> /* for fileno */
61 #endif
62 
63 #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
64 #ifndef WIN32 /* unlink already in stdio.h for WIN32 */
65   extern int unlink OF((const char *));
66 #endif
67 #endif
68 
69 #if defined(UNDER_CE)
70 #  include <windows.h>
71 #  define perror(s) pwinerror(s)
72 
73 /* Map the Windows error number in ERROR to a locale-dependent error
74    message string and return a pointer to it.  Typically, the values
75    for ERROR come from GetLastError.
76 
77    The string pointed to shall not be modified by the application,
78    but may be overwritten by a subsequent call to strwinerror
79 
80    The strwinerror function does not change the current setting
81    of GetLastError.  */
82 
83 static char *strwinerror (error)
84      DWORD error;
85 {
86     static char buf[1024];
87 
88     wchar_t *msgbuf;
89     DWORD lasterr = GetLastError();
90     DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
91         | FORMAT_MESSAGE_ALLOCATE_BUFFER,
92         NULL,
93         error,
94         0, /* Default language */
95         (LPVOID)&msgbuf,
96         0,
97         NULL);
98     if (chars != 0) {
99         /* If there is an \r\n appended, zap it.  */
100         if (chars >= 2
101             && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
102             chars -= 2;
103             msgbuf[chars] = 0;
104         }
105 
106         if (chars > sizeof (buf) - 1) {
107             chars = sizeof (buf) - 1;
108             msgbuf[chars] = 0;
109         }
110 
111         wcstombs(buf, msgbuf, chars + 1);
112         LocalFree(msgbuf);
113     }
114     else {
115         sprintf(buf, "unknown win32 error (%ld)", error);
116     }
117 
118     SetLastError(lasterr);
119     return buf;
120 }
121 
122 static void pwinerror (s)
123     const char *s;
124 {
125     if (s && *s)
126         fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ()));
127     else
128         fprintf(stderr, "%s\n", strwinerror(GetLastError ()));
129 }
130 
131 #endif /* UNDER_CE */
132 
133 #ifndef GZ_SUFFIX
134 #  define GZ_SUFFIX ".gz"
135 #endif
136 #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
137 
138 #define BUFLEN      16384
139 #define MAX_NAME_LEN 1024
140 
141 #ifdef MAXSEG_64K
142 #  define local static
143    /* Needed for systems with limitation on stack size. */
144 #else
145 #  define local
146 #endif
147 
148 #ifdef Z_SOLO
149 /* for Z_SOLO, create simplified gz* functions using deflate and inflate */
150 
151 #if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
152 #  include <unistd.h>       /* for unlink() */
153 #endif
154 
155 void *myalloc OF((void *, unsigned, unsigned));
156 void myfree OF((void *, void *));
157 
158 void *myalloc(q, n, m)
159     void *q;
160     unsigned n, m;
161 {
162     q = Z_NULL;
163     return calloc(n, m);
164 }
165 
166 void myfree(q, p)
167     void *q, *p;
168 {
169     q = Z_NULL;
170     free(p);
171 }
172 
173 typedef struct gzFile_s {
174     FILE *file;
175     int write;
176     int err;
177     char *msg;
178     z_stream strm;
179 } *gzFile;
180 
181 gzFile gzopen OF((const char *, const char *));
182 gzFile gzdopen OF((int, const char *));
183 gzFile gz_open OF((const char *, int, const char *));
184 
185 gzFile gzopen(path, mode)
186 const char *path;
187 const char *mode;
188 {
189     return gz_open(path, -1, mode);
190 }
191 
192 gzFile gzdopen(fd, mode)
193 int fd;
194 const char *mode;
195 {
196     return gz_open(NULL, fd, mode);
197 }
198 
199 gzFile gz_open(path, fd, mode)
200     const char *path;
201     int fd;
202     const char *mode;
203 {
204     gzFile gz;
205     int ret;
206 
207     gz = malloc(sizeof(struct gzFile_s));
208     if (gz == NULL)
209         return NULL;
210     gz->write = strchr(mode, 'w') != NULL;
211     gz->strm.zalloc = myalloc;
212     gz->strm.zfree = myfree;
213     gz->strm.opaque = Z_NULL;
214     if (gz->write)
215         ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0);
216     else {
217         gz->strm.next_in = 0;
218         gz->strm.avail_in = Z_NULL;
219         ret = inflateInit2(&(gz->strm), 15 + 16);
220     }
221     if (ret != Z_OK) {
222         free(gz);
223         return NULL;
224     }
225     gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
226                               fopen(path, gz->write ? "wb" : "rb");
227     if (gz->file == NULL) {
228         gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
229         free(gz);
230         return NULL;
231     }
232     gz->err = 0;
233     gz->msg = "";
234     return gz;
235 }
236 
237 int gzwrite OF((gzFile, const void *, unsigned));
238 
239 int gzwrite(gz, buf, len)
240     gzFile gz;
241     const void *buf;
242     unsigned len;
243 {
244     z_stream *strm;
245     unsigned char out[BUFLEN];
246 
247     if (gz == NULL || !gz->write)
248         return 0;
249     strm = &(gz->strm);
250     strm->next_in = (void *)buf;
251     strm->avail_in = len;
252     do {
253         strm->next_out = out;
254         strm->avail_out = BUFLEN;
255         (void)deflate(strm, Z_NO_FLUSH);
256         fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
257     } while (strm->avail_out == 0);
258     return len;
259 }
260 
261 int gzread OF((gzFile, void *, unsigned));
262 
263 int gzread(gz, buf, len)
264     gzFile gz;
265     void *buf;
266     unsigned len;
267 {
268     int ret;
269     unsigned got;
270     unsigned char in[1];
271     z_stream *strm;
272 
273     if (gz == NULL || gz->write)
274         return 0;
275     if (gz->err)
276         return 0;
277     strm = &(gz->strm);
278     strm->next_out = (void *)buf;
279     strm->avail_out = len;
280     do {
281         got = fread(in, 1, 1, gz->file);
282         if (got == 0)
283             break;
284         strm->next_in = in;
285         strm->avail_in = 1;
286         ret = inflate(strm, Z_NO_FLUSH);
287         if (ret == Z_DATA_ERROR) {
288             gz->err = Z_DATA_ERROR;
289             gz->msg = strm->msg;
290             return 0;
291         }
292         if (ret == Z_STREAM_END)
293             inflateReset(strm);
294     } while (strm->avail_out);
295     return len - strm->avail_out;
296 }
297 
298 int gzclose OF((gzFile));
299 
300 int gzclose(gz)
301     gzFile gz;
302 {
303     z_stream *strm;
304     unsigned char out[BUFLEN];
305 
306     if (gz == NULL)
307         return Z_STREAM_ERROR;
308     strm = &(gz->strm);
309     if (gz->write) {
310         strm->next_in = Z_NULL;
311         strm->avail_in = 0;
312         do {
313             strm->next_out = out;
314             strm->avail_out = BUFLEN;
315             (void)deflate(strm, Z_FINISH);
316             fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
317         } while (strm->avail_out == 0);
318         deflateEnd(strm);
319     }
320     else
321         inflateEnd(strm);
322     fclose(gz->file);
323     free(gz);
324     return Z_OK;
325 }
326 
327 const char *gzerror OF((gzFile, int *));
328 
329 const char *gzerror(gz, err)
330     gzFile gz;
331     int *err;
332 {
333     *err = gz->err;
334     return gz->msg;
335 }
336 
337 #endif
338 
339 char *prog;
340 
341 void error            OF((const char *msg));
342 void gz_compress      OF((FILE   *in, gzFile out));
343 #ifdef USE_MMAP
344 int  gz_compress_mmap OF((FILE   *in, gzFile out));
345 #endif
346 void gz_uncompress    OF((gzFile in, FILE   *out));
347 void file_compress    OF((char  *file, char *mode));
348 void file_uncompress  OF((char  *file));
349 int  main             OF((int argc, char *argv[]));
350 
351 /* ===========================================================================
352  * Display error message and exit
353  */
354 void error(msg)
355     const char *msg;
356 {
357     fprintf(stderr, "%s: %s\n", prog, msg);
358     exit(1);
359 }
360 
361 /* ===========================================================================
362  * Compress input to output then close both files.
363  */
364 
365 void gz_compress(in, out)
366     FILE   *in;
367     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) break;
386 
387         if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
388     }
389     fclose(in);
390     if (gzclose(out) != Z_OK) error("failed gzclose");
391 }
392 
393 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
394 
395 /* Try compressing the input file at once using mmap. Return Z_OK if
396  * if success, Z_ERRNO otherwise.
397  */
398 int gz_compress_mmap(in, out)
399     FILE   *in;
400     gzFile out;
401 {
402     int len;
403     int err;
404     int ifd = fileno(in);
405     caddr_t buf;    /* mmap'ed buffer for the entire input file */
406     off_t buf_len;  /* length of the input file */
407     struct stat sb;
408 
409     /* Determine the size of the file, needed for mmap: */
410     if (fstat(ifd, &sb) < 0) return Z_ERRNO;
411     buf_len = sb.st_size;
412     if (buf_len <= 0) return Z_ERRNO;
413 
414     /* Now do the actual mmap: */
415     buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
416     if (buf == (caddr_t)(-1)) return Z_ERRNO;
417 
418     /* Compress the whole file at once: */
419     len = gzwrite(out, (char *)buf, (unsigned)buf_len);
420 
421     if (len != (int)buf_len) error(gzerror(out, &err));
422 
423     munmap(buf, buf_len);
424     fclose(in);
425     if (gzclose(out) != Z_OK) error("failed gzclose");
426     return Z_OK;
427 }
428 #endif /* USE_MMAP */
429 
430 /* ===========================================================================
431  * Uncompress input to output then close both files.
432  */
433 void gz_uncompress(in, out)
434     gzFile in;
435     FILE   *out;
436 {
437     local char buf[BUFLEN];
438     int len;
439     int err;
440 
441     for (;;) {
442         len = gzread(in, buf, sizeof(buf));
443         if (len < 0) error (gzerror(in, &err));
444         if (len == 0) break;
445 
446         if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
447             error("failed fwrite");
448         }
449     }
450     if (fclose(out)) error("failed fclose");
451 
452     if (gzclose(in) != Z_OK) error("failed gzclose");
453 }
454 
455 
456 /* ===========================================================================
457  * Compress the given file: create a corresponding .gz file and remove the
458  * original.
459  */
460 void file_compress(file, mode)
461     char  *file;
462     char  *mode;
463 {
464     local char outfile[MAX_NAME_LEN];
465     FILE  *in;
466     gzFile out;
467 
468     if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
469         fprintf(stderr, "%s: filename too long\n", prog);
470         exit(1);
471     }
472 
473 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
474     snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
475 #else
476     strcpy(outfile, file);
477     strcat(outfile, GZ_SUFFIX);
478 #endif
479 
480     in = fopen(file, "rb");
481     if (in == NULL) {
482         perror(file);
483         exit(1);
484     }
485     out = gzopen(outfile, mode);
486     if (out == NULL) {
487         fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
488         exit(1);
489     }
490     gz_compress(in, out);
491 
492     unlink(file);
493 }
494 
495 
496 /* ===========================================================================
497  * Uncompress the given file and remove the original.
498  */
499 void file_uncompress(file)
500     char  *file;
501 {
502     local char buf[MAX_NAME_LEN];
503     char *infile, *outfile;
504     FILE  *out;
505     gzFile in;
506     size_t len = strlen(file);
507 
508     if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
509         fprintf(stderr, "%s: filename too long\n", prog);
510         exit(1);
511     }
512 
513 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
514     snprintf(buf, sizeof(buf), "%s", file);
515 #else
516     strcpy(buf, file);
517 #endif
518 
519     if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
520         infile = file;
521         outfile = buf;
522         outfile[len-3] = '\0';
523     } else {
524         outfile = file;
525         infile = buf;
526 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
527         snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
528 #else
529         strcat(infile, GZ_SUFFIX);
530 #endif
531     }
532     in = gzopen(infile, "rb");
533     if (in == NULL) {
534         fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
535         exit(1);
536     }
537     out = fopen(outfile, "wb");
538     if (out == NULL) {
539         perror(file);
540         exit(1);
541     }
542 
543     gz_uncompress(in, out);
544 
545     unlink(infile);
546 }
547 
548 
549 /* ===========================================================================
550  * Usage:  minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
551  *   -c : write to standard output
552  *   -d : decompress
553  *   -f : compress with Z_FILTERED
554  *   -h : compress with Z_HUFFMAN_ONLY
555  *   -r : compress with Z_RLE
556  *   -1 to -9 : compression level
557  */
558 
559 int main(argc, argv)
560     int argc;
561     char *argv[];
562 {
563     int copyout = 0;
564     int uncompr = 0;
565     gzFile file;
566     char *bname, outmode[20];
567 
568 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
569     snprintf(outmode, sizeof(outmode), "%s", "wb6 ");
570 #else
571     strcpy(outmode, "wb6 ");
572 #endif
573 
574     prog = argv[0];
575     bname = strrchr(argv[0], '/');
576     if (bname)
577       bname++;
578     else
579       bname = argv[0];
580     argc--, argv++;
581 
582     if (!strcmp(bname, "gunzip"))
583       uncompr = 1;
584     else if (!strcmp(bname, "zcat"))
585       copyout = uncompr = 1;
586 
587     while (argc > 0) {
588       if (strcmp(*argv, "-c") == 0)
589         copyout = 1;
590       else if (strcmp(*argv, "-d") == 0)
591         uncompr = 1;
592       else if (strcmp(*argv, "-f") == 0)
593         outmode[3] = 'f';
594       else if (strcmp(*argv, "-h") == 0)
595         outmode[3] = 'h';
596       else if (strcmp(*argv, "-r") == 0)
597         outmode[3] = 'R';
598       else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
599                (*argv)[2] == 0)
600         outmode[2] = (*argv)[1];
601       else
602         break;
603       argc--, argv++;
604     }
605     if (outmode[3] == ' ')
606         outmode[3] = 0;
607     if (argc == 0) {
608         SET_BINARY_MODE(stdin);
609         SET_BINARY_MODE(stdout);
610         if (uncompr) {
611             file = gzdopen(fileno(stdin), "rb");
612             if (file == NULL) error("can't gzdopen stdin");
613             gz_uncompress(file, stdout);
614         } else {
615             file = gzdopen(fileno(stdout), outmode);
616             if (file == NULL) error("can't gzdopen stdout");
617             gz_compress(stdin, file);
618         }
619     } else {
620         if (copyout) {
621             SET_BINARY_MODE(stdout);
622         }
623         do {
624             if (uncompr) {
625                 if (copyout) {
626                     file = gzopen(*argv, "rb");
627                     if (file == NULL)
628                         fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
629                     else
630                         gz_uncompress(file, stdout);
631                 } else {
632                     file_uncompress(*argv);
633                 }
634             } else {
635                 if (copyout) {
636                     FILE * in = fopen(*argv, "rb");
637 
638                     if (in == NULL) {
639                         perror(*argv);
640                     } else {
641                         file = gzdopen(fileno(stdout), outmode);
642                         if (file == NULL) error("can't gzdopen stdout");
643 
644                         gz_compress(in, file);
645                     }
646 
647                 } else {
648                     file_compress(*argv, outmode);
649                 }
650             }
651         } while (argv++, --argc);
652     }
653     return 0;
654 }
655