/*****************************************************************************
* Generic parser for the "Irit" solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Sep. 1991   *
*****************************************************************************/

#ifdef USE_VARARGS
#include <varargs.h>
#else
#include <stdarg.h>
#endif /* USE_VARARGS */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <setjmp.h>
#include "irit_sm.h"
#include "prsr_loc.h"
#include "allocate.h"
#include "attribut.h"
#include "irit_soc.h"

#ifdef __WINNT__
#include <fcntl.h>
#include <io.h>
#endif /* __WINNT__ */

#define CONVEX_EPSILON		1e-3
#define ZERO_NUM_EPSILON	1e-15
#define NORMAL_MIN_VALID_LEN	0.03

static char
    GlblTokenError[LINE_LEN_LONG];          /* Last token error was found. */
static IritPrsrPrintFuncType
    IritPrsrPrintFunc = NULL;

jmp_buf _IritPrsrLongJumpBuffer;		   /* Used in error traping. */
int _IritPrsrPolyListCirc = FALSE,
    _IritPrsrGlblParserError = IP_NO_ERR,		/* Last err # found. */
    _IritPrsrReadOneObject = FALSE,	/* If only one object is to be read. */
    IritPrsrWasViewMat = FALSE,
    IritPrsrWasPrspMat = FALSE;
char
    *_IPGlblFloatFormat = "%lg";

MatrixType IritPrsrViewMat = {		      /* Isometric view, by default. */
    { -0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.000000,  0.816496, 0.577350, 0.000000 },
    {  0.000000,  0.000000, 0.000000, 1.000000 }
};
MatrixType IritPrsrPrspMat = {
    { 1, 0, 0, 0 },
    { 0, 1, 0, 0 },
    { 0, 0, 1, -0.35 },
    { 0, 0, 0, 1.0 }
};

static void IritPrsrPutAttributes(int Handler,
				  IPAttributeStruct *Attr,
				  int Indent);
static void IritPrsrPutAllObjects(IPObjectStruct *PObj,
				  int Handler,
				  int Indent);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to update the Plane equation of the given polygon by the order     M
