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