1275793eaSopenharmony_ci/* gzjoin -- command to join gzip files into one gzip file 2275793eaSopenharmony_ci 3275793eaSopenharmony_ci Copyright (C) 2004, 2005, 2012 Mark Adler, all rights reserved 4275793eaSopenharmony_ci version 1.2, 14 Aug 2012 5275793eaSopenharmony_ci 6275793eaSopenharmony_ci This software is provided 'as-is', without any express or implied 7275793eaSopenharmony_ci warranty. In no event will the author be held liable for any damages 8275793eaSopenharmony_ci arising from the use of this software. 9275793eaSopenharmony_ci 10275793eaSopenharmony_ci Permission is granted to anyone to use this software for any purpose, 11275793eaSopenharmony_ci including commercial applications, and to alter it and redistribute it 12275793eaSopenharmony_ci freely, subject to the following restrictions: 13275793eaSopenharmony_ci 14275793eaSopenharmony_ci 1. The origin of this software must not be misrepresented; you must not 15275793eaSopenharmony_ci claim that you wrote the original software. If you use this software 16275793eaSopenharmony_ci in a product, an acknowledgment in the product documentation would be 17275793eaSopenharmony_ci appreciated but is not required. 18275793eaSopenharmony_ci 2. Altered source versions must be plainly marked as such, and must not be 19275793eaSopenharmony_ci misrepresented as being the original software. 20275793eaSopenharmony_ci 3. This notice may not be removed or altered from any source distribution. 21275793eaSopenharmony_ci 22275793eaSopenharmony_ci Mark Adler madler@alumni.caltech.edu 23275793eaSopenharmony_ci */ 24275793eaSopenharmony_ci 25275793eaSopenharmony_ci/* 26275793eaSopenharmony_ci * Change history: 27275793eaSopenharmony_ci * 28275793eaSopenharmony_ci * 1.0 11 Dec 2004 - First version 29275793eaSopenharmony_ci * 1.1 12 Jun 2005 - Changed ssize_t to long for portability 30275793eaSopenharmony_ci * 1.2 14 Aug 2012 - Clean up for z_const usage 31275793eaSopenharmony_ci */ 32275793eaSopenharmony_ci 33275793eaSopenharmony_ci/* 34275793eaSopenharmony_ci gzjoin takes one or more gzip files on the command line and writes out a 35275793eaSopenharmony_ci single gzip file that will uncompress to the concatenation of the 36275793eaSopenharmony_ci uncompressed data from the individual gzip files. gzjoin does this without 37275793eaSopenharmony_ci having to recompress any of the data and without having to calculate a new 38275793eaSopenharmony_ci crc32 for the concatenated uncompressed data. gzjoin does however have to 39275793eaSopenharmony_ci decompress all of the input data in order to find the bits in the compressed 40275793eaSopenharmony_ci data that need to be modified to concatenate the streams. 41275793eaSopenharmony_ci 42275793eaSopenharmony_ci gzjoin does not do an integrity check on the input gzip files other than 43275793eaSopenharmony_ci checking the gzip header and decompressing the compressed data. They are 44275793eaSopenharmony_ci otherwise assumed to be complete and correct. 45275793eaSopenharmony_ci 46275793eaSopenharmony_ci Each joint between gzip files removes at least 18 bytes of previous trailer 47275793eaSopenharmony_ci and subsequent header, and inserts an average of about three bytes to the 48275793eaSopenharmony_ci compressed data in order to connect the streams. The output gzip file 49275793eaSopenharmony_ci has a minimal ten-byte gzip header with no file name or modification time. 50275793eaSopenharmony_ci 51275793eaSopenharmony_ci This program was written to illustrate the use of the Z_BLOCK option of 52275793eaSopenharmony_ci inflate() and the crc32_combine() function. gzjoin will not compile with 53275793eaSopenharmony_ci versions of zlib earlier than 1.2.3. 54275793eaSopenharmony_ci */ 55275793eaSopenharmony_ci 56275793eaSopenharmony_ci#include <stdio.h> /* fputs(), fprintf(), fwrite(), putc() */ 57275793eaSopenharmony_ci#include <stdlib.h> /* exit(), malloc(), free() */ 58275793eaSopenharmony_ci#include <fcntl.h> /* open() */ 59275793eaSopenharmony_ci#include <unistd.h> /* close(), read(), lseek() */ 60275793eaSopenharmony_ci#include "zlib.h" 61275793eaSopenharmony_ci /* crc32(), crc32_combine(), inflateInit2(), inflate(), inflateEnd() */ 62275793eaSopenharmony_ci 63275793eaSopenharmony_ci#define local static 64275793eaSopenharmony_ci 65275793eaSopenharmony_ci/* exit with an error (return a value to allow use in an expression) */ 66275793eaSopenharmony_cilocal int bail(char *why1, char *why2) 67275793eaSopenharmony_ci{ 68275793eaSopenharmony_ci fprintf(stderr, "gzjoin error: %s%s, output incomplete\n", why1, why2); 69275793eaSopenharmony_ci exit(1); 70275793eaSopenharmony_ci return 0; 71275793eaSopenharmony_ci} 72275793eaSopenharmony_ci 73275793eaSopenharmony_ci/* -- simple buffered file input with access to the buffer -- */ 74275793eaSopenharmony_ci 75275793eaSopenharmony_ci#define CHUNK 32768 /* must be a power of two and fit in unsigned */ 76275793eaSopenharmony_ci 77275793eaSopenharmony_ci/* bin buffered input file type */ 78275793eaSopenharmony_citypedef struct { 79275793eaSopenharmony_ci char *name; /* name of file for error messages */ 80275793eaSopenharmony_ci int fd; /* file descriptor */ 81275793eaSopenharmony_ci unsigned left; /* bytes remaining at next */ 82275793eaSopenharmony_ci unsigned char *next; /* next byte to read */ 83275793eaSopenharmony_ci unsigned char *buf; /* allocated buffer of length CHUNK */ 84275793eaSopenharmony_ci} bin; 85275793eaSopenharmony_ci 86275793eaSopenharmony_ci/* close a buffered file and free allocated memory */ 87275793eaSopenharmony_cilocal void bclose(bin *in) 88275793eaSopenharmony_ci{ 89275793eaSopenharmony_ci if (in != NULL) { 90275793eaSopenharmony_ci if (in->fd != -1) 91275793eaSopenharmony_ci close(in->fd); 92275793eaSopenharmony_ci if (in->buf != NULL) 93275793eaSopenharmony_ci free(in->buf); 94275793eaSopenharmony_ci free(in); 95275793eaSopenharmony_ci } 96275793eaSopenharmony_ci} 97275793eaSopenharmony_ci 98275793eaSopenharmony_ci/* open a buffered file for input, return a pointer to type bin, or NULL on 99275793eaSopenharmony_ci failure */ 100275793eaSopenharmony_cilocal bin *bopen(char *name) 101275793eaSopenharmony_ci{ 102275793eaSopenharmony_ci bin *in; 103275793eaSopenharmony_ci 104275793eaSopenharmony_ci in = malloc(sizeof(bin)); 105275793eaSopenharmony_ci if (in == NULL) 106275793eaSopenharmony_ci return NULL; 107275793eaSopenharmony_ci in->buf = malloc(CHUNK); 108275793eaSopenharmony_ci in->fd = open(name, O_RDONLY, 0); 109275793eaSopenharmony_ci if (in->buf == NULL || in->fd == -1) { 110275793eaSopenharmony_ci bclose(in); 111275793eaSopenharmony_ci return NULL; 112275793eaSopenharmony_ci } 113275793eaSopenharmony_ci in->left = 0; 114275793eaSopenharmony_ci in->next = in->buf; 115275793eaSopenharmony_ci in->name = name; 116275793eaSopenharmony_ci return in; 117275793eaSopenharmony_ci} 118275793eaSopenharmony_ci 119275793eaSopenharmony_ci/* load buffer from file, return -1 on read error, 0 or 1 on success, with 120275793eaSopenharmony_ci 1 indicating that end-of-file was reached */ 121275793eaSopenharmony_cilocal int bload(bin *in) 122275793eaSopenharmony_ci{ 123275793eaSopenharmony_ci long len; 124275793eaSopenharmony_ci 125275793eaSopenharmony_ci if (in == NULL) 126275793eaSopenharmony_ci return -1; 127275793eaSopenharmony_ci if (in->left != 0) 128275793eaSopenharmony_ci return 0; 129275793eaSopenharmony_ci in->next = in->buf; 130275793eaSopenharmony_ci do { 131275793eaSopenharmony_ci len = (long)read(in->fd, in->buf + in->left, CHUNK - in->left); 132275793eaSopenharmony_ci if (len < 0) 133275793eaSopenharmony_ci return -1; 134275793eaSopenharmony_ci in->left += (unsigned)len; 135275793eaSopenharmony_ci } while (len != 0 && in->left < CHUNK); 136275793eaSopenharmony_ci return len == 0 ? 1 : 0; 137275793eaSopenharmony_ci} 138275793eaSopenharmony_ci 139275793eaSopenharmony_ci/* get a byte from the file, bail if end of file */ 140275793eaSopenharmony_ci#define bget(in) (in->left ? 0 : bload(in), \ 141275793eaSopenharmony_ci in->left ? (in->left--, *(in->next)++) : \ 142275793eaSopenharmony_ci bail("unexpected end of file on ", in->name)) 143275793eaSopenharmony_ci 144275793eaSopenharmony_ci/* get a four-byte little-endian unsigned integer from file */ 145275793eaSopenharmony_cilocal unsigned long bget4(bin *in) 146275793eaSopenharmony_ci{ 147275793eaSopenharmony_ci unsigned long val; 148275793eaSopenharmony_ci 149275793eaSopenharmony_ci val = bget(in); 150275793eaSopenharmony_ci val += (unsigned long)(bget(in)) << 8; 151275793eaSopenharmony_ci val += (unsigned long)(bget(in)) << 16; 152275793eaSopenharmony_ci val += (unsigned long)(bget(in)) << 24; 153275793eaSopenharmony_ci return val; 154275793eaSopenharmony_ci} 155275793eaSopenharmony_ci 156275793eaSopenharmony_ci/* skip bytes in file */ 157275793eaSopenharmony_cilocal void bskip(bin *in, unsigned skip) 158275793eaSopenharmony_ci{ 159275793eaSopenharmony_ci /* check pointer */ 160275793eaSopenharmony_ci if (in == NULL) 161275793eaSopenharmony_ci return; 162275793eaSopenharmony_ci 163275793eaSopenharmony_ci /* easy case -- skip bytes in buffer */ 164275793eaSopenharmony_ci if (skip <= in->left) { 165275793eaSopenharmony_ci in->left -= skip; 166275793eaSopenharmony_ci in->next += skip; 167275793eaSopenharmony_ci return; 168275793eaSopenharmony_ci } 169275793eaSopenharmony_ci 170275793eaSopenharmony_ci /* skip what's in buffer, discard buffer contents */ 171275793eaSopenharmony_ci skip -= in->left; 172275793eaSopenharmony_ci in->left = 0; 173275793eaSopenharmony_ci 174275793eaSopenharmony_ci /* seek past multiples of CHUNK bytes */ 175275793eaSopenharmony_ci if (skip > CHUNK) { 176275793eaSopenharmony_ci unsigned left; 177275793eaSopenharmony_ci 178275793eaSopenharmony_ci left = skip & (CHUNK - 1); 179275793eaSopenharmony_ci if (left == 0) { 180275793eaSopenharmony_ci /* exact number of chunks: seek all the way minus one byte to check 181275793eaSopenharmony_ci for end-of-file with a read */ 182275793eaSopenharmony_ci lseek(in->fd, skip - 1, SEEK_CUR); 183275793eaSopenharmony_ci if (read(in->fd, in->buf, 1) != 1) 184275793eaSopenharmony_ci bail("unexpected end of file on ", in->name); 185275793eaSopenharmony_ci return; 186275793eaSopenharmony_ci } 187275793eaSopenharmony_ci 188275793eaSopenharmony_ci /* skip the integral chunks, update skip with remainder */ 189275793eaSopenharmony_ci lseek(in->fd, skip - left, SEEK_CUR); 190275793eaSopenharmony_ci skip = left; 191275793eaSopenharmony_ci } 192275793eaSopenharmony_ci 193275793eaSopenharmony_ci /* read more input and skip remainder */ 194275793eaSopenharmony_ci bload(in); 195275793eaSopenharmony_ci if (skip > in->left) 196275793eaSopenharmony_ci bail("unexpected end of file on ", in->name); 197275793eaSopenharmony_ci in->left -= skip; 198275793eaSopenharmony_ci in->next += skip; 199275793eaSopenharmony_ci} 200275793eaSopenharmony_ci 201275793eaSopenharmony_ci/* -- end of buffered input functions -- */ 202275793eaSopenharmony_ci 203275793eaSopenharmony_ci/* skip the gzip header from file in */ 204275793eaSopenharmony_cilocal void gzhead(bin *in) 205275793eaSopenharmony_ci{ 206275793eaSopenharmony_ci int flags; 207275793eaSopenharmony_ci 208275793eaSopenharmony_ci /* verify gzip magic header and compression method */ 209275793eaSopenharmony_ci if (bget(in) != 0x1f || bget(in) != 0x8b || bget(in) != 8) 210275793eaSopenharmony_ci bail(in->name, " is not a valid gzip file"); 211275793eaSopenharmony_ci 212275793eaSopenharmony_ci /* get and verify flags */ 213275793eaSopenharmony_ci flags = bget(in); 214275793eaSopenharmony_ci if ((flags & 0xe0) != 0) 215275793eaSopenharmony_ci bail("unknown reserved bits set in ", in->name); 216275793eaSopenharmony_ci 217275793eaSopenharmony_ci /* skip modification time, extra flags, and os */ 218275793eaSopenharmony_ci bskip(in, 6); 219275793eaSopenharmony_ci 220275793eaSopenharmony_ci /* skip extra field if present */ 221275793eaSopenharmony_ci if (flags & 4) { 222275793eaSopenharmony_ci unsigned len; 223275793eaSopenharmony_ci 224275793eaSopenharmony_ci len = bget(in); 225275793eaSopenharmony_ci len += (unsigned)(bget(in)) << 8; 226275793eaSopenharmony_ci bskip(in, len); 227275793eaSopenharmony_ci } 228275793eaSopenharmony_ci 229275793eaSopenharmony_ci /* skip file name if present */ 230275793eaSopenharmony_ci if (flags & 8) 231275793eaSopenharmony_ci while (bget(in) != 0) 232275793eaSopenharmony_ci ; 233275793eaSopenharmony_ci 234275793eaSopenharmony_ci /* skip comment if present */ 235275793eaSopenharmony_ci if (flags & 16) 236275793eaSopenharmony_ci while (bget(in) != 0) 237275793eaSopenharmony_ci ; 238275793eaSopenharmony_ci 239275793eaSopenharmony_ci /* skip header crc if present */ 240275793eaSopenharmony_ci if (flags & 2) 241275793eaSopenharmony_ci bskip(in, 2); 242275793eaSopenharmony_ci} 243275793eaSopenharmony_ci 244275793eaSopenharmony_ci/* write a four-byte little-endian unsigned integer to out */ 245275793eaSopenharmony_cilocal void put4(unsigned long val, FILE *out) 246275793eaSopenharmony_ci{ 247275793eaSopenharmony_ci putc(val & 0xff, out); 248275793eaSopenharmony_ci putc((val >> 8) & 0xff, out); 249275793eaSopenharmony_ci putc((val >> 16) & 0xff, out); 250275793eaSopenharmony_ci putc((val >> 24) & 0xff, out); 251275793eaSopenharmony_ci} 252275793eaSopenharmony_ci 253275793eaSopenharmony_ci/* Load up zlib stream from buffered input, bail if end of file */ 254275793eaSopenharmony_cilocal void zpull(z_streamp strm, bin *in) 255275793eaSopenharmony_ci{ 256275793eaSopenharmony_ci if (in->left == 0) 257275793eaSopenharmony_ci bload(in); 258275793eaSopenharmony_ci if (in->left == 0) 259275793eaSopenharmony_ci bail("unexpected end of file on ", in->name); 260275793eaSopenharmony_ci strm->avail_in = in->left; 261275793eaSopenharmony_ci strm->next_in = in->next; 262275793eaSopenharmony_ci} 263275793eaSopenharmony_ci 264275793eaSopenharmony_ci/* Write header for gzip file to out and initialize trailer. */ 265275793eaSopenharmony_cilocal void gzinit(unsigned long *crc, unsigned long *tot, FILE *out) 266275793eaSopenharmony_ci{ 267275793eaSopenharmony_ci fwrite("\x1f\x8b\x08\0\0\0\0\0\0\xff", 1, 10, out); 268275793eaSopenharmony_ci *crc = crc32(0L, Z_NULL, 0); 269275793eaSopenharmony_ci *tot = 0; 270275793eaSopenharmony_ci} 271275793eaSopenharmony_ci 272275793eaSopenharmony_ci/* Copy the compressed data from name, zeroing the last block bit of the last 273275793eaSopenharmony_ci block if clr is true, and adding empty blocks as needed to get to a byte 274275793eaSopenharmony_ci boundary. If clr is false, then the last block becomes the last block of 275275793eaSopenharmony_ci the output, and the gzip trailer is written. crc and tot maintains the 276275793eaSopenharmony_ci crc and length (modulo 2^32) of the output for the trailer. The resulting 277275793eaSopenharmony_ci gzip file is written to out. gzinit() must be called before the first call 278275793eaSopenharmony_ci of gzcopy() to write the gzip header and to initialize crc and tot. */ 279275793eaSopenharmony_cilocal void gzcopy(char *name, int clr, unsigned long *crc, unsigned long *tot, 280275793eaSopenharmony_ci FILE *out) 281275793eaSopenharmony_ci{ 282275793eaSopenharmony_ci int ret; /* return value from zlib functions */ 283275793eaSopenharmony_ci int pos; /* where the "last block" bit is in byte */ 284275793eaSopenharmony_ci int last; /* true if processing the last block */ 285275793eaSopenharmony_ci bin *in; /* buffered input file */ 286275793eaSopenharmony_ci unsigned char *start; /* start of compressed data in buffer */ 287275793eaSopenharmony_ci unsigned char *junk; /* buffer for uncompressed data -- discarded */ 288275793eaSopenharmony_ci z_off_t len; /* length of uncompressed data (support > 4 GB) */ 289275793eaSopenharmony_ci z_stream strm; /* zlib inflate stream */ 290275793eaSopenharmony_ci 291275793eaSopenharmony_ci /* open gzip file and skip header */ 292275793eaSopenharmony_ci in = bopen(name); 293275793eaSopenharmony_ci if (in == NULL) 294275793eaSopenharmony_ci bail("could not open ", name); 295275793eaSopenharmony_ci gzhead(in); 296275793eaSopenharmony_ci 297275793eaSopenharmony_ci /* allocate buffer for uncompressed data and initialize raw inflate 298275793eaSopenharmony_ci stream */ 299275793eaSopenharmony_ci junk = malloc(CHUNK); 300275793eaSopenharmony_ci strm.zalloc = Z_NULL; 301275793eaSopenharmony_ci strm.zfree = Z_NULL; 302275793eaSopenharmony_ci strm.opaque = Z_NULL; 303275793eaSopenharmony_ci strm.avail_in = 0; 304275793eaSopenharmony_ci strm.next_in = Z_NULL; 305275793eaSopenharmony_ci ret = inflateInit2(&strm, -15); 306275793eaSopenharmony_ci if (junk == NULL || ret != Z_OK) 307275793eaSopenharmony_ci bail("out of memory", ""); 308275793eaSopenharmony_ci 309275793eaSopenharmony_ci /* inflate and copy compressed data, clear last-block bit if requested */ 310275793eaSopenharmony_ci len = 0; 311275793eaSopenharmony_ci zpull(&strm, in); 312275793eaSopenharmony_ci start = in->next; 313275793eaSopenharmony_ci last = start[0] & 1; 314275793eaSopenharmony_ci if (last && clr) 315275793eaSopenharmony_ci start[0] &= ~1; 316275793eaSopenharmony_ci strm.avail_out = 0; 317275793eaSopenharmony_ci for (;;) { 318275793eaSopenharmony_ci /* if input used and output done, write used input and get more */ 319275793eaSopenharmony_ci if (strm.avail_in == 0 && strm.avail_out != 0) { 320275793eaSopenharmony_ci fwrite(start, 1, strm.next_in - start, out); 321275793eaSopenharmony_ci start = in->buf; 322275793eaSopenharmony_ci in->left = 0; 323275793eaSopenharmony_ci zpull(&strm, in); 324275793eaSopenharmony_ci } 325275793eaSopenharmony_ci 326275793eaSopenharmony_ci /* decompress -- return early when end-of-block reached */ 327275793eaSopenharmony_ci strm.avail_out = CHUNK; 328275793eaSopenharmony_ci strm.next_out = junk; 329275793eaSopenharmony_ci ret = inflate(&strm, Z_BLOCK); 330275793eaSopenharmony_ci switch (ret) { 331275793eaSopenharmony_ci case Z_MEM_ERROR: 332275793eaSopenharmony_ci bail("out of memory", ""); 333275793eaSopenharmony_ci case Z_DATA_ERROR: 334275793eaSopenharmony_ci bail("invalid compressed data in ", in->name); 335275793eaSopenharmony_ci } 336275793eaSopenharmony_ci 337275793eaSopenharmony_ci /* update length of uncompressed data */ 338275793eaSopenharmony_ci len += CHUNK - strm.avail_out; 339275793eaSopenharmony_ci 340275793eaSopenharmony_ci /* check for block boundary (only get this when block copied out) */ 341275793eaSopenharmony_ci if (strm.data_type & 128) { 342275793eaSopenharmony_ci /* if that was the last block, then done */ 343275793eaSopenharmony_ci if (last) 344275793eaSopenharmony_ci break; 345275793eaSopenharmony_ci 346275793eaSopenharmony_ci /* number of unused bits in last byte */ 347275793eaSopenharmony_ci pos = strm.data_type & 7; 348275793eaSopenharmony_ci 349275793eaSopenharmony_ci /* find the next last-block bit */ 350275793eaSopenharmony_ci if (pos != 0) { 351275793eaSopenharmony_ci /* next last-block bit is in last used byte */ 352275793eaSopenharmony_ci pos = 0x100 >> pos; 353275793eaSopenharmony_ci last = strm.next_in[-1] & pos; 354275793eaSopenharmony_ci if (last && clr) 355275793eaSopenharmony_ci in->buf[strm.next_in - in->buf - 1] &= ~pos; 356275793eaSopenharmony_ci } 357275793eaSopenharmony_ci else { 358275793eaSopenharmony_ci /* next last-block bit is in next unused byte */ 359275793eaSopenharmony_ci if (strm.avail_in == 0) { 360275793eaSopenharmony_ci /* don't have that byte yet -- get it */ 361275793eaSopenharmony_ci fwrite(start, 1, strm.next_in - start, out); 362275793eaSopenharmony_ci start = in->buf; 363275793eaSopenharmony_ci in->left = 0; 364275793eaSopenharmony_ci zpull(&strm, in); 365275793eaSopenharmony_ci } 366275793eaSopenharmony_ci last = strm.next_in[0] & 1; 367275793eaSopenharmony_ci if (last && clr) 368275793eaSopenharmony_ci in->buf[strm.next_in - in->buf] &= ~1; 369275793eaSopenharmony_ci } 370275793eaSopenharmony_ci } 371275793eaSopenharmony_ci } 372275793eaSopenharmony_ci 373275793eaSopenharmony_ci /* update buffer with unused input */ 374275793eaSopenharmony_ci in->left = strm.avail_in; 375275793eaSopenharmony_ci in->next = in->buf + (strm.next_in - in->buf); 376275793eaSopenharmony_ci 377275793eaSopenharmony_ci /* copy used input, write empty blocks to get to byte boundary */ 378275793eaSopenharmony_ci pos = strm.data_type & 7; 379275793eaSopenharmony_ci fwrite(start, 1, in->next - start - 1, out); 380275793eaSopenharmony_ci last = in->next[-1]; 381275793eaSopenharmony_ci if (pos == 0 || !clr) 382275793eaSopenharmony_ci /* already at byte boundary, or last file: write last byte */ 383275793eaSopenharmony_ci putc(last, out); 384275793eaSopenharmony_ci else { 385275793eaSopenharmony_ci /* append empty blocks to last byte */ 386275793eaSopenharmony_ci last &= ((0x100 >> pos) - 1); /* assure unused bits are zero */ 387275793eaSopenharmony_ci if (pos & 1) { 388275793eaSopenharmony_ci /* odd -- append an empty stored block */ 389275793eaSopenharmony_ci putc(last, out); 390275793eaSopenharmony_ci if (pos == 1) 391275793eaSopenharmony_ci putc(0, out); /* two more bits in block header */ 392275793eaSopenharmony_ci fwrite("\0\0\xff\xff", 1, 4, out); 393275793eaSopenharmony_ci } 394275793eaSopenharmony_ci else { 395275793eaSopenharmony_ci /* even -- append 1, 2, or 3 empty fixed blocks */ 396275793eaSopenharmony_ci switch (pos) { 397275793eaSopenharmony_ci case 6: 398275793eaSopenharmony_ci putc(last | 8, out); 399275793eaSopenharmony_ci last = 0; 400275793eaSopenharmony_ci case 4: 401275793eaSopenharmony_ci putc(last | 0x20, out); 402275793eaSopenharmony_ci last = 0; 403275793eaSopenharmony_ci case 2: 404275793eaSopenharmony_ci putc(last | 0x80, out); 405275793eaSopenharmony_ci putc(0, out); 406275793eaSopenharmony_ci } 407275793eaSopenharmony_ci } 408275793eaSopenharmony_ci } 409275793eaSopenharmony_ci 410275793eaSopenharmony_ci /* update crc and tot */ 411275793eaSopenharmony_ci *crc = crc32_combine(*crc, bget4(in), len); 412275793eaSopenharmony_ci *tot += (unsigned long)len; 413275793eaSopenharmony_ci 414275793eaSopenharmony_ci /* clean up */ 415275793eaSopenharmony_ci inflateEnd(&strm); 416275793eaSopenharmony_ci free(junk); 417275793eaSopenharmony_ci bclose(in); 418275793eaSopenharmony_ci 419275793eaSopenharmony_ci /* write trailer if this is the last gzip file */ 420275793eaSopenharmony_ci if (!clr) { 421275793eaSopenharmony_ci put4(*crc, out); 422275793eaSopenharmony_ci put4(*tot, out); 423275793eaSopenharmony_ci } 424275793eaSopenharmony_ci} 425275793eaSopenharmony_ci 426275793eaSopenharmony_ci/* join the gzip files on the command line, write result to stdout */ 427275793eaSopenharmony_ciint main(int argc, char **argv) 428275793eaSopenharmony_ci{ 429275793eaSopenharmony_ci unsigned long crc, tot; /* running crc and total uncompressed length */ 430275793eaSopenharmony_ci 431275793eaSopenharmony_ci /* skip command name */ 432275793eaSopenharmony_ci argc--; 433275793eaSopenharmony_ci argv++; 434275793eaSopenharmony_ci 435275793eaSopenharmony_ci /* show usage if no arguments */ 436275793eaSopenharmony_ci if (argc == 0) { 437275793eaSopenharmony_ci fputs("gzjoin usage: gzjoin f1.gz [f2.gz [f3.gz ...]] > fjoin.gz\n", 438275793eaSopenharmony_ci stderr); 439275793eaSopenharmony_ci return 0; 440275793eaSopenharmony_ci } 441275793eaSopenharmony_ci 442275793eaSopenharmony_ci /* join gzip files on command line and write to stdout */ 443275793eaSopenharmony_ci gzinit(&crc, &tot, stdout); 444275793eaSopenharmony_ci while (argc--) 445275793eaSopenharmony_ci gzcopy(*argv++, argc, &crc, &tot, stdout); 446275793eaSopenharmony_ci 447275793eaSopenharmony_ci /* done */ 448275793eaSopenharmony_ci return 0; 449275793eaSopenharmony_ci} 450