/**
 **	$Header: /import/dev-vis/image/imtools/v2.0/imtools/src/RCS/imtools.c,v 1.9 92/12/03 01:20:38 nadeau Exp $
 **	Copyright (c) 1989-1992  San Diego Supercomputer Center (SDSC)
 **		San Diego, California, USA
 **
 **	Users and possessors of this source code are hereby granted a
 **	nonexclusive, royalty-free copyright and design patent license to
 **	use this code in individual software.  License is not granted for
 **	commercial resale, in whole or in part, without prior written
 **	permission from SDSC.  This source is provided "AS IS" without express
 **	or implied warranty of any kind.
 **
 **	For further information contact:
 **		E-Mail:		info@sds.sdsc.edu
 **
 **		Surface Mail:	Information Center
 **				San Diego Supercomputer Center
 **				P.O. Box 85608
 **				San Diego, CA  92138-5608
 **				(619) 534-5000
 **/

#define HEADER	"    $Header: /import/dev-vis/image/imtools/v2.0/imtools/src/RCS/imtools.c,v 1.9 92/12/03 01:20:38 nadeau Exp $"

/**
 **  FILE
 **	imtools.c	-  generic image tools code used by most tools
 **
 **  PROJECT
 **	IM		-  Image Manipulation Tools
 **
 **  DESCRIPTION
 **	imtools.c contains generic tool code used by most of the image tools.
 **
 **  PUBLIC CONTENTS
 **			d =defined constant
 **			f =function
 **			m =defined macro
 **			t =typedef/struct/union
 **			v =variable
 **			? =other
 **
 **	ImToolsProgram		v  the program's name
 **	ImToolsVerbose		v  be verbose?
 **	ImToolsBaseHelp		v  basic help info for most image tools
 **	ImToolsBaseOptions	v  basic option info for most image tools
 **
 **	ImToolsMergeOptions	f  merge option lists
 **	ImToolsMergeEquivs	f  merge equivalences lists
 **	ImToolsIsFormat		f  check if a name is an image file format name
 **
 **	ImToolsBuildFlagsTable	f  build a standard tools flags table
 **	ImToolsChangeTagEntry	f  change a tag table entry
 **
 **	ImToolsErrorHandler	f  standard error code handler
 **	ImToolsInfoHandler	f  standard info handler
 **	ImToolsOpen		f  open a file/stream & get its format
 **	ImToolsFileRead		f  read in an image file
 **	ImToolsFileWrite	f  write out an image file
 **
 **  PRIVATE CONTENTS
 **	none
 **
 **  HISTORY
 **	$Log:	imtools.c,v $
 **	Revision 1.9  92/12/03  01:20:38  nadeau
 **	Minor change to info handler.
 **	
 **	Revision 1.8  92/12/01  14:08:55  nadeau
 **	Fixed bogus debug message.
 **	
 **	Revision 1.7  92/11/04  13:59:11  groening
 **	changed *pFmt to **pFmt
 **	since ImFIleFormats type was changed
 **	
 **	Revision 1.6  92/10/19  14:23:55  groening
 **	added \n for read and write
 **	verbose statements for readabliilty
 **	
 **	Revision 1.5  92/10/12  16:09:32  vle
 **	Fixed -verbose flag option.
 **	
 **	Revision 1.4  92/09/23  13:40:37  vle
 **	Changed all "!= 0" checks in ImToolsBuildFlagsTable() to "> 0"
 **	
 **	Revision 1.3  92/08/31  17:16:22  vle
 **	Updated copyright notice.
 **	
 **	Revision 1.2  91/10/03  13:22:42  nadeau
 **	Changed 'interlace' to 'interleave'.
 **	
 **	Revision 1.1  91/10/03  13:20:06  nadeau
 **	Initial revision
 **	
 **/

#include "imtools.h"





/*
 *  GLOBALS
 *	ImToolsProgram	-  the program's name
 *	ImToolsVerbose	-  be verbose?
 *
 *  DESCRIPTION
 *	The tool's argv[0] is stored in ImToolsProgram and used in
 *	various error messages.
 *
 *	ImToolsVerbose toggles verbose output.
 */

public char   *ImToolsProgram = "imtool";
public boolean ImToolsVerbose = FALSE;





