/******************************************************************************
 *
 * Component: OGDI Core C API
 * Purpose: Code encapsulating memory allocation, and association of the
 *          structure ecs_Result. 
 *
 ******************************************************************************
 * Copyright (C) 1995 Logiciels et Applications Scientifiques (L.A.S.) Inc
 * Permission to use, copy, modify and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies, that
 * both the copyright notice and this permission notice appear in
 * supporting documentation, and that the name of L.A.S. Inc not be used 
 * in advertising or publicity pertaining to distribution of the software 
 * without specific, written prior permission. L.A.S. Inc. makes no
 * representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 ******************************************************************************
 *
 * $Log$
 * Revision 1.6  2016-07-06 08:59:46  erouault
 * ecs_SetError(): display error message on stderr if memory allocation fails
 *
 * Revision 1.5  2016/07/04 17:33:49  erouault
 * Also export ecs_ShouldStopOnError and ecs_SetErrorShouldStop on Windows
 *
 * Revision 1.4  2016/07/04 17:03:12  erouault
 * Error handling: Add a ecs_SetErrorShouldStop() function that can be
 *     used internally when the code is able to recover from an error. The user
 *     may decide if he wants to be resilient on errors by defining OGDI_STOP_ON_ERROR=NO
 *     as environment variable (the default being YES: stop on error).
 *     Add a ecs_SetReportErrorFunction() method to install a custom callback that
 *     will be called when OGDI_STOP_ON_ERROR=YES so that the user code is still
 *     aware of errors that occured. If not defined, the error will be logged in stderr.
 *
 * Revision 1.3  2016/06/28 14:32:45  erouault
 * Fix all warnings about unused variables raised by GCC 4.8
 *
 * Revision 1.2  2001/04/09 15:04:34  warmerda
 * applied new source headers
 *
 */

#include "ecs.h"

ECS_CVSID("$Id$");

char memory_error[] = "not enough memory";

#define ALLOCNULLSTRING(p) \
if (p==NULL) { \
    if ((p = malloc(1)) != NULL) { \
	 p[0] = '\0'; \
    } \
} 

/*
   ----------------------------------------------------------------------

   ecs_assoc: The ecsassoc library encapsulate memory allocation and
   association of the structure ecs_Result. This structure is use
   to send complex information to client without major concern from
   the server.

   Each of these method work exactly the same way. They receive an 
   information to add in the ecs_Result and the information is add
   in ecs_Result. If an error occur during this operation, the functions
   will return an error code (FALSE). Otherwise, the function end the
   operation and return TRUE. It is important to handle the error code
   in the calling code, a quick return must be done in these cases. There
   is no need from the calling function to call SetError if an error append
   during association, an error code is automatically set and ready
   to be sent to client.

   ----------------------------------------------------------------------
   */

/*************************************************/

/*
   ----------------------------------------------------------------------

   ecs_SetError

   Set an error code and a message into ecs_Result. This function will
   not reinitialize the ecs_Result structure.

   IN
       int errorcode: Error code to assign to ecs_Result. Usually 1.
                      0 mean a success message.
       char *error_message: The error message to assign to ecs_Result.
       Could be a null value.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetError (r,errorcode,error_message)
     ecs_Result *r;
     int errorcode;
     char *error_message;
{
  r->error = errorcode;
  r->res.type = SimpleError;
  if (r->message != NULL)
    free(r->message);
  if (error_message == NULL) {
    r->message = NULL;
  } else {
    r->message = (char *) malloc(strlen(error_message)+1);
    if (r->message == NULL)
    {
      fprintf(stderr, "Not enough memory in ecs_SetError(): %s\n", error_message); 
      return FALSE;
    }
    strcpy(r->message,error_message);
  }

  ecs_AdjustResult(r);

  return TRUE;
}

/************************************************************************/
/*                     ecs_ShouldStopOnError()                          */
/************************************************************************/

int ecs_ShouldStopOnError()
{
    const char* pszStopOnError = getenv("OGDI_STOP_ON_ERROR");
    if( pszStopOnError == NULL )
        return TRUE;
    if( strcmp(pszStopOnError, "yes") == 0 ||
        strcmp(pszStopOnError, "YES") == 0 )
    {
         return TRUE;
    }
    else if( strcmp(pszStopOnError, "no") == 0 ||
             strcmp(pszStopOnError, "NO") == 0 )
    {
         return FALSE;
    }
    else
    {
         fprintf(stderr, "Unhandled value for OGDI_STOP_ON_ERROR = %s. "
                         "Considering it is YES\n",
                         pszStopOnError);
         return TRUE;
    }
}

/************************************************************************/
/*                     ecs_ShouldStopOnError()                            */
/************************************************************************/

static int ecs_DefaultReportError(int errorcode, const char *error_message)
{
    fprintf(stderr, "Error %d: %s\n", errorcode, error_message);
    return FALSE; /* go on */
}

/************************************************************************/
/*                   ecs_SetReportErrorFunction()                       */
/************************************************************************/

static ReportErrorType pfnReportError = ecs_DefaultReportError;

/* Installs a custom error handler and returns the previous one */
ReportErrorType ecs_SetReportErrorFunction(ReportErrorType pfn)
{
    ReportErrorType oldErrorFunc = pfnReportError;
    pfnReportError = pfn;
    return oldErrorFunc;
}

/************************************************************************/
/*                       ecs_SetErrorShouldStop()                       */
/************************************************************************/

/* ecs_SetErrorShouldStop() is a variant of ecs_SetError() that can be used */
/* when the error can be recovered. By default it will be considered as non */
/* recoverable, and thus return TRUE, and regular ecs_SetError() will be used. */
/* But the user may have  installed its own error handler with */
/* ecs_SetReportErrorFunction() and decide of the appropriate behaviour */
int ecs_SetErrorShouldStop (r,errorcode,error_message)
     ecs_Result *r;
     int errorcode;
     char *error_message;
{
  if( ecs_ShouldStopOnError() )
  {
      ecs_SetError(r,errorcode,error_message);
      return TRUE;
  }
  else
  {
      return pfnReportError(errorcode,error_message);
  }
}

