/**
 **     $Header: /import/dev-vis/image/imtools/v2.1/imtools/src/RCS/imcat.c,v 1.6 92/12/09 18:44:33 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.1/imtools/src/RCS/imcat.c,v 1.6 92/12/09 18:44:33 nadeau Exp $"

/**
 **  FILE
 **	imcat.c		-  Concatenate images into a multi-image file
 **
 **  PROJECT
 **	IM		-  Image Manipulation Tools
 **
 **  DESCRIPTION
 **	imcat takes multiple input image files, combines their data, and writes
 **	the information out as a multi-image file.
 **
 **  PUBLIC CONTENTS
 **			d =defined constant
 **			f =function
 **			m =defined macro
 **			t =typedef/struct/union
 **			v =variable
 **			? =other
 **
 **	main		f  main program
 **
 **  PRIVATE CONTENTS
 **	toolCommand	v  tool-specific tool info
 **	toolHelp	v  tool-specific help
 **	toolFullHelp	v  tool-specific help
 **	toolOptions	v  tool-specific options
 **	toolEquivs	v  tool-specific equivalent keywords
 **
 **	toolInFilename	v  the name of the input file (could be 'stdin')
 **	toolOutFilename	v  the name of the output file (could be 'stdout')
 **
 **	toolInFormat	v  the name of the input file's format (could be '\0')
 **	toolOutFormat	v  the name of the output file's format (could be '\0')
 **
 **	toolInTable	v  a table for the storage of data read in
 **	toolFlagsTable	v  a table for the storage of read/write flags
 **
 **	toolInit	f  initialize things for the tool
 **
 **  HISTORY
 **     
 **	
 **
 **/

#include <sys/types.h>
#include "imtools.h"
#ifndef R_OK
#include <sys/unistd.h>
#endif


extern void toolInit( );		/* Initialize things		*/



/*
 *  GLOBALS
 *	toolCommand		-  tool-specific tool info
 *	toolHelp		-  tool-specific help
 *	toolOptions		-  tool-specific options
 *	toolEquivs		-  tool-specific equivalent keywords
 *
 *  DESCRIPTION
 *	toolCommand describes the command to the arg package.
 *
 *	toolHelp is the tool-specific help for the tool.  It will be
 *	concatenated with the generic image tools help message and
 *	added to the toolCommand structure as the help string to be
 *	printed after the option listing.
 *
 *	toolOptions is the tool-specific option list.  It will be merged
 *	with the generic image tools options, and the list of image file
 *	format names supported by the library.  This larger option list
 *	is then used as the list of options supported by the tool.
 *
 *	toolEquivs is the tool-specific option equivalent keyword list.
 *	It will be merged with the generic image tools equivs.  This large
 *	equivs list is then used as the list of equivs supported by the tool.
 */

private ArgCommand toolCommand =
{
	/* command name */		"imcat",

	/* major version # */		IMTOOLSMAJOR,
	/* minor version # */		IMTOOLSMINOR,
	/* subminor version # */	IMTOOLSSUBMINOR,

	/* -help pre-option list information				*/
"%command concatenates information found in multiple input image files and\n\
writes it to a single multi-image file.  Input and output files may have\n\
different image file formats.\n\
",
	/* -help post-option list information				*/
	NULL,				/* filled in later on		*/

	/* -fullhelp pre-option list information			*/
	NULL,				/* Use same message as for -help*/
	/* -fullhelp post-option list information			*/
	NULL,				/* filled in later on		*/

	ARGFNONE,			/* No special flags		*/
	"[options...] infilenames... outfilename",
	"[options...] infilenames... outfilename",
	"SDSC Image Tools, October 1992.",
	"Copyright (c) 1989-1992  San Diego Supercomputer Center (SDSC), CA, USA",
	NULL,				/* filled in later on		*/
	NULL,				/* filled in later on		*/
};

private char *toolHelp = "\n\
Typical Invocations:\n\
    Concatenate 6 rgb files (single03-single08) into an HDF multi-image file:\n\
	%command single%02d.rgb -frames 3-8 many.hdf\n\
    Concatenate three differently named XWD images into a TIFF file:\n\
	%command one.xwd tiger.xwd beer.xwd many.tiff\n\
";

