1212397c6Schristos /* minigzip.c -- simulate gzip using the zlib compression library
2212397c6Schristos * Copyright (C) 1995-2006, 2010 Jean-loup Gailly.
3212397c6Schristos * For conditions of distribution and use, see copyright notice in zlib.h
4212397c6Schristos */
5212397c6Schristos
6212397c6Schristos /*
7212397c6Schristos * minigzip is a minimal implementation of the gzip utility. This is
8212397c6Schristos * only an example of using zlib and isn't meant to replace the
9212397c6Schristos * full-featured gzip. No attempt is made to deal with file systems
10212397c6Schristos * limiting names to 14 or 8+3 characters, etc... Error checking is
11212397c6Schristos * very limited. So use minigzip only for testing; use gzip for the
12212397c6Schristos * real thing. On MSDOS, use only on file names without extension
13212397c6Schristos * or in pipe mode.
14212397c6Schristos */
15212397c6Schristos
16*924795e6Schristos /* @(#) Id: minigzip.c,v 1.1.1.2 2002/03/11 21:53:26 tromey Exp */
17212397c6Schristos
18212397c6Schristos #include "zlib.h"
19212397c6Schristos #include <stdio.h>
20212397c6Schristos
21212397c6Schristos #ifdef STDC
22212397c6Schristos # include <string.h>
23212397c6Schristos # include <stdlib.h>
24212397c6Schristos #endif
25212397c6Schristos
26212397c6Schristos #ifdef USE_MMAP
27212397c6Schristos # include <sys/types.h>
28212397c6Schristos # include <sys/mman.h>
29212397c6Schristos # include <sys/stat.h>
30212397c6Schristos #endif
31212397c6Schristos
32212397c6Schristos #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
33212397c6Schristos # include <fcntl.h>
34212397c6Schristos # include <io.h>
35212397c6Schristos # ifdef UNDER_CE
36212397c6Schristos # include <stdlib.h>
37212397c6Schristos # endif
38212397c6Schristos # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
39212397c6Schristos #else
40212397c6Schristos # define SET_BINARY_MODE(file)
41212397c6Schristos #endif
42212397c6Schristos
43212397c6Schristos #ifdef VMS
44212397c6Schristos # define unlink delete
45212397c6Schristos # define GZ_SUFFIX "-gz"
46212397c6Schristos #endif
47212397c6Schristos #ifdef RISCOS
48212397c6Schristos # define unlink remove
49212397c6Schristos # define GZ_SUFFIX "-gz"
50212397c6Schristos # define fileno(file) file->__file
51212397c6Schristos #endif
52212397c6Schristos #if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
53212397c6Schristos # include <unix.h> /* for fileno */
54212397c6Schristos #endif
55212397c6Schristos
56212397c6Schristos #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
57212397c6Schristos #ifndef WIN32 /* unlink already in stdio.h for WIN32 */
58212397c6Schristos extern int unlink OF((const char *));
59212397c6Schristos #endif
60212397c6Schristos #endif
61212397c6Schristos
62212397c6Schristos #if defined(UNDER_CE)
63212397c6Schristos # include <windows.h>
64212397c6Schristos # define perror(s) pwinerror(s)
65212397c6Schristos
66212397c6Schristos /* Map the Windows error number in ERROR to a locale-dependent error
67212397c6Schristos message string and return a pointer to it. Typically, the values
68212397c6Schristos for ERROR come from GetLastError.
69212397c6Schristos
70212397c6Schristos The string pointed to shall not be modified by the application,
71212397c6Schristos but may be overwritten by a subsequent call to strwinerror
72212397c6Schristos
73212397c6Schristos The strwinerror function does not change the current setting
74212397c6Schristos of GetLastError. */
75212397c6Schristos
strwinerror(error)76212397c6Schristos static char *strwinerror (error)
77212397c6Schristos DWORD error;
78212397c6Schristos {
79212397c6Schristos static char buf[1024];
80212397c6Schristos
81212397c6Schristos wchar_t *msgbuf;
82212397c6Schristos DWORD lasterr = GetLastError();
83212397c6Schristos DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
84212397c6Schristos | FORMAT_MESSAGE_ALLOCATE_BUFFER,
85212397c6Schristos NULL,
86212397c6Schristos error,
87212397c6Schristos 0, /* Default language */
88212397c6Schristos (LPVOID)&msgbuf,
89212397c6Schristos 0,
90212397c6Schristos NULL);
91212397c6Schristos if (chars != 0) {
92212397c6Schristos /* If there is an \r\n appended, zap it. */
93212397c6Schristos if (chars >= 2
94212397c6Schristos && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
95212397c6Schristos chars -= 2;
96212397c6Schristos msgbuf[chars] = 0;
97212397c6Schristos }
98212397c6Schristos
99212397c6Schristos if (chars > sizeof (buf) - 1) {
100212397c6Schristos chars = sizeof (buf) - 1;
101212397c6Schristos msgbuf[chars] = 0;
102212397c6Schristos }
103212397c6Schristos
104212397c6Schristos wcstombs(buf, msgbuf, chars + 1);
105212397c6Schristos LocalFree(msgbuf);
106212397c6Schristos }
107212397c6Schristos else {
108212397c6Schristos sprintf(buf, "unknown win32 error (%ld)", error);
109212397c6Schristos }
110212397c6Schristos
111212397c6Schristos SetLastError(lasterr);
112212397c6Schristos return buf;
113212397c6Schristos }
114212397c6Schristos
pwinerror(s)115212397c6Schristos static void pwinerror (s)
116212397c6Schristos const char *s;
117212397c6Schristos {
118212397c6Schristos if (s && *s)
119212397c6Schristos fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ()));
120212397c6Schristos else
121212397c6Schristos fprintf(stderr, "%s\n", strwinerror(GetLastError ()));
122212397c6Schristos }
123212397c6Schristos
124212397c6Schristos #endif /* UNDER_CE */
125212397c6Schristos
126212397c6Schristos #ifndef GZ_SUFFIX
127212397c6Schristos # define GZ_SUFFIX ".gz"
128212397c6Schristos #endif
129212397c6Schristos #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
130212397c6Schristos
131212397c6Schristos #define BUFLEN 16384
132212397c6Schristos #define MAX_NAME_LEN 1024
133212397c6Schristos
134212397c6Schristos #ifdef MAXSEG_64K
135212397c6Schristos # define local static
136212397c6Schristos /* Needed for systems with limitation on stack size. */
137212397c6Schristos #else
138212397c6Schristos # define local
139212397c6Schristos #endif
140212397c6Schristos
141212397c6Schristos char *prog;
142212397c6Schristos
143212397c6Schristos void error OF((const char *msg));
144212397c6Schristos void gz_compress OF((FILE *in, gzFile out));
145212397c6Schristos #ifdef USE_MMAP
146212397c6Schristos int gz_compress_mmap OF((FILE *in, gzFile out));
147212397c6Schristos #endif
148212397c6Schristos void gz_uncompress OF((gzFile in, FILE *out));
149212397c6Schristos void file_compress OF((char *file, char *mode));
150212397c6Schristos void file_uncompress OF((char *file));
151212397c6Schristos int main OF((int argc, char *argv[]));
152212397c6Schristos
153212397c6Schristos /* ===========================================================================
154212397c6Schristos * Display error message and exit
155212397c6Schristos */
error(msg)156212397c6Schristos void error(msg)
157212397c6Schristos const char *msg;
158212397c6Schristos {
159212397c6Schristos fprintf(stderr, "%s: %s\n", prog, msg);
160212397c6Schristos exit(1);
161212397c6Schristos }
162212397c6Schristos
163212397c6Schristos /* ===========================================================================
164212397c6Schristos * Compress input to output then close both files.
165212397c6Schristos */
166212397c6Schristos
gz_compress(in,out)167212397c6Schristos void gz_compress(in, out)
168212397c6Schristos FILE *in;
169212397c6Schristos gzFile out;
170212397c6Schristos {
171212397c6Schristos local char buf[BUFLEN];
172212397c6Schristos int len;
173212397c6Schristos int err;
174212397c6Schristos
175212397c6Schristos #ifdef USE_MMAP
176212397c6Schristos /* Try first compressing with mmap. If mmap fails (minigzip used in a
177212397c6Schristos * pipe), use the normal fread loop.
178212397c6Schristos */
179212397c6Schristos if (gz_compress_mmap(in, out) == Z_OK) return;
180212397c6Schristos #endif
181212397c6Schristos for (;;) {
182212397c6Schristos len = (int)fread(buf, 1, sizeof(buf), in);
183212397c6Schristos if (ferror(in)) {
184212397c6Schristos perror("fread");
185212397c6Schristos exit(1);
186212397c6Schristos }
187212397c6Schristos if (len == 0) break;
188212397c6Schristos
189212397c6Schristos if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
190212397c6Schristos }
191212397c6Schristos fclose(in);
192212397c6Schristos if (gzclose(out) != Z_OK) error("failed gzclose");
193212397c6Schristos }
194212397c6Schristos
195212397c6Schristos #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
196212397c6Schristos
197212397c6Schristos /* Try compressing the input file at once using mmap. Return Z_OK if
198212397c6Schristos * if success, Z_ERRNO otherwise.
199212397c6Schristos */
gz_compress_mmap(in,out)200212397c6Schristos int gz_compress_mmap(in, out)
201212397c6Schristos FILE *in;
202212397c6Schristos gzFile out;
203212397c6Schristos {
204212397c6Schristos int len;
205212397c6Schristos int err;
206212397c6Schristos int ifd = fileno(in);
207212397c6Schristos caddr_t buf; /* mmap'ed buffer for the entire input file */
208212397c6Schristos off_t buf_len; /* length of the input file */
209212397c6Schristos struct stat sb;
210212397c6Schristos
211212397c6Schristos /* Determine the size of the file, needed for mmap: */
212212397c6Schristos if (fstat(ifd, &sb) < 0) return Z_ERRNO;
213212397c6Schristos buf_len = sb.st_size;
214212397c6Schristos if (buf_len <= 0) return Z_ERRNO;
215212397c6Schristos
216212397c6Schristos /* Now do the actual mmap: */
217212397c6Schristos buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
218212397c6Schristos if (buf == (caddr_t)(-1)) return Z_ERRNO;
219212397c6Schristos
220212397c6Schristos /* Compress the whole file at once: */
221212397c6Schristos len = gzwrite(out, (char *)buf, (unsigned)buf_len);
222212397c6Schristos
223212397c6Schristos if (len != (int)buf_len) error(gzerror(out, &err));
224212397c6Schristos
225212397c6Schristos munmap(buf, buf_len);
226212397c6Schristos fclose(in);
227212397c6Schristos if (gzclose(out) != Z_OK) error("failed gzclose");
228212397c6Schristos return Z_OK;
229212397c6Schristos }
230212397c6Schristos #endif /* USE_MMAP */
231212397c6Schristos
232212397c6Schristos /* ===========================================================================
233212397c6Schristos * Uncompress input to output then close both files.
234212397c6Schristos */
gz_uncompress(in,out)235212397c6Schristos void gz_uncompress(in, out)
236212397c6Schristos gzFile in;
237212397c6Schristos FILE *out;
238212397c6Schristos {
239212397c6Schristos local char buf[BUFLEN];
240212397c6Schristos int len;
241212397c6Schristos int err;
242212397c6Schristos
243212397c6Schristos for (;;) {
244212397c6Schristos len = gzread(in, buf, sizeof(buf));
245212397c6Schristos if (len < 0) error (gzerror(in, &err));
246212397c6Schristos if (len == 0) break;
247212397c6Schristos
248212397c6Schristos if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
249212397c6Schristos error("failed fwrite");
250212397c6Schristos }
251212397c6Schristos }
252212397c6Schristos if (fclose(out)) error("failed fclose");
253212397c6Schristos
254212397c6Schristos if (gzclose(in) != Z_OK) error("failed gzclose");
255212397c6Schristos }
256212397c6Schristos
257212397c6Schristos
258212397c6Schristos /* ===========================================================================
259212397c6Schristos * Compress the given file: create a corresponding .gz file and remove the
260212397c6Schristos * original.
261212397c6Schristos */
file_compress(file,mode)262212397c6Schristos void file_compress(file, mode)
263212397c6Schristos char *file;
264212397c6Schristos char *mode;
265212397c6Schristos {
266212397c6Schristos local char outfile[MAX_NAME_LEN];
267212397c6Schristos FILE *in;
268212397c6Schristos gzFile out;
269212397c6Schristos
270212397c6Schristos if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
271212397c6Schristos fprintf(stderr, "%s: filename too long\n", prog);
272212397c6Schristos exit(1);
273212397c6Schristos }
274212397c6Schristos
275212397c6Schristos strcpy(outfile, file);
276212397c6Schristos strcat(outfile, GZ_SUFFIX);
277212397c6Schristos
278212397c6Schristos in = fopen(file, "rb");
279212397c6Schristos if (in == NULL) {
280212397c6Schristos perror(file);
281212397c6Schristos exit(1);
282212397c6Schristos }
283212397c6Schristos out = gzopen(outfile, mode);
284212397c6Schristos if (out == NULL) {
285212397c6Schristos fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
286212397c6Schristos exit(1);
287212397c6Schristos }
288212397c6Schristos gz_compress(in, out);
289212397c6Schristos
290212397c6Schristos unlink(file);
291212397c6Schristos }
292212397c6Schristos
293212397c6Schristos
294212397c6Schristos /* ===========================================================================
295212397c6Schristos * Uncompress the given file and remove the original.
296212397c6Schristos */
file_uncompress(file)297212397c6Schristos void file_uncompress(file)
298212397c6Schristos char *file;
299212397c6Schristos {
300212397c6Schristos local char buf[MAX_NAME_LEN];
301212397c6Schristos char *infile, *outfile;
302212397c6Schristos FILE *out;
303212397c6Schristos gzFile in;
304212397c6Schristos size_t len = strlen(file);
305212397c6Schristos
306212397c6Schristos if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
307212397c6Schristos fprintf(stderr, "%s: filename too long\n", prog);
308212397c6Schristos exit(1);
309212397c6Schristos }
310212397c6Schristos
311212397c6Schristos strcpy(buf, file);
312212397c6Schristos
313212397c6Schristos if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
314212397c6Schristos infile = file;
315212397c6Schristos outfile = buf;
316212397c6Schristos outfile[len-3] = '\0';
317212397c6Schristos } else {
318212397c6Schristos outfile = file;
319212397c6Schristos infile = buf;
320212397c6Schristos strcat(infile, GZ_SUFFIX);
321212397c6Schristos }
322212397c6Schristos in = gzopen(infile, "rb");
323212397c6Schristos if (in == NULL) {
324212397c6Schristos fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
325212397c6Schristos exit(1);
326212397c6Schristos }
327212397c6Schristos out = fopen(outfile, "wb");
328212397c6Schristos if (out == NULL) {
329212397c6Schristos perror(file);
330212397c6Schristos exit(1);
331212397c6Schristos }
332212397c6Schristos
333212397c6Schristos gz_uncompress(in, out);
334212397c6Schristos
335212397c6Schristos unlink(infile);
336212397c6Schristos }
337212397c6Schristos
338212397c6Schristos
339212397c6Schristos /* ===========================================================================
340212397c6Schristos * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
341212397c6Schristos * -c : write to standard output
342212397c6Schristos * -d : decompress
343212397c6Schristos * -f : compress with Z_FILTERED
344212397c6Schristos * -h : compress with Z_HUFFMAN_ONLY
345212397c6Schristos * -r : compress with Z_RLE
346212397c6Schristos * -1 to -9 : compression level
347212397c6Schristos */
348212397c6Schristos
main(argc,argv)349212397c6Schristos int main(argc, argv)
350212397c6Schristos int argc;
351212397c6Schristos char *argv[];
352212397c6Schristos {
353212397c6Schristos int copyout = 0;
354212397c6Schristos int uncompr = 0;
355212397c6Schristos gzFile file;
356212397c6Schristos char *bname, outmode[20];
357212397c6Schristos
358212397c6Schristos strcpy(outmode, "wb6 ");
359212397c6Schristos
360212397c6Schristos prog = argv[0];
361212397c6Schristos bname = strrchr(argv[0], '/');
362212397c6Schristos if (bname)
363212397c6Schristos bname++;
364212397c6Schristos else
365212397c6Schristos bname = argv[0];
366212397c6Schristos argc--, argv++;
367212397c6Schristos
368212397c6Schristos if (!strcmp(bname, "gunzip"))
369212397c6Schristos uncompr = 1;
370212397c6Schristos else if (!strcmp(bname, "zcat"))
371212397c6Schristos copyout = uncompr = 1;
372212397c6Schristos
373212397c6Schristos while (argc > 0) {
374212397c6Schristos if (strcmp(*argv, "-c") == 0)
375212397c6Schristos copyout = 1;
376212397c6Schristos else if (strcmp(*argv, "-d") == 0)
377212397c6Schristos uncompr = 1;
378212397c6Schristos else if (strcmp(*argv, "-f") == 0)
379212397c6Schristos outmode[3] = 'f';
380212397c6Schristos else if (strcmp(*argv, "-h") == 0)
381212397c6Schristos outmode[3] = 'h';
382212397c6Schristos else if (strcmp(*argv, "-r") == 0)
383212397c6Schristos outmode[3] = 'R';
384212397c6Schristos else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
385212397c6Schristos (*argv)[2] == 0)
386212397c6Schristos outmode[2] = (*argv)[1];
387212397c6Schristos else
388212397c6Schristos break;
389212397c6Schristos argc--, argv++;
390212397c6Schristos }
391212397c6Schristos if (outmode[3] == ' ')
392212397c6Schristos outmode[3] = 0;
393212397c6Schristos if (argc == 0) {
394212397c6Schristos SET_BINARY_MODE(stdin);
395212397c6Schristos SET_BINARY_MODE(stdout);
396212397c6Schristos if (uncompr) {
397212397c6Schristos file = gzdopen(fileno(stdin), "rb");
398212397c6Schristos if (file == NULL) error("can't gzdopen stdin");
399212397c6Schristos gz_uncompress(file, stdout);
400212397c6Schristos } else {
401212397c6Schristos file = gzdopen(fileno(stdout), outmode);
402212397c6Schristos if (file == NULL) error("can't gzdopen stdout");
403212397c6Schristos gz_compress(stdin, file);
404212397c6Schristos }
405212397c6Schristos } else {
406212397c6Schristos if (copyout) {
407212397c6Schristos SET_BINARY_MODE(stdout);
408212397c6Schristos }
409212397c6Schristos do {
410212397c6Schristos if (uncompr) {
411212397c6Schristos if (copyout) {
412212397c6Schristos file = gzopen(*argv, "rb");
413212397c6Schristos if (file == NULL)
414212397c6Schristos fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
415212397c6Schristos else
416212397c6Schristos gz_uncompress(file, stdout);
417212397c6Schristos } else {
418212397c6Schristos file_uncompress(*argv);
419212397c6Schristos }
420212397c6Schristos } else {
421212397c6Schristos if (copyout) {
422212397c6Schristos FILE * in = fopen(*argv, "rb");
423212397c6Schristos
424212397c6Schristos if (in == NULL) {
425212397c6Schristos perror(*argv);
426212397c6Schristos } else {
427212397c6Schristos file = gzdopen(fileno(stdout), outmode);
428212397c6Schristos if (file == NULL) error("can't gzdopen stdout");
429212397c6Schristos
430212397c6Schristos gz_compress(in, file);
431212397c6Schristos }
432212397c6Schristos
433212397c6Schristos } else {
434212397c6Schristos file_compress(*argv, outmode);
435212397c6Schristos }
436212397c6Schristos }
437212397c6Schristos } while (argv++, --argc);
438212397c6Schristos }
439212397c6Schristos return 0;
440212397c6Schristos }
441