/*
   ----------------------------------------------------------------------

   ecs_Success

   Set an success code and flush the error message previously set. 
   This function will not reinitialize the ecs_Result structure.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetSuccess (r)
     ecs_Result *r;
{
  r->error = 0;
  if (r->message != NULL)
    free(r->message);
  r->message = NULL;

  ecs_AdjustResult(r);

  return TRUE;
}



/*
   ----------------------------------------------------------------------

   ecs_AdjustResult

   This function is there to correct a deficiency of RPC about data 
   strings. If a string in ecs_Result is NULL, the software will crash. 

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_AdjustResult (r)
     ecs_Result *r;
{
  int val,i;

  ALLOCNULLSTRING(r->message);

  switch(r->res.type) {
  case Object: 
    {
      ALLOCNULLSTRING(ECSRESULT(r).dob.Id);
      ALLOCNULLSTRING(ECSRESULT(r).dob.attr);
      if (ECSGEOMTYPE(r) == Text) {
	ALLOCNULLSTRING(ECSGEOM(r).text.desc);
      }
    };
    break;
  case objAttributeFormat: 
    {
      val = ECSRESULT(r).oaf.oa.oa_len;
      if (ECSRESULT(r).oaf.oa.oa_val != NULL) {
	for(i=0;i<val;i++) {
	  ALLOCNULLSTRING(ECSRESULT(r).oaf.oa.oa_val[i].name);
	}
      }
    };
    break;
  case RasterInfo: 
    {
      val = ECSRESULT(r).ri.cat.cat_len;
      if (ECSRESULT(r).ri.cat.cat_val != NULL) {
	for(i=0;i<val;i++) {
	  ALLOCNULLSTRING(ECSRESULT(r).ri.cat.cat_val[i].label);
	}
      }
    };
    break;
  case AText: 
    {
      ALLOCNULLSTRING(ECSRESULT(r).s);
    };
    break;
  default:
    break;
  };

  return TRUE;
}

/*
   ----------------------------------------------------------------------

   ecs_SetObjectId

   Set the attribute Id in object. Before calling this function,
   the caller must call ecs_SetGeomText, ecs_SetGeomPoint, ecs_SetGeomLine,
   ecs_SetGeomArea, ecs_SetGeomMatrix or ecs_SetGeomImage to initialize the
   geographic object.

   IN
       char *id: Identifier of the object. Must be different than NULL.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetObjectId (r,id)
     ecs_Result *r;
     char *id;
{
  if (r->res.type == Object) {
    if (r->res.ecs_ResultUnion_u.dob.Id != NULL)
      free(r->res.ecs_ResultUnion_u.dob.Id);
    r->res.ecs_ResultUnion_u.dob.Id = (char *) malloc(strlen(id)+1);
    if (r->res.ecs_ResultUnion_u.dob.Id == NULL) {
      ecs_SetError(r,1,memory_error);
      return FALSE;
    }
    strcpy(r->res.ecs_ResultUnion_u.dob.Id,id);
  }
  
  return TRUE;
}

/*
   ----------------------------------------------------------------------

   ecs_SetObjectAttr

   Set the attribute attr in object. Before calling this function,
   the caller must call ecs_SetGeomText, ecs_SetGeomPoint, ecs_SetGeomLine,
   ecs_SetGeomArea, ecs_SetGeomMatrix or ecs_SetGeomImage to initialize the
   geographic object.

   IN
       char *attr: This string contain the attribute to set. This
       argument must be different than NULL.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetObjectAttr (r,attr)
     ecs_Result *r;
     char *attr;
{
  if (r->res.type == Object) {
    if (r->res.ecs_ResultUnion_u.dob.attr != NULL)
      free(r->res.ecs_ResultUnion_u.dob.attr);
    r->res.ecs_ResultUnion_u.dob.attr = (char *) malloc(strlen(attr)+1);
    if (r->res.ecs_ResultUnion_u.dob.attr == NULL) {
      ecs_SetError(r,1,memory_error);
      return FALSE;
    }
    strcpy(r->res.ecs_ResultUnion_u.dob.attr,attr);
  }
  
  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeoRegion

   Set the geographic region. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       double north, south, east, west, ns_res, ew_res: Geographic
       region to set.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeoRegion (r,north,south,east,west,ns_res,ew_res)
     ecs_Result *r;
     double north;
     double south;
     double east;
     double west;
     double ns_res;
     double ew_res;
{
  ecs_CleanUp(r);
  ECSRESULTTYPE(r) = GeoRegion;
  ECSRESULT(r).gr.north = north;
  ECSRESULT(r).gr.south = south;
  ECSRESULT(r).gr.east = east;
  ECSRESULT(r).gr.west = west;
  ECSRESULT(r).gr.ns_res = ns_res;
  ECSRESULT(r).gr.ew_res = ew_res;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetText

   Set the Atext item with a string. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       char *text: Text to set in the structure

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetText (r,text)
     ecs_Result *r;
     char *text;
{
  ecs_CleanUp(r);

  ECSRESULTTYPE(r) = AText;
  ECSRESULT(r).s = (char *) malloc(strlen(text)+1);
  if (ECSRESULT(r).s == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  strcpy(ECSRESULT(r).s, text);
  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_AddText

   Concatenate a text to Atext item. The function ecs_SetText must
   be call before.

   IN
       char *text: Text to concatanate

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_AddText (r,text)
     ecs_Result *r;
     char *text;
{
  char *temp;
  int code;

  temp = ECSRESULT(r).s;
  code = TRUE;
  if ((ECSRESULTTYPE(r) == AText) &&
      (temp != NULL)) {
    ECSRESULT(r).s = NULL;
    ECSRESULT(r).s = (char *) malloc(strlen(text)+strlen(temp)+1);
    if (ECSRESULT(r).s == NULL) {
      ECSRESULT(r).s = temp;
      ecs_SetError(r,1,memory_error);
      return FALSE;
    }
    strcpy(ECSRESULT(r).s, temp);
    strcat(ECSRESULT(r).s, text);
    free(temp);
  } else {
    code = ecs_SetText(r,text);
  }

  return code;
}

/* 
   ----------------------------------------------------------------------

   Set the RasterInfo attribute. This function will make a clean up
   of ecs_Result and perform his operation. 

   IN
       int width, height: Width and height of the raster

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetRasterInfo (r,width,height)
     ecs_Result *r;
     int width;
     int height;
{
  ecs_CleanUp(r);

  ECSRESULTTYPE(r) = RasterInfo;

  /* It is impossible to have maxcat < mincat. In this
     case, ecs_AddRasterInfoCategory will set both values
     with the first value. */
  ECSRESULT(r).ri.mincat = 1;
  ECSRESULT(r).ri.maxcat = 0;
  ECSRESULT(r).ri.width = width;
  ECSRESULT(r).ri.height = height;
  ECSRESULT(r).ri.cat.cat_len = 0;
  ECSRESULT(r).ri.cat.cat_val = NULL;
  return TRUE;
}