/*
 *  GLOBAL
 *	ImToolsBaseHelp	-  basic help info for most image tools
 *
 *  DESCRIPTION
 *	ImToolsBaseHelp is help information used by most of the image tools,
 *	in addition to their own tool-specific help.  This generic help
 *	information describes how to specify image file formats, and describes
 *	the standard image output options.
 */

public char *ImToolsBaseHelp =
"\n\
Image File Formats and Filenames:\n\
    By default, input and output file's image file formats are determined by\n\
    the file's magic number (input only), or the filename extension (like\n\
    .hdf for HDF files, or .pix for PIX files).\n\
\n\
    To override the default, explicit format names may precede the filename,\n\
    such as -hdf for an HDF file, or -pix for a PIX file.\n\
\n\
    A single dash ('-') for an input or output filename indicates stdin or\n\
    stdout.  When using stdin or stdout, an explicit format name is necessary.\n\
\n\
Output File Format Controls:\n\
    -outindex forces storage of the image as color indexes (pseudo-color).\n\
    -outrgb forces storage of the image as RGB color values (true-color).\n\
    The default is the best match to the image to be stored.\n\
\n\
    -outnchan selects the number of channels to store in the output image\n\
    (usually 1 or 3).  -outchandepth selects the number of bits per channel\n\
    (usually 8).  The default is the best match to the image to be stored.\n\
\n\
    -outclt forces storage of a CLT.  -outnoclt blocks storage of a CLT.\n\
    The default is to store a CLT if the image to be stored has one.\n\
\n\
    -outalpha forces storage of an alpha plane.  -outnoalpha blocks storage\n\
    of an alpha plane.  The default is to store an alpha plane if the image\n\
    has one.\n\
\n\
    -outcompress forces use of a specific image compression scheme:\n\
        none                    Do not compress\n\
        lzw                     Use Limpel-Ziv Welsh compression\n\
        mac, pb, or packbits    Use Macintosh PackBits compression\n\
        rle                     Use Run-Length Encoded compression\n\
    Not all formats support all schemes.  The default is the best or most\n\
    widly used compression scheme supported by the format.\n\
\n\
    -outinterleave forces the use of an RGB interleaving scheme:\n\
        none      Do not interleave             RGBRGBRGBRGBRGBRGB...\n\
        line      Use scanline interleaving     RRR..GGG..BBB..RRR..GGG..BBB...\n\
        plane     Use plane interleaving        RRRRRR...GGGGGG...BBBBBB...\n\
    Interleave only applies to RGB image storage.  Not all formats support all\n\
    schemes.  The default is the best or most widly used scheme supported by\n\
    the format.\n\
";





/*
 *  GLOBAL
 *	ImToolsBaseOptions	-  basic option info for most image tools
 *	ImToolsBaseEquivs	-  basic equiv info for most image tools
 *
 *  DESCRIPTION
 *	ImToolsBaseOptions are options used by most of the image tools.  The
 *	options include those to select output image file format variants.
 *
 *	ImToolsBaseEquivs are equivalent keywords for the base options used
 *	by most of the image tools.
 */

public ArgOption ImToolsBaseOptions[IMTOOLSNBASEOPTIONS] =
{
	{ "outindex", NULL, "Force output as a color indexed image",
	  ARGFFULLHELP, 0, 0, ARGTNONE },

	{ "outrgb", NULL, "Force output as an RGB image",
	  ARGFFULLHELP, 0, 0, ARGTNONE },

	{ "outnchan", "no_channels", "Force output of # of channels/pixel",
	  ARGFFULLHELP, 1, 1, ARGTINT },

	{ "outchandepth", "no_bits", "Force output of # of bits/channel/pixel",
	  ARGFFULLHELP, 1, 1, ARGTINT },

	{ "outclt", NULL, "Force output of a CLT with the image",
	  ARGFFULLHELP, 0, 0, ARGTNONE },

	{ "outnoclt", NULL, "Block output of a CLT with the image",
	  ARGFFULLHELP, 0, 0, ARGTNONE },

	{ "outalpha", NULL, "Force output of alpha plane with image",
	  ARGFFULLHELP, 0, 0, ARGTNONE },

	{ "outnoalpha", NULL, "Block output of alpha plane with image",
	  ARGFFULLHELP, 0, 0, ARGTNONE },

	{ "outinterleave", "inter_type", "Force output of interleaved RGB image",
	  ARGFFULLHELP, 1, 1, ARGTSTRING },

	{ "outcompress", "comp_type", "Force output of compress image",
	  ARGFFULLHELP, 1, 1, ARGTSTRING },
};

