1 /************************************************************************/
2 /* */
3 /* Simple io streams, Data on the stream travels in In/Deflate format */
4 /* using zlib. The implementation is a VERY SIMPLE ONE: It assumes */
5 /* that the compressed files fit in memory. */
6 /* */
7 /************************************************************************/
8
9 # include "appUtilConfig.h"
10
11 # include <stdlib.h>
12 # include <string.h>
13 # include <zlib.h>
14 # include <time.h>
15
16 # include "sioZip.h"
17 # include "sioEndian.h"
18 # include "utilMemoryBuffer.h"
19 # include "utilDosTime.h"
20
21 # include <appDebugon.h>
22
23 # define ZIPmethodSTORED 0
24 # define ZIPmethodDEFLATED 8
25
26 # define ZIPflagsSTORED 0
27 # define ZIPflagsDEFLATED 0
28
29 # define ZIPversionSTORED 0xa
30 # define ZIPversionDEFLATED 0xa
31
32 # define ZIPsignatureDIRECTORY_ENTRY 0x02014b50
33 # define ZIPsignatureDIRECTORY_END 0x06054b50
34 # define ZIPsignatureMEMBER 0x04034b50
35
36 # define ZIPattributesINTERNAL 0 /* binary */
37 # define ZIPattributesEXTERNAL 0 /* from stdin */
38
39 /************************************************************************/
40 /* */
41 /* Actual Output stream object. */
42 /* */
43 /************************************************************************/
44
45 typedef struct ZipOutputStream
46 {
47 z_stream zosZstream;
48 unsigned char zosHasZstream;
49 unsigned char zosOutputBuffer[SIOsizBUF];
50 ZipOutput * zosZipOutput;
51 int zosIndex;
52 unsigned long zosHeaderOffset;
53
54 int zosVersion;
55 int zosFlags;
56 int zosMethod;
57 int zosDosTime;
58 int zosDosDate;
59
60 MemoryBuffer zosCollectedBuffer;
61
62 unsigned long zosAdlerCrc;
63 unsigned long zosUncompressedSize;
64 unsigned long zosCollectedSize;
65
66 char * zosFileName;
67 } ZipOutputStream;
68
sioZipInitStream(ZipOutputStream * zos)69 static void sioZipInitStream( ZipOutputStream * zos )
70 {
71 zos->zosHasZstream= 0;
72 zos->zosZipOutput= (ZipOutput *)0;
73 zos->zosIndex= -1;
74 zos->zosHeaderOffset= 0;
75
76 zos->zosVersion= ZIPversionSTORED;
77 zos->zosFlags= ZIPflagsSTORED;
78 zos->zosMethod= ZIPmethodSTORED;
79 zos->zosDosTime= 0;
80 zos->zosDosDate= 0;
81
82 utilInitMemoryBuffer( &(zos->zosCollectedBuffer) );
83
84 zos->zosAdlerCrc= 0;
85 zos->zosUncompressedSize= 0;
86 zos->zosCollectedSize= 0;
87
88 zos->zosFileName= (char *)0;
89 }
90
sioZipCleanStream(ZipOutputStream * zos)91 static void sioZipCleanStream( ZipOutputStream * zos )
92 {
93 utilCleanMemoryBuffer( &(zos->zosCollectedBuffer) );
94
95 if ( zos->zosHasZstream )
96 {
97 int ret= deflateEnd( &(zos->zosZstream) );
98 if ( ret != Z_OK )
99 { LLDEB(zos->zosHasZstream,ret); }
100 }
101
102 if ( zos->zosFileName )
103 { free( zos->zosFileName ); }
104 }
105
106 /************************************************************************/
107
sioZipInitOutput(ZipOutput * zo)108 void sioZipInitOutput( ZipOutput * zo )
109 {
110 zo->zoSosZip= (SimpleOutputStream *)0;
111 zo->zoBytesWritten= 0;
112
113 utilInitPagedList( &(zo->zoFileList) );
114 utilStartPagedList( &(zo->zoFileList),
115 sizeof(ZipOutputStream),
116 (InitPagedListItem)sioZipInitStream,
117 (InitPagedListItem)sioZipCleanStream );
118
119 zo->zoEntryCount= 0;
120 zo->zoDirectoryOffset= 0;
121 zo->zoDirectorySize= 0;
122
123 return;
124 }
125
sioZipCleanOutput(ZipOutput * zo)126 void sioZipCleanOutput( ZipOutput * zo )
127 {
128 /* zo->zoSosZip Is owned by the consumer */
129
130 utilCleanPagedList( &(zo->zoFileList) );
131
132 return;
133 }
134
135 /************************************************************************/
136
sioZipWriteLocalFileHeader(const ZipOutputStream * zos)137 static int sioZipWriteLocalFileHeader( const ZipOutputStream * zos )
138 {
139 SimpleOutputStream * sosZip= zos->zosZipOutput->zoSosZip;
140
141 const int extraLength= 0;
142
143 int done= 0;
144 int fileNameLength= 0;
145
146 if ( zos->zosFileName )
147 { fileNameLength= strlen( zos->zosFileName ); }
148
149 sioEndianPutLeUint32( ZIPsignatureMEMBER, sosZip ); done += 4;
150 sioEndianPutLeUint16( zos->zosVersion, sosZip ); done += 2;
151 sioEndianPutLeUint16( zos->zosFlags, sosZip ); done += 2;
152 sioEndianPutLeUint16( zos->zosMethod, sosZip ); done += 2;
153 sioEndianPutLeUint16( zos->zosDosTime, sosZip ); done += 2;
154 sioEndianPutLeUint16( zos->zosDosDate, sosZip ); done += 2;
155 sioEndianPutLeUint32( zos->zosAdlerCrc, sosZip ); done += 4;
156 sioEndianPutLeUint32( zos->zosCollectedSize, sosZip ); done += 4;
157 sioEndianPutLeUint32( zos->zosUncompressedSize, sosZip ); done += 4;
158 sioEndianPutLeUint16( fileNameLength, sosZip ); done += 2;
159 sioEndianPutLeUint16( extraLength, sosZip ); done += 2;
160
161 if ( fileNameLength > 0 )
162 {
163 sioOutPutString( zos->zosFileName, sosZip );
164 done += fileNameLength;
165 }
166
167 /* no extra field */
168
169 return done;
170 }
171
sioZipWriteDirectoryEntry(const ZipOutputStream * zos)172 static int sioZipWriteDirectoryEntry( const ZipOutputStream * zos )
173 {
174 SimpleOutputStream * sosZip= zos->zosZipOutput->zoSosZip;
175
176 const int extraLength= 0;
177 const int commentLength= 0;
178 const int diskNumberStart= 0;
179
180 int done= 0;
181 int fileNameLength= 0;
182
183 if ( zos->zosFileName )
184 { fileNameLength= strlen( zos->zosFileName ); }
185
186 sioEndianPutLeUint32( ZIPsignatureDIRECTORY_ENTRY, sosZip ); done += 4;
187 sioEndianPutLeUint16( zos->zosVersion, sosZip ); done += 2;
188 sioEndianPutLeUint16( zos->zosVersion, sosZip ); done += 2;
189 sioEndianPutLeUint16( zos->zosFlags, sosZip ); done += 2;
190 sioEndianPutLeUint16( zos->zosMethod, sosZip ); done += 2;
191 sioEndianPutLeUint16( zos->zosDosTime, sosZip ); done += 2;
192 sioEndianPutLeUint16( zos->zosDosDate, sosZip ); done += 2;
193 sioEndianPutLeUint32( zos->zosAdlerCrc, sosZip ); done += 4;
194 sioEndianPutLeUint32( zos->zosCollectedSize, sosZip ); done += 4;
195 sioEndianPutLeUint32( zos->zosUncompressedSize, sosZip ); done += 4;
196 sioEndianPutLeUint16( fileNameLength, sosZip ); done += 2;
197 sioEndianPutLeUint16( extraLength, sosZip ); done += 2;
198
199 sioEndianPutLeUint16( commentLength, sosZip ); done += 2;
200 sioEndianPutLeUint16( diskNumberStart, sosZip ); done += 2;
201 sioEndianPutLeUint16( ZIPattributesINTERNAL, sosZip ); done += 2;
202 sioEndianPutLeUint32( ZIPattributesEXTERNAL, sosZip ); done += 4;
203
204 sioEndianPutLeUint32( zos->zosHeaderOffset, sosZip ); done += 4;
205
206 if ( fileNameLength > 0 )
207 {
208 sioOutPutString( zos->zosFileName, sosZip );
209 done += fileNameLength;
210 }
211
212 /* no extra field */
213 /* no comment */
214
215 return done;
216 }
217
sioZipWriteDirectoryEnd(const ZipOutput * zo)218 static int sioZipWriteDirectoryEnd( const ZipOutput * zo )
219 {
220 SimpleOutputStream * sosZip= zo->zoSosZip;
221
222 const int diskNumber= 0;
223 const int commentLength= 0;
224
225 int done= 0;
226
227 sioEndianPutLeUint32( ZIPsignatureDIRECTORY_END, sosZip ); done += 4;
228 /* number of this disk */
229 sioEndianPutLeUint16( diskNumber, sosZip ); done += 2;
230 /* number of the disk where the central dir starts */
231 sioEndianPutLeUint16( diskNumber, sosZip ); done += 2;
232 /* Entry count on this disk */
233 sioEndianPutLeUint16( diskNumber, sosZip ); done += 2;
234 /* Total entry count */
235 sioEndianPutLeUint16( zo->zoEntryCount, sosZip ); done += 2;
236 sioEndianPutLeUint32( zo->zoDirectorySize, sosZip ); done += 4;
237 sioEndianPutLeUint32( zo->zoDirectoryOffset, sosZip ); done += 4;
238 sioEndianPutLeUint16( commentLength, sosZip ); done += 2;
239
240 /* no comment */
241
242 return done;
243 }
244
245 /************************************************************************/
246
247 typedef struct ZipStats
248 {
249 int zsEntryCount;
250 unsigned long zsDirectorySize;
251 } ZipStats;
252
sioZipEmitDirectoryEntry(int n,void * voidzos,void * voidzs)253 static int sioZipEmitDirectoryEntry( int n,
254 void * voidzos,
255 void * voidzs )
256 {
257 ZipOutputStream * zos= (ZipOutputStream *)voidzos;
258 ZipStats * zs= (ZipStats *)voidzs;
259 ZipOutput * zo= zos->zosZipOutput;
260
261 int step;
262
263 step= sioZipWriteDirectoryEntry( zos );
264 if ( step < 0 )
265 { LDEB(step); return -1; }
266
267 zs->zsEntryCount++;
268 zs->zsDirectorySize += step;
269
270 zo->zoBytesWritten += step;
271 zo->zoDirectorySize += step;
272
273 return 0;
274 }
275
sioZipFlushOutput(ZipOutput * zo)276 int sioZipFlushOutput( ZipOutput * zo )
277 {
278 ZipStats zs;
279 int n;
280 int step;
281
282 zs.zsEntryCount= 0;
283 zs.zsDirectorySize= 0;
284
285 zo->zoDirectoryOffset= zo->zoBytesWritten;
286
287 n= utilPagedListForAll( &(zo->zoFileList),
288 sioZipEmitDirectoryEntry, (void *)&zs );
289 if ( n < 0 )
290 { LDEB(n); return -1; }
291 if ( n!= zs.zsEntryCount || n != zo->zoEntryCount )
292 { LLLDEB(n,zs.zsEntryCount,zo->zoEntryCount); }
293
294 step= sioZipWriteDirectoryEnd( zo );
295 if ( step < 0 )
296 { LDEB(step); return -1; }
297 zo->zoBytesWritten += step;
298
299 return 0;
300 }
301
302 /************************************************************************/
303 /* */
304 /* The Zip compression routine: */
305 /* */
306 /************************************************************************/
307
sioOutZipFlushBytesDeflated(ZipOutputStream * zos,int n)308 static int sioOutZipFlushBytesDeflated( ZipOutputStream * zos,
309 int n )
310 {
311 z_stream * c_stream= &(zos->zosZstream);
312 const unsigned char * b= zos->zosOutputBuffer;
313
314 if ( n > 0 && utilMemoryBufferAppendBytes( &(zos->zosCollectedBuffer), b, n ) )
315 { LDEB(n); return -1; }
316
317 zos->zosCollectedSize += n;
318
319 c_stream->next_out= zos->zosOutputBuffer;
320 c_stream->avail_out= SIOsizBUF;
321
322 return n;
323 }
324
sioOutZipWriteBytesDeflated(void * voidzos,const unsigned char * buffer,int count)325 static int sioOutZipWriteBytesDeflated( void * voidzos,
326 const unsigned char * buffer,
327 int count )
328 {
329 ZipOutputStream * zos= (ZipOutputStream *)voidzos;
330 z_stream * c_stream= &(zos->zosZstream);
331
332 zos->zosUncompressedSize += count;
333 zos->zosAdlerCrc= crc32( zos->zosAdlerCrc, buffer, count );
334
335 c_stream->next_in= (unsigned char *)buffer;
336 c_stream->avail_in= count;
337
338 while( c_stream->avail_in > 0 )
339 {
340 int ret;
341
342 if ( c_stream->avail_out == 0 )
343 {
344 if ( sioOutZipFlushBytesDeflated( zos, SIOsizBUF ) != SIOsizBUF )
345 { LDEB(SIOsizBUF); return -1; }
346 }
347
348 ret= deflate( c_stream, Z_NO_FLUSH );
349 if ( ret != Z_OK )
350 { LDEB(ret); return -1; }
351 }
352
353 return count;
354 }
355
sioOutZipWriteBytesStored(void * voidzos,const unsigned char * buffer,int count)356 static int sioOutZipWriteBytesStored( void * voidzos,
357 const unsigned char * buffer,
358 int count )
359 {
360 ZipOutputStream * zos= (ZipOutputStream *)voidzos;
361
362 zos->zosUncompressedSize += count;
363 zos->zosAdlerCrc= crc32( zos->zosAdlerCrc, buffer, count );
364
365 if ( utilMemoryBufferAppendBytes( &(zos->zosCollectedBuffer), buffer, count ) )
366 { LDEB(count); return -1; }
367
368 zos->zosCollectedSize += count;
369
370 return count;
371 }
372
373 /************************************************************************/
374 /* */
375 /* Close a Zip output stream. */
376 /* */
377 /************************************************************************/
378
sioOutZipFinishFile(ZipOutputStream * zos,int rval)379 static void sioOutZipFinishFile( ZipOutputStream * zos,
380 int rval )
381 {
382 ZipOutput * zo= zos->zosZipOutput;
383
384 if ( zos->zosHasZstream )
385 {
386 int ret= deflateEnd( &(zos->zosZstream) );
387 if ( ret != Z_OK )
388 { LLDEB(zos->zosHasZstream,ret); }
389
390 zos->zosHasZstream= 0;
391 }
392
393 utilCleanMemoryBuffer( &(zos->zosCollectedBuffer) );
394 utilInitMemoryBuffer( &(zos->zosCollectedBuffer) );
395
396 if ( rval )
397 {
398 if ( zos->zosIndex >= 0 )
399 {
400 utilPagedListDeleteItemByNumber( &(zo->zoFileList), zos->zosIndex );
401 }
402 }
403 else{
404 zo->zoEntryCount++;
405 }
406
407 return;
408 }
409
410 /************************************************************************/
411 /* */
412 /* Emit a single local file */
413 /* */
414 /************************************************************************/
415
sioZipEmitFile(ZipOutputStream * zos)416 static int sioZipEmitFile( ZipOutputStream * zos )
417 {
418 ZipOutput * zo= zos->zosZipOutput;
419 SimpleOutputStream * sosZip= zo->zoSosZip;
420
421 int size;
422 const unsigned char * bytes;
423
424 int step;
425
426 zos->zosHeaderOffset= zo->zoBytesWritten;
427
428 step= sioZipWriteLocalFileHeader( zos );
429 if ( step < 0 )
430 { LDEB(step); return -1; }
431 zo->zoBytesWritten += step;
432
433 bytes= utilMemoryBufferGetBytes( &size, &(zos->zosCollectedBuffer) );
434 step= sioOutWriteBytes( sosZip, bytes, size );
435 if ( step != size )
436 { LLDEB(step,size); return -1; }
437 zo->zoBytesWritten += step;
438
439 /*
440 Only if the file header flags had bit 0xa and the fileds were 0 there
441
442 sioEndianPutLeInt32( zos->zosAdlerCrc, zos->zosSosZip ); done += 4;
443 sioEndianPutLeInt32( zos->zosCollectedSize, zos->zosSosZip ); done += 4;
444 sioEndianPutLeInt32( zos->zosUncompressedSize, zos->zosSosZip ); done += 4;
445 */
446
447 return 0;
448 }
449
sioOutZipCloseDeflated(void * voidzos)450 static int sioOutZipCloseDeflated( void * voidzos )
451 {
452 int rval= 0;
453 int ret;
454
455 ZipOutputStream * zos= (ZipOutputStream *)voidzos;
456 z_stream * c_stream= &(zos->zosZstream);
457
458 c_stream->next_in= (unsigned char *)0;
459 c_stream->avail_in= 0;
460
461 for (;;)
462 {
463 if ( c_stream->avail_out == 0 )
464 {
465 if ( sioOutZipFlushBytesDeflated( zos, SIOsizBUF ) != SIOsizBUF )
466 { LDEB(SIOsizBUF); rval= -1; goto ready; }
467 }
468
469 ret= deflate( c_stream, Z_FINISH );
470 if ( ret == Z_STREAM_END )
471 { break; }
472 if ( ret != Z_OK )
473 { LDEB(ret); rval= -1; goto ready; }
474 }
475
476 if ( c_stream->avail_out < SIOsizBUF )
477 {
478 int n= SIOsizBUF- c_stream->avail_out;
479
480 if ( sioOutZipFlushBytesDeflated( zos, n ) != n )
481 { LDEB(n); return -1; }
482 }
483
484 if ( sioZipEmitFile( zos ) )
485 { LDEB(1); rval= -1; goto ready; }
486
487 ready:
488
489 sioOutZipFinishFile( zos, rval );
490
491 return rval;
492 }
493
sioOutZipCloseStored(void * voidzos)494 static int sioOutZipCloseStored( void * voidzos )
495 {
496 int rval= 0;
497
498 ZipOutputStream * zos= (ZipOutputStream *)voidzos;
499
500 if ( sioZipEmitFile( zos ) )
501 { LDEB(1); rval= -1; goto ready; }
502
503 ready:
504
505 sioOutZipFinishFile( zos, rval );
506
507 return rval;
508 }
509
sioZipStartDeflated(ZipOutputStream * zos)510 static int sioZipStartDeflated( ZipOutputStream * zos )
511 {
512 int ret;
513
514 zos->zosZstream.zalloc = (alloc_func)0;
515 zos->zosZstream.zfree = (free_func)0;
516 zos->zosZstream.opaque = (voidpf)0;
517
518 ret= deflateInit2( &(zos->zosZstream), Z_DEFAULT_COMPRESSION, Z_DEFLATED,
519 -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY );
520 if ( ret != Z_OK )
521 { LDEB(ret); return -1; }
522 zos->zosHasZstream= 1;
523
524 zos->zosZstream.next_in= (unsigned char *)0;
525 zos->zosZstream.avail_in= 0;
526
527 zos->zosZstream.next_out= zos->zosOutputBuffer;
528 zos->zosZstream.avail_out= SIOsizBUF;
529
530 zos->zosVersion= ZIPversionDEFLATED;
531 zos->zosFlags= ZIPflagsDEFLATED;
532 zos->zosMethod= ZIPmethodDEFLATED;
533
534 return 0;
535 }
536
sioZipStartStored(ZipOutputStream * zos)537 static int sioZipStartStored( ZipOutputStream * zos )
538 {
539 zos->zosVersion= ZIPversionSTORED;
540 zos->zosFlags= ZIPflagsSTORED;
541 zos->zosMethod= ZIPmethodSTORED;
542
543 return 0;
544 }
545
sioOutZipOpen(ZipOutput * zo,const char * fileName,int deflated)546 SimpleOutputStream * sioOutZipOpen( ZipOutput * zo,
547 const char * fileName,
548 int deflated )
549 {
550 SimpleOutputStream * sos= (SimpleOutputStream *)0;
551 ZipOutputStream * zos= (ZipOutputStream *)0;
552
553 {
554 int n= -1;
555
556 zos= (ZipOutputStream *)utilPagedListClaimNewItem( &n,
557 &(zo->zoFileList) );
558 if ( ! zos )
559 { XDEB(zos); goto ready; }
560
561 zos->zosIndex= n;
562 }
563
564 zos->zosZipOutput= zo;
565 utilDosTimeDate( &(zos->zosDosTime), &(zos->zosDosDate),
566 time( (time_t *)0 ) );
567 if ( fileName )
568 {
569 zos->zosFileName= strdup( fileName );
570 if ( ! zos->zosFileName )
571 { XDEB(zos->zosFileName); goto ready; }
572 }
573
574 zos->zosAdlerCrc= crc32( 0L, Z_NULL, 0 );
575
576 if ( deflated )
577 {
578 if ( sioZipStartDeflated( zos ) )
579 { LDEB(deflated); goto ready; }
580
581 sos= sioOutOpen( (void *)zos,
582 sioOutZipWriteBytesDeflated, sioOutZipCloseDeflated );
583 if ( ! sos )
584 { LXDEB(deflated,sos); goto ready; }
585 zos= (ZipOutputStream *)0; /* steal */
586 }
587 else{
588 if ( sioZipStartStored( zos ) )
589 { LDEB(deflated); goto ready; }
590
591 sos= sioOutOpen( (void *)zos,
592 sioOutZipWriteBytesStored, sioOutZipCloseStored );
593 if ( ! sos )
594 { LXDEB(deflated,sos); goto ready; }
595 zos= (ZipOutputStream *)0; /* steal */
596 }
597
598 ready:
599
600 if ( zos )
601 { utilPagedListDeleteItemByNumber( &(zo->zoFileList), zos->zosIndex ); }
602
603 return sos;
604 }
605
606