private char *toolFullHelp = "\n\
Files:\n\
    -infile lists the image files to be concatenated.  -outfile gives the\n\
    name of the file in which to place the results.\n\
\n\
    When listing input files explicitly, each input file name is given on\n\
    the command line.\n\
\n\
    Input files may be listed implicitly by giving a file name template with\n\
    an embedded %d (ala printf) where the frame number should be placed.\n\
    The -frames argument then gives a range of frame numbers to use for\n\
    input file names.\n\
";

private char *toolNote = "\n\
Additional Help:\n\
    This is an abbreviated help listing.  For a full listing of options,\n\
    including a list of image file formats supported, type:\n\
        %command -fullhelp\n\
";

#define TOOLNOPTIONS	3
private ArgOption toolOptions[TOOLNOPTIONS] =
{
	{ "file", "image_filename", "Specify an image file name",
	  ARGFREQUIRED|ARGFMULTIPLE|ARGFIMPKEYWORD, 1, 1, ARGTSTRING },

	{ "frames", "range", "Specify frame numbers of input files",
	  ARGFNONE , 1, 1, ARGTINT | ARGTRANGE },

	{ "verbose", NULL, "Be verbose",
	  ARGFFULLHELP, 0, 0, ARGTNONE },
};

#define TOOLNEQUIVS	0
#if TOOLNEQUIVS == 0
private ArgEquiv *toolEquivs;
#else
private ArgEquiv toolEquivs[TOOLNEQUIVS] =
{
};
#endif





/*
 *  GLOBALS
 *	toolInFilename	-  the name of the input file (could be 'stdin')
 *	toolOutFilename	-  the name of the output file (could be 'stdout')
 *
 *	toolInFormat	-  the name of the input file's format (could be NULL)
 *	toolOutFormat	-  the name of the output file's format (could be NULL)
 *
 *	toolInTable	-  a table for the storage of data read in
 *	toolFlagsTable	-  a table for the storage of read/write flags
 *
 *  DESCRIPTION
 *	Data held by these variables is used throughout the tool.
 */

private char      **toolInFilenames;	/* Input file name		*/
private int	  toolNInFiles;		/* # of input files		*/
private char      **toolInFormats;	/* Input file's format name	*/
private char      toolInFormat[1024];	/* Input file's format name	*/

private char      toolOutFilename[1024];/* Output file name		*/
private char      toolOutFormat[1024];	/* Output file's format name	*/

private TagTable *toolInTable;		/* Data tag table		*/
private TagTable *toolOutTable;		/* Data tag table		*/
private TagTable *toolFlagsTable;	/* Flags tag table		*/

private ImVfb	 *toolInVfb;		/* input image			*/
private ImVfb	 *toolOutVfb;		/* output image			*/

private int	  toolFrameStart;	/* start of frames		*/
private int	  toolFrameEnd;		/* end of frames		*/





/*
 *  FUNCTION
 *	main	-  main program
 *
 *  DESCRIPTION
 *	Control things:
 *		-  Initialize things (parse arguments and set up tables).
 *		-  Read in all input files, putting data into data table.
 *		-  Write out the output file (get data from data table).
 *	That's about it.
 *
 *  NOTES
 *	This 'main' is pretty much the same for each of the image tools.
 *	Differences between tools include:
 *		-  the number of input files read in
 *		-  the number of output files written out
 *		-  the actions taken on the data between read and write
 */