#if IMTOOLSNBASEEQUIVS == 0
public ArgEquiv ImToolsBaseEquivs[1];
#else
public ArgEquiv ImToolsBaseEquivs[IMTOOLSNBASEEQUIVS] =
{
};
#endif





/*
 *  FUNCTION
 *	ImToolsMergeOptions	-  merge option lists
 *	ImToolsMergeEquivs	-  merge equivalences lists
 *
 *  DESCRIPTION
 *	Typically used to merge the base Image Tools option and equivs lists
 *	with the tool's specific option and equivs lists.  Both functions
 *	create new lists consisting of the contents of the two source lists.
 *
 *	Note:  on an error, the routines exit the program in the standard way.
 */

public int				/* Returns # of options in list	*/
ImToolsMergeOptions( nOptions1, options1, nOptions2, options2, newoptions )
	register int nOptions1;		/* Length of options1		*/
	register ArgOption *options1;	/* 1st list of options		*/
	register int nOptions2;		/* Length of options2		*/
	register ArgOption *options2;	/* 2nd list of options		*/
	ArgOption **newoptions;		/* Returned new option list	*/
{
	register ArgOption *pOpt;	/* Option list pointer		*/
	int n;				/* Length of new option list	*/

	if ( nOptions1 == 0 )
	{
		*newoptions = options2;
		return ( nOptions2 );
	}
	if ( nOptions2 == 0 )
	{
		*newoptions = options1;
		return ( nOptions1 );
	}
	n = nOptions1 + nOptions2;
	if ( (*newoptions = pOpt = (ArgOption *)malloc( n *
		sizeof( ArgOption ) )) == NULL )
	{
		perror( ImToolsProgram );
		exit( 1 );
	}

	for ( ; nOptions1; nOptions1-- )
		*pOpt++ = *options1++;
	for ( ; nOptions2; nOptions2-- )
		*pOpt++ = *options2++;

	return ( n );
}

public int				/* Returns # of options in list	*/
ImToolsMergeEquivs( nEquivs1, equivs1, nEquivs2, equivs2, newequivs )
	register int nEquivs1;		/* Length of equivs1		*/
	register ArgEquiv *equivs1;	/* 1st list of equivs		*/
	register int nEquivs2;		/* Length of equivs2		*/
	register ArgEquiv *equivs2;	/* 2nd list of equivs		*/
	ArgEquiv **newequivs;		/* Returned new equiv list	*/
{
	register ArgEquiv *pEquiv;	/* Equiv list pointer		*/
	int n;				/* Length of new equiv list	*/

	if ( nEquivs1 == 0 )
	{
		*newequivs = equivs2;
		return ( nEquivs2 );
	}
	if ( nEquivs2 == 0 )
	{
		*newequivs = equivs1;
		return ( nEquivs1 );
	}
	n = nEquivs1 + nEquivs2;
	if ( (*newequivs = pEquiv = (ArgEquiv *)malloc( n *
		sizeof( ArgEquiv ) )) == NULL )
	{
		perror( ImToolsProgram );
		exit( 1 );
	}

	for ( ; nEquivs1; nEquivs1-- )
		*pEquiv++ = *equivs1++;
	for ( ; nEquivs2; nEquivs2-- )
		*pEquiv++ = *equivs2++;

	return ( n );
}





/*
 *  FUNCTION
 *	ImToolsIsFormat	-  check if a name is an image file format name
 *
 *  DESCRIPTION
 *	The image format table is scanned to find out if the given
 *	name is the name of a format.  If so, TRUE is returned.  Otherwise
 *	FALSE.
 */

public boolean				/* Returns TRUE or FALSE	*/
ImToolsIsFormat( name )
	char *name;			/* Name to look up		*/
{
	register ImFileFormat **pFmt;	/* Format list pointer		*/
	char        **pNames;		/* Format name list pointer	*/

	for ( pFmt = &ImFileFormats[0]; *pFmt; pFmt++ )
	{
		pNames = (*pFmt)->format_names;
		for ( ; *pNames; pNames++ )
		{
			if ( strcmp( *pNames, name ) == 0 )
				return ( TRUE );
		}
	}
	return ( FALSE );
}





