/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is 
 * preserved on all copies.
 * 
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the 
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */
/* 
 * rle_hdr.c - Functions to manipulate rle_hdr structures.
 * 
 * Author:	Spencer W. Thomas
 * 		EECS Dept.
 * 		University of Michigan
 * Date:	Mon May 20 1991
 * Copyright (c) 1991, University of Michigan
 */

#include <string.h>

#include "rle_config.h"
#include "rle.h"

/*****************************************************************
 * TAG( rle_names )
 *
 * Load program and file names into header.
 * Inputs:
 * 	the_hdr:	Header to modify.
 * 	pgmname:	The program name.
 * 	fname:		The file name.
 * 	img_num:	Number of the image within the file.
 * Outputs:
 * 	the_hdr:	Modified header.
 * Algorithm:
 * 	If values previously filled in (by testing is_init field),
 * 	free them.  Make copies of file name and program name,
 * 	modifying file name for standard i/o.  Set is_init field.
 */
void
rle_names( the_hdr, pgmname, fname, img_num )
rle_hdr *the_hdr;
CONST_DECL char *pgmname;
CONST_DECL char *fname;
int img_num;
{
#if 0
    /* Can't do this because people do hdr1 = hdr2, which copies
       the pointers. */

    /* If filled in, free previous values. */
    if ( the_hdr->is_init == RLE_INIT_MAGIC &&
	 the_hdr->cmd != NULL && the_hdr->file_name != NULL )
    {
	if ( pgmname != the_hdr->cmd )
	    free( the_hdr->cmd );
	if ( fname != the_hdr->file_name )
	    free( the_hdr->file_name );
    }
#endif

    /* Mark as filled in. */
    the_hdr->is_init = RLE_INIT_MAGIC;

    /* Default file name for stdin/stdout. */
    if ( fname == NULL || strcmp( fname, "-" ) == 0 || *fname == '\0' )
	fname = "Standard I/O";
    if ( pgmname == NULL )
	pgmname = rle_dflt_hdr.cmd;

    /* Fill in with copies of the strings. */
    if ( the_hdr->cmd != pgmname )
    {
        char *tmp;

        overflow_add(strlen(pgmname), 1);
        tmp = malloc( strlen(pgmname) + 1 );
	RLE_CHECK_ALLOC( pgmname, tmp, 0 );
	strcpy( tmp, pgmname );
	the_hdr->cmd = tmp;
    }

    if ( the_hdr->file_name != fname )
    {
        char *tmp;
        overflow_add(strlen(fname), 1);
        tmp = malloc( strlen( fname ) + 1 );
        RLE_CHECK_ALLOC( pgmname, tmp, 0 );
	strcpy( tmp, fname );
	the_hdr->file_name = tmp;
    }

    the_hdr->img_num = img_num;
}


/* Used by rle_hdr_cp and rle_hdr_init to avoid recursion loops. */
static int no_recurse = 0;

/*****************************************************************
 * TAG( rle_hdr_cp )
 * 
 * Make a "safe" copy of a rle_hdr structure.
 * Inputs:
 * 	from_hdr:	Header to be copied.
 * Outputs:
 * 	to_hdr:		Copy of from_hdr, with all memory referred to
 * 			by pointers copied.  Also returned as function
 * 			value.  If NULL, a static header is used.
 * Assumptions:
 * 	It is safe to call rle_hdr_init on to_hdr.
 * Algorithm:
 * 	Initialize to_hdr, copy from_hdr to it, then copy the memory
 * 	referred to by all non-null pointers.
 */
