Fossil SCM

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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button