annotate src/zlib-1.2.8/examples/gzjoin.c @ 56:af97cad61ff0

Add updated build of PortAudio for OSX
author Chris Cannam <cannam@all-day-breakfast.com>
date Tue, 03 Jan 2017 15:10:52 +0000
parents 5ea0608b923f
children
rev   line source
Chris@43 1 /* gzjoin -- command to join gzip files into one gzip file
Chris@43 2
Chris@43 3 Copyright (C) 2004, 2005, 2012 Mark Adler, all rights reserved
Chris@43 4 version 1.2, 14 Aug 2012
Chris@43 5
Chris@43 6 This software is provided 'as-is', without any express or implied
Chris@43 7 warranty. In no event will the author be held liable for any damages
Chris@43 8 arising from the use of this software.
Chris@43 9
Chris@43 10 Permission is granted to anyone to use this software for any purpose,
Chris@43 11 including commercial applications, and to alter it and redistribute it
Chris@43 12 freely, subject to the following restrictions:
Chris@43 13
Chris@43 14 1. The origin of this software must not be misrepresented; you must not
Chris@43 15 claim that you wrote the original software. If you use this software
Chris@43 16 in a product, an acknowledgment in the product documentation would be
Chris@43 17 appreciated but is not required.
Chris@43 18 2. Altered source versions must be plainly marked as such, and must not be
Chris@43 19 misrepresented as being the original software.
Chris@43 20 3. This notice may not be removed or altered from any source distribution.
Chris@43 21
Chris@43 22 Mark Adler madler@alumni.caltech.edu
Chris@43 23 */
Chris@43 24
Chris@43 25 /*
Chris@43 26 * Change history:
Chris@43 27 *
Chris@43 28 * 1.0 11 Dec 2004 - First version
Chris@43 29 * 1.1 12 Jun 2005 - Changed ssize_t to long for portability
Chris@43 30 * 1.2 14 Aug 2012 - Clean up for z_const usage
Chris@43 31 */
Chris@43 32
Chris@43 33 /*
Chris@43 34 gzjoin takes one or more gzip files on the command line and writes out a
Chris@43 35 single gzip file that will uncompress to the concatenation of the
Chris@43 36 uncompressed data from the individual gzip files. gzjoin does this without
Chris@43 37 having to recompress any of the data and without having to calculate a new
Chris@43 38 crc32 for the concatenated uncompressed data. gzjoin does however have to
Chris@43 39 decompress all of the input data in order to find the bits in the compressed
Chris@43 40 data that need to be modified to concatenate the streams.
Chris@43 41
Chris@43 42 gzjoin does not do an integrity check on the input gzip files other than
Chris@43 43 checking the gzip header and decompressing the compressed data. They are
Chris@43 44 otherwise assumed to be complete and correct.
Chris@43 45
Chris@43 46 Each joint between gzip files removes at least 18 bytes of previous trailer
Chris@43 47 and subsequent header, and inserts an average of about three bytes to the
Chris@43 48 compressed data in order to connect the streams. The output gzip file
Chris@43 49 has a minimal ten-byte gzip header with no file name or modification time.
Chris@43 50
Chris@43 51 This program was written to illustrate the use of the Z_BLOCK option of
Chris@43 52 inflate() and the crc32_combine() function. gzjoin will not compile with
Chris@43 53 versions of zlib earlier than 1.2.3.
Chris@43 54 */
Chris@43 55
Chris@43 56 #include <stdio.h> /* fputs(), fprintf(), fwrite(), putc() */
Chris@43 57 #include <stdlib.h> /* exit(), malloc(), free() */
Chris@43 58 #include <fcntl.h> /* open() */
Chris@43 59 #include <unistd.h> /* close(), read(), lseek() */
Chris@43 60 #include "zlib.h"
Chris@43 61 /* crc32(), crc32_combine(), inflateInit2(), inflate(), inflateEnd() */
Chris@43 62
Chris@43 63 #define local static
Chris@43 64
Chris@43 65 /* exit with an error (return a value to allow use in an expression) */
Chris@43 66 local int bail(char *why1, char *why2)
Chris@43 67 {
Chris@43 68 fprintf(stderr, "gzjoin error: %s%s, output incomplete\n", why1, why2);
Chris@43 69 exit(1);
Chris@43 70 return 0;
Chris@43 71 }
Chris@43 72
Chris@43 73 /* -- simple buffered file input with access to the buffer -- */
Chris@43 74
Chris@43 75 #define CHUNK 32768 /* must be a power of two and fit in unsigned */
Chris@43 76
Chris@43 77 /* bin buffered input file type */
Chris@43 78 typedef struct {
Chris@43 79 char *name; /* name of file for error messages */
Chris@43 80 int fd; /* file descriptor */
Chris@43 81 unsigned left; /* bytes remaining at next */
Chris@43 82 unsigned char *next; /* next byte to read */
Chris@43 83 unsigned char *buf; /* allocated buffer of length CHUNK */
Chris@43 84 } bin;
Chris@43 85
Chris@43 86 /* close a buffered file and free allocated memory */
Chris@43 87 local void bclose(bin *in)
Chris@43 88 {
Chris@43 89 if (in != NULL) {
Chris@43 90 if (in->fd != -1)
Chris@43 91 close(in->fd);
Chris@43 92 if (in->buf != NULL)
Chris@43 93 free(in->buf);
Chris@43 94 free(in);
Chris@43 95 }
Chris@43 96 }
Chris@43 97
Chris@43 98 /* open a buffered file for input, return a pointer to type bin, or NULL on
Chris@43 99 failure */
Chris@43 100 local bin *bopen(char *name)
Chris@43 101 {
Chris@43 102 bin *in;
Chris@43 103
Chris@43 104 in = malloc(sizeof(bin));
Chris@43 105 if (in == NULL)
Chris@43 106 return NULL;
Chris@43 107 in->buf = malloc(CHUNK);
Chris@43 108 in->fd = open(name, O_RDONLY, 0);
Chris@43 109 if (in->buf == NULL || in->fd == -1) {
Chris@43 110 bclose(in);
Chris@43 111 return NULL;
Chris@43 112 }
Chris@43 113 in->left = 0;
Chris@43 114 in->next = in->buf;
Chris@43 115 in->name = name;
Chris@43 116 return in;
Chris@43 117 }
Chris@43 118
Chris@43 119 /* load buffer from file, return -1 on read error, 0 or 1 on success, with
Chris@43 120 1 indicating that end-of-file was reached */
Chris@43 121 local int bload(bin *in)
Chris@43 122 {
Chris@43 123 long len;
Chris@43 124
Chris@43 125 if (in == NULL)
Chris@43 126 return -1;
Chris@43 127 if (in->left != 0)
Chris@43 128 return 0;
Chris@43 129 in->next = in->buf;
Chris@43 130 do {
Chris@43 131 len = (long)read(in->fd, in->buf + in->left, CHUNK - in->left);
Chris@43 132 if (len < 0)
Chris@43 133 return -1;
Chris@43 134 in->left += (unsigned)len;
Chris@43 135 } while (len != 0 && in->left < CHUNK);
Chris@43 136 return len == 0 ? 1 : 0;
Chris@43 137 }
Chris@43 138
Chris@43 139 /* get a byte from the file, bail if end of file */
Chris@43 140 #define bget(in) (in->left ? 0 : bload(in), \
Chris@43 141 in->left ? (in->left--, *(in->next)++) : \
Chris@43 142 bail("unexpected end of file on ", in->name))
Chris@43 143
Chris@43 144 /* get a four-byte little-endian unsigned integer from file */
Chris@43 145 local unsigned long bget4(bin *in)
Chris@43 146 {
Chris@43 147 unsigned long val;
Chris@43 148
Chris@43 149 val = bget(in);
Chris@43 150 val += (unsigned long)(bget(in)) << 8;
Chris@43 151 val += (unsigned long)(bget(in)) << 16;
Chris@43 152 val += (unsigned long)(bget(in)) << 24;
Chris@43 153 return val;
Chris@43 154 }
Chris@43 155
Chris@43 156 /* skip bytes in file */
Chris@43 157 local void bskip(bin *in, unsigned skip)
Chris@43 158 {
Chris@43 159 /* check pointer */
Chris@43 160 if (in == NULL)
Chris@43 161 return;
Chris@43 162
Chris@43 163 /* easy case -- skip bytes in buffer */
Chris@43 164 if (skip <= in->left) {
Chris@43 165 in->left -= skip;
Chris@43 166 in->next += skip;
Chris@43 167 return;
Chris@43 168 }
Chris@43 169
Chris@43 170 /* skip what's in buffer, discard buffer contents */
Chris@43 171 skip -= in->left;
Chris@43 172 in->left = 0;
Chris@43 173
Chris@43 174 /* seek past multiples of CHUNK bytes */
Chris@43 175 if (skip > CHUNK) {
Chris@43 176 unsigned left;
Chris@43 177
Chris@43 178 left = skip & (CHUNK - 1);
Chris@43 179 if (left == 0) {
Chris@43 180 /* exact number of chunks: seek all the way minus one byte to check
Chris@43 181 for end-of-file with a read */
Chris@43 182 lseek(in->fd, skip - 1, SEEK_CUR);
Chris@43 183 if (read(in->fd, in->buf, 1) != 1)
Chris@43 184 bail("unexpected end of file on ", in->name);
Chris@43 185 return;
Chris@43 186 }
Chris@43 187
Chris@43 188 /* skip the integral chunks, update skip with remainder */
Chris@43 189 lseek(in->fd, skip - left, SEEK_CUR);
Chris@43 190 skip = left;
Chris@43 191 }
Chris@43 192
Chris@43 193 /* read more input and skip remainder */
Chris@43 194 bload(in);
Chris@43 195 if (skip > in->left)
Chris@43 196 bail("unexpected end of file on ", in->name);
Chris@43 197 in->left -= skip;
Chris@43 198 in->next += skip;
Chris@43 199 }
Chris@43 200
Chris@43 201 /* -- end of buffered input functions -- */
Chris@43 202
Chris@43 203 /* skip the gzip header from file in */
Chris@43 204 local void gzhead(bin *in)
Chris@43 205 {
Chris@43 206 int flags;
Chris@43 207
Chris@43 208 /* verify gzip magic header and compression method */
Chris@43 209 if (bget(in) != 0x1f || bget(in) != 0x8b || bget(in) != 8)
Chris@43 210 bail(in->name, " is not a valid gzip file");
Chris@43 211
Chris@43 212 /* get and verify flags */
Chris@43 213 flags = bget(in);
Chris@43 214 if ((flags & 0xe0) != 0)
Chris@43 215 bail("unknown reserved bits set in ", in->name);
Chris@43 216
Chris@43 217 /* skip modification time, extra flags, and os */
Chris@43 218 bskip(in, 6);
Chris@43 219
Chris@43 220 /* skip extra field if present */
Chris@43 221 if (flags & 4) {
Chris@43 222 unsigned len;
Chris@43 223
Chris@43 224 len = bget(in);
Chris@43 225 len += (unsigned)(bget(in)) << 8;
Chris@43 226 bskip(in, len);
Chris@43 227 }
Chris@43 228
Chris@43 229 /* skip file name if present */
Chris@43 230 if (flags & 8)
Chris@43 231 while (bget(in) != 0)
Chris@43 232 ;
Chris@43 233
Chris@43 234 /* skip comment if present */
Chris@43 235 if (flags & 16)
Chris@43 236 while (bget(in) != 0)
Chris@43 237 ;
Chris@43 238
Chris@43 239 /* skip header crc if present */
Chris@43 240 if (flags & 2)
Chris@43 241 bskip(in, 2);
Chris@43 242 }
Chris@43 243
Chris@43 244 /* write a four-byte little-endian unsigned integer to out */
Chris@43 245 local void put4(unsigned long val, FILE *out)
Chris@43 246 {
Chris@43 247 putc(val & 0xff, out);
Chris@43 248 putc((val >> 8) & 0xff, out);
Chris@43 249 putc((val >> 16) & 0xff, out);
Chris@43 250 putc((val >> 24) & 0xff, out);
Chris@43 251 }
Chris@43 252
Chris@43 253 /* Load up zlib stream from buffered input, bail if end of file */
Chris@43 254 local void zpull(z_streamp strm, bin *in)
Chris@43 255 {
Chris@43 256 if (in->left == 0)
Chris@43 257 bload(in);
Chris@43 258 if (in->left == 0)
Chris@43 259 bail("unexpected end of file on ", in->name);
Chris@43 260 strm->avail_in = in->left;
Chris@43 261 strm->next_in = in->next;
Chris@43 262 }
Chris@43 263
Chris@43 264 /* Write header for gzip file to out and initialize trailer. */
Chris@43 265 local void gzinit(unsigned long *crc, unsigned long *tot, FILE *out)
Chris@43 266 {
Chris@43 267 fwrite("\x1f\x8b\x08\0\0\0\0\0\0\xff", 1, 10, out);
Chris@43 268 *crc = crc32(0L, Z_NULL, 0);
Chris@43 269 *tot = 0;
Chris@43 270 }
Chris@43 271
Chris@43 272 /* Copy the compressed data from name, zeroing the last block bit of the last
Chris@43 273 block if clr is true, and adding empty blocks as needed to get to a byte
Chris@43 274 boundary. If clr is false, then the last block becomes the last block of
Chris@43 275 the output, and the gzip trailer is written. crc and tot maintains the
Chris@43 276 crc and length (modulo 2^32) of the output for the trailer. The resulting
Chris@43 277 gzip file is written to out. gzinit() must be called before the first call
Chris@43 278 of gzcopy() to write the gzip header and to initialize crc and tot. */
Chris@43 279 local void gzcopy(char *name, int clr, unsigned long *crc, unsigned long *tot,
Chris@43 280 FILE *out)
Chris@43 281 {
Chris@43 282 int ret; /* return value from zlib functions */
Chris@43 283 int pos; /* where the "last block" bit is in byte */
Chris@43 284 int last; /* true if processing the last block */
Chris@43 285 bin *in; /* buffered input file */
Chris@43 286 unsigned char *start; /* start of compressed data in buffer */
Chris@43 287 unsigned char *junk; /* buffer for uncompressed data -- discarded */
Chris@43 288 z_off_t len; /* length of uncompressed data (support > 4 GB) */
Chris@43 289 z_stream strm; /* zlib inflate stream */
Chris@43 290
Chris@43 291 /* open gzip file and skip header */
Chris@43 292 in = bopen(name);
Chris@43 293 if (in == NULL)
Chris@43 294 bail("could not open ", name);
Chris@43 295 gzhead(in);
Chris@43 296
Chris@43 297 /* allocate buffer for uncompressed data and initialize raw inflate
Chris@43 298 stream */
Chris@43 299 junk = malloc(CHUNK);
Chris@43 300 strm.zalloc = Z_NULL;
Chris@43 301 strm.zfree = Z_NULL;
Chris@43 302 strm.opaque = Z_NULL;
Chris@43 303 strm.avail_in = 0;
Chris@43 304 strm.next_in = Z_NULL;
Chris@43 305 ret = inflateInit2(&strm, -15);
Chris@43 306 if (junk == NULL || ret != Z_OK)
Chris@43 307 bail("out of memory", "");
Chris@43 308
Chris@43 309 /* inflate and copy compressed data, clear last-block bit if requested */
Chris@43 310 len = 0;
Chris@43 311 zpull(&strm, in);
Chris@43 312 start = in->next;
Chris@43 313 last = start[0] & 1;
Chris@43 314 if (last && clr)
Chris@43 315 start[0] &= ~1;
Chris@43 316 strm.avail_out = 0;
Chris@43 317 for (;;) {
Chris@43 318 /* if input used and output done, write used input and get more */
Chris@43 319 if (strm.avail_in == 0 && strm.avail_out != 0) {
Chris@43 320 fwrite(start, 1, strm.next_in - start, out);
Chris@43 321 start = in->buf;
Chris@43 322 in->left = 0;
Chris@43 323 zpull(&strm, in);
Chris@43 324 }
Chris@43 325
Chris@43 326 /* decompress -- return early when end-of-block reached */
Chris@43 327 strm.avail_out = CHUNK;
Chris@43 328 strm.next_out = junk;
Chris@43 329 ret = inflate(&strm, Z_BLOCK);
Chris@43 330 switch (ret) {
Chris@43 331 case Z_MEM_ERROR:
Chris@43 332 bail("out of memory", "");
Chris@43 333 case Z_DATA_ERROR:
Chris@43 334 bail("invalid compressed data in ", in->name);
Chris@43 335 }
Chris@43 336
Chris@43 337 /* update length of uncompressed data */
Chris@43 338 len += CHUNK - strm.avail_out;
Chris@43 339
Chris@43 340 /* check for block boundary (only get this when block copied out) */
Chris@43 341 if (strm.data_type & 128) {
Chris@43 342 /* if that was the last block, then done */
Chris@43 343 if (last)
Chris@43 344 break;
Chris@43 345
Chris@43 346 /* number of unused bits in last byte */
Chris@43 347 pos = strm.data_type & 7;
Chris@43 348
Chris@43 349 /* find the next last-block bit */
Chris@43 350 if (pos != 0) {
Chris@43 351 /* next last-block bit is in last used byte */
Chris@43 352 pos = 0x100 >> pos;
Chris@43 353 last = strm.next_in[-1] & pos;
Chris@43 354 if (last && clr)
Chris@43 355 in->buf[strm.next_in - in->buf - 1] &= ~pos;
Chris@43 356 }
Chris@43 357 else {
Chris@43 358 /* next last-block bit is in next unused byte */
Chris@43 359 if (strm.avail_in == 0) {
Chris@43 360 /* don't have that byte yet -- get it */
Chris@43 361 fwrite(start, 1, strm.next_in - start, out);
Chris@43 362 start = in->buf;
Chris@43 363 in->left = 0;
Chris@43 364 zpull(&strm, in);
Chris@43 365 }
Chris@43 366 last = strm.next_in[0] & 1;
Chris@43 367 if (last && clr)
Chris@43 368 in->buf[strm.next_in - in->buf] &= ~1;
Chris@43 369 }
Chris@43 370 }
Chris@43 371 }
Chris@43 372
Chris@43 373 /* update buffer with unused input */
Chris@43 374 in->left = strm.avail_in;
Chris@43 375 in->next = in->buf + (strm.next_in - in->buf);
Chris@43 376
Chris@43 377 /* copy used input, write empty blocks to get to byte boundary */
Chris@43 378 pos = strm.data_type & 7;
Chris@43 379 fwrite(start, 1, in->next - start - 1, out);
Chris@43 380 last = in->next[-1];
Chris@43 381 if (pos == 0 || !clr)
Chris@43 382 /* already at byte boundary, or last file: write last byte */
Chris@43 383 putc(last, out);
Chris@43 384 else {
Chris@43 385 /* append empty blocks to last byte */
Chris@43 386 last &= ((0x100 >> pos) - 1); /* assure unused bits are zero */
Chris@43 387 if (pos & 1) {
Chris@43 388 /* odd -- append an empty stored block */
Chris@43 389 putc(last, out);
Chris@43 390 if (pos == 1)
Chris@43 391 putc(0, out); /* two more bits in block header */
Chris@43 392 fwrite("\0\0\xff\xff", 1, 4, out);
Chris@43 393 }
Chris@43 394 else {
Chris@43 395 /* even -- append 1, 2, or 3 empty fixed blocks */
Chris@43 396 switch (pos) {
Chris@43 397 case 6:
Chris@43 398 putc(last | 8, out);
Chris@43 399 last = 0;
Chris@43 400 case 4:
Chris@43 401 putc(last | 0x20, out);
Chris@43 402 last = 0;
Chris@43 403 case 2:
Chris@43 404 putc(last | 0x80, out);
Chris@43 405 putc(0, out);
Chris@43 406 }
Chris@43 407 }
Chris@43 408 }
Chris@43 409
Chris@43 410 /* update crc and tot */
Chris@43 411 *crc = crc32_combine(*crc, bget4(in), len);
Chris@43 412 *tot += (unsigned long)len;
Chris@43 413
Chris@43 414 /* clean up */
Chris@43 415 inflateEnd(&strm);
Chris@43 416 free(junk);
Chris@43 417 bclose(in);
Chris@43 418
Chris@43 419 /* write trailer if this is the last gzip file */
Chris@43 420 if (!clr) {
Chris@43 421 put4(*crc, out);
Chris@43 422 put4(*tot, out);
Chris@43 423 }
Chris@43 424 }
Chris@43 425
Chris@43 426 /* join the gzip files on the command line, write result to stdout */
Chris@43 427 int main(int argc, char **argv)
Chris@43 428 {
Chris@43 429 unsigned long crc, tot; /* running crc and total uncompressed length */
Chris@43 430
Chris@43 431 /* skip command name */
Chris@43 432 argc--;
Chris@43 433 argv++;
Chris@43 434
Chris@43 435 /* show usage if no arguments */
Chris@43 436 if (argc == 0) {
Chris@43 437 fputs("gzjoin usage: gzjoin f1.gz [f2.gz [f3.gz ...]] > fjoin.gz\n",
Chris@43 438 stderr);
Chris@43 439 return 0;
Chris@43 440 }
Chris@43 441
Chris@43 442 /* join gzip files on command line and write to stdout */
Chris@43 443 gzinit(&crc, &tot, stdout);
Chris@43 444 while (argc--)
Chris@43 445 gzcopy(*argv++, argc, &crc, &tot, stdout);
Chris@43 446
Chris@43 447 /* done */
Chris@43 448 return 0;
Chris@43 449 }