text: /*-------------------------------------------------------------------------
   *
   * pg_backup_files.c
   *
   *	This file is copied from the 'custom' format file, but dumps data into
   *	separate files, and the TOC into the 'main' file.
   *
   *	IT IS FOR DEMONSTRATION PURPOSES ONLY.
   *
   *	(and could probably be used as a basis for writing a tar file)
   *
   *	See the headers to pg_restore for more details.
   *
   * Copyright (c) 2000, Philip Warner
   *		Rights are granted to use this software in any way so long
   *		as this notice is not removed.
   *
   *	The author is not responsible for loss or damages that may
   *	result from it's use.
   *
   *
   * IDENTIFICATION
   *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_files.c,v 1.29.2.2 2007/08/06 01:38:24 tgl Exp $
   *
   *-------------------------------------------------------------------------
   */
  
  #include "pg_backup_archiver.h"
  
  static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
  static void _StartData(ArchiveHandle *AH, TocEntry *te);
  static size_t _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
  static void _EndData(ArchiveHandle *AH, TocEntry *te);
  static int	_WriteByte(ArchiveHandle *AH, const int i);
  static int	_ReadByte(ArchiveHandle *);
  static size_t _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
  static size_t _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
  static void _CloseArchive(ArchiveHandle *AH);
  static void _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
  static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
  static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
  static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
  
  static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
  static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
  static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
  static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
  
  #define K_STD_BUF_SIZE 1024
  
  typedef struct
  {
  	int			hasSeek;
  	pgoff_t		filePos;
  	FILE	   *blobToc;
  } lclContext;
  
  typedef struct
  {
  #ifdef HAVE_LIBZ
  	gzFile	   *FH;
  #else
  	FILE	   *FH;
  #endif
  	char	   *filename;
  } lclTocEntry;
  
  static const char *modulename = gettext_noop("file archiver");
  static void _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt);
  static void _getBlobTocEntry(ArchiveHandle *AH, Oid *oid, char *fname);
  
  /*
   *	Initializer
   */
  void
  InitArchiveFmt_Files(ArchiveHandle *AH)
  {
  	lclContext *ctx;
  
  	/* Assuming static functions, this can be copied for each format. */
  	AH->ArchiveEntryPtr = _ArchiveEntry;
  	AH->StartDataPtr = _StartData;
  	AH->WriteDataPtr = _WriteData;
  	AH->EndDataPtr = _EndData;
  	AH->WriteBytePtr = _WriteByte;
  	AH->ReadBytePtr = _ReadByte;
  	AH->WriteBufPtr = _WriteBuf;
  	AH->ReadBufPtr = _ReadBuf;
  	AH->ClosePtr = _CloseArchive;
  	AH->PrintTocDataPtr = _PrintTocData;
  	AH->ReadExtraTocPtr = _ReadExtraToc;
  	AH->WriteExtraTocPtr = _WriteExtraToc;
  	AH->PrintExtraTocPtr = _PrintExtraToc;
  
  	AH->StartBlobsPtr = _StartBlobs;
  	AH->StartBlobPtr = _StartBlob;
  	AH->EndBlobPtr = _EndBlob;
  	AH->EndBlobsPtr = _EndBlobs;
  
  	/*
  	 * Set up some special context used in compressing data.
  	 */
  	ctx = (lclContext *) calloc(1, sizeof(lclContext));
  	AH->formatData = (void *) ctx;
  	ctx->filePos = 0;
  
  	/* Initialize LO buffering */
  	AH->lo_buf_size = LOBBUFSIZE;
  	AH->lo_buf = (void *) malloc(LOBBUFSIZE);
  	if (AH->lo_buf == NULL)
  		die_horribly(AH, modulename, "out of memory\n");
  
  	/*
  	 * Now open the TOC file
  	 */
  	if (AH->mode == archModeWrite)
  	{
  
  		write_msg(modulename, "WARNING:\n"
  				  "  This format is for demonstration purposes; it is not intended for\n"
  				  "  normal use. Files will be written in the current working directory.\n");
  
  		if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
  			AH->FH = fopen(AH->fSpec, PG_BINARY_W);
  		else
  			AH->FH = stdout;
  
  		if (AH->FH == NULL)
  			die_horribly(NULL, modulename, "could not open output file: %s\n", strerror(errno));
  
  		ctx->hasSeek = checkSeek(AH->FH);
  
  		if (AH->compression < 0 || AH->compression > 9)
  			AH->compression = Z_DEFAULT_COMPRESSION;
  
  
  	}
  	else
  	{							/* Read Mode */
  
  		if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
  			AH->FH = fopen(AH->fSpec, PG_BINARY_R);
  		else
  			AH->FH = stdin;
  
  		if (AH->FH == NULL)
  			die_horribly(NULL, modulename, "could not open input file: %s\n", strerror(errno));
  
  		ctx->hasSeek = checkSeek(AH->FH);
  
  		ReadHead(AH);
  		ReadToc(AH);
  		/* Nothing else in the file... */
  		if (fclose(AH->FH) != 0)
  			die_horribly(AH, modulename, "could not close TOC file: %s\n", strerror(errno));
  	}
  }
  
  /*
   * - Start a new TOC entry
   *	 Setup the output file name.
   */
  static void
  _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
  {
  	lclTocEntry *ctx;
  	char		fn[K_STD_BUF_SIZE];
  
  	ctx = (lclTocEntry *) calloc(1, sizeof(lclTocEntry));
  	if (te->dataDumper)
  	{
  #ifdef HAVE_LIBZ
  		if (AH->compression == 0)
  			sprintf(fn, "%d.dat", te->dumpId);
  		else
  			sprintf(fn, "%d.dat.gz", te->dumpId);
  #else
  		sprintf(fn, "%d.dat", te->dumpId);
  #endif
  		ctx->filename = strdup(fn);
  	}
  	else
  	{
  		ctx->filename = NULL;
  		ctx->FH = NULL;
  	}
  	te->formatData = (void *) ctx;
  }
  
  static void
  _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
  {
  	lclTocEntry *ctx = (lclTocEntry *) te->formatData;
  
  	if (ctx->filename)
  		WriteStr(AH, ctx->filename);
  	else
  		WriteStr(AH, "");
  }
  
  static void
  _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
  {
  	lclTocEntry *ctx = (lclTocEntry *) te->formatData;
  
  	if (ctx == NULL)
  	{
  		ctx = (lclTocEntry *) calloc(1, sizeof(lclTocEntry));
  		te->formatData = (void *) ctx;
  	}
  
  	ctx->filename = ReadStr(AH);
  	if (strlen(ctx->filename) == 0)
  	{
  		free(ctx->filename);
  		ctx->filename = NULL;
  	}
  	ctx->FH = NULL;
  }
  
  static void
  _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
  {
  	lclTocEntry *ctx = (lclTocEntry *) te->formatData;
  
  	if (AH->public.verbose)
  		ahprintf(AH, "-- File: %s\n", ctx->filename);
  }
  
  static void
  _StartData(ArchiveHandle *AH, TocEntry *te)
  {
  	lclTocEntry *tctx = (lclTocEntry *) te->formatData;
  	char		fmode[10];
  
  	sprintf(fmode, "wb%d", AH->compression);
  
  #ifdef HAVE_LIBZ
  	tctx->FH = gzopen(tctx->filename, fmode);
  #else
  	tctx->FH = fopen(tctx->filename, PG_BINARY_W);
  #endif
  
  	if (tctx->FH == NULL)
  		die_horribly(AH, modulename, "could not open output file: %s\n", strerror(errno));
  }
  
  static size_t
  _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
  {
  	lclTocEntry *tctx = (lclTocEntry *) AH->currToc->formatData;
  
  	GZWRITE((void *) data, 1, dLen, tctx->FH);
  
  	return dLen;
  }
  
  static void
  _EndData(ArchiveHandle *AH, TocEntry *te)
  {
  	lclTocEntry *tctx = (lclTocEntry *) te->formatData;
  
  	/* Close the file */
  	if (GZCLOSE(tctx->FH) != 0)
  		die_horribly(AH, modulename, "could not close data file\n");
  
  	tctx->FH = NULL;
  }
  
  /*
   * Print data for a given file
   */
  static void
  _PrintFileData(ArchiveHandle *AH, char *filename, RestoreOptions *ropt)
  {
  	char		buf[4096];
  	size_t		cnt;
  
  	if (!filename)
  		return;
  
  #ifdef HAVE_LIBZ
  	AH->FH = gzopen(filename, "rb");
  #else
  	AH->FH = fopen(filename, PG_BINARY_R);
  #endif
  
  	if (AH->FH == NULL)
  		die_horribly(AH, modulename, "could not open input file: %s\n", strerror(errno));
  
  	while ((cnt = GZREAD(buf, 1, 4095, AH->FH)) > 0)
  	{
  		buf[cnt] = '\0';
  		ahwrite(buf, 1, cnt, AH);
  	}
  
  	if (GZCLOSE(AH->FH) != 0)
  		die_horribly(AH, modulename, "could not close data file after reading\n");
  }
  
  
  /*
   * Print data for a given TOC entry
  */
  static void
  _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
  {
  	lclTocEntry *tctx = (lclTocEntry *) te->formatData;
  
  	if (!tctx->filename)
  		return;
  
  	if (strcmp(te->desc, "BLOBS") == 0)
  		_LoadBlobs(AH, ropt);
  	else
  		_PrintFileData(AH, tctx->filename, ropt);
  }
  
  static void
  _getBlobTocEntry(ArchiveHandle *AH, Oid *oid, char fname[K_STD_BUF_SIZE])
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  	char		blobTe[K_STD_BUF_SIZE];
  	size_t		fpos;
  	size_t		eos;
  
  	if (fgets(&blobTe[0], K_STD_BUF_SIZE - 1, ctx->blobToc) != NULL)
  	{
  		*oid = atooid(blobTe);
  
  		fpos = strcspn(blobTe, " ");
  
  		strlcpy(fname, &blobTe[fpos + 1], K_STD_BUF_SIZE);
  
  		eos = strlen(fname) - 1;
  
  		if (fname[eos] == '\n')
  			fname[eos] = '\0';
  	}
  	else
  	{
  		*oid = 0;
  		fname[0] = '\0';
  	}
  }
  
  static void
  _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt)
  {
  	Oid			oid;
  	lclContext *ctx = (lclContext *) AH->formatData;
  	char		fname[K_STD_BUF_SIZE];
  
  	StartRestoreBlobs(AH);
  
  	ctx->blobToc = fopen("blobs.toc", PG_BINARY_R);
  
  	if (ctx->blobToc == NULL)
  		die_horribly(AH, modulename, "could not open large object TOC for input: %s\n", strerror(errno));
  
  	_getBlobTocEntry(AH, &oid, fname);
  
  	while (oid != 0)
  	{
  		StartRestoreBlob(AH, oid);
  		_PrintFileData(AH, fname, ropt);
  		EndRestoreBlob(AH, oid);
  		_getBlobTocEntry(AH, &oid, fname);
  	}
  
  	if (fclose(ctx->blobToc) != 0)
  		die_horribly(AH, modulename, "could not close large object TOC file: %s\n", strerror(errno));
  
  	EndRestoreBlobs(AH);
  }
  
  
  static int
  _WriteByte(ArchiveHandle *AH, const int i)
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  
  	if (fputc(i, AH->FH) == EOF)
  		die_horribly(AH, modulename, "could not write byte\n");
  
  	ctx->filePos += 1;
  
  	return 1;
  }
  
  static int
  _ReadByte(ArchiveHandle *AH)
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  	int			res;
  
  	res = getc(AH->FH);
  	if (res == EOF)
  		die_horribly(AH, modulename, "unexpected end of file\n");
  	ctx->filePos += 1;
  	return res;
  }
  
  static size_t
  _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  	size_t		res;
  
  	res = fwrite(buf, 1, len, AH->FH);
  	if (res != len)
  		die_horribly(AH, modulename, "could not write to output file: %s\n", strerror(errno));
  
  	ctx->filePos += res;
  	return res;
  }
  
  static size_t
  _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  	size_t		res;
  
  	res = fread(buf, 1, len, AH->FH);
  	ctx->filePos += res;
  	return res;
  }
  
  static void
  _CloseArchive(ArchiveHandle *AH)
  {
  	if (AH->mode == archModeWrite)
  	{
  		WriteHead(AH);
  		WriteToc(AH);
  		if (fclose(AH->FH) != 0)
  			die_horribly(AH, modulename, "could not close TOC file: %s\n", strerror(errno));
  		WriteDataChunks(AH);
  	}
  
  	AH->FH = NULL;
  }
  
  
  
  /*
   * BLOB support
   */
  
  /*
   * Called by the archiver when starting to save all BLOB DATA (not schema).
   * This routine should save whatever format-specific information is needed
   * to read the BLOBs back into memory.
   *
   * It is called just prior to the dumper's DataDumper routine.
   *
   * Optional, but strongly recommended.
   */
  static void
  _StartBlobs(ArchiveHandle *AH, TocEntry *te)
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  	char		fname[K_STD_BUF_SIZE];
  
  	sprintf(fname, "blobs.toc");
  	ctx->blobToc = fopen(fname, PG_BINARY_W);
  
  	if (ctx->blobToc == NULL)
  		die_horribly(AH, modulename,
  		"could not open large object TOC for output: %s\n", strerror(errno));
  }
  
  /*
   * Called by the archiver when the dumper calls StartBlob.
   *
   * Mandatory.
   *
   * Must save the passed OID for retrieval at restore-time.
   */
  static void
  _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  	lclTocEntry *tctx = (lclTocEntry *) te->formatData;
  	char		fmode[10];
  	char		fname[255];
  	char	   *sfx;
  
  	if (oid == 0)
  		die_horribly(AH, modulename, "invalid OID for large object (%u)\n", oid);
  
  	if (AH->compression != 0)
  		sfx = ".gz";
  	else
  		sfx = "";
  
  	sprintf(fmode, "wb%d", AH->compression);
  	sprintf(fname, "blob_%u.dat%s", oid, sfx);
  
  	fprintf(ctx->blobToc, "%u %s\n", oid, fname);
  
  #ifdef HAVE_LIBZ
  	tctx->FH = gzopen(fname, fmode);
  #else
  	tctx->FH = fopen(fname, PG_BINARY_W);
  #endif
  
  	if (tctx->FH == NULL)
  		die_horribly(AH, modulename, "could not open large object file \"%s\" for input: %s\n",
  					 fname, strerror(errno));
  }
  
  /*
   * Called by the archiver when the dumper calls EndBlob.
   *
   * Optional.
   */
  static void
  _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
  {
  	lclTocEntry *tctx = (lclTocEntry *) te->formatData;
  
  	if (GZCLOSE(tctx->FH) != 0)
  		die_horribly(AH, modulename, "could not close large object file\n");
  }
  
  /*
   * Called by the archiver when finishing saving all BLOB DATA.
   *
   * Optional.
   */
  static void
  _EndBlobs(ArchiveHandle *AH, TocEntry *te)
  {
  	lclContext *ctx = (lclContext *) AH->formatData;
  
  	/* Write out a fake zero OID to mark end-of-blobs. */
  	/* WriteInt(AH, 0); */
  
  	if (fclose(ctx->blobToc) != 0)
  		die_horribly(AH, modulename, "could not close large object TOC file: %s\n", strerror(errno));
  }
  