* of the most robust three vertices of that polygon to define the normal.    M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:     To update its normal/plane equation.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrUpdatePolyPlane, files, parser                                   M
*****************************************************************************/
void IritPrsrUpdatePolyPlane(IPPolygonStruct *PPoly)
{
    int i;
    RealType Len, V1[3], V2[3],
	MaxLen = SQR(EPSILON);
    IPVertexStruct
	*VLast = NULL,
	*V = PPoly -> PVertex;
    PlaneType Plane;

    if (V == NULL || V -> Pnext == NULL || V -> Pnext -> Pnext == NULL)
	_IPParserAbort(IP_ERR_DEGEN_POLYGON, "");

    for (i = 0; i < 4; i++)
	PPoly -> Plane[i] = 0.0;

    /* Force list to be circular. Will be recovered immediately after. */
    if (!_IritPrsrPolyListCirc) {
	VLast = IritPrsrGetLastVrtx(V);
	VLast -> Pnext = V;
    }

    do {
	PT_SUB(V1, V -> Coord, V -> Pnext -> Coord);
	V = V -> Pnext;
	PT_SUB(V2, V -> Coord, V -> Pnext -> Coord);

	Plane[0] = V1[1] * V2[2] - V2[1] * V1[2];
	Plane[1] = V1[2] * V2[0] - V2[2] * V1[0];
	Plane[2] = V1[0] * V2[1] - V2[0] * V1[1];

	/* Normalize the plane such that the normal has length of 1: */
	Len = PT_LENGTH(Plane);
	if (Len > MaxLen) {
	    for (i = 0; i < 3; i++)
		PPoly -> Plane[i] = Plane[i] / Len;

	    MaxLen = Len;

	    if (MaxLen > SQR(NORMAL_MIN_VALID_LEN))
	        break;
	}

	V = V -> Pnext;
    }
    while (V != PPoly -> PVertex &&
	   V -> Pnext != NULL &&
	   V -> Pnext -> Pnext != NULL);

    if (VLast != NULL) /* Recover non circular list, if was non circular. */
	VLast -> Pnext = NULL;

    if (MaxLen < SQR(EPSILON))
	_IPParserAbort(IP_ERR_DEGEN_NORMAL, "");

    PPoly -> Plane[3] =
	-DOT_PROD(PPoly -> Plane, PPoly -> PVertex -> Coord);

    IP_SET_PLANE_POLY(PPoly);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to update the Plane equation of the given polygon such that the  M
* Vin vertex will be in the positive side of it.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:     To update its normal/plane equation.                          M
*   Vin:       A vertex to be considered in the inside, respective to PPoly. M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrUpdatePolyPlane2, files, parser                                  M
*****************************************************************************/
void IritPrsrUpdatePolyPlane2(IPPolygonStruct *PPoly, VectorType Vin)
{
    int i;

    IritPrsrUpdatePolyPlane(PPoly);

    if (DOT_PROD(PPoly -> Plane, Vin) + PPoly -> Plane[3] < 0) {
	/* Flip plane normal and reverse the vertex list. */
	IritPrsrReverseVrtxList(PPoly);
	for (i = 0; i < 4; i++)
	    PPoly -> Plane[i] = (-PPoly -> Plane[i]);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to update all vertices in polygon to hold a default normal if      M
* have none already.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   PPoly:       Polygon to update normal information.                       M
*   DefNrml:     Normal tp use in update.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrUpdateVrtxNrml, files, parser				     M
*****************************************************************************/
void IritPrsrUpdateVrtxNrml(IPPolygonStruct *PPoly, VectorType DefNrml)
{
    IPVertexStruct
	*V = PPoly -> PVertex;

    do {
	if (!IP_HAS_NORMAL_VRTX(V)) {
	    PT_COPY(V -> Normal, DefNrml);
	    IP_SET_NORMAL_VRTX(V);
	}
	V = V -> Pnext;
    }
    while (V != NULL && V != PPoly -> PVertex);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reverses a list of objects, in place.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      A list of objects to reverse.                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:  Reverse list of objects, in place.                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrReverseObjList, reverse, files, parser                           M
*                                                                            M
*****************************************************************************/
IPObjectStruct *IritPrsrReverseObjList(IPObjectStruct *PObj)
{
    IPObjectStruct
	*NewPObjs = NULL;

    while (PObj) {
	IPObjectStruct
	    *Pnext = PObj -> Pnext;

	PObj -> Pnext = NewPObjs;
	NewPObjs = PObj;

	PObj = Pnext;
    }

    return NewPObjs;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reverses the vertex list of a given polygon. This is used mainly to        M
* reverse polygons such that cross product of consecutive edges which form   M
* a convex corner will point in the polygon normal direction.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Pl:        A polygon to reverse its vertex list, in place.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrReverseVrtxList , reverse, files, parser                         M
*****************************************************************************/
void IritPrsrReverseVrtxList(IPPolygonStruct *Pl)
{
    ByteType Tags, Count;
    IPVertexStruct *VNextNext, *VLast,
	*V = Pl -> PVertex,
	*VNext = V -> Pnext;

    /* Force list to be circular. Will be recovered immediately after. */
    if (!_IritPrsrPolyListCirc) {
	VLast = IritPrsrGetLastVrtx(V);
	VLast -> Pnext = V;
    }

    do {
	VNextNext = VNext -> Pnext;
	VNext -> Pnext = V;			     /* Reverse the pointer! */

	V = VNext;			   /* Advance all 3 pointers by one. */
	VNext = VNextNext;
	VNextNext = VNextNext -> Pnext;
    }
    while (V != Pl -> PVertex);

    V = Pl -> PVertex;	  /* Move the Tags/Count by one - to the right edge. */
    Tags = V -> Tags;
    Count = V -> Count;
    do {
	if (V -> Pnext == Pl -> PVertex) {
	    V -> Tags = Tags;
	    V -> Count = Count;
	}
	else {
	    V -> Tags = V -> Pnext -> Tags;
	    V -> Count = V -> Pnext -> Count;
	}

	V = V -> Pnext;
    }
    while (V != Pl -> PVertex);

    /* Recover non circular list, if needs to. */
    if (!_IritPrsrPolyListCirc) {
	VLast = IritPrsrGetLastVrtx(Pl -> PVertex);
	VLast -> Pnext = NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to abort parsing operation and save error reported.		     M
*   See also function IritPrsrParseError.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   ErrNum:   Type of error that had occured.                                M
*   Msg:      A message to accompany the error number.                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   _IPParserAbort, error handling, files, parser                            M
*****************************************************************************/
void _IPParserAbort(IritPrsrErrType ErrNum, char *Msg)
{
    _IritPrsrGlblParserError = ErrNum;
    strcpy(GlblTokenError, Msg);	/* Keep the message in safe place... */

    longjmp(_IritPrsrLongJumpBuffer, 1);		       /* Jump to... */
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns TRUE if error has happend since last call to this function during  M
* data read or write, FALSE otherwise.					     M
*   If error, then ErrorMsg is updated to point on static str describing it. M
*                                                                            *
* PARAMETERS:                                                                M
*   LineNum:    Line number of error, in file/stream.	                     M
*   ErrorMsg:   To be updated with latest error to have happened in parser.  M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE  if error occured since last call, FALSE otherwise.     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrParseError, error handling, files, parser                        M
*****************************************************************************/
int IritPrsrParseError(int LineNum, char **ErrorMsg)
{
    IritPrsrErrType Temp;
    char TempCopy[LINE_LEN_LONG];

    if ((Temp = _IritPrsrGlblParserError) == IP_NO_ERR)
	return FALSE;

    strcpy(TempCopy, GlblTokenError);
    _IritPrsrGlblParserError = IP_NO_ERR;

    switch (Temp) {
	case IP_ERR_NUMBER_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: Numeric data expected - found %s",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_OPEN_PAREN_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: '[' expected - found '%s'",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_CLOSE_PAREN_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: ']' expected - found '%s'",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_LIST_COMP_UNDEF:
	    sprintf(GlblTokenError, "Line %d: Undefined list element - \"%s\"",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_UNDEF_EXPR_HEADER:
	    sprintf(GlblTokenError, "Line %d: Undefined TOKEN - \"%s\"",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_PT_TYPE_EXPECTED:
	    sprintf(GlblTokenError, "Line %d: Point type expected",
		    LineNum);
	    break;
	case IP_ERR_OBJECT_EMPTY:
	    sprintf(GlblTokenError, "Line %d: Empty object found",
		    LineNum);
	    break;
	case IP_ERR_FILE_EMPTY:
	    sprintf(GlblTokenError, "Line %d: Empty object found",
		    LineNum);
	    break;
	case IP_ERR_MIXED_TYPES:
	    sprintf(GlblTokenError,
		    "Line %d: Mixed data types in same object",
		    LineNum);
	    break;
	case IP_ERR_STR_NOT_IN_QUOTES:
	    sprintf(GlblTokenError,
		    "Line %d: String not in quotes (%s)",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_OBJECT_EXPECTED:
	    sprintf(GlblTokenError,
		    "Line %d: 'OBJECT' expected, found '%s'",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_CAGD_LIB_ERR:
	case IP_ERR_TRIM_LIB_ERR:
	case IP_ERR_TRIV_LIB_ERR:
	    sprintf(GlblTokenError, "Line %d: %s",
		    LineNum, TempCopy);
	    break;
	case IP_ERR_STACK_OVERFLOW:
	    sprintf(GlblTokenError, "Line %d: Parser Stack overflow",
		    LineNum);
	    break;
	case IP_ERR_DEGEN_POLYGON:
	    sprintf(GlblTokenError, "Line %d: Degenerate polygon",
		    LineNum);
	    break;
	case IP_ERR_DEGEN_NORMAL:
	    sprintf(GlblTokenError, "Line %d: Degenerate normal",
		    LineNum);
	    break;
	case IP_ERR_SOCKET_BROKEN:
	    sprintf(GlblTokenError, "Line %d: Socket connection is broken",
		    LineNum);
	    break;
	case IP_ERR_SOCKET_TIME_OUT:
	    sprintf(GlblTokenError, "Line %d: Socket connection is broken",
		    LineNum);
	    break;
	case IP_ERR_BIN_IN_TEXT:
	    sprintf(GlblTokenError, "Binary information in text file - %s",
		    TempCopy);
	    break;
	case IP_ERR_BIN_UNDEF_OBJ:
	    sprintf(GlblTokenError, "Binary stream: Undefined object");
	    break;
	default:
	    sprintf(GlblTokenError,
		    "Line %d: Data file parser - undefined error",
		    LineNum);
	    break;
    }

    *ErrorMsg = GlblTokenError;

    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to test if the given polygon is convex (by IRIT definition) or     M
* not.									     M
*   Algorithm: The polygon is convex iff the normals generated from cross    M
* products of two consecutive edges points to the same direction. The same   M
* direction is tested by a positive dot product.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   Pl:         To test for convexity.                                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE if PL convex, FALSE otherwise.                          M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrIsConvexPolygon, convexity, files, parser                        M
*****************************************************************************/
int IritPrsrIsConvexPolygon(IPPolygonStruct *Pl)
{
    RealType Size, V1[3], V2[3], LastNormal[3], Normal[3];
    IPVertexStruct *VNext, *VNextNext,
	*V = Pl -> PVertex;

    LastNormal[0] = LastNormal[1] = LastNormal[2] = 0.0;

    do {
	if ((VNext = V -> Pnext) == NULL)
	    VNext = Pl -> PVertex;
	if ((VNextNext = VNext -> Pnext) == NULL)
	    VNextNext = Pl -> PVertex;

	PT_SUB(V1, VNext -> Coord, V -> Coord);
	if ((Size = PT_LENGTH(V1)) > EPSILON) {
	    Size = 1.0 / Size;
	    PT_SCALE(V1, Size);
	}
	PT_SUB(V2, VNextNext -> Coord, VNext -> Coord);
	if ((Size = PT_LENGTH(V2)) > EPSILON) {
	    Size = 1.0 / Size;
	    PT_SCALE(V2, Size);
	}
	CROSS_PROD(Normal, V1, V2);

	if (V != Pl -> PVertex) {
	    if (PT_LENGTH(Normal) > CONVEX_EPSILON &&
		DOT_PROD(Normal, LastNormal) < -CONVEX_EPSILON)
		return FALSE;
	}

	PT_COPY(LastNormal, Normal);

	V = VNext;
    }
    while (V != Pl -> PVertex && V != NULL);

    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into stdout.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To be put out to stdout.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrStdoutObject, files                                              M
*****************************************************************************/
void IritPrsrStdoutObject(IPObjectStruct *PObj)
{
    IritPrsrPutObjectToFile(stdout, PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into stderr.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To be put out to stderr.                                      M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrStderrObject, files                                              M
*****************************************************************************/
void IritPrsrStderrObject(IPObjectStruct *PObj)
{
    IritPrsrPutObjectToFile(stderr, PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into given file FileName.	     M
*   If FileName is NULL or empty, print using IritPrsrPrintFunc.	     M
*   See function IritPrsrSetPrintFunc, IritPrsrSetFloatFormat.    	     M
*                                                                            *
* PARAMETERS:                                                                M
*   f:        Output stream.                                                 M
*   PObj:     Object to put on output stream.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPutObjectToFile, files                                           M
*****************************************************************************/
void IritPrsrPutObjectToFile(FILE *f, IPObjectStruct *PObj)
{
    int Handler = -1;

    /* If the following gain control and is non zero - its from error! */
    if (setjmp(_IritPrsrLongJumpBuffer) != 0) {
	IritPrsrCloseStream(Handler, TRUE);
	return;
    }

    Handler = IritPrsrOpenStreamFromFile(f, FALSE, FALSE, FALSE);

    if (f != NULL && f != stdout && f != stderr)
	IRIT_DATA_HEADER(f, "Irit");

    if (_IPStream[Handler].IsBinary)
        IritPrsrPutBinObject(Handler, PObj);
    else
	IritPrsrPutAllObjects(PObj, Handler, 0);

    IritPrsrCloseStream(Handler, TRUE);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to print the data from given object into given file FileName.	     M
*   If FileName is NULL or empty, print using IritPrsrPrintFunc.	     M
*   See function IritPrsrSetPrintFunc, IritPrsrSetFloatFormat.   	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:  A handler to the open stream.				     M
*   PObj:     Object to put on output stream.                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPutObjectToHandler, files                                        M
*****************************************************************************/
void IritPrsrPutObjectToHandler(int Handler, IPObjectStruct *PObj)
{
    if (_IPStream[Handler].IsBinary)
        IritPrsrPutBinObject(Handler, PObj);
    else
	IritPrsrPutAllObjects(PObj, Handler, 0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to print out the data from given object.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:      Object to put out.                                            *
*   Handler:   A handler to the open stream.				     *
*   Indent:    Indentation to put object at.                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IritPrsrPutAllObjects(IPObjectStruct *PObj,
				  int Handler,
				  int Indent)
{
    int i, IsRational, NumCoords;
    char Str[LINE_LEN],
	*ErrStr = NULL;
    CagdRType *Coords;
    IPObjectStruct *PObjTmp;
    IPPolygonStruct *PPolygon;
    IPVertexStruct *PVertex;
    IPAttributeStruct
	*Attr = AttrTraceAttributes(PObj -> Attrs, PObj -> Attrs);

    if (Attr) {
	_IPFprintf(Handler, Indent, "[OBJECT\n");

	IritPrsrPutAttributes(Handler, Attr, Indent);

	_IPFprintf(Handler, Indent + 4,
		 "%s\n", strlen(PObj -> Name) ? PObj -> Name : "NONE");
    }
    else {
	_IPFprintf(Handler, Indent, "[OBJECT %s\n",
	         strlen(PObj -> Name) ? PObj -> Name : "NONE");
    }
    Indent += 4;

    switch (PObj -> ObjType) {
	case IP_OBJ_POLY:
	    for (PPolygon = PObj -> U.Pl;
		 PPolygon != NULL;
		 PPolygon = PPolygon -> Pnext) {
		if (PPolygon -> PVertex == NULL)
		    continue;

		if (IP_IS_POLYLINE_OBJ(PObj))
		    _IPFprintf(Handler, Indent, "[POLYLINE ");
		else if (IP_IS_POINTLIST_OBJ(PObj))
		    _IPFprintf(Handler, Indent, "[POINTLIST ");
		else
		    _IPFprintf(Handler,
			Indent, "[POLYGON [PLANE %s %s %s %s] ",
			_IPReal2Str(PPolygon -> Plane[0]),
			_IPReal2Str(PPolygon -> Plane[1]),
			_IPReal2Str(PPolygon -> Plane[2]),
			_IPReal2Str(PPolygon -> Plane[3]));

		IritPrsrPutAttributes(Handler, PPolygon -> Attrs, Indent);

		for (PVertex = PPolygon -> PVertex -> Pnext, i = 1;
		     PVertex != PPolygon -> PVertex && PVertex != NULL;
		     PVertex = PVertex -> Pnext, i++);
		_IPFprintf(Handler, Indent + 4, "%d\n", i);

		PVertex = PPolygon -> PVertex;
		do {		     /* Assume at least one edge in polygon! */
		    _IPFprintf(Handler, Indent + 4, "[");
		    
		    IritPrsrPutAttributes(Handler, PVertex -> Attrs, Indent);

		    if (IP_IS_POLYLINE_OBJ(PObj) ||
			(IP_IS_POLYGON_OBJ(PObj) &&
			 PT_APX_EQ(PPolygon -> Plane, PVertex -> Normal)))
			_IPFprintf(Handler, 0, "%s%s %s %s]\n",
			    IP_IS_INTERNAL_VRTX(PVertex) ? "[INTERNAL] " : "",
			    _IPReal2Str(PVertex -> Coord[0]),
			    _IPReal2Str(PVertex -> Coord[1]),
			    _IPReal2Str(PVertex -> Coord[2]));
		    else if (IP_IS_POINTLIST_OBJ(PObj))
			_IPFprintf(Handler, 0, "%s %s %s]\n",
			    _IPReal2Str(PVertex -> Coord[0]),
			    _IPReal2Str(PVertex -> Coord[1]),
			    _IPReal2Str(PVertex -> Coord[2]));
		    else
			_IPFprintf(Handler, 0,
			    "%s[NORMAL %s %s %s] %s %s %s]\n",
			    IP_IS_INTERNAL_VRTX(PVertex) ? "[INTERNAL] " : "",
			    _IPReal2Str(PVertex -> Normal[0]),
			    _IPReal2Str(PVertex -> Normal[1]),
			    _IPReal2Str(PVertex -> Normal[2]),
			    _IPReal2Str(PVertex -> Coord[0]),
			    _IPReal2Str(PVertex -> Coord[1]),
			    _IPReal2Str(PVertex -> Coord[2]));

		    PVertex = PVertex -> Pnext;
		}
		while (PVertex != PPolygon -> PVertex && PVertex != NULL);
		_IPFprintf(Handler, Indent, "]\n");    /* Close the polygon. */
	    }
	    break;
	case IP_OBJ_NUMERIC:
	    _IPFprintf(Handler, Indent, "[NUMBER %s]\n",
		       _IPReal2Str(PObj -> U.R));
	    break;
	case IP_OBJ_POINT:
	    _IPFprintf(Handler, Indent, "[POINT %s %s %s]\n",
		     _IPReal2Str(PObj -> U.Pt[0]),
		     _IPReal2Str(PObj -> U.Pt[1]),
		     _IPReal2Str(PObj -> U.Pt[2]));
	    break;
	case IP_OBJ_VECTOR:
	    _IPFprintf(Handler, Indent, "[VECTOR %s %s %s]\n",
		     _IPReal2Str(PObj -> U.Vec[0]),
		     _IPReal2Str(PObj -> U.Vec[1]),
		     _IPReal2Str(PObj -> U.Vec[2]));
	    break;
	case IP_OBJ_PLANE:
	    _IPFprintf(Handler, Indent, "[PLANE %s %s %s %s]\n",
		     _IPReal2Str(PObj -> U.Plane[0]),
		     _IPReal2Str(PObj -> U.Plane[1]),
		     _IPReal2Str(PObj -> U.Plane[2]),
		     _IPReal2Str(PObj -> U.Plane[3]));
	    break;
	case IP_OBJ_CTLPT:
	    Coords = PObj -> U.CtlPt.Coords;
	    IsRational = CAGD_IS_RATIONAL_PT(PObj -> U.CtlPt.PtType);
	    NumCoords = CAGD_NUM_OF_PT_COORD(PObj -> U.CtlPt.PtType);

	    sprintf(Str, "[CTLPT %c%d %s", IsRational ? 'P' : 'E', NumCoords,
		    IsRational ? _IPReal2Str(Coords[0]) : "");
	    
	    for (i = 1; i <= NumCoords; i++) {
		strcat(Str, " ");
	        strcat(Str, _IPReal2Str(Coords[i]));
	    }
	    strcat(Str,"]\n");
	    _IPFprintf(Handler, Indent, Str);
	    break;
	case IP_OBJ_MATRIX:
	    _IPFprintf(Handler, Indent, "[MATRIX\n");
	    for (i = 0; i < 4; i++)
		_IPFprintf(Handler, Indent + 8, "%s %s %s %s%s\n",
			 _IPReal2Str((*PObj -> U.Mat)[i][0]),
			 _IPReal2Str((*PObj -> U.Mat)[i][1]),
			 _IPReal2Str((*PObj -> U.Mat)[i][2]),
			 _IPReal2Str((*PObj -> U.Mat)[i][3]),
			 i == 3 ? "]" : "");
	    break;
	case IP_OBJ_STRING:
	    _IPFprintf(Handler, Indent, "[STRING \"%s\"]\n", PObj -> U.Str);
	    break;
	case IP_OBJ_LIST_OBJ:
	    for (i = 0; (PObjTmp = ListObjectGet(PObj, i)) != NULL; i++)
		IritPrsrPutAllObjects(PObjTmp, Handler, Indent);
	    break;
	case IP_OBJ_CURVE:
    	    CagdCrvWriteToFile2(PObj -> U.Crvs, Handler,
				Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_SURFACE:
	    CagdSrfWriteToFile2(PObj -> U.Srfs, Handler,
				Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_TRIMSRF:
	    TrimWriteTrimmedSrfToFile2(PObj -> U.TrimSrfs, Handler,
				       Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_TRIM_LIB_ERR, ErrStr);
	    break;
	case IP_OBJ_TRIVAR:
	    TrivTVWriteToFile2(PObj -> U.Trivars, Handler,
			       Indent, NULL, &ErrStr);
	    if (ErrStr != NULL)
		_IPParserAbort(IP_ERR_TRIV_LIB_ERR, ErrStr);
	    break;
	default:
	    IritPrsrFatalError("Attemp to print undefine object type.");
	    break;
    }

    Indent -= 4;
    _IPFprintf(Handler, Indent, "]\n");			/* Close the object. */
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Same as fprintf but with indentation.					     *
*   See function IritPrsrSetPrintFunc, IritPrsrSetFloatFormat		     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:    A handler to the open stream.				     *
*   Indent:     All printing will start at this column.                      *
*   va_alist:   Do "man stdarg"                                              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
#ifdef USE_VARARGS
void _IPFprintf(int Handler, int Indent, char *va_alist, ...)
{
    char *Format, Line[LINE_LEN_LONG];
    int i;
    va_list ArgPtr;

    va_start(ArgPtr);
    Format = va_arg(ArgPtr, char *);
#else
void _IPFprintf(int Handler, int Indent, char *Format, ...)
{
    char Line[LINE_LEN_LONG];
    int i;
    va_list ArgPtr;

    va_start(ArgPtr, Format);
#endif /* USE_VARARGS */

    if (IritPrsrPrintFunc != NULL || _IPStream[Handler].f != NULL) {
	for (i = 0; Indent >= 8; i++, Indent -= 8)
	    Line[i] = '\t';
	while (Indent--)
	    Line[i++] = ' ';
	vsprintf(&Line[i], Format, ArgPtr);

	if (_IPStream[Handler].f != NULL)
	    fprintf(_IPStream[Handler].f, Line);
	else
	    IritPrsrPrintFunc(Line);
    }
    else {     /* _IPStream[Handler].f == NULL and it is a socket connction. */
	/* No need for indentation if writing to a socket. */
	vsprintf(Line, Format, ArgPtr);
	SocWriteLine(Handler, Line, strlen(Line));
    }

    va_end(ArgPtr);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to print the attributes of given attribute list.		     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:    A handler to the open stream.				     *
*   Attr:     Attributes to put out.                                         *
*   Indent:   Indentation to put attributes at.                              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IritPrsrPutAttributes(int Handler,
				  IPAttributeStruct *Attr,
				  int Indent)
{
    Attr = AttrTraceAttributes(Attr, Attr);

    while (Attr) {
	if (Attr -> Type == IP_ATTR_OBJ) {
	    _IPFprintf(Handler, Indent + 4, "[%s\n", Attr -> Name);
	    IritPrsrPutAllObjects(Attr -> U.PObj, Handler, Indent + 8);
	    _IPFprintf(Handler, Indent + 4, "]\n");
	}
	else 
	    _IPFprintf(Handler, Indent + 4, "%s\n", Attr2String(Attr));

	Attr = AttrTraceAttributes(Attr, NULL);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Convert a real number into a string.					     *
*   The routine maintains six different buffers simultanuously so up to six  *
* consecutive calls can be issued from same printf and stiil have valid      *
* strings.								     *
*                                                                            *
* PARAMETERS:                                                                *
*   R:        A  real number to convert to a string.                         *
*                                                                            *
* RETURN VALUE:                                                              *
*   char *:   A string representing R.                                       *
*****************************************************************************/
char *_IPReal2Str(RealType R)
{
    static int j, k,
	i = 0;
    static char Buffer[6][LINE_LEN_SHORT], Line[LINE_LEN];

    if (ABS(R) < ZERO_NUM_EPSILON)
	R = 0.0;	 		   /* Round off very small numbers. */

    sprintf(Buffer[i], _IPGlblFloatFormat, R);

    for (k = 0; !isdigit(Buffer[i][k]) && k < LINE_LEN; k++);
    if (k >= LINE_LEN) {
	sprintf(Line, "Conversion of real number (%f) failed.", R);
	IritPrsrFatalError(Line);
    }

    for (j = strlen(Buffer[i]) - 1; Buffer[i][j] == ' ' && j > k; j--);
    if (strchr(Buffer[i], '.') != NULL &&
	strchr(Buffer[i], 'e') == NULL &&
	strchr(Buffer[i], 'E') == NULL)
	for (; Buffer[i][j] == '0' && j > k; j--);
    Buffer[i][j + 1] = 0;

    j = i;
    i = (i + 1) % 6;
    return Buffer[j];
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to last vertex of a list.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   VList:    A list of vertices                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:    Last vertex in VList.                               M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetLastVrtx, linked lists, last element                          M
*****************************************************************************/
IPVertexStruct *IritPrsrGetLastVrtx(IPVertexStruct *VList)
{
    return IritPrsrGetPrevVrtx(VList, NULL);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to previous vertex in VList to V.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   VList:    A list of vertices.                                            M
*   V:        For which the previous vertex in VList is pursuit.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:   Previous vertex to V in VList if found, NULL         M
*                       otherwise.                                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetPrevVrtx, previous element, linked lists                      M
*****************************************************************************/
IPVertexStruct *IritPrsrGetPrevVrtx(IPVertexStruct *VList, IPVertexStruct *V)
{
    IPVertexStruct
	*VHead = VList;

    if (VList == NULL || VList == V)
	return NULL;

    for ( ;
	  VList != NULL && VList -> Pnext != V && VList -> Pnext != VHead;
	  VList = VList -> Pnext);

    return VList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Appends two vertex lists together.         				     M
*                                                                            *
* PARAMETERS:                                                                M
*   VList1, VList2:  Two lists to append.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:    Appended list.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrAppendVrtxLists, linked lists                                    M
*****************************************************************************/
IPVertexStruct *IritPrsrAppendVrtxLists(IPVertexStruct *VList1,
					IPVertexStruct *VList2)
{
    if (VList1 == NULL)
        return VList2;
    else if (VList2 == NULL)
        return VList1;
    else {
	IPVertexStruct
	    *VLast = IritPrsrGetLastVrtx(VList1);

	VLast -> Pnext = VList2;

	return VList1;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to last polygon/line of a list.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PList:    A list of polygons.                                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Last polygon in list PList.                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetLastPoly, linked lists, last element                          M
*****************************************************************************/
IPPolygonStruct *IritPrsrGetLastPoly(IPPolygonStruct *PList)
{
    return IritPrsrGetPrevPoly(PList, NULL);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to previous polygon in PList to P.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   PList:    A list of polygons.                                            M
*   P:        For which the previous polygon in PList is pursuit.            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:   Previous polygon to P in PList if found, NULL        M
*                       otherwise.                                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetPrevPoly, previous element, linked lists                      M
*****************************************************************************/
IPPolygonStruct *IritPrsrGetPrevPoly(IPPolygonStruct *PList,
				     IPPolygonStruct *P)
{
    if (PList == NULL || PList == P)
	return NULL;

    for ( ; PList != NULL && PList -> Pnext != P; PList = PList -> Pnext);

    return PList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Appends two poly lists together.         				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PList1, PList2:  Two lists to append.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Appended list.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrAppendPolyLists, linked lists                                    M
*****************************************************************************/
IPPolygonStruct *IritPrsrAppendPolyLists(IPPolygonStruct *PList1,
					 IPPolygonStruct *PList2)
{
    if (PList1 == NULL)
        return PList2;
    else if (PList2 == NULL)
        return PList1;
    else {
	IPPolygonStruct
	    *PLast = IritPrsrGetLastPoly(PList1);

	PLast -> Pnext = PList2;

	return PList1;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to last object of a list.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   OList:    A list of objects.                                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Last object in list OList.                         M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetLastObj, linked lists, last element                           M
*****************************************************************************/
IPObjectStruct *IritPrsrGetLastObj(IPObjectStruct *OList)
{
    return IritPrsrGetPrevObj(OList, NULL);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns a pointer to previous object in OList to O.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   OList:    A list of objects.                                             M
*   O:        For which the previous object in OList is pursuit.             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPVertexStruct *:   Previous object to O in OList if found, NULL         M
*                       otherwise.                                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetPrevObj, previous element, linked lists                       M
*****************************************************************************/
IPObjectStruct *IritPrsrGetPrevObj(IPObjectStruct *OList, IPObjectStruct *O)
{
    if (OList == NULL || OList == O)
	return NULL;

    for ( ; OList != NULL && OList -> Pnext != O; OList = OList -> Pnext);
	if (OList == NULL)
	    return NULL;

    return OList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Appends two object lists together.          				     M
*                                                                            *
* PARAMETERS:                                                                M
*   OList1, OList2:  Two lists to append.                                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:    Appended list.                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrAppendObjLists, linked lists                                     M
*****************************************************************************/
IPObjectStruct *IritPrsrAppendObjLists(IPObjectStruct *OList1,
				       IPObjectStruct *OList2)
{
    if (OList1 == NULL)
        return OList2;
    else if (OList2 == NULL)
        return OList1;
    else {
	IPObjectStruct
	    *OLast = IritPrsrGetLastObj(OList1);

	OLast -> Pnext = OList2;

	return OList1;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns the length of a list of vertices.				     M
*                                                                            *
* PARAMETERS:                                                                M
*   V:        Vertex list to compute its length.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Number of elements in V list.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrVrtxListLen, length, linked lists                                M
*****************************************************************************/
int IritPrsrVrtxListLen(IPVertexStruct *V)
{
    int i;

    for (i = 0; V != NULL; i++, V = V -> Pnext);

    return i;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns the length of a list of polygons. 				     M
*                                                                            *
* PARAMETERS:                                                                M
*   P:        Polygon list to compute its length.                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Number of elements in P list.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPolyListLen, length, linked lists                                M
*****************************************************************************/
int IritPrsrPolyListLen(IPPolygonStruct *P)
{
    int i;

    for (i = 0; P != NULL; i++, P = P -> Pnext);

    return i;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Returns the length of a list of objects. 				     M
*                                                                            *
* PARAMETERS:                                                                M
*   O:        Object list to compute its length.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Number of elements in O list.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrObjListLen, length, linked lists                                 M
*****************************************************************************/
int IritPrsrObjListLen(IPObjectStruct *O)
{
    int i;

    for (i = 0; O != NULL; i++, O = O -> Pnext);

    return i;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the printing function to call if needs to redirect printing.          M
*                                                                            *
* PARAMETERS:                                                                M
*   PrintFunc:   A function that gets a single string it should print.       M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetPrintFunc, files                                              M
*****************************************************************************/
void IritPrsrSetPrintFunc(IritPrsrPrintFuncType PrintFunc)
{
    IritPrsrPrintFunc = PrintFunc;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the floating point printing format.                                   M
*                                                                            *
* PARAMETERS:                                                                M
*   FloatFormat:    A printf style floating point printing format string.    M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetFloatFormat, files                                            M
*****************************************************************************/
void IritPrsrSetFloatFormat(char *FloatFormat)
{
    char Str[LINE_LEN];

    /* Not a full-proof test but something. */
    if (strlen(FloatFormat) >= 2 &&
	strchr(FloatFormat, '%') != NULL &&
	(strchr(FloatFormat, 'e') != NULL ||
	 strchr(FloatFormat, 'f') != NULL ||
	 strchr(FloatFormat, 'g') != NULL ||
	 strchr(FloatFormat, 'E') != NULL ||
	 strchr(FloatFormat, 'F') != NULL ||
	 strchr(FloatFormat, 'G') != NULL)) {
	_IPGlblFloatFormat = FloatFormat;
    }
    else {
	sprintf(Str, "Illegal floating point format \"%s\".", FloatFormat);
	IritPrsrFatalError(Str);
    }	
}
