]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libz/test/minigzip.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / lib / libz / test / minigzip.c
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
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
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
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
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
178 gzFile gzopen(path, mode)
179 const char *path;
180 const char *mode;
181 {
182     return gz_open(path, -1, mode);
183 }
184
185 gzFile gzdopen(fd, mode)
186 int fd;
187 const char *mode;
188 {
189     return gz_open(NULL, fd, mode);
190 }
191
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(struct gzFile_s));
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
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
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
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
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  */
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
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  */
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  */
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  */
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  */
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
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 }