/*************************************************/

/* 
   ----------------------------------------------------------------------

   ecs_AddRasterInfoCategory

   Add a raster info category. Must be called after ecs_SetRasterInfo

   IN
       long no_cat: Category number
       unsigned int red,green,blue: Color of the category
       char *label: Label of the category
       unsigned long qty: Statistical informations about the 
                          raster. (How many points)

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_AddRasterInfoCategory (r,no_cat,red,green,blue,label,qty)
     ecs_Result *r;
     long no_cat;
     unsigned int red;
     unsigned int green;
     unsigned int blue;
     char *label;
     unsigned long qty;
{
  ecs_Category *ptr;

  /* Allocate a new category */
  ECSRESULT(r).ri.cat.cat_val = 
    (ecs_Category *) realloc(ECSRESULT(r).ri.cat.cat_val,
			     sizeof(ecs_Category)*(ECSRESULT(r).ri.cat.cat_len + 1));
  if (ECSRESULT(r).ri.cat.cat_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  ECSRESULT(r).ri.cat.cat_len++;
  ptr = &(ECSRESULT(r).ri.cat.cat_val[ECSRESULT(r).ri.cat.cat_len - 1]);

  /* Update mincat and maxcat with no_cat */
  if (ECSRESULT(r).ri.mincat > ECSRESULT(r).ri.maxcat) {
    ECSRESULT(r).ri.mincat = no_cat;
    ECSRESULT(r).ri.maxcat = no_cat;
  } else {
    if (ECSRESULT(r).ri.mincat > no_cat)
      ECSRESULT(r).ri.mincat = no_cat;
    if (ECSRESULT(r).ri.maxcat < no_cat)
      ECSRESULT(r).ri.maxcat = no_cat;
  }

  ptr->no_cat = no_cat;
  ptr->r = red;
  ptr->g = green;
  ptr->b = blue;
  ptr->label = (char *) malloc(strlen(label)+1);
  if (ptr->label == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  strcpy(ptr->label,label);
  ptr->qty = qty;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetObjAttributeFormat

   Set the objAttributeFormat attribute. This function will make a clean up
   of ecs_Result and perform his operation.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetObjAttributeFormat (r)
     ecs_Result *r;
{
  ecs_CleanUp(r);

  ECSRESULTTYPE(r) = objAttributeFormat;
  ECSRESULT(r).oaf.oa.oa_len = 0;
  ECSRESULT(r).oaf.oa.oa_val = NULL;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_AddAttributeFormat

   Add an attribute format to objAttributeFormat attribute

   IN
       char *name: Name of the attribute
       ecs_AttributeFormat type: 
       int lenght              :   Attribute format info
       int precision           :   (ODBC)
       int nullable            :  

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_AddAttributeFormat (r,name,type,lenght,precision,nullable)
     ecs_Result *r;
     char *name;     
     ecs_AttributeFormat type;
     int lenght;
     int precision;
     int nullable;
{
  ecs_ObjAttribute *ptr;

  /* Allocate a new attribute */
  ECSRESULT(r).oaf.oa.oa_val = 
    (ecs_ObjAttribute *) realloc(ECSRESULT(r).oaf.oa.oa_val,
				 sizeof(ecs_ObjAttribute)*(ECSRESULT(r).oaf.oa.oa_len + 1));
  if (ECSRESULT(r).oaf.oa.oa_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  ECSRESULT(r).oaf.oa.oa_len++;
  ptr = &(ECSRESULT(r).oaf.oa.oa_val[ECSRESULT(r).oaf.oa.oa_len - 1]);

  ptr->name = (char *) malloc(strlen(name)+1);
  if (ptr->name == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  strcpy(ptr->name,name);
  ptr->type = type;
  ptr->lenght = lenght;
  ptr->precision = precision;
  ptr->nullable = nullable;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomPoint

   Set a point geographical object. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       double x,y : Point to assign

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomPoint (r,x,y)
     ecs_Result *r;
     double x;
     double y;
{
  ecs_CleanUp(r);

  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Point;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  
  ECSGEOM(r).point.c.x = x;
  ECSGEOM(r).point.c.y = y;
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomText

   Set a text geographical object. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       double x,y : Position of the text
       char *desc:  Description string

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomText (r,x,y,desc)
     ecs_Result *r;
     double x;
     double y;
     char *desc;
{
  ecs_CleanUp(r);

  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Text;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  
  ECSGEOM(r).text.c.x = x;
  ECSGEOM(r).text.c.y = y;
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;
  if (desc != NULL) {
    ECSGEOM(r).text.desc = (char *) malloc(strlen(desc)+1);
    if (ECSGEOM(r).text.desc == NULL) {
      ecs_SetError(r,1,memory_error);
      return FALSE;
    }
    strcpy(ECSGEOM(r).text.desc,desc);
  } else {
    ECSGEOM(r).text.desc = NULL;
  }
  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomLine

   Set a line geographical object. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       unsigned int lenght : Number of points in Polyline

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.


   ----------------------------------------------------------------------
   */

int ecs_SetGeomLine (r,lenght)
     ecs_Result *r;
     unsigned int lenght;
{
  ecs_CleanUp(r);
  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Line;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;
  
  ECSGEOM(r).line.c.c_len = lenght;
  ECSGEOM(r).line.c.c_val = (ecs_Coordinate *) malloc(sizeof(ecs_Coordinate)*lenght);
  if (ECSGEOM(r).line.c.c_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomArea

   Set a area geographical object. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       unsigned int lenght : Number of rings in this area

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomArea (r,lenght)
     ecs_Result *r;
     unsigned int lenght;
{
  ecs_CleanUp(r);
  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Area;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;  

  ECSGEOM(r).area.ring.ring_len = lenght;
  ECSGEOM(r).area.ring.ring_val = (ecs_FeatureRing *) malloc(sizeof(ecs_FeatureRing)*lenght);
  if (ECSGEOM(r).line.c.c_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }

  return TRUE;
}

/*************************************************/

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomAreaRing

   Set a ring into an area object.

   IN
       int position: Position in ring table
       unsigned int lenght: Number of points in the ring
       double centroid_x: 
       double centroid_y: Centroid of the ring      

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomAreaRing (r,position,lenght,centroid_x,centroid_y)
     ecs_Result *r;
     int position;
     unsigned int lenght;
     double centroid_x;
     double centroid_y;
{
  ecs_FeatureRing *ptr;

  ptr = &(ECSGEOM(r).area.ring.ring_val[position]);

  ptr->centroid.x = centroid_x;
  ptr->centroid.y = centroid_y;

  ptr->c.c_len = lenght;
  ptr->c.c_val = (ecs_Coordinate *) malloc(sizeof(ecs_Coordinate)*lenght);
  if (ptr->c.c_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomMatrixWithArray

   Set a matrix geographical object with a array of unsigned int already
   allocated. This function will make a clean up of ecs_Result and perform 
   his operation.

   IN
       int size: Size of array
       unsigned int *array: Table of values

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomMatrixWithArray (r, size, array)
     ecs_Result *r;
     int size;
     unsigned int *array;
{
  ecs_CleanUp(r);
  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Matrix;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  ECSGEOM(r).matrix.x.x_len = size;
  ECSGEOM(r).matrix.x.x_val = (u_int *) malloc(sizeof(u_int)*size);
  if (ECSGEOM(r).matrix.x.x_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  memcpy(ECSGEOM(r).matrix.x.x_val,array,sizeof(u_int)*size);
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomMatrix

   Set a matrix geographical object. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       int size: Number of colums in this raster row

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomMatrix (r, size)
     ecs_Result *r;
     int size;
{
  ecs_CleanUp(r);
  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Matrix;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  ECSGEOM(r).matrix.x.x_len = size;
  ECSGEOM(r).matrix.x.x_val = (u_int *) malloc(sizeof(u_int)*size);
  if (ECSGEOM(r).matrix.x.x_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomImageWithArray

   Set a image geographical object with a array of unsigned int already
   allocated. This function will make a clean up of ecs_Result and 
   perform his operation.

   IN
       int size: Size of array
       unsigned int *array: Table of values

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomImageWithArray (r, size, array)
     ecs_Result *r;
     int size;
     unsigned int *array;
{
  ecs_CleanUp(r);
  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Image;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  ECSGEOM(r).image.x.x_len = size;
  ECSGEOM(r).image.x.x_val = (u_int *) malloc(sizeof(u_int)*size);
  if (ECSGEOM(r).image.x.x_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  memcpy(ECSGEOM(r).image.x.x_val,array,sizeof(u_int)*size);
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;

  return TRUE;
}

/* 
   ----------------------------------------------------------------------

   ecs_SetGeomImage

   Set a image geographical object. This function will make a clean up
   of ecs_Result and perform his operation.

   IN
       int size: Number of colums in this raster row

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_SetGeomImage(r, size)
     ecs_Result *r;
     int size;
{
  ecs_CleanUp(r);
  ECSRESULTTYPE(r) = Object;
  ECSGEOMTYPE(r) = Image;
  r->res.ecs_ResultUnion_u.dob.Id = NULL;
  r->res.ecs_ResultUnion_u.dob.attr = NULL;
  ECSGEOM(r).image.x.x_len = size;
  ECSGEOM(r).image.x.x_val = (u_int *) malloc(sizeof(u_int)*size);
  if (ECSGEOM(r).image.x.x_val == NULL) {
    ecs_SetError(r,1,memory_error);
    return FALSE;
  }
  r->res.ecs_ResultUnion_u.dob.xmin = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymin = 0.0;
  r->res.ecs_ResultUnion_u.dob.xmax = 0.0;
  r->res.ecs_ResultUnion_u.dob.ymax = 0.0;

  return TRUE;
}

/*
   ----------------------------------------------------------------------

   ecs_CleanUp

   Will make a complete clean up and reinitialisation of ecs_Result.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */


int ecs_CleanUp (r)
     ecs_Result *r;
{
  r->error = 0;

  if (r->message != NULL)
    free(r->message);
  r->message = NULL;

  return ecs_CleanUpResultUnion (&r->res);
}

/*
   ----------------------------------------------------------------------

   ecs_CleanUpResultUnion

   Will make a complete clean up of the ResultUnion.

   IN/OUT
       ecs_ResultUnion *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */


int ecs_CleanUpResultUnion (r)
     ecs_ResultUnion *r;
{
  int val,i;

  switch (r->type) {
  case Object: 
    {
      ecs_CleanUpObject(&r->ecs_ResultUnion_u.dob);
    };
    break;
  case objAttributeFormat: 
    {
      val = r->ecs_ResultUnion_u.oaf.oa.oa_len;
      if (r->ecs_ResultUnion_u.oaf.oa.oa_val != NULL) {
	for(i=0;i<val;i++) {
	  if (r->ecs_ResultUnion_u.oaf.oa.oa_val[i].name != NULL)
	    free(r->ecs_ResultUnion_u.oaf.oa.oa_val[i].name);
	  r->ecs_ResultUnion_u.oaf.oa.oa_val[i].name = NULL;
	}
	free(r->ecs_ResultUnion_u.oaf.oa.oa_val);
      }
      r->ecs_ResultUnion_u.oaf.oa.oa_val = NULL;
    };
    break;
  case RasterInfo: 
    {
      val = r->ecs_ResultUnion_u.ri.cat.cat_len;
      if (r->ecs_ResultUnion_u.ri.cat.cat_val != NULL) {
	for(i=0;i<val;i++) {
	  if (r->ecs_ResultUnion_u.ri.cat.cat_val[i].label != NULL)
	    free(r->ecs_ResultUnion_u.ri.cat.cat_val[i].label);
	  r->ecs_ResultUnion_u.ri.cat.cat_val[i].label = NULL;
	}
	free(r->ecs_ResultUnion_u.ri.cat.cat_val);
      }
      r->ecs_ResultUnion_u.ri.cat.cat_val = NULL;     
    };
    break;
  case AText: 
    {
      if (r->ecs_ResultUnion_u.s != NULL)
	free(r->ecs_ResultUnion_u.s);
      r->ecs_ResultUnion_u.s = NULL;
    };
    break;
  case MultiResult:
    for (i = 0; i < (int) r->ecs_ResultUnion_u.results.results_len; i++) {
      ecs_CleanUpResultUnion(&r->ecs_ResultUnion_u.results.results_val[i]);
    }
    free(r->ecs_ResultUnion_u.results.results_val);
    break;
  default:
    break;
  };
  r->type = 0;

  return TRUE;
}

/*
   ----------------------------------------------------------------------

   ecs_CleanUpObject

   Will make a complete clean up and reinitialisation of ecs_Object in
   ecs_Result.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will clean up the structure and put his information into it.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_CleanUpObject (obj)
     ecs_Object *obj;
{
  int val,i;

  if (obj->Id != NULL)
    free(obj->Id);
  obj->Id = NULL;

  if (obj->attr != NULL)
    free(obj->attr);
  obj->attr = NULL;

  switch (obj->geom.family) {
  case Area:
    {
      val = obj->geom.ecs_Geometry_u.area.ring.ring_len;
      if (obj->geom.ecs_Geometry_u.area.ring.ring_val != NULL) {
	for(i=0;i<val;i++) {
	  if (obj->geom.ecs_Geometry_u.area.ring.ring_val[i].c.c_val != NULL)
	    free(obj->geom.ecs_Geometry_u.area.ring.ring_val[i].c.c_val);
	  obj->geom.ecs_Geometry_u.area.ring.ring_val[i].c.c_val = NULL;
	}
	free(obj->geom.ecs_Geometry_u.area.ring.ring_val);
      }
      obj->geom.ecs_Geometry_u.area.ring.ring_val = NULL;
    };
    break;
  case Line:
    {
      if (obj->geom.ecs_Geometry_u.line.c.c_val != NULL)
	free(obj->geom.ecs_Geometry_u.line.c.c_val);
      obj->geom.ecs_Geometry_u.line.c.c_val = NULL;
    };
    break;
  case Matrix:
    {
      if (obj->geom.ecs_Geometry_u.matrix.x.x_val != NULL)
	free(obj->geom.ecs_Geometry_u.matrix.x.x_val);
      obj->geom.ecs_Geometry_u.matrix.x.x_val = NULL;
    };
    break;
  case Image:
    {
      if (obj->geom.ecs_Geometry_u.image.x.x_val != NULL)
	free(obj->geom.ecs_Geometry_u.image.x.x_val);
      obj->geom.ecs_Geometry_u.image.x.x_val = NULL;
    };
    break;
  case Text:
    {
      if (obj->geom.ecs_Geometry_u.text.desc != NULL)
	free(obj->geom.ecs_Geometry_u.text.desc);
      obj->geom.ecs_Geometry_u.text.desc = NULL;
    };
    break;
  default:
    break;
  };

  return TRUE;
}

/*************************************************/

/*
   ----------------------------------------------------------------------

   ecs_ResultInit

   First initialisation of ecs_Result. Perform when the structure
   is create to initialise it.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_ResultInit (r)
     ecs_Result *r;
{
  r->error = 0;
  r->message = NULL;
  r->res.type = 0;

  return TRUE;

}

/* 
   ----------------------------------------------------------------------

   ecs_CalcObjectMBR

   Given a ecs_Result with a ecs_Object already define.
   Found the bounding rectangle of this object and 
   assign it in ecs_Result.

   IN
       ecs_Server *s: Server structure contain some complement information
       about region.

   IN/OUT
       ecs_Result *r: A pointer to a structure already defined. This
       method will only update the contain of this structure.

   OUT
       return int: A error code. If a problem occur in the method,
       "r" will be update to infor the user about the problem and
       will return a FALSE value. The default return value is TRUE.

   ----------------------------------------------------------------------
   */

int ecs_CalcObjectMBR (s,obj)
     ecs_Server *s;
     ecs_Object *obj;
{
  register unsigned int i,j,len;
  register ecs_Coordinate *coord;

  switch(obj->geom.family) {
  case Point:
    obj->xmin = obj->geom.ecs_Geometry_u.point.c.x;
    obj->ymin = obj->geom.ecs_Geometry_u.point.c.y;
    obj->xmax = obj->geom.ecs_Geometry_u.point.c.x;
    obj->ymax = obj->geom.ecs_Geometry_u.point.c.y;
    break;
  case Text:
    obj->xmin = obj->geom.ecs_Geometry_u.text.c.x;
    obj->ymin = obj->geom.ecs_Geometry_u.text.c.y;
    obj->xmax = obj->geom.ecs_Geometry_u.text.c.x;
    obj->ymax = obj->geom.ecs_Geometry_u.text.c.y;
    break;
  case Line:
    len = obj->geom.ecs_Geometry_u.line.c.c_len;
    if (len > 0) {
      obj->xmin = obj->geom.ecs_Geometry_u.line.c.c_val[0].x;
      obj->ymin = obj->geom.ecs_Geometry_u.line.c.c_val[0].y;
      obj->xmax = obj->geom.ecs_Geometry_u.line.c.c_val[0].x;
      obj->ymax = obj->geom.ecs_Geometry_u.line.c.c_val[0].y;
    }
    for(i=1;i<len;i++) {
      coord = &(obj->geom.ecs_Geometry_u.line.c.c_val[i]);
      if (obj->xmin > coord->x)
	obj->xmin = coord->x;
      if (obj->ymin > coord->y)
	obj->ymin = coord->y;
      if (obj->xmax < coord->x)
	obj->xmax = coord->x;
      if (obj->ymax < coord->y)
	obj->ymax = coord->y;
    }
    break;
  case Area:
    if ((obj->geom.ecs_Geometry_u.area.ring.ring_len > 0) && 
	(obj->geom.ecs_Geometry_u.area.ring.ring_val[0].c.c_len > 0)) {
      obj->xmin = obj->geom.ecs_Geometry_u.area.ring.ring_val[0].c.c_val[0].x;
      obj->ymin = obj->geom.ecs_Geometry_u.area.ring.ring_val[0].c.c_val[0].y;
      obj->xmax = obj->geom.ecs_Geometry_u.area.ring.ring_val[0].c.c_val[0].x;
      obj->ymax = obj->geom.ecs_Geometry_u.area.ring.ring_val[0].c.c_val[0].y;
    }
    for(i=0;i<obj->geom.ecs_Geometry_u.area.ring.ring_len;i++) {
      for(j=0;j<obj->geom.ecs_Geometry_u.area.ring.ring_val[i].c.c_len;j++) {
	coord = &(obj->geom.ecs_Geometry_u.area.ring.ring_val[i].c.c_val[j]);
	if (obj->xmin > coord->x)
	  obj->xmin = coord->x;
	if (obj->ymin > coord->y)
	  obj->ymin = coord->y;
	if (obj->xmax < coord->x)
	  obj->xmax = coord->x;
	if (obj->ymax < coord->y)
	  obj->ymax = coord->y;
      }
    }
    break;
  case Matrix:
  case Image:
    obj->xmin = s->currentRegion.west;
    obj->ymin = s->currentRegion.south;
    obj->xmax = s->currentRegion.east;
    obj->ymax = s->currentRegion.north;
    break;
  default:
    break;
  }
  return TRUE;
}


/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyResult: Copy an object contain in a ecs_Result structure.

   IN
       ecs_Result *source: Original object
   OUT
       ecs_Result *copy: Copied object
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyResult(source,copy)
     ecs_Result *source;
     ecs_Result **copy;
{
  return ecs_CopyResultFromUnion(&source->res,copy);
}


/*
 *----------------------------------------------------------------------
 * ecs_CopyResultFromUnion --
 *
 *	Creates an ecs_Result structure from an ecs_ResultUnion
 *	structure.
 *
 * IN
 *	ecs_ResultUnion *source: Original object piece
 * OUT
 *	ecs_Result **copy:	 Copied object piece with wrapping header
 *
 * Results:
 *	TRUE if successful.
 *	FALSE if unsuccessful.
 *----------------------------------------------------------------------
 */
int
ecs_CopyResultFromUnion(source,copy)
     ecs_ResultUnion *source;
     ecs_Result **copy;
{
  int returncode;
  ecs_Result *result;

  returncode = TRUE;
  if (source->type == Object) {

    /* Allocate the new object */

    if ((result = (ecs_Result *) malloc(sizeof(ecs_Result))) == NULL) {
      returncode = FALSE;
    } 

    /* Prepare the object structure */

    result->error = 0;
    result->message = NULL;

    returncode = ecs_CopyResultUnionWork(source,&result->res);
    if (returncode == FALSE) {
      free(result);
      result = NULL;
    }
  } else {
    returncode = FALSE;
    result = NULL;
  }

  *copy = result;
  return returncode;
}

/*
 *----------------------------------------------------------------------
 * ecs_CopyResultUnion --
 *
 *	Creates an ecs_Result structure from an ecs_ResultUnion
 *	structure.
 *
 * IN
 *	ecs_ResultUnion *source: Original object piece
 * OUT
 *	ecs_Result **copy:	 Copied object piece with wrapping header
 *
 * Results:
 *	TRUE if successful.
 *	FALSE if unsuccessful.
 *----------------------------------------------------------------------
 */
int
ecs_CopyResultUnion(source,copy)
     ecs_ResultUnion *source;
     ecs_ResultUnion **copy;
{
  int returncode;
  ecs_ResultUnion *result;

  returncode = TRUE;
  if (source->type == Object) {

    /* Allocate the new object */

    result = (ecs_ResultUnion *) malloc(sizeof(ecs_ResultUnion));
    if (result == NULL) {
      returncode = FALSE;
    } else {
      returncode = ecs_CopyResultUnionWork(source, result);
      if (returncode == FALSE) {
	free(result);
	result = NULL;
      }
    }
  } else {
    returncode = FALSE;
    result = NULL;
  }

  *copy = result;
  return returncode;
}

/*
 *----------------------------------------------------------------------
 * ecs_CopyResultUnionWork --
 *
 *	Sets one ecs_ResultUnion to be exactly like the other.
 *	Allocates any substructure that are needed.
 *
 * IN
 *	ecs_ResultUnion *source: Original object piece
 * OUT
 *	ecs_ResultUnion *dest:	 Copied object piece
 *
 * Results:
 *	TRUE if successful.
 *	FALSE if unsuccessful.
 *----------------------------------------------------------------------
 */
int
ecs_CopyResultUnionWork(source,copy)
     ecs_ResultUnion *source;
     ecs_ResultUnion *copy;
{
  int returncode;
  ecs_Object *obj;
  ecs_Object *newobj;

  returncode = TRUE;
  if (source->type == Object) {
    copy->type = Object;
    obj = &(source->ecs_ResultUnion_u.dob);

    newobj = &copy->ecs_ResultUnion_u.dob;

    /* Allocate in the new object Id and attr */

    if ((returncode == TRUE) && (obj->Id != NULL) && ((newobj->Id = (char *) malloc(strlen(obj->Id)+1)) == NULL)) {
      returncode = 1;
    }     

    if ((returncode == TRUE) && (obj->attr != NULL) && ((newobj->attr = (char *) malloc(strlen(obj->attr)+1)) == NULL)) {
      returncode = FALSE;
    } 
    
    /* Fill the new ecs_Object structure with obj values */

    if (obj->Id != NULL) {
      strcpy(newobj->Id,obj->Id);
    } else {
      newobj->Id = NULL;
    }
    if (obj->attr != NULL) {
      strcpy(newobj->attr,obj->attr);
    } else {
      newobj->attr = NULL;
    }
    newobj->xmin = obj->xmin;
    newobj->ymin = obj->ymin;
    newobj->xmax = obj->xmax;
    newobj->ymax = obj->ymax;

    /* Call ecs_CopyGeometry to fill the geom argument */

    returncode = ecs_CopyGeometry(obj,newobj);

    if (returncode == FALSE) {
      ecs_FreeObject(newobj);
    }
  } else {
    returncode = FALSE;
  }

  return returncode;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyObject: Copy an object contain in a ecs_Result structure.

   IN
       ecs_Object *obj: Original object
   OUT
       ecs_Object *copy: Copied object preallocated

       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyObject(obj,newobj)
     ecs_Object *obj;
     ecs_Object *newobj;
{
  int returncode;

  returncode = TRUE;

  /* Allocate in the new object Id and attr */
  
  if ((returncode == TRUE) && (obj->Id != NULL) && ((newobj->Id = (char *) malloc(strlen(obj->Id)+1)) == NULL)) {
    returncode = FALSE;
  }     

  if ((returncode == TRUE) && (obj->attr != NULL) && ((newobj->attr = (char *) malloc(strlen(obj->attr)+1)) == NULL)) {
    returncode = FALSE;
  } 
  
  /* Fill the new ecs_Object structure with obj values */
  
  if (obj->Id != NULL) {
    strcpy(newobj->Id,obj->Id);
  } else {
    newobj->Id = NULL;
  }
  if (obj->attr != NULL) {
    strcpy(newobj->attr,obj->attr);
  } else {
    newobj->attr = NULL;
  }
  newobj->xmin = obj->xmin;
  newobj->ymin = obj->ymin;
  newobj->xmax = obj->xmax;
  newobj->ymax = obj->ymax;
  
  /* Call ecs_CopyGeometry to fill the geom argument */
  
  returncode = ecs_CopyGeometry(obj,newobj);
  
  if (returncode == FALSE) {
    ecs_FreeObject(newobj);
  }
  
  return returncode;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyGeometry: Copy an object geometry into a new object

   IN
       ecs_Object *source: Original object
   IN/OUT
       ecs_Object *copy: Copied object
   OUT
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyGeometry(source,copy)
     ecs_Object *source;
     ecs_Object *copy;
{
  int returncode;

  returncode = TRUE;
  copy->geom.family = source->geom.family; 
  switch(copy->geom.family) {
  case Area:
    returncode = ecs_CopyArea(&(source->geom.ecs_Geometry_u.area),
			      &(copy->geom.ecs_Geometry_u.area));
    break;
  case Line:
    returncode = ecs_CopyLine(&(source->geom.ecs_Geometry_u.line),
			      &(copy->geom.ecs_Geometry_u.line));
    break;
  case Point:
    returncode = ecs_CopyPoint(&(source->geom.ecs_Geometry_u.point),
			      &(copy->geom.ecs_Geometry_u.point));
    break;
  case Text:
    returncode = ecs_CopyText(&(source->geom.ecs_Geometry_u.text),
			      &(copy->geom.ecs_Geometry_u.text));
    break;
  case Matrix:
    returncode = ecs_CopyMatrix(&(source->geom.ecs_Geometry_u.matrix),
				&(copy->geom.ecs_Geometry_u.matrix));
    break;
  case Image:
    returncode = ecs_CopyImage(&(source->geom.ecs_Geometry_u.image),
			       &(copy->geom.ecs_Geometry_u.image));
    break;
  default:
    break;
  }

  return returncode;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyArea: Copy the area from an original object to a copy

   IN
       ecs_Area *source: Pointer to the source area
   IN/OUT
       ecs_Area *copy: Pointer to the copy area
   OUT
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyArea(source,copy)
     ecs_Area *source;
     ecs_Area *copy; 
{
  int i,j;

  copy->ring.ring_len = source->ring.ring_len;
  if (source->ring.ring_val != NULL) {
    /* Allocate ring table */

    copy->ring.ring_val = (ecs_FeatureRing *) malloc(sizeof(ecs_FeatureRing)*source->ring.ring_len);
    if (copy->ring.ring_val == NULL) {
      return FALSE;
    }

    /* Copy ring table containt */

    for(i=0;i<(int) source->ring.ring_len;i++) {
      copy->ring.ring_val[i].centroid.x = source->ring.ring_val[i].centroid.x;
      copy->ring.ring_val[i].centroid.y = source->ring.ring_val[i].centroid.y;
      copy->ring.ring_val[i].c.c_len = source->ring.ring_val[i].c.c_len;
      if (source->ring.ring_val[i].c.c_val != NULL) {
	/* Allocate c table */

	copy->ring.ring_val[i].c.c_val = (ecs_Coordinate *) malloc(sizeof(ecs_Coordinate)*source->ring.ring_val[i].c.c_len);
	if (copy->ring.ring_val[i].c.c_val == NULL) {
	  return FALSE;
	}

	/* Copy the c table contain */

	for(j=0;j<(int) source->ring.ring_val[i].c.c_len;j++) {
	  copy->ring.ring_val[i].c.c_val[j].x = source->ring.ring_val[i].c.c_val[j].x;
	  copy->ring.ring_val[i].c.c_val[j].y = source->ring.ring_val[i].c.c_val[j].y;
	}

      } else {
	copy->ring.ring_val[i].c.c_val = NULL;
      }
    }
  } else {
    copy->ring.ring_val = NULL;
  }

  return TRUE;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyLine: Copy the Line from an original object to a copy

   IN
       ecs_Line *source: Pointer to the source Line
   IN/OUT
       ecs_Line *copy: Pointer to the copy Line
   OUT
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyLine(source,copy)
     ecs_Line *source; 
     ecs_Line *copy;
{
  int i;

  copy->c.c_len = source->c.c_len;
  if (source->c.c_val != NULL) {
    /* Allocate c table */

    copy->c.c_val = (ecs_Coordinate *) malloc(sizeof(ecs_Coordinate)*source->c.c_len);
    if (copy->c.c_val == NULL) {
      return FALSE;
    }

    /* Copy c table containt */

    for(i=0;i<(int) source->c.c_len;i++) {
      copy->c.c_val[i].x = source->c.c_val[i].x;
      copy->c.c_val[i].y = source->c.c_val[i].y;
    }
  } else {
    copy->c.c_val = NULL;
  }

  return TRUE;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyPoint: Copy the point from an original object to a copy

   IN
       ecs_Point *source: Pointer to the source point
   IN/OUT
       ecs_Point *copy: Pointer to the copy point
   OUT
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyPoint(source,copy)
     ecs_Point *source; 
     ecs_Point *copy; 
{
  copy->c.x = source->c.x;
  copy->c.y = source->c.y;

  return TRUE;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyText: Copy the text from an original object to a copy

   IN
       ecs_Text *source: Pointer to the source text
   IN/OUT
       ecs_Text *copy: Pointer to the copy text
   OUT
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyText(source,copy)
     ecs_Text *source; 
     ecs_Text *copy; 
{
  copy->c.x = source->c.x;
  copy->c.y = source->c.y;

  if (source->desc != NULL) {
    copy->desc = (char *) malloc(strlen(source->desc)+1);
    if (copy->desc == NULL) {
      return FALSE;
    }
    strcpy(copy->desc,source->desc);
  } else {
    copy->desc = NULL;
  }

  return TRUE;  
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyMatrix: Copy the matrix from an original object to a copy

   IN
       ecs_Matrix *source: Pointer to the source matrix
   IN/OUT
       ecs_Matrix *copy: Pointer to the copy matrix
   OUT
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyMatrix(source,copy)
     ecs_Matrix *source; 
     ecs_Matrix *copy; 
{
  int i;

  copy->x.x_len = source->x.x_len;
  if (source->x.x_val != NULL) {
    /* Allocate x table */

    copy->x.x_val = (u_int *) malloc(sizeof(ecs_Coordinate)*source->x.x_len);
    if (copy->x.x_val == NULL) {
      return FALSE;
    }

    /* Copy x table containt */

    for(i=0;i<(int) source->x.x_len;i++) {
      copy->x.x_val[i] = source->x.x_val[i];
    }
  } else {
    copy->x.x_val = NULL;
  }

  return TRUE;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_CopyImage: Copy the image from an original object to a copy

   IN
       ecs_Image *source: Pointer to the source image
   IN/OUT
       ecs_Image *copy: Pointer to the copy image
   OUT
       return int: Error message returned.
                    TRUE: Success
		    FALSE: Failure

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

int ecs_CopyImage(source,copy)
     ecs_Image *source; 
     ecs_Image *copy; 
{
  int i;

  copy->x.x_len = source->x.x_len;
  if (source->x.x_val != NULL) {
    /* Allocate x table */

    copy->x.x_val = (u_int *) malloc(sizeof(ecs_Coordinate)*source->x.x_len);
    if (copy->x.x_val == NULL) {
      return FALSE;
    }

    /* Copy x table containt */

    for(i=0;i<(int) source->x.x_len;i++) {
      copy->x.x_val[i] = source->x.x_val[i];
    }
  } else {
    copy->x.x_val = NULL;
  }

  return TRUE;
}

/*
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

   ecs_FreeObject: Free an object

   IN
       ecs_Object *obj: object to be free

   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   */

void ecs_FreeObject(obj)
     ecs_Object *obj;
{
  int i;

  if (obj != NULL) {
    if (obj->Id != NULL)
      free(obj->Id);
    if (obj->attr != NULL)
      free(obj->attr);
    switch(obj->geom.family) {
    case Area:
      if (obj->geom.ecs_Geometry_u.area.ring.ring_val != NULL) {
	for(i=0;i<(int) obj->geom.ecs_Geometry_u.area.ring.ring_len;i++) {
	  if (obj->geom.ecs_Geometry_u.area.ring.ring_val[i].c.c_val != NULL)
	    free(obj->geom.ecs_Geometry_u.area.ring.ring_val[i].c.c_val);
	}
	free(obj->geom.ecs_Geometry_u.area.ring.ring_val);
      }
      break;
    case Line:
      if (obj->geom.ecs_Geometry_u.line.c.c_val != NULL)
	free(obj->geom.ecs_Geometry_u.line.c.c_val);
      break;
    case Text:
      if (obj->geom.ecs_Geometry_u.text.desc != NULL)
	free(obj->geom.ecs_Geometry_u.text.desc);
      break;
    case Matrix:
      if (obj->geom.ecs_Geometry_u.matrix.x.x_val != NULL)
	free(obj->geom.ecs_Geometry_u.matrix.x.x_val);
      break;
    case Image:
      if (obj->geom.ecs_Geometry_u.image.x.x_val != NULL)
	free(obj->geom.ecs_Geometry_u.image.x.x_val);
      break;
    default:
      break;
    }
  }
  return;
}