main( argc, argv )
	int argc;			/* Argument count		*/
	char *argv[];			/* Argument vector		*/
{
	int         i, j;		/* Counter			*/
	int	    nInVfb;		/* Number of input images	*/
	char	    inFilename[1024];	/* Input file name		*/
	boolean	    hadError = FALSE;	/* Did we have an error?	*/
	boolean	    framesNeeded = FALSE;/* Is a -frames arg needed?	*/


	/*
	 *  Initialize things:
	 *	-  Prepare the arg parsing data, then parse the command-line.
	 *	-  Prepare the flags table based upon command-line args.
	 *	-  Determine the file formats for input and output files.
	 */
	toolInit( argc, argv );

	/*
	 *  Make sure all the input files exist.  Doing this first, before
	 *  trying to open and read the files can save the user quite a bit
	 *  of reading time when they make a filename typo.
	 */
	for ( i = 0; i < toolNInFiles-1; i++ )
	{
		if ( strchr( toolInFilenames[i], '%' ) == NULL )
		{
			/*
			 *  Explicit file name without embedded printf %d.
			 *  Check the file.
			 */
			if ( access( toolInFilenames[i], R_OK ) == -1 )
			{
				fprintf( stderr, "%s: Input file %s cannot be read\n", ImToolsProgram, toolInFilenames[i] );
				hadError = TRUE;
			}
			continue;
		}

		/*
		 *  Implicit file name with embedded printf %d.  Read them
		 *  all in.
		 */
		framesNeeded = TRUE;
		if ( toolFrameStart == -1 )
		{
			/* No frame range given.  Complain.		*/
			fprintf( stderr, "%s: Use of a %%d in an input file name requires a -frames option\n", ImToolsProgram );
			fprintf( stderr, "%s: to indicate what range of frame numbers to put there.\n", ImToolsProgram );
			exit( 1 );
		}
		if ( toolFrameStart < toolFrameEnd )
		{
			for ( j = toolFrameStart; j <= toolFrameEnd; j++ )
			{
				sprintf( inFilename, toolInFilenames[i], j );
				if ( access( inFilename, R_OK ) == -1 )
				{
					fprintf( stderr, "%s: Input file %s cannot be read\n", ImToolsProgram, inFilename );
					hadError = TRUE;
				}
			}
		}
		else
		{
			for ( j = toolFrameStart; j <= toolFrameEnd; j-- )
			{
				sprintf( inFilename, toolInFilenames[i], j );
				if ( access( inFilename, R_OK ) == -1 )
				{
					fprintf( stderr, "%s: Input file %s cannot be read\n", ImToolsProgram, inFilename );
					hadError = TRUE;
				}
			}
		}
	}
	if ( hadError )
	{
		fprintf( stderr, "%s: Aborted due to problems with input files.\n", ImToolsProgram );
		exit( 1 );
	}
	if ( toolFrameStart != -1 && framesNeeded == FALSE )
	{
		fprintf( stderr, "%s: -frames requires that input files include %%d in their names\n", ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Read in each input file.
	 *	-  Open the file (or stdin) and read data into the data table.
	 */
	for ( i = 0; i < toolNInFiles-1; i++ )
	{
		if ( strchr( toolInFilenames[i], '%' ) == NULL )
		{
			/*
			 *  Explicit file name without embedded printf %d.
			 *  Read the file in.
			 */
			ImToolsFileRead( toolInFilenames[i], toolInFormats[i],
				toolFlagsTable, toolInTable );
			continue;
		}

		/*
		 *  Implicit file name with embedded printf %d.  Read them
		 *  all in.
		 */
		if ( toolFrameStart < toolFrameEnd )
		{
			for ( j = toolFrameStart; j <= toolFrameEnd; j++ )
			{
				sprintf( inFilename, toolInFilenames[i], j );
				ImToolsFileRead( inFilename, toolInFormats[i],
					toolFlagsTable, toolInTable );
			}
		}
		else
		{
			for ( j = toolFrameStart; j <= toolFrameEnd; j-- )
			{
				sprintf( inFilename, toolInFilenames[i], j );
				ImToolsFileRead( inFilename, toolInFormats[i],
					toolFlagsTable, toolInTable );
			}
		}
	}


	/*
	 *  Check for errors
	 *	-  no input images
	 */
	nInVfb = TagTableQNEntry( toolInTable, "image vfb" );
	if ( nInVfb == 0 )
	{
		fprintf( stderr, "%s: Input files contained no images!\n",
			ImToolsProgram );
		exit( 1 );
	}

	/*
	 *  Write out the output file.
	 *	-  Open the file (or stdout) and write the data in the data
	 *	   table.  Upon failure, remove the bad output file.
	 */
	ImToolsFileWrite( toolInFilenames[toolNInFiles-1], 
		toolInFormats[toolNInFiles-1], toolFlagsTable, toolInTable );
}





/*
 *  FUNCTION
 *	toolInit	-  initialize things for the tool
 *
 *  DESCRIPTION
 *	The tool's argument parsing data structures are set up to include:
 *		- the full help message (generic help + tool-specific help)
 *		- the full option list (generic options + tool-specific options)
 *		- the full equivs list (generic equivs + tool-specific equivs)
 *
 *	Command-line arguments are then parsed.  The results are used to
 *	set up the flags table (the generic -out* options).
 *
 *	Input and output file's names and formats are determined from the
 *	command-line arguments.
 *
 *  NOTES
 *	This function is included in most of the image tools and differs
 *	only in slight ways.  Typical differences include:
 *		-  the number of input and output file names found
 *		-  the number of input and output file formats found
 *		-  the number of command-line arg flags checked for
 */

private void				/* Returns nothing		*/
toolInit( argc, argv )
	int argc;			/* Argument count		*/
	char *argv[ ];			/* Argument vector		*/
{
	int	    i,j,k,counter;	/* Counter			*/
	int	    noccur;		/* Number of option occurrences	*/
	int         nOpt;		/* Number of options		*/
	int         nEquiv;		/* Number of equivalences	*/
	ArgOption  *options1;		/* Argument options		*/
	ArgOption  *options;		/* Argument options		*/
	ArgEquiv   *equivs1;		/* Argument equivalent keywords	*/
	ArgEquiv   *equivs;		/* Argument equivalent keywords	*/
        ArgValue   *value;              /* Argument value               */

	char       *tmp;		/* Temporary string holder	*/
	char       *tmpFormat;		/* Tmp format name		*/
	char	   inFilename[1024];	/* incoming filename		*/
	int	   nInVfb;		/* number of incoming vfb's	*/

        TagEntry   *dataEntry;          /* Entry from data table        */
        TagEntry   *reEntry;            /* copy of Entry from data table*/
	int	   count;		/* a count of the number of files */



	/*
	 *  Save the name of the program, as invoked.
	 */
	ImToolsProgram = argv[0];


	/*
	 *  Make a data table to hold the incomming data.
	 */
	if ( (toolInTable = TagTableAlloc( )) == TAGTABLENULL )
	{
		TagPError( ImToolsProgram );
		exit( 1 );
	}

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


	/*
	 *  Use the standard Image Tools user registration and feedback forms.
	 */
	toolCommand.arg_register = ImToolsRegister;
	toolCommand.arg_feedback = ImToolsFeedback;


	/*
	 *  Allocate space for the total help string for the tool.  Copy the
	 *  tool-specific help in, then concatenate on the generic help text
	 *  used by most of the image tools.
	 */
	if ( (tmp = (char *)malloc( sizeof( char ) * (strlen( toolNote ) +
		strlen( toolHelp ) + 1) )) == NULL )
	{
		perror( ImToolsProgram );
		exit( 1 );
	}
	strcpy( tmp, toolHelp );
	strcat( tmp, toolNote );
	toolCommand.arg_help2 = tmp;

	if ( (tmp = (char *)malloc( sizeof( char ) * (strlen( ImToolsBaseHelp) +
		strlen( toolHelp ) + strlen( toolFullHelp ) + 1) )) == NULL )
	{
		perror( ImToolsProgram );
		exit( 1 );
	}
	strcpy( tmp, toolHelp );
	strcat( tmp, toolFullHelp );
	strcat( tmp, ImToolsBaseHelp );
	toolCommand.arg_fullhelp2 = tmp;


	/*
	 *  Build up an option list by merging the tool-specific options,
	 *  the standard (base) tool options, and those for the various
	 *  image file formats.
	 */
	nOpt = ImToolsMergeOptions( TOOLNOPTIONS, toolOptions,
		IMTOOLSNBASEOPTIONS, ImToolsBaseOptions, &options1 );
	if ( (nOpt = ImFileFormatOptions( nOpt, options1, &options )) == -1)
	{
		ImPError( ImToolsProgram );
		exit( 1 );
	}


	/*
	 *  Build up an equivalent keyword list by merging the tool-specific
	 *  equivalences, the standard (base) tool equivalences, and those
	 *  for the various image file formats.
	 */
	nEquiv = ImToolsMergeEquivs( TOOLNEQUIVS, toolEquivs,
		 IMTOOLSNBASEEQUIVS, ImToolsBaseEquivs, &equivs1 );
	if ( (nEquiv = ImFileFormatEquivs( nEquiv, equivs1, &equivs )) == -1)
	{
		ImPError( ImToolsProgram );
		exit( 1 );
	}

	/*
	 *  Parse the command line!
	 */
	nOpt = ArgParse( argc, argv, &toolCommand, nOpt, options,
		nEquiv, equivs );
	if ( ArgQNOccur( "verbose" ) != 0 )
		ImToolsVerbose = TRUE;


	/*
	 *  Set up the flags table based upon command-line arguments.
	 *  This is primarily derived from the -out* directives part of the
	 *  standard image tool option set (see ImToolsBaseOptions above).
	 *  Also included are flags to direct error messages to stderr and
	 *  a flag giving the program's name for later use in error messages.
	 */
	toolFlagsTable = ImToolsBuildFlagsTable( );


        /*
         *  Get the number of frames.
         */
        toolFrameStart = toolFrameEnd = -1;
        if ( ArgQNOccur( "frames" ) != 0 )
        {
                value = ArgQValue( "frames", 0, 0 );
                switch ( value->arg_rform )
                {
                case ARGRCLOSED:        /* n-m  start and end values    */
                        toolFrameStart = value->arg_ir1;
                        toolFrameEnd   = value->arg_ir2;
                        break;

                case ARGROPEN:          /* n-   length only             */
			fprintf( stderr, "%s: Missing end of frame range on -frames option\n", ImToolsProgram );
			exit( 1 );

                case ARGRSINGLE:        /* n    length only             */
			toolFrameStart = 0;
                        toolFrameEnd   = value->arg_i - 1;
                        break;
                }

		if (toolFrameStart<0)
		{
			fprintf (stderr, "%s: -frame start number must be positive!\n",
				ImToolsProgram);
			exit(1);
		}
		if (toolFrameEnd<0)
		{
			fprintf (stderr, "%s: -frame end number msut be positive!\n",
				ImToolsProgram);
			exit(1);
		}
        }


	/*
	 *  Determine how many input files we've got and allocate space for
	 *  their names.
	 */
	toolNInFiles = 0;
	for (j =0; j< ArgQNOccur("file"); j++)
	  toolNInFiles+=ArgQNValue("file",j);

	toolInFilenames = (char **)malloc( sizeof( char *) * toolNInFiles );
	toolInFormats = (char **)malloc( sizeof( char *) * toolNInFiles );


	/*
	 *  Get each input file's name (-infile), and search backwards in the
	 *  command-line option list to find the last format selection (if
	 *  any).  Stop the search on the beginning of the command-line, or
	 *  on -outfile.
	 */
	count = -1;
	for (k = 0; k< ArgQNOccur("file");k++)
	{
		count++;

		toolInFilenames[count] = ArgQValue( "file", k, 0 )->arg_s;
		tmpFormat = NULL;
		for ( i = ArgQOccurOpt( "file", k ) - 1; i >= 0; i-- )
		{
			tmp = ArgQOpt( i, &noccur );


			/*
			 *  Stop looking backward when we reach any other file
			 *  name argument.
			 */
			if ( strcmp( tmp, "file" ) == 0 )
				break;


			/*
			 *  Skip it if it isn't the name of a file format.
			 */
			if ( !ImToolsIsFormat( tmp ) )
				continue;


			if ( tmpFormat != NULL )
			{
				fprintf( stderr, "%s:  Only 1 file format selection may precede -infile.\n",
					ImToolsProgram );
				exit( 1 );
			}
			tmpFormat = tmp;
		}
		toolInFormats[count] = (char *)malloc( sizeof( char ) * 1024 );
		if ( tmpFormat == NULL )
			*toolInFormats[count] = '\0';
		else
			strcpy( toolInFormats[count], tmpFormat );
	}


}