rle_hdr *
rle_hdr_cp( from_hdr, to_hdr )
rle_hdr *from_hdr, *to_hdr;
{
    static rle_hdr dflt_hdr;
    CONST_DECL char *cmd, *file;
    int num;

    /* Save command, file name, and image number if already initialized. */
    if ( to_hdr &&  to_hdr->is_init == RLE_INIT_MAGIC )
    {
	cmd = to_hdr->cmd;
	file = to_hdr->file_name;
	num = to_hdr->img_num;
    }
    else
    {
	cmd = file = NULL;
	num = 0;
    }

    if ( !no_recurse )
    {
	no_recurse++;
	rle_hdr_init( to_hdr );
	no_recurse--;
    }

    if ( to_hdr == NULL )
	to_hdr = &dflt_hdr;

    *to_hdr = *from_hdr;

    if ( to_hdr->bg_color )
    {
	int size = to_hdr->ncolors * sizeof(int);
        overflow2(to_hdr->ncolors, sizeof(int));
	to_hdr->bg_color = (int *)malloc( size );
	RLE_CHECK_ALLOC( to_hdr->cmd, to_hdr->bg_color, "background color" );
	memcpy( to_hdr->bg_color, from_hdr->bg_color, size );
    }

    if ( to_hdr->cmap )
    {
	int size = to_hdr->ncmap * (1 << to_hdr->cmaplen) * sizeof(rle_map);
        to_hdr->cmap = (rle_map *)malloc3( to_hdr->ncmap, 1<<to_hdr->cmaplen, sizeof(rle_map));
	RLE_CHECK_ALLOC( to_hdr->cmd, to_hdr->cmap, "color map" );
	memcpy( to_hdr->cmap, from_hdr->cmap, size );
    }

    /* Only copy array of pointers, as the original comment memory
     * never gets overwritten.
     */
    if ( to_hdr->comments )
    {
	int size = 0;
	CONST_DECL char **cp;
        for ( cp=to_hdr->comments; *cp; cp++ )
        {
            overflow_add(size, 1);
	    size++;		/* Count the comments. */
        }
	/* Check if there are really any comments. */
	if ( size )
	{
            overflow_add(size, 1);
	    size++;		/* Copy the NULL pointer, too. */
            overflow2(size, sizeof(char *));
	    size *= sizeof(char *);
	    to_hdr->comments = (CONST_DECL char **)malloc( size );
	    RLE_CHECK_ALLOC( to_hdr->cmd, to_hdr->comments, "comments" );
	    memcpy( to_hdr->comments, from_hdr->comments, size );
	}
	else
	    to_hdr->comments = NULL;	/* Blow off empty comment list. */
    }

    /* Restore the names to their original values. */
    to_hdr->cmd = cmd;
    to_hdr->file_name = file;

    /* Lines above mean nothing much happens if cmd and file are != NULL. */
    rle_names( to_hdr, to_hdr->cmd, to_hdr->file_name, num );

    return to_hdr;
}

/*****************************************************************
 * TAG( rle_hdr_clear )
 * 
 * Clear out the allocated memory pieces of a header.
 *
 * This routine is intended to be used internally by the library, to
 * clear a header before putting new data into it.  It clears all the
 * fields that would be set by reading in a new image header.
 * Therefore, it does not clear the program and file names.
 * 
 * Inputs:
 * 	the_hdr:	To be cleared.
 * Outputs:
 * 	the_hdr:	After clearing.
 * Assumptions:
 * 	If is_init field is RLE_INIT_MAGIC, the header has been
 * 	properly initialized.  This will fail every 2^(-32) times, on
 * 	average.
 * Algorithm:
 * 	Free memory and set to zero all pointers, except program and
 * 	file name.
 */
void
rle_hdr_clear( the_hdr )
rle_hdr *the_hdr;
{
    /* Try to free memory.  Assume if is_init is properly set that this
     * header has been previously initialized, therefore it is safe to
     * free memory.
     */
    if ( the_hdr && the_hdr->is_init == RLE_INIT_MAGIC )
    {
	if ( the_hdr->bg_color )
	    free( the_hdr->bg_color );
	the_hdr->bg_color = 0;
	if ( the_hdr->cmap )
	    free( the_hdr->cmap );
	the_hdr->cmap = 0;
	/* Unfortunately, we don't know how to free the comment memory. */
	if ( the_hdr->comments )
	    free( the_hdr->comments );
	the_hdr->comments = 0;
    }
}



/*****************************************************************
 * TAG( rle_hdr_init )
 * 
 * Initialize a rle_hdr structure.
 * Inputs:
 * 	the_hdr:	Header to be initialized.
 * Outputs:
 * 	the_hdr:	Initialized header.
 * Assumptions:
 * 	If the_hdr->is_init is RLE_INIT_MAGIC, the header has been
 * 	previously initialized.
 * 	If the_hdr is a copy of another rle_hdr structure, the copy
 * 	was made with rle_hdr_cp.
 * Algorithm:
 *  Fill in fields of rle_dflt_hdr that could not be set by the loader
 *	If the_hdr is rle_dflt_hdr, do nothing else
 *  Else:
 *	  If the_hdr is NULL, return a copy of rle_dflt_hdr in static storage
 * 	  If the_hdr->is_init is RLE_INIT_MAGIC, free all memory
 * 	     pointed to by non-null pointers.
 *    If this is a recursive call to rle_hdr_init, clear *the_hdr and
 *      return the_hdr.
 *    Else make a copy of rle_dflt_hdr and return its address.  Make the
 *      copy in static storage if the_hdr is NULL, and in the_hdr otherwise.
 */
rle_hdr *
rle_hdr_init( the_hdr )
rle_hdr *the_hdr;
{
    rle_hdr *ret_hdr;

    rle_dflt_hdr.rle_file = stdout;
    /* The rest of rle_dflt_hdr is set by the loader's data initialization */

    if ( the_hdr == &rle_dflt_hdr )
	return the_hdr;

    rle_hdr_clear( the_hdr );

    /* Only call rle_hdr_cp if not called from there. */
    if ( !no_recurse )
    {
	no_recurse++;
	ret_hdr = rle_hdr_cp( &rle_dflt_hdr, the_hdr );
	no_recurse--;
    }
    else
	ret_hdr = the_hdr;

    return ret_hdr;
}
