]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/pg_backup_custom.c
- Added CVS headers to files
[postgresql] / src / bin / pg_dump / pg_backup_custom.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_backup_custom.c
4  *
5  *      Implements the custom output format.
6  *
7  *  The comments with the routined in this code are a good place to 
8  *      understand how to write a new format.
9  *
10  *      See the headers to pg_restore for more details.
11  *
12  * Copyright (c) 2000, Philip Warner
13  *      Rights are granted to use this software in any way so long
14  *      as this notice is not removed.
15  *
16  *      The author is not responsible for loss or damages that may
17  *      and any liability will be limited to the time taken to fix any
18  *      related bug.
19  *
20  *
21  * IDENTIFICATION
22  *              $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_custom.c,v 1.8 2001/03/19 02:35:28 pjw Exp $
23  *
24  * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
25  *
26  *      Initial version. 
27  *
28  * Modifications - 04-Jan-2001 - pjw@rhyme.com.au
29  *
30  *    - Check results of IO routines more carefully.
31  *
32  *-------------------------------------------------------------------------
33  */
34
35 #include "pg_backup.h"
36 #include "pg_backup_archiver.h"
37
38 #include <stdlib.h>
39 #include <errno.h>
40
41 /*--------
42  * Routines in the format interface
43  *--------
44  */
45
46 static void     _ArchiveEntry(ArchiveHandle* AH, TocEntry* te);
47 static void             _StartData(ArchiveHandle* AH, TocEntry* te);
48 static int              _WriteData(ArchiveHandle* AH, const void* data, int dLen);
49 static void     _EndData(ArchiveHandle* AH, TocEntry* te);
50 static int      _WriteByte(ArchiveHandle* AH, const int i);
51 static int      _ReadByte(ArchiveHandle* );
52 static int      _WriteBuf(ArchiveHandle* AH, const void* buf, int len);
53 static int      _ReadBuf(ArchiveHandle* AH, void* buf, int len);
54 static void     _CloseArchive(ArchiveHandle* AH);
55 static void             _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt);
56 static void             _WriteExtraToc(ArchiveHandle* AH, TocEntry* te);
57 static void             _ReadExtraToc(ArchiveHandle* AH, TocEntry* te);
58 static void             _PrintExtraToc(ArchiveHandle* AH, TocEntry* te);
59
60 static void             _PrintData(ArchiveHandle* AH);
61 static void     _skipData(ArchiveHandle* AH);
62 static void             _skipBlobs(ArchiveHandle* AH);
63
64 static void             _StartBlobs(ArchiveHandle* AH, TocEntry* te);
65 static void             _StartBlob(ArchiveHandle* AH, TocEntry* te, int oid);
66 static void             _EndBlob(ArchiveHandle* AH, TocEntry* te, int oid);
67 static void             _EndBlobs(ArchiveHandle* AH, TocEntry* te);
68 static void             _LoadBlobs(ArchiveHandle* AH);
69
70 /*------------
71  * Buffers used in zlib compression and extra data stored in archive and
72  * in TOC entries.
73  *------------
74  */
75 #define zlibOutSize     4096
76 #define zlibInSize      4096
77
78 typedef struct {
79     z_streamp   zp;
80     char*       zlibOut;
81     char*       zlibIn;
82     int         inSize;
83     int         hasSeek;
84     int         filePos;
85     int         dataStart;
86 } lclContext;
87
88 typedef struct {
89     int         dataPos;
90     int         dataLen;
91 } lclTocEntry;
92
93
94 /*------
95  * Static declarations 
96  *------
97  */
98 static void             _readBlockHeader(ArchiveHandle *AH, int *type, int *id);
99 static void     _StartDataCompressor(ArchiveHandle* AH, TocEntry* te);
100 static void     _EndDataCompressor(ArchiveHandle* AH, TocEntry* te);
101 static int              _getFilePos(ArchiveHandle* AH, lclContext* ctx);
102 static int              _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush);
103
104 static char*    progname = "Archiver(custom)";
105
106
107
108 /*
109  *  Init routine required by ALL formats. This is a global routine
110  *      and should be declared in pg_backup_archiver.h
111  *
112  *      It's task is to create any extra archive context (using AH->formatData),
113  *      and to initialize the supported function pointers.
114  *
115  *      It should also prepare whatever it's input source is for reading/writing,
116  *      and in the case of a read mode connection, it should load the Header & TOC. 
117  */
118 void InitArchiveFmt_Custom(ArchiveHandle* AH) 
119 {
120     lclContext*         ctx;
121
122     /* Assuming static functions, this can be copied for each format. */
123     AH->ArchiveEntryPtr = _ArchiveEntry;
124     AH->StartDataPtr = _StartData;
125     AH->WriteDataPtr = _WriteData;
126     AH->EndDataPtr = _EndData;
127     AH->WriteBytePtr = _WriteByte;
128     AH->ReadBytePtr = _ReadByte;
129     AH->WriteBufPtr = _WriteBuf;
130     AH->ReadBufPtr = _ReadBuf;
131     AH->ClosePtr = _CloseArchive;
132     AH->PrintTocDataPtr = _PrintTocData;
133     AH->ReadExtraTocPtr = _ReadExtraToc;
134     AH->WriteExtraTocPtr = _WriteExtraToc;
135     AH->PrintExtraTocPtr = _PrintExtraToc;
136
137     AH->StartBlobsPtr = _StartBlobs;
138     AH->StartBlobPtr = _StartBlob;
139     AH->EndBlobPtr = _EndBlob;
140     AH->EndBlobsPtr = _EndBlobs;
141
142     /*
143      *  Set up some special context used in compressing data.
144     */
145     ctx = (lclContext*)malloc(sizeof(lclContext));
146     if (ctx == NULL)
147                 die_horribly(AH, "%s: Unable to allocate archive context",progname);
148     AH->formatData = (void*)ctx;
149
150     ctx->zp = (z_streamp)malloc(sizeof(z_stream));
151     if (ctx->zp == NULL)
152         die_horribly(AH, "%s: unable to allocate zlib stream archive context",progname);
153
154         /*
155          * zlibOutSize is the buffer size we tell zlib it can output to.  We
156          * actually allocate one extra byte because some routines want to append
157          * a trailing zero byte to the zlib output.  The input buffer is expansible
158          * and is always of size ctx->inSize; zlibInSize is just the initial
159          * default size for it.
160          */
161     ctx->zlibOut = (char*)malloc(zlibOutSize+1);
162     ctx->zlibIn = (char*)malloc(zlibInSize);
163     ctx->inSize = zlibInSize;
164     ctx->filePos = 0;
165
166     if (ctx->zlibOut == NULL || ctx->zlibIn == NULL)
167                 die_horribly(AH, "%s: unable to allocate buffers in archive context",progname);
168
169     /*
170      * Now open the file
171     */
172     if (AH->mode == archModeWrite) {
173
174                 if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
175                         AH->FH = fopen(AH->fSpec, PG_BINARY_W);
176                 } else {
177                         AH->FH = stdout;
178                 }
179
180                 if (!AH->FH)
181                         die_horribly(AH, "%s: unable to open archive file %s",progname, AH->fSpec);
182
183                 ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
184
185         } else {
186
187                 if (AH->fSpec && strcmp(AH->fSpec,"") != 0) {
188                         AH->FH = fopen(AH->fSpec, PG_BINARY_R);
189                 } else {
190                         AH->FH = stdin;
191                 }
192                 if (!AH->FH)
193                         die_horribly(AH, "%s: unable to open archive file %s",progname, AH->fSpec);
194
195                 ctx->hasSeek = (fseek(AH->FH, 0, SEEK_CUR) == 0);
196
197                 ReadHead(AH);
198                 ReadToc(AH);
199                 ctx->dataStart = _getFilePos(AH, ctx);
200     }
201
202 }
203
204 /*
205  * Called by the Archiver when the dumper creates a new TOC entry.
206  *
207  * Optional.
208  *
209  * Set up extrac format-related TOC data.  
210 */
211 static void     _ArchiveEntry(ArchiveHandle* AH, TocEntry* te) 
212 {
213     lclTocEntry*        ctx;
214
215     ctx = (lclTocEntry*)calloc(1, sizeof(lclTocEntry));
216     if (te->dataDumper) {
217                 ctx->dataPos = -1;
218     } else {
219                 ctx->dataPos = 0;
220     }
221     ctx->dataLen = 0;
222     te->formatData = (void*)ctx;
223
224 }
225
226 /*
227  * Called by the Archiver to save any extra format-related TOC entry
228  * data.
229  *
230  * Optional.
231  *
232  * Use the Archiver routines to write data - they are non-endian, and
233  * maintain other important file information.
234  */
235 static void     _WriteExtraToc(ArchiveHandle* AH, TocEntry* te)
236 {
237     lclTocEntry*        ctx = (lclTocEntry*)te->formatData;
238
239     WriteInt(AH, ctx->dataPos);
240     WriteInt(AH, ctx->dataLen);
241 }
242
243 /*
244  * Called by the Archiver to read any extra format-related TOC data.
245  *
246  * Optional.
247  *
248  * Needs to match the order defined in _WriteExtraToc, and sould also
249  * use the Archiver input routines.
250  */
251 static void     _ReadExtraToc(ArchiveHandle* AH, TocEntry* te)
252 {
253     lclTocEntry*        ctx = (lclTocEntry*)te->formatData;
254
255     if (ctx == NULL) {
256                 ctx = (lclTocEntry*)malloc(sizeof(lclTocEntry));
257                 te->formatData = (void*)ctx;
258     }
259
260     ctx->dataPos = ReadInt( AH );
261     ctx->dataLen = ReadInt( AH );
262 }
263
264 /*
265  * Called by the Archiver when restoring an archive to output a comment
266  * that includes useful information about the TOC entry.
267  *
268  * Optional.
269  *
270  */
271 static void     _PrintExtraToc(ArchiveHandle* AH, TocEntry* te)
272 {
273     lclTocEntry*        ctx = (lclTocEntry*)te->formatData;
274
275     ahprintf(AH, "-- Data Pos: %d (Length %d)\n", ctx->dataPos, ctx->dataLen);
276 }
277
278 /*
279  * Called by the archiver when saving TABLE DATA (not schema). This routine
280  * should save whatever format-specific information is needed to read
281  * the archive back.
282  *
283  * It is called just prior to the dumper's 'DataDumper' routine being called.
284  *
285  * Optional, but strongly recommended.
286  *
287  */
288 static void     _StartData(ArchiveHandle* AH, TocEntry* te)
289 {
290     lclContext*         ctx = (lclContext*)AH->formatData;
291     lclTocEntry*        tctx = (lclTocEntry*)te->formatData;
292
293     tctx->dataPos = _getFilePos(AH, ctx);
294
295     _WriteByte(AH, BLK_DATA); /* Block type */
296     WriteInt(AH, te->id); /* For sanity check */
297
298     _StartDataCompressor(AH, te);
299
300 }
301
302 /*
303  * Called by archiver when dumper calls WriteData. This routine is 
304  * called for both BLOB and TABLE data; it is the responsibility of
305  * the format to manage each kind of data using StartBlob/StartData.
306  *
307  * It should only be called from withing a DataDumper routine.
308  *
309  * Mandatory.
310  *
311  */
312 static int      _WriteData(ArchiveHandle* AH, const void* data, int dLen)
313 {
314     lclContext* ctx = (lclContext*)AH->formatData;
315     z_streamp   zp = ctx->zp;
316
317     zp->next_in = (void*)data;
318     zp->avail_in = dLen;
319
320     while (zp->avail_in != 0) {
321         /* printf("Deflating %d bytes\n", dLen); */
322                 _DoDeflate(AH, ctx, 0);
323     }
324     return dLen;
325 }
326
327 /*
328  * Called by the archiver when a dumper's 'DataDumper' routine has
329  * finished.
330  *
331  * Optional.
332  *
333  */
334 static void     _EndData(ArchiveHandle* AH, TocEntry* te)
335 {
336     lclContext*         ctx = (lclContext*)AH->formatData;
337     lclTocEntry*        tctx = (lclTocEntry*) te->formatData;
338
339     _EndDataCompressor(AH, te);
340
341     tctx->dataLen = _getFilePos(AH, ctx) - tctx->dataPos;
342 }
343
344 /*
345  * Called by the archiver when starting to save all BLOB DATA (not schema). 
346  * This routine should save whatever format-specific information is needed
347  * to read the BLOBs back into memory. 
348  *
349  * It is called just prior to the dumper's DataDumper routine.
350  *
351  * Optional, but strongly recommended.
352  *
353  */
354 static void     _StartBlobs(ArchiveHandle* AH, TocEntry* te)
355 {
356     lclContext*         ctx = (lclContext*)AH->formatData;
357     lclTocEntry*        tctx = (lclTocEntry*)te->formatData;
358
359     tctx->dataPos = _getFilePos(AH, ctx);
360
361     _WriteByte(AH, BLK_BLOBS); /* Block type */
362     WriteInt(AH, te->id); /* For sanity check */
363
364 }
365
366 /*
367  * Called by the archiver when the dumper calls StartBlob.
368  *
369  * Mandatory.
370  *
371  * Must save the passed OID for retrieval at restore-time.
372  */
373 static void     _StartBlob(ArchiveHandle* AH, TocEntry* te, int oid)
374 {
375     if (oid == 0) 
376         die_horribly(AH, "%s: illegal OID for BLOB (%d)\n", progname, oid);
377
378     WriteInt(AH, oid);
379     _StartDataCompressor(AH, te);
380 }
381
382 /*
383  * Called by the archiver when the dumper calls EndBlob.
384  *
385  * Optional.
386  *
387  */
388 static void     _EndBlob(ArchiveHandle* AH, TocEntry* te, int oid)
389 {
390     _EndDataCompressor(AH, te);
391 }
392
393 /*
394  * Called by the archiver when finishing saving all BLOB DATA. 
395  *
396  * Optional.
397  *
398  */
399 static void     _EndBlobs(ArchiveHandle* AH, TocEntry* te)
400 {
401         /* Write out a fake zero OID to mark end-of-blobs. */
402     WriteInt(AH, 0);
403 }
404
405 /*
406  * Print data for a gievn TOC entry
407 */
408 static void     _PrintTocData(ArchiveHandle* AH, TocEntry* te, RestoreOptions *ropt)
409 {
410     lclContext*         ctx = (lclContext*)AH->formatData;
411     int                 id;
412     lclTocEntry*        tctx = (lclTocEntry*) te->formatData;
413     int                 blkType;
414     int                 found = 0;
415
416     if (tctx->dataPos == 0) 
417                 return;
418
419     if (!ctx->hasSeek || tctx->dataPos < 0) {
420
421         /* Skip over unnecessary blocks until we get the one we want. */
422
423         found = 0;
424
425         _readBlockHeader(AH, &blkType, &id);
426
427         while (id != te->id) {
428
429             if ( (TocIDRequired(AH, id, ropt) & 2) != 0)
430                         die_horribly(AH, "%s: Dumping a specific TOC data block out of order is not supported"
431                                                         " without on this input stream (fseek required)\n", progname);
432
433             switch (blkType) {
434
435                         case BLK_DATA:
436
437                                 _skipData(AH);
438                                 break;
439
440                         case BLK_BLOBS:
441
442                                 _skipBlobs(AH);
443                                 break;
444
445                         default: /* Always have a default */
446
447                                 die_horribly(AH, "%s: unrecognized data block type while searching archive %d\n", 
448                                                                 progname, blkType);
449                                 break;
450             }
451
452             _readBlockHeader(AH, &blkType, &id);
453
454         }
455
456     } else {
457
458         /* Grab it */
459
460                 if (fseek(AH->FH, tctx->dataPos, SEEK_SET) != 0)
461                         die_horribly(AH, "%s: error %d in file seek\n",progname, errno);
462
463                 _readBlockHeader(AH, &blkType, &id);
464
465     }
466
467     /* Are we sane? */
468     if (id != te->id)
469                 die_horribly(AH, "%s: Found unexpected block ID (%d) when reading data - expected %d\n",
470                                                 progname, id, te->id);
471
472     switch (blkType) {
473
474                 case BLK_DATA:
475
476                         _PrintData(AH);
477                         break;
478
479                 case BLK_BLOBS:
480
481                         if (!AH->connection)
482                                 die_horribly(AH, "%s: BLOBs can not be loaded without a database connection\n", progname);
483
484                         _LoadBlobs(AH);
485                         break;
486
487                 default: /* Always have a default */
488
489                         die_horribly(AH, "%s: unrecognized data block type %d while restoring archive\n", 
490                                                         progname, blkType);
491                         break;
492     }
493
494     ahprintf(AH, "\n\n");
495 }
496
497 /*
498  * Print data from current file position.
499 */
500 static void     _PrintData(ArchiveHandle* AH)
501 {
502     lclContext* ctx = (lclContext*)AH->formatData;
503     z_streamp   zp = ctx->zp;
504     int         blkLen;
505     char*       in = ctx->zlibIn;
506     int         cnt;
507 #ifdef HAVE_LIBZ
508     int         res;
509     char*       out = ctx->zlibOut;
510 #endif
511
512 #ifdef HAVE_LIBZ
513
514     res = Z_OK;
515
516     if (AH->compression != 0) {
517                 zp->zalloc = Z_NULL;
518                 zp->zfree = Z_NULL;
519                 zp->opaque = Z_NULL;
520
521                 if (inflateInit(zp) != Z_OK)
522                         die_horribly(AH, "%s: could not initialize compression library - %s\n", progname, zp->msg);
523     }
524
525 #endif
526
527     blkLen = ReadInt(AH);
528     while (blkLen != 0) {
529                 if (blkLen+1 > ctx->inSize) {
530                         free(ctx->zlibIn);
531                         ctx->zlibIn = NULL;
532                         ctx->zlibIn = (char*)malloc(blkLen+1);
533                         if (!ctx->zlibIn)
534                                 die_horribly(AH, "%s: failed to allocate decompression buffer\n", progname);
535
536                         ctx->inSize = blkLen+1;
537                         in = ctx->zlibIn;
538                 }
539
540                 cnt = fread(in, 1, blkLen, AH->FH);
541                 if (cnt != blkLen) 
542                         die_horribly(AH, "%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);
543
544                 ctx->filePos += blkLen;
545
546                 zp->next_in = in;
547                 zp->avail_in = blkLen;
548
549 #ifdef HAVE_LIBZ
550
551                 if (AH->compression != 0) {
552
553                         while (zp->avail_in != 0) {
554                                 zp->next_out = out;
555                                 zp->avail_out = zlibOutSize;
556                                 res = inflate(zp, 0);
557                                 if (res != Z_OK && res != Z_STREAM_END)
558                                         die_horribly(AH, "%s: unable to uncompress data - %s\n", progname, zp->msg);
559
560                                 out[zlibOutSize - zp->avail_out] = '\0';
561                                 ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
562                         }
563                 } else {
564 #endif
565                         in[zp->avail_in] = '\0';
566                         ahwrite(in, 1, zp->avail_in, AH);
567                         zp->avail_in = 0;
568
569 #ifdef HAVE_LIBZ
570                 }
571 #endif
572
573                 blkLen = ReadInt(AH);
574
575     }
576
577 #ifdef HAVE_LIBZ
578     if (AH->compression != 0) 
579     {
580                 zp->next_in = NULL;
581                 zp->avail_in = 0;
582                 while (res != Z_STREAM_END) {
583                         zp->next_out = out;
584                         zp->avail_out = zlibOutSize;
585                         res = inflate(zp, 0);
586                         if (res != Z_OK && res != Z_STREAM_END)
587                                 die_horribly(AH, "%s: unable to uncompress data - %s\n", progname, zp->msg);
588
589                         out[zlibOutSize - zp->avail_out] = '\0';
590                         ahwrite(out, 1, zlibOutSize - zp->avail_out, AH);
591                 }
592     }
593 #endif
594
595 }
596
597 static void     _LoadBlobs(ArchiveHandle* AH)
598 {
599     int         oid;
600
601         StartRestoreBlobs(AH);
602
603     oid = ReadInt(AH);
604     while(oid != 0)
605     {
606                 StartRestoreBlob(AH, oid);
607                 _PrintData(AH);
608                 EndRestoreBlob(AH, oid);
609                 oid = ReadInt(AH);
610     }
611
612         EndRestoreBlobs(AH);
613
614 }
615
616 /*
617  * Skip the BLOBs from the current file position.
618  * BLOBS are written sequentially as data blocks (see below).
619  * Each BLOB is preceded by it's original OID. 
620  * A zero OID indicated the end of the BLOBS
621  */
622 static void     _skipBlobs(ArchiveHandle* AH)
623 {
624     int         oid;
625
626     oid = ReadInt(AH);
627     while(oid != 0)
628     {
629                 _skipData(AH);
630                 oid = ReadInt(AH);
631     }
632 }
633
634 /*
635  * Skip data from current file position.
636  * Data blocks are formatted as an integer length, followed by data. 
637  * A zero length denoted the end of the block.
638 */
639 static void     _skipData(ArchiveHandle* AH)
640 {
641     lclContext* ctx = (lclContext*)AH->formatData;
642     int         blkLen;
643     char*       in = ctx->zlibIn;
644     int         cnt;
645
646     blkLen = ReadInt(AH);
647     while (blkLen != 0) {
648         if (blkLen > ctx->inSize) {
649             free(ctx->zlibIn);
650             ctx->zlibIn = (char*)malloc(blkLen);
651             ctx->inSize = blkLen;
652             in = ctx->zlibIn;
653         }
654         cnt = fread(in, 1, blkLen, AH->FH);
655         if (cnt != blkLen) 
656             die_horribly(AH, "%s: could not read data block - expected %d, got %d\n", progname, blkLen, cnt);
657
658         ctx->filePos += blkLen;
659
660         blkLen = ReadInt(AH);
661     }
662
663 }
664
665 /*
666  * Write a byte of data to the archive.
667  *
668  * Mandatory.
669  *
670  * Called by the archiver to do integer & byte output to the archive.
671  * These routines are only used to read & write headers & TOC.
672  *
673  */
674 static int      _WriteByte(ArchiveHandle* AH, const int i)
675 {
676     lclContext*         ctx = (lclContext*)AH->formatData;
677     int                 res;
678
679     res = fputc(i, AH->FH);
680     if (res != EOF) {
681                 ctx->filePos += 1;
682         } else {
683                 die_horribly(AH, "%s: could not write byte./n",progname);
684     }
685     return res;
686 }
687
688 /*
689  * Read a byte of data from the archive.
690  *
691  * Mandatory
692  *
693  * Called by the archiver to read bytes & integers from the archive.
694  * These routines are only used to read & write headers & TOC.
695  *
696  */
697 static int      _ReadByte(ArchiveHandle* AH)
698 {
699     lclContext*         ctx = (lclContext*)AH->formatData;
700     int                 res;
701
702     res = fgetc(AH->FH);
703     if (res != EOF) {
704                 ctx->filePos += 1;
705     }
706     return res;
707 }
708
709 /*
710  * Write a buffer of data to the archive.
711  *
712  * Mandatory.
713  *
714  * Called by the archiver to write a block of bytes to the archive.
715  * These routines are only used to read & write headers & TOC.
716  *
717  */
718 static int      _WriteBuf(ArchiveHandle* AH, const void* buf, int len)
719 {
720     lclContext*         ctx = (lclContext*)AH->formatData;
721     int                 res;
722     res = fwrite(buf, 1, len, AH->FH);
723
724         if (res != len)
725                 die_horribly(AH, "%s: write error in _WriteBuf (%d != %d)\n", progname, res, len);
726
727     ctx->filePos += res;
728     return res;
729 }
730
731 /*
732  * Read a block of bytes from the archive.
733  * 
734  * Mandatory.
735  * 
736  * Called by the archiver to read a block of bytes from the archive
737  * These routines are only used to read & write headers & TOC.
738  *
739  */
740 static int      _ReadBuf(ArchiveHandle* AH, void* buf, int len)
741 {
742     lclContext*         ctx = (lclContext*)AH->formatData;
743     int                         res;
744     res = fread(buf, 1, len, AH->FH);
745     ctx->filePos += res;
746     return res;
747 }
748
749 /*
750  * Close the archive.
751  *
752  * Mandatory.
753  *
754  * When writing the archive, this is the routine that actually starts 
755  * the process of saving it to files. No data should be written prior 
756  * to this point, since the user could sort the TOC after creating it.
757  * 
758  * If an archive is to be written, this toutine must call:
759  *              WriteHead                       to save the archive header
760  *              WriteToc                        to save the TOC entries
761  *              WriteDataChunks         to save all DATA & BLOBs.
762  *
763  */
764 static void     _CloseArchive(ArchiveHandle* AH)
765 {
766     lclContext*         ctx = (lclContext*)AH->formatData;
767     int                 tpos;
768
769     if (AH->mode == archModeWrite) {
770                 WriteHead(AH);
771                 tpos = ftell(AH->FH);
772                 WriteToc(AH);
773                 ctx->dataStart = _getFilePos(AH, ctx);
774                 WriteDataChunks(AH);
775                 /* This is not an essential operation - it is really only
776              * needed if we expect to be doing seeks to read the data back
777                  * - it may be ok to just use the existing self-consistent block
778                  * formatting.
779              */
780                 if (ctx->hasSeek) {
781                         fseek(AH->FH, tpos, SEEK_SET);
782                         WriteToc(AH);
783                 }
784     }
785
786     if (fclose(AH->FH) != 0)
787                 die_horribly(AH, "%s: could not close archive file\n",progname);
788
789     AH->FH = NULL; 
790 }
791
792 /*--------------------------------------------------
793  * END OF FORMAT CALLBACKS
794  *--------------------------------------------------
795  */
796
797 /*
798  * Get the current position in the archive file.
799  */
800 static int      _getFilePos(ArchiveHandle* AH, lclContext* ctx) 
801 {
802     int         pos;
803     if (ctx->hasSeek) {
804                 pos = ftell(AH->FH);
805                 if (pos != ctx->filePos) {
806                         fprintf(stderr, "Warning: ftell mismatch with filePos\n");
807                 }
808     } else {
809                 pos = ctx->filePos;
810     }
811     return pos;
812 }
813
814 /*
815  * Read a data block header. The format changed in V1.3, so we 
816  * put the code here for simplicity.
817  */
818 static void _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
819 {
820     if (AH->version < K_VERS_1_3)
821                 *type = BLK_DATA;
822     else
823                 *type = _ReadByte(AH);;
824
825     *id = ReadInt(AH);
826 }
827
828 /*
829  * If zlib is available, then startit up. This is called from
830  * StartData & StartBlob. The buffers are setup in the Init routine.
831  *
832  */
833 static void     _StartDataCompressor(ArchiveHandle* AH, TocEntry* te)
834 {
835     lclContext*         ctx = (lclContext*)AH->formatData;
836     z_streamp           zp = ctx->zp;
837
838 #ifdef HAVE_LIBZ
839
840     if (AH->compression < 0 || AH->compression > 9) {
841                 AH->compression = Z_DEFAULT_COMPRESSION;
842     }
843
844     if (AH->compression != 0) {
845         zp->zalloc = Z_NULL;
846         zp->zfree = Z_NULL;
847         zp->opaque = Z_NULL;
848
849         if (deflateInit(zp, AH->compression) != Z_OK)
850             die_horribly(AH, "%s: could not initialize compression library - %s\n",progname, zp->msg);
851     }
852
853 #else
854
855     AH->compression = 0;
856
857 #endif
858
859     /* Just be paranoid - maybe End is called after Start, with no Write */
860     zp->next_out = ctx->zlibOut;
861     zp->avail_out = zlibOutSize;
862 }
863
864 /*
865  * Send compressed data to the output stream (via ahwrite).
866  * Each data chunk is preceded by it's length.
867  * In the case of Z0, or no zlib, just write the raw data.
868  *
869  */
870 static int      _DoDeflate(ArchiveHandle* AH, lclContext* ctx, int flush) 
871 {
872     z_streamp   zp = ctx->zp;
873
874 #ifdef HAVE_LIBZ
875     char*       out = ctx->zlibOut;
876     int         res = Z_OK;
877
878     if (AH->compression != 0) 
879     {
880                 res = deflate(zp, flush);
881                 if (res == Z_STREAM_ERROR)
882                         die_horribly(AH, "%s: could not compress data - %s\n",progname, zp->msg);
883
884                 if      (      ( (flush == Z_FINISH) && (zp->avail_out < zlibOutSize) )
885                         || (zp->avail_out == 0) 
886                         || (zp->avail_in != 0)
887                         ) 
888                 {
889                         /*
890                          * Extra paranoia: avoid zero-length chunks since a zero 
891                          * length chunk is the EOF marker. This should never happen
892                      * but...
893                          */
894                         if (zp->avail_out < zlibOutSize) {
895                                 /* printf("Wrote %d byte deflated chunk\n", zlibOutSize - zp->avail_out); */
896                                 WriteInt(AH, zlibOutSize - zp->avail_out);
897                                 if (fwrite(out, 1, zlibOutSize - zp->avail_out, AH->FH) != (zlibOutSize - zp->avail_out))
898                                         die_horribly(AH, "%s: could not write compressed chunk\n",progname);
899                                 ctx->filePos += zlibOutSize - zp->avail_out;
900                         }
901                         zp->next_out = out;
902                         zp->avail_out = zlibOutSize;
903                 }
904     } else {
905 #endif
906                 if (zp->avail_in > 0)
907                 {
908                         WriteInt(AH, zp->avail_in);
909                         if (fwrite(zp->next_in, 1, zp->avail_in, AH->FH) != zp->avail_in)
910                                 die_horribly(AH, "%s: could not write uncompressed chunk\n", progname);
911                         ctx->filePos += zp->avail_in;
912                         zp->avail_in = 0;
913                 } else {
914 #ifdef HAVE_LIBZ
915                         if (flush == Z_FINISH)
916                                 res = Z_STREAM_END;
917 #endif
918                 }
919
920
921 #ifdef HAVE_LIBZ
922     }
923
924     return res;
925 #else
926     return 1;
927 #endif
928
929 }
930
931 /*
932  * Terminate zlib context and flush it's buffers. If no zlib 
933  * then just return.
934  *
935  */
936 static void     _EndDataCompressor(ArchiveHandle* AH, TocEntry* te)
937 {
938
939 #ifdef HAVE_LIBZ
940     lclContext*         ctx = (lclContext*)AH->formatData;
941     z_streamp           zp = ctx->zp;
942     int                 res;
943
944     if (AH->compression != 0)
945     {
946                 zp->next_in = NULL;
947                 zp->avail_in = 0;
948
949                 do {    
950                         /* printf("Ending data output\n"); */
951                         res = _DoDeflate(AH, ctx, Z_FINISH);
952                 } while (res != Z_STREAM_END);
953
954                 if (deflateEnd(zp) != Z_OK)
955                         die_horribly(AH, "%s: error closing compression stream - %s\n", progname, zp->msg);
956         }
957 #endif
958
959         /* Send the end marker */
960         WriteInt(AH, 0);
961 }
962
963