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 | |
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 | |
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) |
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 | |
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 | |
648db22b |
157 | void *myalloc _Z_OF((void *, unsigned, unsigned)); |
158 | void myfree _Z_OF((void *, void *)); |
9e052883 |
159 | |
160 | void *myalloc(q, n, m) |
161 | void *q; |
162 | unsigned n, m; |
163 | { |
648db22b |
164 | q = Z_NULL; |
9e052883 |
165 | return calloc(n, m); |
166 | } |
167 | |
168 | void myfree(q, p) |
169 | void *q, *p; |
170 | { |
648db22b |
171 | q = Z_NULL; |
9e052883 |
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 | |
648db22b |
183 | gzFile gzopen _Z_OF((const char *, const char *)); |
184 | gzFile gzdopen _Z_OF((int, const char *)); |
185 | gzFile gz_open _Z_OF((const char *, int, const char *)); |
9e052883 |
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 | |
648db22b |
239 | int gzwrite _Z_OF((gzFile, const void *, unsigned)); |
9e052883 |
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 | |
648db22b |
263 | int gzread _Z_OF((gzFile, void *, unsigned)); |
9e052883 |
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 | |
648db22b |
300 | int gzclose _Z_OF((gzFile)); |
9e052883 |
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 | |
648db22b |
329 | const char *gzerror _Z_OF((gzFile, int *)); |
9e052883 |
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 | |
648db22b |
341 | char *prog; |
9e052883 |
342 | |
648db22b |
343 | void error _Z_OF((const char *msg)); |
344 | void gz_compress _Z_OF((FILE *in, gzFile out)); |
9e052883 |
345 | #ifdef USE_MMAP |
648db22b |
346 | int gz_compress_mmap _Z_OF((FILE *in, gzFile out)); |
9e052883 |
347 | #endif |
648db22b |
348 | void gz_uncompress _Z_OF((gzFile in, FILE *out)); |
349 | void file_compress _Z_OF((char *file, char *mode)); |
350 | void file_uncompress _Z_OF((char *file)); |
351 | int main _Z_OF((int argc, char *argv[])); |
9e052883 |
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 | |
9e052883 |
475 | strcpy(outfile, file); |
476 | strcat(outfile, GZ_SUFFIX); |
9e052883 |
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; |
648db22b |
504 | size_t len = strlen(file); |
9e052883 |
505 | |
506 | if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) { |
507 | fprintf(stderr, "%s: filename too long\n", prog); |
508 | exit(1); |
509 | } |
510 | |
9e052883 |
511 | strcpy(buf, file); |
9e052883 |
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; |
9e052883 |
520 | strcat(infile, GZ_SUFFIX); |
9e052883 |
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 | |
9e052883 |
558 | strcpy(outmode, "wb6 "); |
9e052883 |
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 | } |