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