1275793eaSopenharmony_ci/* gzappend -- command to append to a gzip file 2275793eaSopenharmony_ci 3275793eaSopenharmony_ci Copyright (C) 2003, 2012 Mark Adler, all rights reserved 4275793eaSopenharmony_ci version 1.2, 11 Oct 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 19 Oct 2003 - First version 29275793eaSopenharmony_ci * 1.1 4 Nov 2003 - Expand and clarify some comments and notes 30275793eaSopenharmony_ci * - Add version and copyright to help 31275793eaSopenharmony_ci * - Send help to stdout instead of stderr 32275793eaSopenharmony_ci * - Add some preemptive typecasts 33275793eaSopenharmony_ci * - Add L to constants in lseek() calls 34275793eaSopenharmony_ci * - Remove some debugging information in error messages 35275793eaSopenharmony_ci * - Use new data_type definition for zlib 1.2.1 36275793eaSopenharmony_ci * - Simplify and unify file operations 37275793eaSopenharmony_ci * - Finish off gzip file in gztack() 38275793eaSopenharmony_ci * - Use deflatePrime() instead of adding empty blocks 39275793eaSopenharmony_ci * - Keep gzip file clean on appended file read errors 40275793eaSopenharmony_ci * - Use in-place rotate instead of auxiliary buffer 41275793eaSopenharmony_ci * (Why you ask? Because it was fun to write!) 42275793eaSopenharmony_ci * 1.2 11 Oct 2012 - Fix for proper z_const usage 43275793eaSopenharmony_ci * - Check for input buffer malloc failure 44275793eaSopenharmony_ci */ 45275793eaSopenharmony_ci 46275793eaSopenharmony_ci/* 47275793eaSopenharmony_ci gzappend takes a gzip file and appends to it, compressing files from the 48275793eaSopenharmony_ci command line or data from stdin. The gzip file is written to directly, to 49275793eaSopenharmony_ci avoid copying that file, in case it's large. Note that this results in the 50275793eaSopenharmony_ci unfriendly behavior that if gzappend fails, the gzip file is corrupted. 51275793eaSopenharmony_ci 52275793eaSopenharmony_ci This program was written to illustrate the use of the new Z_BLOCK option of 53275793eaSopenharmony_ci zlib 1.2.x's inflate() function. This option returns from inflate() at each 54275793eaSopenharmony_ci block boundary to facilitate locating and modifying the last block bit at 55275793eaSopenharmony_ci the start of the final deflate block. Also whether using Z_BLOCK or not, 56275793eaSopenharmony_ci another required feature of zlib 1.2.x is that inflate() now provides the 57275793eaSopenharmony_ci number of unused bits in the last input byte used. gzappend will not work 58275793eaSopenharmony_ci with versions of zlib earlier than 1.2.1. 59275793eaSopenharmony_ci 60275793eaSopenharmony_ci gzappend first decompresses the gzip file internally, discarding all but 61275793eaSopenharmony_ci the last 32K of uncompressed data, and noting the location of the last block 62275793eaSopenharmony_ci bit and the number of unused bits in the last byte of the compressed data. 63275793eaSopenharmony_ci The gzip trailer containing the CRC-32 and length of the uncompressed data 64275793eaSopenharmony_ci is verified. This trailer will be later overwritten. 65275793eaSopenharmony_ci 66275793eaSopenharmony_ci Then the last block bit is cleared by seeking back in the file and rewriting 67275793eaSopenharmony_ci the byte that contains it. Seeking forward, the last byte of the compressed 68275793eaSopenharmony_ci data is saved along with the number of unused bits to initialize deflate. 69275793eaSopenharmony_ci 70275793eaSopenharmony_ci A deflate process is initialized, using the last 32K of the uncompressed 71275793eaSopenharmony_ci data from the gzip file to initialize the dictionary. If the total 72275793eaSopenharmony_ci uncompressed data was less than 32K, then all of it is used to initialize 73275793eaSopenharmony_ci the dictionary. The deflate output bit buffer is also initialized with the 74275793eaSopenharmony_ci last bits from the original deflate stream. From here on, the data to 75275793eaSopenharmony_ci append is simply compressed using deflate, and written to the gzip file. 76275793eaSopenharmony_ci When that is complete, the new CRC-32 and uncompressed length are written 77275793eaSopenharmony_ci as the trailer of the gzip file. 78275793eaSopenharmony_ci */ 79275793eaSopenharmony_ci 80275793eaSopenharmony_ci#include <stdio.h> 81275793eaSopenharmony_ci#include <stdlib.h> 82275793eaSopenharmony_ci#include <string.h> 83275793eaSopenharmony_ci#include <fcntl.h> 84275793eaSopenharmony_ci#include <unistd.h> 85275793eaSopenharmony_ci#include "zlib.h" 86275793eaSopenharmony_ci 87275793eaSopenharmony_ci#define local static 88275793eaSopenharmony_ci#define LGCHUNK 14 89275793eaSopenharmony_ci#define CHUNK (1U << LGCHUNK) 90275793eaSopenharmony_ci#define DSIZE 32768U 91275793eaSopenharmony_ci 92275793eaSopenharmony_ci/* print an error message and terminate with extreme prejudice */ 93275793eaSopenharmony_cilocal void bye(char *msg1, char *msg2) 94275793eaSopenharmony_ci{ 95275793eaSopenharmony_ci fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2); 96275793eaSopenharmony_ci exit(1); 97275793eaSopenharmony_ci} 98275793eaSopenharmony_ci 99275793eaSopenharmony_ci/* return the greatest common divisor of a and b using Euclid's algorithm, 100275793eaSopenharmony_ci modified to be fast when one argument much greater than the other, and 101275793eaSopenharmony_ci coded to avoid unnecessary swapping */ 102275793eaSopenharmony_cilocal unsigned gcd(unsigned a, unsigned b) 103275793eaSopenharmony_ci{ 104275793eaSopenharmony_ci unsigned c; 105275793eaSopenharmony_ci 106275793eaSopenharmony_ci while (a && b) 107275793eaSopenharmony_ci if (a > b) { 108275793eaSopenharmony_ci c = b; 109275793eaSopenharmony_ci while (a - c >= c) 110275793eaSopenharmony_ci c <<= 1; 111275793eaSopenharmony_ci a -= c; 112275793eaSopenharmony_ci } 113275793eaSopenharmony_ci else { 114275793eaSopenharmony_ci c = a; 115275793eaSopenharmony_ci while (b - c >= c) 116275793eaSopenharmony_ci c <<= 1; 117275793eaSopenharmony_ci b -= c; 118275793eaSopenharmony_ci } 119275793eaSopenharmony_ci return a + b; 120275793eaSopenharmony_ci} 121275793eaSopenharmony_ci 122275793eaSopenharmony_ci/* rotate list[0..len-1] left by rot positions, in place */ 123275793eaSopenharmony_cilocal void rotate(unsigned char *list, unsigned len, unsigned rot) 124275793eaSopenharmony_ci{ 125275793eaSopenharmony_ci unsigned char tmp; 126275793eaSopenharmony_ci unsigned cycles; 127275793eaSopenharmony_ci unsigned char *start, *last, *to, *from; 128275793eaSopenharmony_ci 129275793eaSopenharmony_ci /* normalize rot and handle degenerate cases */ 130275793eaSopenharmony_ci if (len < 2) return; 131275793eaSopenharmony_ci if (rot >= len) rot %= len; 132275793eaSopenharmony_ci if (rot == 0) return; 133275793eaSopenharmony_ci 134275793eaSopenharmony_ci /* pointer to last entry in list */ 135275793eaSopenharmony_ci last = list + (len - 1); 136275793eaSopenharmony_ci 137275793eaSopenharmony_ci /* do simple left shift by one */ 138275793eaSopenharmony_ci if (rot == 1) { 139275793eaSopenharmony_ci tmp = *list; 140275793eaSopenharmony_ci memmove(list, list + 1, len - 1); 141275793eaSopenharmony_ci *last = tmp; 142275793eaSopenharmony_ci return; 143275793eaSopenharmony_ci } 144275793eaSopenharmony_ci 145275793eaSopenharmony_ci /* do simple right shift by one */ 146275793eaSopenharmony_ci if (rot == len - 1) { 147275793eaSopenharmony_ci tmp = *last; 148275793eaSopenharmony_ci memmove(list + 1, list, len - 1); 149275793eaSopenharmony_ci *list = tmp; 150275793eaSopenharmony_ci return; 151275793eaSopenharmony_ci } 152275793eaSopenharmony_ci 153275793eaSopenharmony_ci /* otherwise do rotate as a set of cycles in place */ 154275793eaSopenharmony_ci cycles = gcd(len, rot); /* number of cycles */ 155275793eaSopenharmony_ci do { 156275793eaSopenharmony_ci start = from = list + cycles; /* start index is arbitrary */ 157275793eaSopenharmony_ci tmp = *from; /* save entry to be overwritten */ 158275793eaSopenharmony_ci for (;;) { 159275793eaSopenharmony_ci to = from; /* next step in cycle */ 160275793eaSopenharmony_ci from += rot; /* go right rot positions */ 161275793eaSopenharmony_ci if (from > last) from -= len; /* (pointer better not wrap) */ 162275793eaSopenharmony_ci if (from == start) break; /* all but one shifted */ 163275793eaSopenharmony_ci *to = *from; /* shift left */ 164275793eaSopenharmony_ci } 165275793eaSopenharmony_ci *to = tmp; /* complete the circle */ 166275793eaSopenharmony_ci } while (--cycles); 167275793eaSopenharmony_ci} 168275793eaSopenharmony_ci 169275793eaSopenharmony_ci/* structure for gzip file read operations */ 170275793eaSopenharmony_citypedef struct { 171275793eaSopenharmony_ci int fd; /* file descriptor */ 172275793eaSopenharmony_ci int size; /* 1 << size is bytes in buf */ 173275793eaSopenharmony_ci unsigned left; /* bytes available at next */ 174275793eaSopenharmony_ci unsigned char *buf; /* buffer */ 175275793eaSopenharmony_ci z_const unsigned char *next; /* next byte in buffer */ 176275793eaSopenharmony_ci char *name; /* file name for error messages */ 177275793eaSopenharmony_ci} file; 178275793eaSopenharmony_ci 179275793eaSopenharmony_ci/* reload buffer */ 180275793eaSopenharmony_cilocal int readin(file *in) 181275793eaSopenharmony_ci{ 182275793eaSopenharmony_ci int len; 183275793eaSopenharmony_ci 184275793eaSopenharmony_ci len = read(in->fd, in->buf, 1 << in->size); 185275793eaSopenharmony_ci if (len == -1) bye("error reading ", in->name); 186275793eaSopenharmony_ci in->left = (unsigned)len; 187275793eaSopenharmony_ci in->next = in->buf; 188275793eaSopenharmony_ci return len; 189275793eaSopenharmony_ci} 190275793eaSopenharmony_ci 191275793eaSopenharmony_ci/* read from file in, exit if end-of-file */ 192275793eaSopenharmony_cilocal int readmore(file *in) 193275793eaSopenharmony_ci{ 194275793eaSopenharmony_ci if (readin(in) == 0) bye("unexpected end of ", in->name); 195275793eaSopenharmony_ci return 0; 196275793eaSopenharmony_ci} 197275793eaSopenharmony_ci 198275793eaSopenharmony_ci#define read1(in) (in->left == 0 ? readmore(in) : 0, \ 199275793eaSopenharmony_ci in->left--, *(in->next)++) 200275793eaSopenharmony_ci 201275793eaSopenharmony_ci/* skip over n bytes of in */ 202275793eaSopenharmony_cilocal void skip(file *in, unsigned n) 203275793eaSopenharmony_ci{ 204275793eaSopenharmony_ci unsigned bypass; 205275793eaSopenharmony_ci 206275793eaSopenharmony_ci if (n > in->left) { 207275793eaSopenharmony_ci n -= in->left; 208275793eaSopenharmony_ci bypass = n & ~((1U << in->size) - 1); 209275793eaSopenharmony_ci if (bypass) { 210275793eaSopenharmony_ci if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1) 211275793eaSopenharmony_ci bye("seeking ", in->name); 212275793eaSopenharmony_ci n -= bypass; 213275793eaSopenharmony_ci } 214275793eaSopenharmony_ci readmore(in); 215275793eaSopenharmony_ci if (n > in->left) 216275793eaSopenharmony_ci bye("unexpected end of ", in->name); 217275793eaSopenharmony_ci } 218275793eaSopenharmony_ci in->left -= n; 219275793eaSopenharmony_ci in->next += n; 220275793eaSopenharmony_ci} 221275793eaSopenharmony_ci 222275793eaSopenharmony_ci/* read a four-byte unsigned integer, little-endian, from in */ 223275793eaSopenharmony_ciunsigned long read4(file *in) 224275793eaSopenharmony_ci{ 225275793eaSopenharmony_ci unsigned long val; 226275793eaSopenharmony_ci 227275793eaSopenharmony_ci val = read1(in); 228275793eaSopenharmony_ci val += (unsigned)read1(in) << 8; 229275793eaSopenharmony_ci val += (unsigned long)read1(in) << 16; 230275793eaSopenharmony_ci val += (unsigned long)read1(in) << 24; 231275793eaSopenharmony_ci return val; 232275793eaSopenharmony_ci} 233275793eaSopenharmony_ci 234275793eaSopenharmony_ci/* skip over gzip header */ 235275793eaSopenharmony_cilocal void gzheader(file *in) 236275793eaSopenharmony_ci{ 237275793eaSopenharmony_ci int flags; 238275793eaSopenharmony_ci unsigned n; 239275793eaSopenharmony_ci 240275793eaSopenharmony_ci if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file"); 241275793eaSopenharmony_ci if (read1(in) != 8) bye("unknown compression method in", in->name); 242275793eaSopenharmony_ci flags = read1(in); 243275793eaSopenharmony_ci if (flags & 0xe0) bye("unknown header flags set in", in->name); 244275793eaSopenharmony_ci skip(in, 6); 245275793eaSopenharmony_ci if (flags & 4) { 246275793eaSopenharmony_ci n = read1(in); 247275793eaSopenharmony_ci n += (unsigned)(read1(in)) << 8; 248275793eaSopenharmony_ci skip(in, n); 249275793eaSopenharmony_ci } 250275793eaSopenharmony_ci if (flags & 8) while (read1(in) != 0) ; 251275793eaSopenharmony_ci if (flags & 16) while (read1(in) != 0) ; 252275793eaSopenharmony_ci if (flags & 2) skip(in, 2); 253275793eaSopenharmony_ci} 254275793eaSopenharmony_ci 255275793eaSopenharmony_ci/* decompress gzip file "name", return strm with a deflate stream ready to 256275793eaSopenharmony_ci continue compression of the data in the gzip file, and return a file 257275793eaSopenharmony_ci descriptor pointing to where to write the compressed data -- the deflate 258275793eaSopenharmony_ci stream is initialized to compress using level "level" */ 259275793eaSopenharmony_cilocal int gzscan(char *name, z_stream *strm, int level) 260275793eaSopenharmony_ci{ 261275793eaSopenharmony_ci int ret, lastbit, left, full; 262275793eaSopenharmony_ci unsigned have; 263275793eaSopenharmony_ci unsigned long crc, tot; 264275793eaSopenharmony_ci unsigned char *window; 265275793eaSopenharmony_ci off_t lastoff, end; 266275793eaSopenharmony_ci file gz; 267275793eaSopenharmony_ci 268275793eaSopenharmony_ci /* open gzip file */ 269275793eaSopenharmony_ci gz.name = name; 270275793eaSopenharmony_ci gz.fd = open(name, O_RDWR, 0); 271275793eaSopenharmony_ci if (gz.fd == -1) bye("cannot open ", name); 272275793eaSopenharmony_ci gz.buf = malloc(CHUNK); 273275793eaSopenharmony_ci if (gz.buf == NULL) bye("out of memory", ""); 274275793eaSopenharmony_ci gz.size = LGCHUNK; 275275793eaSopenharmony_ci gz.left = 0; 276275793eaSopenharmony_ci 277275793eaSopenharmony_ci /* skip gzip header */ 278275793eaSopenharmony_ci gzheader(&gz); 279275793eaSopenharmony_ci 280275793eaSopenharmony_ci /* prepare to decompress */ 281275793eaSopenharmony_ci window = malloc(DSIZE); 282275793eaSopenharmony_ci if (window == NULL) bye("out of memory", ""); 283275793eaSopenharmony_ci strm->zalloc = Z_NULL; 284275793eaSopenharmony_ci strm->zfree = Z_NULL; 285275793eaSopenharmony_ci strm->opaque = Z_NULL; 286275793eaSopenharmony_ci ret = inflateInit2(strm, -15); 287275793eaSopenharmony_ci if (ret != Z_OK) bye("out of memory", " or library mismatch"); 288275793eaSopenharmony_ci 289275793eaSopenharmony_ci /* decompress the deflate stream, saving append information */ 290275793eaSopenharmony_ci lastbit = 0; 291275793eaSopenharmony_ci lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left; 292275793eaSopenharmony_ci left = 0; 293275793eaSopenharmony_ci strm->avail_in = gz.left; 294275793eaSopenharmony_ci strm->next_in = gz.next; 295275793eaSopenharmony_ci crc = crc32(0L, Z_NULL, 0); 296275793eaSopenharmony_ci have = full = 0; 297275793eaSopenharmony_ci do { 298275793eaSopenharmony_ci /* if needed, get more input */ 299275793eaSopenharmony_ci if (strm->avail_in == 0) { 300275793eaSopenharmony_ci readmore(&gz); 301275793eaSopenharmony_ci strm->avail_in = gz.left; 302275793eaSopenharmony_ci strm->next_in = gz.next; 303275793eaSopenharmony_ci } 304275793eaSopenharmony_ci 305275793eaSopenharmony_ci /* set up output to next available section of sliding window */ 306275793eaSopenharmony_ci strm->avail_out = DSIZE - have; 307275793eaSopenharmony_ci strm->next_out = window + have; 308275793eaSopenharmony_ci 309275793eaSopenharmony_ci /* inflate and check for errors */ 310275793eaSopenharmony_ci ret = inflate(strm, Z_BLOCK); 311275793eaSopenharmony_ci if (ret == Z_STREAM_ERROR) bye("internal stream error!", ""); 312275793eaSopenharmony_ci if (ret == Z_MEM_ERROR) bye("out of memory", ""); 313275793eaSopenharmony_ci if (ret == Z_DATA_ERROR) 314275793eaSopenharmony_ci bye("invalid compressed data--format violated in", name); 315275793eaSopenharmony_ci 316275793eaSopenharmony_ci /* update crc and sliding window pointer */ 317275793eaSopenharmony_ci crc = crc32(crc, window + have, DSIZE - have - strm->avail_out); 318275793eaSopenharmony_ci if (strm->avail_out) 319275793eaSopenharmony_ci have = DSIZE - strm->avail_out; 320275793eaSopenharmony_ci else { 321275793eaSopenharmony_ci have = 0; 322275793eaSopenharmony_ci full = 1; 323275793eaSopenharmony_ci } 324275793eaSopenharmony_ci 325275793eaSopenharmony_ci /* process end of block */ 326275793eaSopenharmony_ci if (strm->data_type & 128) { 327275793eaSopenharmony_ci if (strm->data_type & 64) 328275793eaSopenharmony_ci left = strm->data_type & 0x1f; 329275793eaSopenharmony_ci else { 330275793eaSopenharmony_ci lastbit = strm->data_type & 0x1f; 331275793eaSopenharmony_ci lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in; 332275793eaSopenharmony_ci } 333275793eaSopenharmony_ci } 334275793eaSopenharmony_ci } while (ret != Z_STREAM_END); 335275793eaSopenharmony_ci inflateEnd(strm); 336275793eaSopenharmony_ci gz.left = strm->avail_in; 337275793eaSopenharmony_ci gz.next = strm->next_in; 338275793eaSopenharmony_ci 339275793eaSopenharmony_ci /* save the location of the end of the compressed data */ 340275793eaSopenharmony_ci end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left; 341275793eaSopenharmony_ci 342275793eaSopenharmony_ci /* check gzip trailer and save total for deflate */ 343275793eaSopenharmony_ci if (crc != read4(&gz)) 344275793eaSopenharmony_ci bye("invalid compressed data--crc mismatch in ", name); 345275793eaSopenharmony_ci tot = strm->total_out; 346275793eaSopenharmony_ci if ((tot & 0xffffffffUL) != read4(&gz)) 347275793eaSopenharmony_ci bye("invalid compressed data--length mismatch in", name); 348275793eaSopenharmony_ci 349275793eaSopenharmony_ci /* if not at end of file, warn */ 350275793eaSopenharmony_ci if (gz.left || readin(&gz)) 351275793eaSopenharmony_ci fprintf(stderr, 352275793eaSopenharmony_ci "gzappend warning: junk at end of gzip file overwritten\n"); 353275793eaSopenharmony_ci 354275793eaSopenharmony_ci /* clear last block bit */ 355275793eaSopenharmony_ci lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET); 356275793eaSopenharmony_ci if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name); 357275793eaSopenharmony_ci *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7))); 358275793eaSopenharmony_ci lseek(gz.fd, -1L, SEEK_CUR); 359275793eaSopenharmony_ci if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name); 360275793eaSopenharmony_ci 361275793eaSopenharmony_ci /* if window wrapped, build dictionary from window by rotating */ 362275793eaSopenharmony_ci if (full) { 363275793eaSopenharmony_ci rotate(window, DSIZE, have); 364275793eaSopenharmony_ci have = DSIZE; 365275793eaSopenharmony_ci } 366275793eaSopenharmony_ci 367275793eaSopenharmony_ci /* set up deflate stream with window, crc, total_in, and leftover bits */ 368275793eaSopenharmony_ci ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); 369275793eaSopenharmony_ci if (ret != Z_OK) bye("out of memory", ""); 370275793eaSopenharmony_ci deflateSetDictionary(strm, window, have); 371275793eaSopenharmony_ci strm->adler = crc; 372275793eaSopenharmony_ci strm->total_in = tot; 373275793eaSopenharmony_ci if (left) { 374275793eaSopenharmony_ci lseek(gz.fd, --end, SEEK_SET); 375275793eaSopenharmony_ci if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name); 376275793eaSopenharmony_ci deflatePrime(strm, 8 - left, *gz.buf); 377275793eaSopenharmony_ci } 378275793eaSopenharmony_ci lseek(gz.fd, end, SEEK_SET); 379275793eaSopenharmony_ci 380275793eaSopenharmony_ci /* clean up and return */ 381275793eaSopenharmony_ci free(window); 382275793eaSopenharmony_ci free(gz.buf); 383275793eaSopenharmony_ci return gz.fd; 384275793eaSopenharmony_ci} 385275793eaSopenharmony_ci 386275793eaSopenharmony_ci/* append file "name" to gzip file gd using deflate stream strm -- if last 387275793eaSopenharmony_ci is true, then finish off the deflate stream at the end */ 388275793eaSopenharmony_cilocal void gztack(char *name, int gd, z_stream *strm, int last) 389275793eaSopenharmony_ci{ 390275793eaSopenharmony_ci int fd, len, ret; 391275793eaSopenharmony_ci unsigned left; 392275793eaSopenharmony_ci unsigned char *in, *out; 393275793eaSopenharmony_ci 394275793eaSopenharmony_ci /* open file to compress and append */ 395275793eaSopenharmony_ci fd = 0; 396275793eaSopenharmony_ci if (name != NULL) { 397275793eaSopenharmony_ci fd = open(name, O_RDONLY, 0); 398275793eaSopenharmony_ci if (fd == -1) 399275793eaSopenharmony_ci fprintf(stderr, "gzappend warning: %s not found, skipping ...\n", 400275793eaSopenharmony_ci name); 401275793eaSopenharmony_ci } 402275793eaSopenharmony_ci 403275793eaSopenharmony_ci /* allocate buffers */ 404275793eaSopenharmony_ci in = malloc(CHUNK); 405275793eaSopenharmony_ci out = malloc(CHUNK); 406275793eaSopenharmony_ci if (in == NULL || out == NULL) bye("out of memory", ""); 407275793eaSopenharmony_ci 408275793eaSopenharmony_ci /* compress input file and append to gzip file */ 409275793eaSopenharmony_ci do { 410275793eaSopenharmony_ci /* get more input */ 411275793eaSopenharmony_ci len = read(fd, in, CHUNK); 412275793eaSopenharmony_ci if (len == -1) { 413275793eaSopenharmony_ci fprintf(stderr, 414275793eaSopenharmony_ci "gzappend warning: error reading %s, skipping rest ...\n", 415275793eaSopenharmony_ci name); 416275793eaSopenharmony_ci len = 0; 417275793eaSopenharmony_ci } 418275793eaSopenharmony_ci strm->avail_in = (unsigned)len; 419275793eaSopenharmony_ci strm->next_in = in; 420275793eaSopenharmony_ci if (len) strm->adler = crc32(strm->adler, in, (unsigned)len); 421275793eaSopenharmony_ci 422275793eaSopenharmony_ci /* compress and write all available output */ 423275793eaSopenharmony_ci do { 424275793eaSopenharmony_ci strm->avail_out = CHUNK; 425275793eaSopenharmony_ci strm->next_out = out; 426275793eaSopenharmony_ci ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH); 427275793eaSopenharmony_ci left = CHUNK - strm->avail_out; 428275793eaSopenharmony_ci while (left) { 429275793eaSopenharmony_ci len = write(gd, out + CHUNK - strm->avail_out - left, left); 430275793eaSopenharmony_ci if (len == -1) bye("writing gzip file", ""); 431275793eaSopenharmony_ci left -= (unsigned)len; 432275793eaSopenharmony_ci } 433275793eaSopenharmony_ci } while (strm->avail_out == 0 && ret != Z_STREAM_END); 434275793eaSopenharmony_ci } while (len != 0); 435275793eaSopenharmony_ci 436275793eaSopenharmony_ci /* write trailer after last entry */ 437275793eaSopenharmony_ci if (last) { 438275793eaSopenharmony_ci deflateEnd(strm); 439275793eaSopenharmony_ci out[0] = (unsigned char)(strm->adler); 440275793eaSopenharmony_ci out[1] = (unsigned char)(strm->adler >> 8); 441275793eaSopenharmony_ci out[2] = (unsigned char)(strm->adler >> 16); 442275793eaSopenharmony_ci out[3] = (unsigned char)(strm->adler >> 24); 443275793eaSopenharmony_ci out[4] = (unsigned char)(strm->total_in); 444275793eaSopenharmony_ci out[5] = (unsigned char)(strm->total_in >> 8); 445275793eaSopenharmony_ci out[6] = (unsigned char)(strm->total_in >> 16); 446275793eaSopenharmony_ci out[7] = (unsigned char)(strm->total_in >> 24); 447275793eaSopenharmony_ci len = 8; 448275793eaSopenharmony_ci do { 449275793eaSopenharmony_ci ret = write(gd, out + 8 - len, len); 450275793eaSopenharmony_ci if (ret == -1) bye("writing gzip file", ""); 451275793eaSopenharmony_ci len -= ret; 452275793eaSopenharmony_ci } while (len); 453275793eaSopenharmony_ci close(gd); 454275793eaSopenharmony_ci } 455275793eaSopenharmony_ci 456275793eaSopenharmony_ci /* clean up and return */ 457275793eaSopenharmony_ci free(out); 458275793eaSopenharmony_ci free(in); 459275793eaSopenharmony_ci if (fd > 0) close(fd); 460275793eaSopenharmony_ci} 461275793eaSopenharmony_ci 462275793eaSopenharmony_ci/* process the compression level option if present, scan the gzip file, and 463275793eaSopenharmony_ci append the specified files, or append the data from stdin if no other file 464275793eaSopenharmony_ci names are provided on the command line -- the gzip file must be writable 465275793eaSopenharmony_ci and seekable */ 466275793eaSopenharmony_ciint main(int argc, char **argv) 467275793eaSopenharmony_ci{ 468275793eaSopenharmony_ci int gd, level; 469275793eaSopenharmony_ci z_stream strm; 470275793eaSopenharmony_ci 471275793eaSopenharmony_ci /* ignore command name */ 472275793eaSopenharmony_ci argc--; argv++; 473275793eaSopenharmony_ci 474275793eaSopenharmony_ci /* provide usage if no arguments */ 475275793eaSopenharmony_ci if (*argv == NULL) { 476275793eaSopenharmony_ci printf( 477275793eaSopenharmony_ci "gzappend 1.2 (11 Oct 2012) Copyright (C) 2003, 2012 Mark Adler\n" 478275793eaSopenharmony_ci ); 479275793eaSopenharmony_ci printf( 480275793eaSopenharmony_ci "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n"); 481275793eaSopenharmony_ci return 0; 482275793eaSopenharmony_ci } 483275793eaSopenharmony_ci 484275793eaSopenharmony_ci /* set compression level */ 485275793eaSopenharmony_ci level = Z_DEFAULT_COMPRESSION; 486275793eaSopenharmony_ci if (argv[0][0] == '-') { 487275793eaSopenharmony_ci if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0) 488275793eaSopenharmony_ci bye("invalid compression level", ""); 489275793eaSopenharmony_ci level = argv[0][1] - '0'; 490275793eaSopenharmony_ci if (*++argv == NULL) bye("no gzip file name after options", ""); 491275793eaSopenharmony_ci } 492275793eaSopenharmony_ci 493275793eaSopenharmony_ci /* prepare to append to gzip file */ 494275793eaSopenharmony_ci gd = gzscan(*argv++, &strm, level); 495275793eaSopenharmony_ci 496275793eaSopenharmony_ci /* append files on command line, or from stdin if none */ 497275793eaSopenharmony_ci if (*argv == NULL) 498275793eaSopenharmony_ci gztack(NULL, gd, &strm, 1); 499275793eaSopenharmony_ci else 500275793eaSopenharmony_ci do { 501275793eaSopenharmony_ci gztack(*argv, gd, &strm, argv[1] == NULL); 502275793eaSopenharmony_ci } while (*++argv != NULL); 503275793eaSopenharmony_ci return 0; 504275793eaSopenharmony_ci} 505