648db22b |
1 | /* minigzip.c contains minimal changes required to be compiled with zlibWrapper: |
2 | * - #include "zlib.h" was changed to #include "zstd_zlibwrapper.h" */ |
3 | |
9e052883 |
4 | /* minigzip.c -- simulate gzip using the zlib compression library |
648db22b |
5 | * Copyright (C) 1995-2006, 2010, 2011 Jean-loup Gailly. |
6 | * For conditions of distribution and use, see https://www.zlib.net/zlib_license.html |
9e052883 |
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 | |
648db22b |
21 | #define _POSIX_SOURCE /* fileno */ |
22 | |
23 | #include "zstd_zlibwrapper.h" |
9e052883 |
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 | |
f535537f |
37 | #if defined(MSDOS) || defined(OS2) || defined(_WIN32) || defined(__CYGWIN__) |
9e052883 |
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 | |
648db22b |
48 | #ifdef _MSC_VER |
9e052883 |
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) |
f535537f |
66 | #ifndef _WIN32 /* unlink already in stdio.h for WIN32 */ |
648db22b |
67 | extern int unlink _Z_OF((const char *)); |
9e052883 |
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 | |
f535537f |
85 | static char *strwinerror(DWORD error) |
9e052883 |
86 | { |
87 | static char buf[1024]; |
88 | |
89 | wchar_t *msgbuf; |
90 | DWORD lasterr = GetLastError(); |
91 | DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
92 | | FORMAT_MESSAGE_ALLOCATE_BUFFER, |
93 | NULL, |
94 | error, |
95 | 0, /* Default language */ |
96 | (LPVOID)&msgbuf, |
97 | 0, |
98 | NULL); |
99 | if (chars != 0) { |
100 | /* If there is an \r\n appended, zap it. */ |
101 | if (chars >= 2 |
102 | && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { |
103 | chars -= 2; |
104 | msgbuf[chars] = 0; |
105 | } |
106 | |
107 | if (chars > sizeof (buf) - 1) { |
108 | chars = sizeof (buf) - 1; |
109 | msgbuf[chars] = 0; |
110 | } |
111 | |
112 | wcstombs(buf, msgbuf, chars + 1); |
113 | LocalFree(msgbuf); |
114 | } |
115 | else { |
116 | sprintf(buf, "unknown win32 error (%ld)", error); |
117 | } |
118 | |
119 | SetLastError(lasterr); |
120 | return buf; |
121 | } |
122 | |
f535537f |
123 | static void pwinerror (const char *s) |
9e052883 |
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 | |
648db22b |
155 | void *myalloc _Z_OF((void *, unsigned, unsigned)); |
156 | void myfree _Z_OF((void *, void *)); |
9e052883 |
157 | |
158 | void *myalloc(q, n, m) |
159 | void *q; |
160 | unsigned n, m; |
161 | { |
648db22b |
162 | q = Z_NULL; |
9e052883 |
163 | return calloc(n, m); |
164 | } |
165 | |
166 | void myfree(q, p) |
167 | void *q, *p; |
168 | { |
648db22b |
169 | q = Z_NULL; |
9e052883 |
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 | |
648db22b |
181 | gzFile gzopen _Z_OF((const char *, const char *)); |
182 | gzFile gzdopen _Z_OF((int, const char *)); |
183 | gzFile gz_open _Z_OF((const char *, int, const char *)); |
9e052883 |
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 | |
f535537f |
199 | gzFile gz_open(const char *path, int fd, const char *mode) { |
9e052883 |
200 | gzFile gz; |
201 | int ret; |
202 | |
203 | gz = malloc(sizeof(struct gzFile_s)); |
204 | if (gz == NULL) |
205 | return NULL; |
206 | gz->write = strchr(mode, 'w') != NULL; |
207 | gz->strm.zalloc = myalloc; |
208 | gz->strm.zfree = myfree; |
209 | gz->strm.opaque = Z_NULL; |
210 | if (gz->write) |
211 | ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0); |
212 | else { |
213 | gz->strm.next_in = 0; |
214 | gz->strm.avail_in = Z_NULL; |
215 | ret = inflateInit2(&(gz->strm), 15 + 16); |
216 | } |
217 | if (ret != Z_OK) { |
218 | free(gz); |
219 | return NULL; |
220 | } |
221 | gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") : |
222 | fopen(path, gz->write ? "wb" : "rb"); |
223 | if (gz->file == NULL) { |
224 | gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm)); |
225 | free(gz); |
226 | return NULL; |
227 | } |
228 | gz->err = 0; |
229 | gz->msg = ""; |
230 | return gz; |
231 | } |
232 | |
648db22b |
233 | int gzwrite _Z_OF((gzFile, const void *, unsigned)); |
9e052883 |
234 | |
f535537f |
235 | int gzwrite(gzFile gz, const void *buf, unsigned len) { |
9e052883 |
236 | z_stream *strm; |
237 | unsigned char out[BUFLEN]; |
238 | |
239 | if (gz == NULL || !gz->write) |
240 | return 0; |
241 | strm = &(gz->strm); |
242 | strm->next_in = (void *)buf; |
243 | strm->avail_in = len; |
244 | do { |
245 | strm->next_out = out; |
246 | strm->avail_out = BUFLEN; |
247 | (void)deflate(strm, Z_NO_FLUSH); |
248 | fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); |
249 | } while (strm->avail_out == 0); |
250 | return len; |
251 | } |
252 | |
648db22b |
253 | int gzread _Z_OF((gzFile, void *, unsigned)); |
9e052883 |
254 | |
f535537f |
255 | int gzread(gzFile gz, void *buf, unsigned len) { |
9e052883 |
256 | int ret; |
257 | unsigned got; |
258 | unsigned char in[1]; |
259 | z_stream *strm; |
260 | |
261 | if (gz == NULL || gz->write) |
262 | return 0; |
263 | if (gz->err) |
264 | return 0; |
265 | strm = &(gz->strm); |
266 | strm->next_out = (void *)buf; |
267 | strm->avail_out = len; |
268 | do { |
269 | got = fread(in, 1, 1, gz->file); |
270 | if (got == 0) |
271 | break; |
272 | strm->next_in = in; |
273 | strm->avail_in = 1; |
274 | ret = inflate(strm, Z_NO_FLUSH); |
275 | if (ret == Z_DATA_ERROR) { |
276 | gz->err = Z_DATA_ERROR; |
277 | gz->msg = strm->msg; |
278 | return 0; |
279 | } |
280 | if (ret == Z_STREAM_END) |
281 | inflateReset(strm); |
282 | } while (strm->avail_out); |
283 | return len - strm->avail_out; |
284 | } |
285 | |
648db22b |
286 | int gzclose _Z_OF((gzFile)); |
9e052883 |
287 | |
f535537f |
288 | int gzclose(gzFile gz) { |
9e052883 |
289 | z_stream *strm; |
290 | unsigned char out[BUFLEN]; |
291 | |
292 | if (gz == NULL) |
293 | return Z_STREAM_ERROR; |
294 | strm = &(gz->strm); |
295 | if (gz->write) { |
296 | strm->next_in = Z_NULL; |
297 | strm->avail_in = 0; |
298 | do { |
299 | strm->next_out = out; |
300 | strm->avail_out = BUFLEN; |
301 | (void)deflate(strm, Z_FINISH); |
302 | fwrite(out, 1, BUFLEN - strm->avail_out, gz->file); |
303 | } while (strm->avail_out == 0); |
304 | deflateEnd(strm); |
305 | } |
306 | else |
307 | inflateEnd(strm); |
308 | fclose(gz->file); |
309 | free(gz); |
310 | return Z_OK; |
311 | } |
312 | |
648db22b |
313 | const char *gzerror _Z_OF((gzFile, int *)); |
9e052883 |
314 | |
f535537f |
315 | const char *gzerror(gzFile gz, int *err) |
9e052883 |
316 | { |
317 | *err = gz->err; |
318 | return gz->msg; |
319 | } |
320 | |
321 | #endif |
322 | |
648db22b |
323 | char *prog; |
9e052883 |
324 | |
648db22b |
325 | void error _Z_OF((const char *msg)); |
326 | void gz_compress _Z_OF((FILE *in, gzFile out)); |
9e052883 |
327 | #ifdef USE_MMAP |
648db22b |
328 | int gz_compress_mmap _Z_OF((FILE *in, gzFile out)); |
9e052883 |
329 | #endif |
648db22b |
330 | void gz_uncompress _Z_OF((gzFile in, FILE *out)); |
331 | void file_compress _Z_OF((char *file, char *mode)); |
332 | void file_uncompress _Z_OF((char *file)); |
333 | int main _Z_OF((int argc, char *argv[])); |
9e052883 |
334 | |
335 | /* =========================================================================== |
336 | * Display error message and exit |
337 | */ |
f535537f |
338 | void error(const char *msg) |
9e052883 |
339 | { |
340 | fprintf(stderr, "%s: %s\n", prog, msg); |
341 | exit(1); |
342 | } |
343 | |
344 | /* =========================================================================== |
345 | * Compress input to output then close both files. |
346 | */ |
347 | |
f535537f |
348 | void gz_compress(FILE *in, gzFile out) |
9e052883 |
349 | { |
350 | local char buf[BUFLEN]; |
351 | int len; |
352 | int err; |
353 | |
354 | #ifdef USE_MMAP |
355 | /* Try first compressing with mmap. If mmap fails (minigzip used in a |
356 | * pipe), use the normal fread loop. |
357 | */ |
358 | if (gz_compress_mmap(in, out) == Z_OK) return; |
359 | #endif |
360 | for (;;) { |
361 | len = (int)fread(buf, 1, sizeof(buf), in); |
362 | if (ferror(in)) { |
363 | perror("fread"); |
364 | exit(1); |
365 | } |
366 | if (len == 0) break; |
367 | |
368 | if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); |
369 | } |
370 | fclose(in); |
371 | if (gzclose(out) != Z_OK) error("failed gzclose"); |
372 | } |
373 | |
374 | #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */ |
375 | |
376 | /* Try compressing the input file at once using mmap. Return Z_OK if |
377 | * if success, Z_ERRNO otherwise. |
378 | */ |
f535537f |
379 | int gz_compress_mmap(FILE *in, gzFile out) { |
9e052883 |
380 | int len; |
381 | int err; |
382 | int ifd = fileno(in); |
383 | caddr_t buf; /* mmap'ed buffer for the entire input file */ |
384 | off_t buf_len; /* length of the input file */ |
385 | struct stat sb; |
386 | |
387 | /* Determine the size of the file, needed for mmap: */ |
388 | if (fstat(ifd, &sb) < 0) return Z_ERRNO; |
389 | buf_len = sb.st_size; |
390 | if (buf_len <= 0) return Z_ERRNO; |
391 | |
392 | /* Now do the actual mmap: */ |
393 | buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); |
394 | if (buf == (caddr_t)(-1)) return Z_ERRNO; |
395 | |
396 | /* Compress the whole file at once: */ |
397 | len = gzwrite(out, (char *)buf, (unsigned)buf_len); |
398 | |
399 | if (len != (int)buf_len) error(gzerror(out, &err)); |
400 | |
401 | munmap(buf, buf_len); |
402 | fclose(in); |
403 | if (gzclose(out) != Z_OK) error("failed gzclose"); |
404 | return Z_OK; |
405 | } |
406 | #endif /* USE_MMAP */ |
407 | |
408 | /* =========================================================================== |
409 | * Uncompress input to output then close both files. |
410 | */ |
f535537f |
411 | void gz_uncompress(gzFile in, FILE *out) { |
9e052883 |
412 | local char buf[BUFLEN]; |
413 | int len; |
414 | int err; |
415 | |
416 | for (;;) { |
417 | len = gzread(in, buf, sizeof(buf)); |
418 | if (len < 0) error (gzerror(in, &err)); |
419 | if (len == 0) break; |
420 | |
421 | if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { |
422 | error("failed fwrite"); |
423 | } |
424 | } |
425 | if (fclose(out)) error("failed fclose"); |
426 | |
427 | if (gzclose(in) != Z_OK) error("failed gzclose"); |
428 | } |
429 | |
430 | |
431 | /* =========================================================================== |
432 | * Compress the given file: create a corresponding .gz file and remove the |
433 | * original. |
434 | */ |
f535537f |
435 | void file_compress(char *file, char *mode) { |
9e052883 |
436 | local char outfile[MAX_NAME_LEN]; |
437 | FILE *in; |
438 | gzFile out; |
439 | |
440 | if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) { |
441 | fprintf(stderr, "%s: filename too long\n", prog); |
442 | exit(1); |
443 | } |
444 | |
9e052883 |
445 | strcpy(outfile, file); |
446 | strcat(outfile, GZ_SUFFIX); |
9e052883 |
447 | |
448 | in = fopen(file, "rb"); |
449 | if (in == NULL) { |
450 | perror(file); |
451 | exit(1); |
452 | } |
453 | out = gzopen(outfile, mode); |
454 | if (out == NULL) { |
455 | fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); |
456 | exit(1); |
457 | } |
458 | gz_compress(in, out); |
459 | |
460 | unlink(file); |
461 | } |
462 | |
463 | |
464 | /* =========================================================================== |
465 | * Uncompress the given file and remove the original. |
466 | */ |
f535537f |
467 | void file_uncompress(char *file) { |
9e052883 |
468 | local char buf[MAX_NAME_LEN]; |
469 | char *infile, *outfile; |
470 | FILE *out; |
471 | gzFile in; |
648db22b |
472 | size_t len = strlen(file); |
9e052883 |
473 | |
474 | if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) { |
475 | fprintf(stderr, "%s: filename too long\n", prog); |
476 | exit(1); |
477 | } |
478 | |
9e052883 |
479 | strcpy(buf, file); |
9e052883 |
480 | |
481 | if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { |
482 | infile = file; |
483 | outfile = buf; |
484 | outfile[len-3] = '\0'; |
485 | } else { |
486 | outfile = file; |
487 | infile = buf; |
9e052883 |
488 | strcat(infile, GZ_SUFFIX); |
9e052883 |
489 | } |
490 | in = gzopen(infile, "rb"); |
491 | if (in == NULL) { |
492 | fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); |
493 | exit(1); |
494 | } |
495 | out = fopen(outfile, "wb"); |
496 | if (out == NULL) { |
497 | perror(file); |
498 | exit(1); |
499 | } |
500 | |
501 | gz_uncompress(in, out); |
502 | |
503 | unlink(infile); |
504 | } |
505 | |
506 | |
507 | /* =========================================================================== |
508 | * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...] |
509 | * -c : write to standard output |
510 | * -d : decompress |
511 | * -f : compress with Z_FILTERED |
512 | * -h : compress with Z_HUFFMAN_ONLY |
513 | * -r : compress with Z_RLE |
514 | * -1 to -9 : compression level |
515 | */ |
516 | |
f535537f |
517 | int main(int argc, char *argv[]) { |
9e052883 |
518 | int copyout = 0; |
519 | int uncompr = 0; |
520 | gzFile file; |
521 | char *bname, outmode[20]; |
522 | |
9e052883 |
523 | strcpy(outmode, "wb6 "); |
9e052883 |
524 | |
525 | prog = argv[0]; |
526 | bname = strrchr(argv[0], '/'); |
527 | if (bname) |
528 | bname++; |
529 | else |
530 | bname = argv[0]; |
531 | argc--, argv++; |
532 | |
533 | if (!strcmp(bname, "gunzip")) |
534 | uncompr = 1; |
535 | else if (!strcmp(bname, "zcat")) |
536 | copyout = uncompr = 1; |
537 | |
538 | while (argc > 0) { |
539 | if (strcmp(*argv, "-c") == 0) |
540 | copyout = 1; |
541 | else if (strcmp(*argv, "-d") == 0) |
542 | uncompr = 1; |
543 | else if (strcmp(*argv, "-f") == 0) |
544 | outmode[3] = 'f'; |
545 | else if (strcmp(*argv, "-h") == 0) |
546 | outmode[3] = 'h'; |
547 | else if (strcmp(*argv, "-r") == 0) |
548 | outmode[3] = 'R'; |
549 | else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && |
550 | (*argv)[2] == 0) |
551 | outmode[2] = (*argv)[1]; |
552 | else |
553 | break; |
554 | argc--, argv++; |
555 | } |
556 | if (outmode[3] == ' ') |
557 | outmode[3] = 0; |
558 | if (argc == 0) { |
559 | SET_BINARY_MODE(stdin); |
560 | SET_BINARY_MODE(stdout); |
561 | if (uncompr) { |
562 | file = gzdopen(fileno(stdin), "rb"); |
563 | if (file == NULL) error("can't gzdopen stdin"); |
564 | gz_uncompress(file, stdout); |
565 | } else { |
566 | file = gzdopen(fileno(stdout), outmode); |
567 | if (file == NULL) error("can't gzdopen stdout"); |
568 | gz_compress(stdin, file); |
569 | } |
570 | } else { |
571 | if (copyout) { |
572 | SET_BINARY_MODE(stdout); |
573 | } |
574 | do { |
575 | if (uncompr) { |
576 | if (copyout) { |
577 | file = gzopen(*argv, "rb"); |
578 | if (file == NULL) |
579 | fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv); |
580 | else |
581 | gz_uncompress(file, stdout); |
582 | } else { |
583 | file_uncompress(*argv); |
584 | } |
585 | } else { |
586 | if (copyout) { |
587 | FILE * in = fopen(*argv, "rb"); |
588 | |
589 | if (in == NULL) { |
590 | perror(*argv); |
591 | } else { |
592 | file = gzdopen(fileno(stdout), outmode); |
593 | if (file == NULL) error("can't gzdopen stdout"); |
594 | |
595 | gz_compress(in, file); |
596 | } |
597 | |
598 | } else { |
599 | file_compress(*argv, outmode); |
600 | } |
601 | } |
602 | } while (argv++, --argc); |
603 | } |
604 | return 0; |
605 | } |