/*
 *  FUNCTION
 *	ImToolsBuildFlagsTable	-  build a standard tools flags table
 *
 *  DESCRIPTION
 *	The standard tools flags table is built up with entries for the
 *	tool's name, error handling mechanism, and all of the output
 *	image control options (if any were given).
 *
 *	If the user entered nonsense arguments, error messages are output
 *	and the program exits in the standard way.
 */

public TagTable *			/* Returns new flags table	*/
ImToolsBuildFlagsTable( )
{
	TagTable *flagsTable;		/* New flags table		*/
	int i;				/* Flag/Counter			*/
	int itmp;			/* Temporary integer holder	*/
	char *tmp;			/* Temporary string holder	*/
	int (*handler)( );		/* Temporary handler func ptr	*/


	if ( (flagsTable = TagTableAlloc( )) == TAGTABLENULL )
	{
		TagPError( ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Program name.
	 */
	TagTableAppend( flagsTable,
		TagEntryAlloc( "program name", POINTER, &ImToolsProgram ) );


	/*
	 *  Error handling:  print to stderr.
	 */
	handler = ImToolsErrorHandler;
	TagTableAppend( flagsTable,
		TagEntryAlloc( "error handler", POINTER, &handler ) );


	/*
	 *  Verbosity handling:  print to stderr.
	 */
	if ( ArgQNOccur( "verbose" ) > 0 )
	{
		handler = ImToolsInfoHandler;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "info handler", POINTER, &handler ) );
	}



	/*
	 *  Output type forceing.  Check for conflicting options.
	 */
	i = 0;
	if ( ArgQNOccur( "outindex" ) > 0 )
	{
		i++;
		itmp = IMTYPEINDEX;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image type request", INT, &itmp ) );
	}
	if ( ArgQNOccur( "outrgb" ) > 0 )
	{
		i++;
		itmp = IMTYPERGB;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image type request", INT, &itmp ) );
	}
	if ( i > 1 )
	{
		fprintf( stderr, "%s:  Only one of -outindex or -outrgb may be given\n", ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Output CLT forces.  Check for conflicting options.
	 */
	i = 0;
	if ( ArgQNOccur( "outclt" ) > 0 )
	{
		i++;
		itmp = IMCLTYES;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image clt request", INT, &itmp ) );
	}
	if ( ArgQNOccur( "outnoclt" ) > 0 )
	{
		i++;
		itmp = IMCLTNO;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image clt request", INT, &itmp ) );
	}
	if ( i > 1 )
	{
		fprintf( stderr, "%s:  Only one of -outclt or -outnoclt may be given\n", ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Output alpha plane forces.  Check for conflicting options.
	 */
	i = 0;
	if ( ArgQNOccur( "outalpha" ) > 0 )
	{
		i++;
		itmp = IMALPHAYES;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image alpha request", INT, &itmp ) );
	}
	if ( ArgQNOccur( "outnoalpha" ) > 0 )
	{
		i++;
		itmp = IMALPHANO;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image alpha request", INT, &itmp ) );
	}
	if ( i > 1 )
	{
		fprintf( stderr, "%s:  Only one of -outalpha or -outnoalpha may be given\n", ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Output channel number forcing.
	 */
	if ( ArgQNOccur( "outnchan" ) > 0 )
	{
		itmp = ArgQValue( "outnchan", 0, 0 )->arg_i;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image channel number request", INT, &itmp ) );
	}


	/*
	 *  Output channel depth forcing.
	 */
	if ( ArgQNOccur( "outchandepth" ) > 0 )
	{
		itmp = ArgQValue( "outchandepth", 0, 0 )->arg_i;
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image channel depth request", INT, &itmp ) );
	}


	/*
	 *  Output interleaving forcing.  Check for good scheme name.
	 */
	if ( ArgQNOccur( "outinterleave" ) > 0 )
	{
		tmp = ArgQValue( "outinterleave", 0, 0 )->arg_s;
		i = strlen( tmp );
		if ( strncmp( tmp, "none", i ) == 0 )
			itmp = IMINTERNONE;
		else if ( strncmp( tmp, "line", i ) == 0 )
			itmp = IMINTERLINE;
		else if ( strncmp( tmp, "plane", i ) == 0 )
			itmp = IMINTERPLANE;
		else
		{
			fprintf( stderr, "%s:  Unknown RGB interleave scheme.  Use one of:\n",
				ImToolsProgram );
			fprintf( stderr, "        'none'  Uninterleaved RGB (RGBRGBRGB...)\n" );
			fprintf( stderr, "        'line'  Scanline-interleaved RGB (RR..GG..BB..RR..GG..BB...)\n" );
			fprintf( stderr, "        'plane' Plane-interleaved RGB (RRR..GGG..BBB...)\n" );
			exit( 1 );
		}
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image interleave request", INT, &itmp ) );
	}


	/*
	 *  Output compression forcing.  Check for good scheme name.
	 */
	if ( ArgQNOccur( "outcompress" ) > 0 )
	{
		tmp = ArgQValue( "outcompress", 0, 0 )->arg_s;
		i = strlen( tmp );
		if ( strncmp( tmp, "none", i ) == 0 )
			itmp = IMCOMPNONE;
		else if ( strncmp( tmp, "rle", i ) == 0 )
			itmp = IMCOMPRLE;
		else if ( strncmp( tmp, "pb", i ) == 0 )
			itmp = IMCOMPPB;
		else if ( strncmp( tmp, "packbits", i ) == 0 )
			itmp = IMCOMPPB;
		else if ( strncmp( tmp, "mac", i ) == 0 )
			itmp = IMCOMPPB;
		else if ( strncmp( tmp, "lzw", i ) == 0 )
			itmp = IMCOMPLZW;
		else
		{
			fprintf( stderr, "%s:  Unknown image compression scheme.  Use one of:\n",
				ImToolsProgram );
			fprintf( stderr, "        'none'     Uncompressed\n" );
			fprintf( stderr, "        'lzw'      Limpel-Ziv & Welsh compressed\n" );
			fprintf( stderr, "        'mac'      Macintosh PackBits\n" );
			fprintf( stderr, "        'pb'       Macintosh PackBits (same as 'mac')\n" );
			fprintf( stderr, "        'packbits' Macintosh PackBits (same as 'mac')\n" );
			fprintf( stderr, "        'rle'      Run-length encoded\n" );
			exit( 1 );
		}
		TagTableAppend( flagsTable,
			TagEntryAlloc( "image compression request", INT, &itmp ) );
	}

	return ( flagsTable );
}





/*
 *  FUNCTION
 *	ImToolsChangeTagEntry	-  change a tag table entry
 *
 *  DESCRIPTION
 *	Used by the image tools primarily to change the "file name"
 *	entry in the flags table, this function replaces an entry in
 *	a table, or appends a new one if it isn't already there.
 */

public void				/* Returns nothing		*/
ImToolsChangeTagEntry( table, tag, newvalue )
	TagTable *table;		/* Table to change		*/
	char     *tag;			/* Tag whose value is to change	*/
	int      *newvalue;		/* Pointer to new value		*/
{
	TagEntry *oldEntry;		/* Old tag table entry		*/
	TagEntry *newEntry;		/* New tag table entry		*/
	int       n;			/* Tag entry location		*/


	/*
	 *  Make a new tag table entry.
	 */
	newEntry = TagEntryAlloc( tag, POINTER, &newvalue );


	/*
	 *  Replace an existing entry or append the new one.
	 */
	oldEntry = TagTableQDirect( table, tag, 0 );
	if ( oldEntry == TAGENTRYNULL )
	{
		/* No entry in the flags table yet.  Add one.		*/
		TagTableAppend( table, newEntry );
	}
	else
	{
		/* Replace an existing entry.				*/
		n = TagEntryQNthEntry( oldEntry );
		TagTableReplace( table, n, newEntry );
	}
}





/*
 *  FUNCTION
 *	ImToolsErrorHandler	-  standard error code handler
 *	ImToolsInfoHandler	-  standard info handler
 *
 *  DESCRIPTION
 *	Print the error message unless it is one we want to handle specially.
 */

public int				/* Returns status		*/
ImToolsErrorHandler( severity, imerrno, message )
	int severity;			/* Error severity		*/
	int imerrno;			/* Error number			*/
	char *message;			/* Message to display		*/
{
	switch ( imerrno )
	{
	case IMENOTPOSSIBLE:
	case IMEMANYVFB:
	case IMENOVFB:
		/* We'll handle these specially later.			*/
		return ( 0 );
	}

	fprintf( stderr, message );
	return ( 0 );
}

public int				/* Returns status		*/
ImToolsInfoHandler( program, filename, message )
	char *program;			/* Program name			*/
	char *filename;			/* File name			*/
	char *message;			/* Message to display		*/
{
	fprintf( stderr, "%s: %s: %s", program, filename, message );
	return ( 0 );
}





/*
 *  FUNCTION
 *	ImToolsOpen	-  open a file/stream & get its format
 *
 *  DESCRIPTION
 *	If the file name is a '-', use stdin or stdout, depending upon if
 *	we are reading or writing.  Check that a file format has been
 *	specified.
 *
 *	Otherwise open the file.  If a format has not been specified, get
 *	its format.
 */

public FILE *				/* Returns open file pointer	*/
ImToolsOpen( fileName, flags, format, actualFileName, actualFormat )
	char *fileName;			/* Name of file to open		*/
	char *flags;			/* Open flags			*/
	char *format;			/* File format to use (if any)	*/
	char *actualFileName;		/* Processed file name		*/
	char *actualFormat;		/* Processed file format	*/
{
	FILE *fp;			/* File pointer			*/

	if ( fileName[0] == '-' && fileName[1] == '\0' )
	{
		if ( flags[0] == 'r' )
		{
			strcpy( actualFileName, "stdin" );
			fp = stdin;
		}
		else
		{
			fileName = "stdout";
			fp = stdout;
		}
		if ( format == NULL )
		{
			fprintf( stderr, "%s: Explicit format selection needed when using %s\n", ImToolsProgram, fileName );
			exit( 1 );
		}
		strcpy( actualFormat, format );
		return ( fp );
	}

	/*
	 *  Open the file.
	 */
	strcpy( actualFileName, fileName );
	if ( (fp = fopen( fileName, flags )) == NULL )
	{
		fprintf( stderr, "%s: Cannot open '%s'\n", ImToolsProgram, fileName );
		exit( 1 );
	}


	/*
	 *  Figure out what format we've got, if not given explicitly.
	 */
	if ( (format == NULL || *format == '\0') &&
		(format = ImFileQFFormat( fp, fileName )) == NULL )
	{
		fprintf( stderr, "%s: Cannot determine file format for '%s'.\n",
			ImToolsProgram, fileName );
		fprintf( stderr, "%s: Please select one explicitly.  Type '%s -help' for a list.\n",
			ImToolsProgram, ImToolsProgram );
		exit( 1 );
	}
	strcpy( actualFormat, format );
	return ( fp );
}





/*
 *  FUNCTION
 *	ImToolsFileRead	-  read in an image file
 *
 *  DESCRIPTION
 *	The file is opened.  The flags table is updated to include the
 *	name of the file.  The file is read in, and then closed.  Data
 *	read in is returned to the caller.
 */

public void				/* Returns nothing		*/
ImToolsFileRead( filename, format, flags, data )
	char     *filename;		/* Input file name		*/
	char     *format;		/* File format			*/
	TagTable *flags;		/* Read flags			*/
	TagTable *data;			/* Where to put the data	*/
{
	FILE     *fp;			/* File pointer			*/
	char      tmpFilename[1024];	/* Tmp string pointer		*/
	char      tmpFormat[1024];	/* Tmp format name		*/


	/*
	 *  Open the file.
	 *
	 *  'filename' is the name of the file, as given on the command-line.
	 *  If this is a '-', then we'll open stdin.  In this case the filename
	 *  returned by ImToolsOpen() will be different from that given.
	 *  
	 *  'format' is the image file format of the file, as given on the
	 *  command-line.  If not given at all, 'format' will be a '\0' string.
	 */
	fp = ImToolsOpen( filename, "r", format, tmpFilename, tmpFormat );

	strcpy( filename, tmpFilename );
	strcpy( format,   tmpFormat );


	/*
	 *  If we're being verbose, tell the user what we've got.
	 */
	if ( ImToolsVerbose )
		fprintf( stderr, "%s: Reading '%s' using format '%s'\n",
			ImToolsProgram, filename, format );


	/*
	 *  Update the flags table to include the name of the file we're
	 *  reading.
	 */
	if ( flags != TAGTABLENULL )
		ImToolsChangeTagEntry( flags, "file name", filename );


	/*
	 *  Read in the file (might be stdin).
	 */
	if ( ImFileFRead( fp, format, flags, data ) == -1 )
	{
		fprintf( stderr, "%s: Read failed\n", ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Close the file, if not stdin.
	 */
	if ( fp != stdin )
		fclose( fp );
}





/*
 *  FUNCTION
 *	ImToolsFileWrite-  write out an image file
 *
 *  DESCRIPTION
 *	The file is opened.  The flags table is updated to include the
 *	name of the file.  The file is written out, and then closed.
 */

public void				/* Returns nothing		*/
ImToolsFileWrite( filename, format, flags, data )
	char    *filename;		/* Output file name		*/
	char    *format;		/* File format			*/
	TagTable *flags;		/* Write flags			*/
	TagTable *data;			/* Where to get the data	*/
{
	FILE     *fp;			/* File pointer			*/
	char      tmpFilename[1024];	/* Tmp string pointer		*/
	char      tmpFormat[1024];	/* Tmp format name		*/

	/*
	 *  Open the file.
	 *
	 *  'filename' is the name of the file, as given on the command-line.
	 *  If this is a '-', then we'll open stdout.  In this case the filename
	 *  returned by ImToolsOpen() will be different from that given.
	 *  
	 *  'format' is the image file format of the file, as given on the
	 *  command-line.  If not given at all, 'format' will be a '\0' string.
	 */
	fp = ImToolsOpen( filename, "w", format, tmpFilename, tmpFormat );
	strcpy( filename, tmpFilename );
	strcpy( format,   tmpFormat );


	/*
	 *  If we're being verbose, tell the user what we've got.
	 */
	if ( ImToolsVerbose )
		fprintf( stderr, "%s: Writing '%s' using format '%s'\n",
			ImToolsProgram, filename, format );


	/*
	 *  Update the flags table to include the name of the file we're
	 *  writing.
	 */
	if ( flags != TAGTABLENULL )
		ImToolsChangeTagEntry( flags, "file name", filename );


	/*
	 *  Write the file.
	 *
	 *  The image tool's standard error handler handles most errors by
	 *  simply printing them to stderr.  Some errors, however, have
	 *  rather brief default error messages.  These are handled specially
	 *  here.
	 */
	if ( ImFileFWrite( fp, format, flags, data ) == -1 )
	{
		switch ( ImErrNo )
		{
		case IMENOTPOSSIBLE:
			fprintf( stderr, "%s: The %s format cannot support the output force options given.\n", ImToolsProgram, format );
			fprintf( stderr, "%s: A list of what the format can support may be obtained by\n", ImToolsProgram );
			fprintf( stderr, "%s: executing:  imformats -long -%s\n", ImToolsProgram, format );
			unlink( filename );
			exit( 1 );

		case IMEMANYVFB:
			fprintf( stderr, "%s: The input file contained multiple images, however the\n", ImToolsProgram );
			fprintf( stderr, "%s: output %s format can only support 1 image per file.\n", ImToolsProgram, format );
#ifdef new
			fprintf( stderr, "%s: Use the 'imscatter' tool to split the input file into\n", ImToolsProgram );
			fprintf( stderr, "%s: multiple output files before converting to the %s format.\n", ImToolsProgram, format );
#endif
			unlink( filename );
			exit( 1 );

		case IMENOVFB:
			fprintf( stderr, "%s: The input file contained no images suitable for output\n", ImToolsProgram );
			fprintf( stderr, "%s: in the %s format.\n", ImToolsProgram, format );
			unlink( filename );
			exit( 1 );

		default:
			fprintf( stderr, "%s: Write failed\n", ImToolsProgram );
			unlink( filename );
			exit( 1 );
		}
	}


	/*
	 *  Close the file, if not stdout.
	 */
	if ( fp != stdout )
		fclose( fp );
}
