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