/*****************************************************************************
* Generic parser for the "Irit" solid modeller.				     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* 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 "geomat3d.h"
#include "allocate.h"
#include "attribut.h"
#include "irit_soc.h"

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

#if defined(AMIGA) && defined(__SASC)
#include "popen.h"
#endif

IPStreamInfoStruct _IPStream[MAX_NUM_OF_STREAMS];

static int
    GlblFlattenObjects = TRUE,	   /* If input list hierarchy is to be kept. */
    GlblFlattenInvisibObjects = TRUE;   /* Should we flatten invisible objs. */

static IPObjectStruct
    *GlblResolveInstObjects = NULL;

static IPObjectStruct *EliminateDegenLists(IPObjectStruct *PObj);
static void IritPrsrPropagateAttrsAux(IPObjectStruct *PObj, 
				      IPAttributeStruct *Attrs,
				      IPObjectStruct *AnimAttrs);
static int InputGetC(int Handler);
static int InputEOF(int Handler);
static int GetStringToken(int Handler, char *StringToken, int *Quoted);
static void GetVertexAttributes(IPVertexStruct *PVertex, int Handler);
static void GetPolygonAttributes(IPPolygonStruct *PPolygon,
				 int Handler);
static void GetObjectAttributes(IPObjectStruct *PObject, int Handler);
static void GetGenericAttribute(IPAttributeStruct **Attrs,
				int Handler,
				char *Name);
static void GetPointData(int Handler,
			 IPPolygonStruct *PPolygon,
			 int IsPolygon);
static void IritPrsrGetAllObjects(int Handler,
				  IPObjectStruct *PObjParent,
				  int Level);
static void GetNumericToken(int Handler, RealType *r);
static void IritPrsrGetAuxObject(int Handler, IPObjectStruct *PObj);
static int FindFileHandler(void);
static IPObjectStruct *IritPrsrResolveInstancesAux(IPObjectStruct *PObj);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Open a data file for read/write.					     M
*   Data file can be either Ascii IRIT data file or binary IRIT data file.   M
*   A binary data file must have a ".bdt" (for Binary DaTa) file type.       M
*   Under unix, file names with the psotfix ".Z" are assumed compressed and  M
* treated accordingly.							     M
*                                                                            *
* PARAMETERS:                                                                M
*   FileName:   To try and open.                                             M
*   Read:       If TRUE assume a read operation, otheriwse write.            M
*   Messages:   Do we want error/warning messages to stderr?                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        A handler to the open file, -1 if error.		     M
*                                                                            *
* SEE ALSO:                                                                  M
*   IritPrsrGetObjects, IritPrsrSetPolyListCirc, IritPrsrSetFlattenObjects,  M
* IritPrsrSetReadOneObject						     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrOpenDataFile, files, parser                                      M
*****************************************************************************/
int IritPrsrOpenDataFile(char *FileName, int Read, int Messages)
{
    FILE *f;
    int ReadWriteBinary = IritPrsrSenseBinaryFile(FileName),
	IsPipe = FALSE;
#if defined(__UNIX__) || defined(OS2GCC)
    char *p;
#endif /* __UNIX__ || OS2GCC */

    if (Read) {
	if (strcmp(FileName, "-") == 0) {
	    f = stdin;
	}
#if defined(__UNIX__) || defined(OS2GCC)
	else if ((p = strrchr(FileName, '.')) != NULL &&
		 strcmp(p, ".Z") == 0) {
	    char Cmd[LINE_LEN];

	    sprintf(Cmd, "zcat %s", FileName);
	    f = popen(Cmd, "r");
	    IsPipe = TRUE;
	}
#endif /* __UNIX__ || OS2GCC */
	else {
	    if ((f = fopen(FileName, "r")) == NULL) {
		if (Messages)
		    fprintf(stderr, "Can't open data file %s.\n", FileName);
		return -1;
	    }
	}
    }
    else { /* Write */
	if (strcmp(FileName, "-") == 0) {
	    f = stdout;
	}
#if defined(__UNIX__) || defined(OS2GCC)
	else if ((p = strrchr(FileName, '.')) != NULL &&
		 strcmp(p, ".Z") == 0) {
	    char Cmd[LINE_LEN];

	    sprintf(Cmd, "compress > %s", FileName);
	    f = popen(Cmd, "w");
	    IsPipe = TRUE;
	}
#endif /* __UNIX__ || OS2GCC */
	else {
	    if ((f = fopen(FileName, "w")) == NULL) {
		if (Messages)
		    fprintf(stderr, "Can't open data file %s.\n", FileName);
		return -1;
	    }
	}
    }

    return IritPrsrOpenStreamFromFile(f, Read, ReadWriteBinary, IsPipe);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Senses if a given file (name) is a binary or a text file.                M
*                                                                            *
* PARAMETERS:                                                                M
*   FileName:  File to sense.                                                M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:       TRUE if binary, FALSE if text.                                M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSenseBinaryFile                                                  M
*****************************************************************************/
int IritPrsrSenseBinaryFile(char *FileName)
{
    return strstr(FileName, ".bdt") || strstr(FileName, ".BDT");
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Close a data file for read/write.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:     A handler to the open stream.				     M
*   Free:	 If TRUE, release content.      			     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrCloseStream, files, stream, parser                               M
*****************************************************************************/
void IritPrsrCloseStream(int Handler, int Free)
{
    if (Handler >= 0 && Handler < MAX_NUM_OF_STREAMS) {
	if (Free) {
	    if (_IPStream[Handler].f != NULL) {
#ifdef __UNIX__
		if (_IPStream[Handler].IsPipe)
		    pclose(_IPStream[Handler].f);
		else
#endif /* __UNIX__ */
		    if (_IPStream[Handler].f != stdin &&
			_IPStream[Handler].f != stdout &&
			_IPStream[Handler].f != stderr)
			fclose(_IPStream[Handler].f);
	    }

	    if (_IPStream[Handler].CommuSoc) {
		if (_IPStream[Handler].Read)
		    SocClientCloseSocket(Handler);
		else
		    SocServerCloseSocket(Handler);
	    }
	}
	_IPStream[Handler].InUse = FALSE;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Converts an open file into a stream.                                     M
*                                                                            *
* PARAMETERS:                                                                M
*   f:         A handle to the open file.                                    M
*   Read:      TRUE for reading from f, FALSE for writing to f.              M
*   IsBinary:  Is it a binary file?                                          M
*   IsPipe:    Is it a pipe?                                                 M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:       A handle on the constructed stream.                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrOpenStreamFromFile                                               M
*****************************************************************************/
int IritPrsrOpenStreamFromFile(FILE *f, int Read, int IsBinary, int IsPipe)
{
    int Handler = FindFileHandler();

#if defined(__OS2GCC__)
    if (IsBinary)
	setmode(fileno(f), O_BINARY);    /* Make sure it is in binary mode. */
#endif /* __OS2GCC__ */
#if defined(__WINNT__)
    if (IsBinary)
	_setmode(_fileno(f), _O_BINARY); /* Make sure it is in binary mode. */
#endif /* __WINNT__ */

    if (Handler >= 0) {
	_IPStream[Handler].f = f;
	_IPStream[Handler].Read = Read;
	_IPStream[Handler].IsBinary = IsBinary;
	_IPStream[Handler].IsPipe = IsPipe;
    }

    return Handler;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Converts an open socket into a stream.                                   M
*                                                                            *
* PARAMETERS:                                                                M
*   Soc:       A handle to the open socket.                                  M
*   Read:      TRUE for reading from f, FALSE for writing to f.              M
*   IsBinary:  Is it a binary file?                                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:       A handle on the constructed stream.                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrOpenStreamFromSocket                                             M
*****************************************************************************/
int IritPrsrOpenStreamFromSocket(int Soc, int Read, int IsBinary)
{
    int Handler = FindFileHandler();

    if (Handler >= 0) {
	_IPStream[Handler].f = NULL;
	_IPStream[Handler].CommuSoc = Soc;
	_IPStream[Handler].IsBinary = IsBinary;
	_IPStream[Handler].Read = Read;
    }

    return Handler;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Searches and returns a free Open File handler.                           *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:     Free handler, or -1 if none found.                              *
*****************************************************************************/
static int FindFileHandler(void)
{
    int i,
	Handler = -1;

    for (i = 0; i < MAX_NUM_OF_STREAMS; i++)
	if (!_IPStream[i].InUse) {
	    _IPStream[i].InUse = TRUE;
	    _IPStream[i].TokenStackPtr = 0;
	    _IPStream[i].LineNum = 0;
	    _IPStream[i].UnGetChar = -1;
	    _IPStream[i].BufferSize = 0;
	    _IPStream[i].BufferPtr = 0;
	    Handler = i;
	    break;
	}

    if (i < 0)
	IritPrsrFatalError("Stream table is full.");

    return Handler;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Reads data from a set of files specified by file names.		     M
*    Messages and MoreMessages controls the level of printout to stderr.     M
*    Freeform geometry read in is handed out to a call back function named   M
* IritPrsrProcessFreeForm before it is returned from this routine. This      M
* is done so applications that do not want to deal with freeform shapes will M
* be able to provide a call back that processes the freeform shapes into     M
* other geometry such as polygons.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   DataFileNames:    Array of strings (file names) to process.              M
*   NumOfDataFiles:   Number of elements in DataFileNames.                   M
*   Messages:         Do we want error messages?                             M
*   MoreMessages:     Do we want informative messages?                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Objects read from all files.                         M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetDataFiles, files, parser                                      M
*****************************************************************************/
IPObjectStruct *IritPrsrGetDataFiles(char **DataFileNames,
				     int NumOfDataFiles,
				     int Messages,
				     int MoreMessages)
{
    int	i, Handler;
    char *ErrorMsg;
    IPObjectStruct
	*PObjHead = NULL;

    for	(i = 0; i < NumOfDataFiles; i++) {
	if (MoreMessages)
	    fprintf(stderr, "Reading data file %s\n", *DataFileNames);

	if ((Handler = IritPrsrOpenDataFile(*DataFileNames,
					    TRUE, Messages)) < 0)
	    continue;

	PObjHead = IritPrsrAppendObjLists(IritPrsrGetObjects(Handler),
					  PObjHead);

	if (Messages &&
	    IritPrsrParseError(_IPStream[Handler].LineNum, &ErrorMsg))
	    fprintf(stderr, "File %s, %s\n", *DataFileNames, ErrorMsg);

	IritPrsrCloseStream(Handler, TRUE);

	DataFileNames++;			  /* Skip to next file name. */
    }

    if (PObjHead == NULL) {
	if (Messages) {
	    char *ErrMsg = "";

	    if (_IritPrsrGlblParserError != IP_NO_ERR)
		IritPrsrParseError(0, &ErrMsg);
	    fprintf(stderr, "No data found. %s\n", ErrMsg);
	}
	return NULL;
    }

    return PObjHead;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Resolves, in place, all instances in the objects into their proper       M
* geometry.  This functions hence eliminates all instances' objects while    M
* increasing the size of the data, and do so in place.                       M
*                                                                            *
* PARAMETERS:                                                                M
*   PObjects:      To eliminate instances from, in place.                    M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:    Same geometry as in PObjects but without instances. M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrResolveInstances                                                 M
*****************************************************************************/
IPObjectStruct *IritPrsrResolveInstances(IPObjectStruct *PObjects)
{
    IPObjectStruct *PObj, *Prev;

    GlblResolveInstObjects = PObjects;

    /* Map the instances to their geometry, in line. */
    for (PObj = PObjects, Prev = NULL; PObj != NULL; ) {
	if (PObj == PObjects) {
	    GlblResolveInstObjects =
	        PObjects = IritPrsrResolveInstancesAux(PObj);
	    Prev = PObjects;
	}
	else {
	    Prev -> Pnext = IritPrsrResolveInstancesAux(PObj);
	    Prev = Prev -> Pnext;
	}
	PObj = Prev -> Pnext;
    }

    GlblResolveInstObjects = NULL;

    return PObjects;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Auxiliary function of IritPrsrResolveInstances, in place                 *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:      To eliminate instances from, in place.                        *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:  Same geometry but instances free.                     *
*****************************************************************************/
static IPObjectStruct *IritPrsrResolveInstancesAux(IPObjectStruct *PObj)
{
    if (IP_IS_INSTNC_OBJ(PObj)) {
	IPObjectStruct
	    *PTmp = IPGetObjectByName(PObj -> U.Instance -> Name,
				      GlblResolveInstObjects, FALSE);

	if (PTmp != NULL) {
	    PTmp = GMTransformObject(PTmp, PObj -> U.Instance -> Mat);
	    PTmp -> Pnext = PObj -> Pnext;
	    IPFreeObject(PObj);
	    AttrSetObjectIntAttrib(PTmp, "WasInstance", TRUE);
	    AttrFreeObjectAttribute(PTmp, "invisible");

	    return PTmp;
	}
	else {
	    fprintf(stderr,
		    "Failed to locate base geometry \"%s\" of instance \"%s\"\n",
		    PObj -> U.Instance -> Name, PObj -> Name);
	    return NULL;
	}
    }
    else if (IP_IS_OLST_OBJ(PObj)) {
	int i;
	IPObjectStruct *PTmp;

	for (i = 0; (PTmp = ListObjectGet(PObj, i)) != NULL; i++) {
	    IPObjectStruct
		*PTmpNew = IritPrsrResolveInstancesAux(PTmp);

	    if (PTmpNew != PTmp)
	        ListObjectInsert(PObj, i, PTmpNew);
	}
	return PObj;
    }
    else
	return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to read the data from	a given	file.				     M
*   Returns NULL if EOF was reached or error occured.			     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:     A handler to the open stream.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Read object, or NULl if failed.                      M
*                                                                            *
* SEE ALSO:                                                                  M
*    IritPrsrSetPolyListCirc, IritPrsrSetFlattenObjects,		     M
* IritPrsrSetReadOneObject, IritPrsrTransformInstances.			     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrGetObjects, files, parser                                        M
*****************************************************************************/
IPObjectStruct *IritPrsrGetObjects(int Handler)
{
    IPObjectStruct *PObj;

    /* If the following gain control and is non zero - its from error! */
    if (setjmp(_IritPrsrLongJumpBuffer) != 0)
	return NULL;

    if (_IPStream[Handler].IsBinary) {
	PObj = IritPrsrGetBinObject(Handler);
    }
    else {
	PObj = IPAllocObject("", IP_OBJ_UNDEF, NULL);

	_IritPrsrGlblParserError = IP_NO_ERR;		    /* Reset errors. */

	IritPrsrGetAllObjects(Handler, PObj, 0);

	if (IP_IS_UNDEF_OBJ(PObj)) {
	    IPFreeObject(PObj);
	    return NULL;
	}
    }

    return IritPrsrProcessReadObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Filters out degenetared list objects with zero or one elements.	     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:      Read object.                                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPObjectStruct *:   Same object as Pobj but if PObj is a degenerated     *
*                       of one element or even zero element, it is fixed up. *
*****************************************************************************/
static IPObjectStruct *EliminateDegenLists(IPObjectStruct *PObj)
{
    if (PObj == NULL)
	return NULL;

    if (IP_IS_OLST_OBJ(PObj)) {
        if (ListObjectGet(PObj, 0) == NULL) {
	    /* Nothing read in. */
	    IPFreeObject(PObj);
	    PObj = NULL;
	    _IPParserAbort(IP_ERR_FILE_EMPTY, "");
	}
	else if (ListObjectGet(PObj, 1) == NULL) {
	    IPObjectStruct
		*PTmp = ListObjectGet(PObj, 0);

	    /* Only one object in list - return the object instead. */
	    ListObjectInsert(PObj, 0, NULL);
	    IPFreeObject(PObj);
	    PObj = PTmp;
	}
    }

    return PObj;
}
    
/*****************************************************************************
* DESCRIPTION:                                                               M
* Process a read object, in place, before returning it to the caller.	     M
*   List objects of zero or one elements are eliminated.                     M
*   Attributes are propagated throughout the hierarchy.			     M
*   If FlattenTree mode (see IritPrsrSetFlattenObjects) hierarchy is         M
* flattened out.			       				     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Object to process.                                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Processed object, in place.                          M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrProcessReadObject, files, parser                                 M
*****************************************************************************/
IPObjectStruct *IritPrsrProcessReadObject(IPObjectStruct *PObj)
{
    if (PObj == NULL)
	return NULL;

    PObj = EliminateDegenLists(PObj);

    IritPrsrPropagateAttrs(PObj);

    if (GlblFlattenObjects && PObj != NULL)
	PObj = IritPrsrFlattenTree(PObj);

    return PObj;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Controls vertex list in polygons. Do we want it circular?		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Circ:     If TRUE, vertex lists of polygons will be circular. If FALSE,  M
*             the lists will be NULL terminated.			     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      old value of flag.                                             M
*                                                                            *
* SEE ALSO:                                                                  M
*   IritPrsrGetObjects                                                       M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetPolyListCirc, files, parser                                   M
*****************************************************************************/
int IritPrsrSetPolyListCirc(int Circ)
{
    int OldVal = _IritPrsrPolyListCirc;

    _IritPrsrPolyListCirc = Circ;

    return OldVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Controls the hierarchy flattening of a read object.       		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Flatten:  If TRUE, list objects will be flattened out to a long linear   M
*             list. If FALSE, read object will be unchanged.		     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Old value of flatten state.                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetFlattenObjects, files, parser                                 M
*****************************************************************************/
int IritPrsrSetFlattenObjects(int Flatten)
{
    int OldFlatten = GlblFlattenObjects;

    GlblFlattenObjects = Flatten;

    return OldFlatten;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Controls the hierarchy flattening of a read object.       		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Flatten:  If TRUE, list objects will be flattened out to a long linear   M
*             list. If FALSE, read object will be unchanged.		     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      Old value of flatten state.                                    M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetFlattenObjects, files, parser                                 M
*****************************************************************************/
int IritPrsrFlattenInvisibleObjects(int FlattenInvisib)
{
    int OldFlattenInvisib = GlblFlattenInvisibObjects;

    GlblFlattenInvisibObjects = FlattenInvisib;

    return OldFlattenInvisib;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Controls the way the Ascii parser handle multiple objects in a file.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   OneObject: If TRUE, only next object will be read by IritPrsrGetObjectst.M
*             If FALSE, objects will be read until EOF is detected and       M
*	      placed in a linked list.					     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* SEE ALSO:                                                                  M
*   IritPrsrGetObjects                                                       M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetReadOneObject, files, parser                                  M
*****************************************************************************/
void IritPrsrSetReadOneObject(int OneObject)
{
    _IritPrsrReadOneObject = OneObject;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Propagate attributes from list objects down into their elements.	     M
* Animation Attributes are accumulated.                          	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:       To propagate down Attrs attributes.                          M
*   Attrs:      Attributes to propagate.                                     M
*   AnimAttrs:  Animations Attributes to concatenate (object list).          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPropagateAttrs, attributes, files, parser                        M
*****************************************************************************/
void IritPrsrPropagateAttrs(IPObjectStruct *PObj)
{
    IritPrsrPropagateAttrsAux(PObj, NULL, NULL);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Auxiliary function of IritPrsrPropagateAttrs.                            *
*****************************************************************************/
static void IritPrsrPropagateAttrsAux(IPObjectStruct *PObj, 
				      IPAttributeStruct *Attrs,
				      IPObjectStruct *AnimAttrs)
{
    IPAttributeStruct *Attr;
    IPObjectStruct *AnimCurr, *AnimObj, *TmpAnimAttrs, *TmpAnimAttrs2;
    
    if (AnimAttrs != NULL) {
        AnimAttrs = CopyObject(NULL, AnimAttrs, TRUE);
	AnimAttrs -> Pnext = NULL;
    }

    /* Concatenate the PObj's animation attributes after AnimAttrs. */
    if ((AnimObj = AttrGetObjAttrib(PObj -> Attrs, "animation")) != NULL) {
	if (AnimAttrs == NULL)
	    AnimAttrs = IPAllocObject("AnimAttrs", IP_OBJ_LIST_OBJ, NULL);

	if (IP_IS_OLST_OBJ(AnimObj)) {
	    TmpAnimAttrs = AnimAttrs;
	    AnimAttrs = IritPrsrAppendLists(AnimObj,TmpAnimAttrs); /* concat */
	    IPFreeObject(TmpAnimAttrs);
	}
	else {
	    /* Insert in the beginning. */
	    AnimCurr = CopyObject(NULL, AnimObj, TRUE);
	    AnimCurr -> Pnext = NULL;
            TmpAnimAttrs2 = IPAllocObject("AnimAttrs", IP_OBJ_LIST_OBJ, NULL);
	    ListObjectInsert(TmpAnimAttrs2, 0, AnimCurr);
	    ListObjectInsert(TmpAnimAttrs2, 1, NULL);
            TmpAnimAttrs = AnimAttrs;
	    AnimAttrs = IritPrsrAppendLists(TmpAnimAttrs2,TmpAnimAttrs);
	    IPFreeObject(TmpAnimAttrs);
	    IPFreeObject(TmpAnimAttrs2);
	}
    }
    
    if (IP_IS_OLST_OBJ(PObj)) {
	int i;
	IPObjectStruct *PTmp;
	
	/* Collect all attributes of this list (including inherited ones)    */
	/* and propagate them down to the list items.			     */
	if (Attrs != NULL)
	    Attrs = AttrCopyAttributes(Attrs);
	
	for (Attr = PObj -> Attrs; Attr != NULL; Attr = Attr -> Pnext) {
	    if ((stricmp(Attr -> Name, "animation") != 0) && 
                (!AttrFindAttribute(Attrs, Attr -> Name))) {
		IPAttributeStruct
		    *TmpAttr = AttrCopyOneAttribute(Attr);
		
		TmpAttr -> Pnext = Attrs;
		Attrs = TmpAttr;
	    }
	}
	
	for (i = 0; (PTmp = ListObjectGet(PObj, i)) != NULL; i++) {
	    IritPrsrPropagateAttrsAux(PTmp, Attrs, AnimAttrs);
        }

	AttrFreeAttributes(&Attrs);
	IPFreeObject(AnimAttrs);
	AttrFreeOneAttribute(&PObj -> Attrs, "animation");
    }
    else {
	/* Regular object - add to its attribute list every attribute in   */
	/* Attrs that is not found in its attribute list.		   */
        /* Also concatenate AnimAttrs before its animation attribute.      */
	
        /* Inserts the modified animation attributes list. */
	if (AnimAttrs != NULL) {
	    AttrSetObjAttrib(&PObj -> Attrs, "animation", AnimAttrs, 0);
	}

	for (Attr = Attrs; Attr != NULL; Attr = Attr -> Pnext) {
	    if (!AttrFindAttribute(PObj -> Attrs, Attr -> Name)) {
		IPAttributeStruct
		    *TmpAttr = AttrCopyOneAttribute(Attr);

		TmpAttr -> Pnext = PObj -> Attrs;
		PObj -> Attrs = TmpAttr;
	    }
	}
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Flattens out a tree hierarchy of objects into a linear list, in place. As  M
* a side effect freeform entities are processed by IritPrsrProcessFreeForm.  M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Object(s) to flatten out, in place.                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Flattened hierarchy.                                 M
*                                                                            *
* SEE ALSO:                                                                  M
*   IritPrsrProcessFreeForm, IritPrsrEvalFreeForms                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrFlattenTree                                                      M
*****************************************************************************/
IPObjectStruct *IritPrsrFlattenTree(IPObjectStruct *PObj)
{
    IritPrsrFreeFormStruct IPFreeForm;

    IPFreeForm.CrvObjs = NULL;
    IPFreeForm.SrfObjs = NULL;
    IPFreeForm.TrimSrfObjs = NULL;
    IPFreeForm.TrivarObjs = NULL;
    IPFreeForm.TriSrfObjs = NULL;
    IPFreeForm.ModelObjs = NULL;

    if (ATTR_OBJ_IS_INVISIBLE(PObj) && !GlblFlattenInvisibObjects)
       return NULL;

    if (PObj -> Pnext != NULL)
	return PObj;		    /* Can only flatten a single hierarchy. */

    if (IP_IS_OLST_OBJ(PObj)) {
	int i;
	IPObjectStruct *PTmp, *PTmp2,
	    *RetListTail = NULL,
	    *RetList = NULL;

	for (i = 0; (PTmp = ListObjectGet(PObj, i)) != NULL; i++) {
#ifdef IRIT_PRSR_ZERO_NAMES
	    /* Zero name of object if it is interior to list. */
	    if (strnicmp(PTmp -> Name, "VIEW_MAT", 8) != 0 &&
		strnicmp(PTmp -> Name, "PRSP_MAT", 8) != 0)
		PTmp -> Name[0] = 0;
#endif /* IRIT_PRSR_ZERO_NAMES */

	    if ((PTmp2 = IritPrsrFlattenTree(PTmp)) != NULL) {
		if (RetList != NULL)
		    RetListTail -> Pnext = PTmp2;
		else
		    RetList = PTmp2;
		RetListTail = IritPrsrGetLastObj(PTmp2);
	    }
	}

	ListObjectInsert(PObj, 0, NULL);
	IPFreeObject(PObj);

	return RetList;
    }
    else if (IP_IS_CRV_OBJ(PObj)) {
	IPFreeForm.CrvObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_SRF_OBJ(PObj)) {
	IPFreeForm.SrfObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRIMSRF_OBJ(PObj)) {
	IPFreeForm.TrimSrfObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRIVAR_OBJ(PObj)) {
	IPFreeForm.TrivarObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRISRF_OBJ(PObj)) {
	IPFreeForm.TriSrfObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_MODEL_OBJ(PObj)) {
	IPFreeForm.ModelObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else {
	return PObj;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Evaluates the freeforms in the given hierarchy - usually to convert into   M
* a polygonal approximation.  This function invokes IritPrsrProcessFreeForm  M
* for the evaluation of the individual freeform entities.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Object(s) to freeform evaluate, in place.                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Evaluated hierarchy, in place.                       M
*                                                                            *
* SEE ALSO:                                                                  M
*   IritPrsrProcessFreeForm                                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrEvalFreeForms                                                    M
*****************************************************************************/
IPObjectStruct *IritPrsrEvalFreeForms(IPObjectStruct *PObj)
{
    IritPrsrFreeFormStruct IPFreeForm;

    IPFreeForm.CrvObjs = NULL;
    IPFreeForm.SrfObjs = NULL;
    IPFreeForm.TrimSrfObjs = NULL;
    IPFreeForm.TrivarObjs = NULL;
    IPFreeForm.TriSrfObjs = NULL;
    IPFreeForm.ModelObjs = NULL;

    if (IP_IS_OLST_OBJ(PObj)) {
	int i;
	IPObjectStruct *PTmp;

	for (i = 0; (PTmp = ListObjectGet(PObj, i)) != NULL; i++)
	    ListObjectInsert(PObj, i, IritPrsrEvalFreeForms(PTmp));

	return PObj;
    }
    else if (IP_IS_CRV_OBJ(PObj)) {
	IPFreeForm.CrvObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_SRF_OBJ(PObj)) {
	IPFreeForm.SrfObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRIMSRF_OBJ(PObj)) {
	IPFreeForm.TrimSrfObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRIVAR_OBJ(PObj)) {
	IPFreeForm.TrivarObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRISRF_OBJ(PObj)) {
	IPFreeForm.TriSrfObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_MODEL_OBJ(PObj)) {
	IPFreeForm.ModelObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else {
	return PObj;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Flattens out a list of trees' hierarchy (a forrest) of objects into a      M
* linear list, in place.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      List of object(s) to flatten out.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Flattened hierarchy.                                 M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrFlattenForrest                                                   M
*****************************************************************************/
IPObjectStruct *IritPrsrFlattenForrest(IPObjectStruct *PObjList)
{
    IPObjectStruct *PObj,
	*PNewList = NULL,
	*PNewLast = NULL;

    for (PObj = PObjList; PObj != NULL; ) {
	IPObjectStruct *PFlat,
	    *Pnext = PObj -> Pnext;

	PObj -> Pnext = NULL;
	if ((PFlat = IritPrsrFlattenTree(PObj)) != NULL) {
	    if (PNewList == NULL) {
		PNewList = PFlat;
		PNewLast = IritPrsrGetLastObj(PFlat);
	    }
	    else {
		PNewLast -> Pnext = PFlat;
		PNewLast = IritPrsrGetLastObj(PFlat);
	    }
	}

	PObj = Pnext;
    }

    return PNewList;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to read the geometry data from a given file. Reads "[OBJECT ..."   *
* prefixes only and invoke the auxiliary routine.			     *
*   Objects may be recursively defined.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:     A handler to the open stream.				     *
*   PObjParent:  One list object, this read object should be hooked as an    *
*                element.						     *
*   Level:       Of recursion.                                               *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IritPrsrGetAllObjects(int Handler,
				  IPObjectStruct *PObjParent,
				  int Level)
{
    char StringToken[LINE_LEN_LONG];
    IPTokenType Token;
    int	i,
	WasObjectToken = FALSE,
	ObjCount = 0,
	Quit = FALSE;
    IPObjectStruct *PObj;

    while (!Quit) {
    	while ((Token = _IPGetToken(Handler, StringToken)) !=
							IP_TOKEN_OPEN_PAREN &&
	       Token != IP_TOKEN_CLOSE_PAREN &&
	       Token != IP_TOKEN_EOF);

	if (Token == IP_TOKEN_CLOSE_PAREN || Token == IP_TOKEN_EOF) {
	    if (Token == IP_TOKEN_CLOSE_PAREN)
		_IPUnGetToken(Handler, StringToken);
	    Quit = TRUE;
	    break;
	}

	switch (_IPGetToken(Handler, StringToken)) {
	    case IP_TOKEN_OBJECT:
		WasObjectToken = TRUE;

	        ReallocNewTypeObject(PObjParent, IP_OBJ_LIST_OBJ);
		PObj = IPAllocObject("", IP_OBJ_UNDEF, NULL);

		/* The following handle optional attributes in record. */
		if (_IPGetToken(Handler, StringToken) == IP_TOKEN_OPEN_PAREN)
		    GetObjectAttributes(PObj, Handler);
		else {
		    _IPUnGetToken(Handler, StringToken);
		}

		if (AttrGetObjectColor(PObj) == IP_ATTR_NO_COLOR)
		    AttrSetObjectColor(PObj, IP_LOAD_COLOR);

		if (_IPGetToken(Handler, StringToken) == IP_TOKEN_OTHER &&
		    stricmp(StringToken, "NONE") != 0) {
		    for (i = 0; i < strlen(StringToken); i++)
		        PObj -> Name[i] =
			    islower(StringToken[i]) ? toupper(StringToken[i])
						    : StringToken[i];
		    PObj -> Name[i] = 0;
		}

		IritPrsrGetAllObjects(Handler, PObj, Level + 1);

		_IPGetCloseParenToken(Handler);

		if (IP_IS_UNDEF_OBJ(PObj))
		    _IPParserAbort(IP_ERR_OBJECT_EMPTY, "");

		ListObjectInsert(PObjParent, ObjCount++, PObj);
		break;
	    default:
		if (WasObjectToken) {
		    _IPParserAbort(IP_ERR_OBJECT_EXPECTED, StringToken);
		}
		_IPUnGetToken(Handler, StringToken);
		_IPUnGetToken(Handler, "[");
		IritPrsrGetAuxObject(Handler, PObjParent);
		if (IP_IS_POLY_OBJ(PObjParent)) {
		    PObjParent -> U.Pl =
		        IritPrsrReversePlList(PObjParent -> U.Pl);
		}
		Quit = TRUE;
		break;
	}

	if (Level == 0 && WasObjectToken && _IritPrsrReadOneObject)
	    Quit = TRUE;
    }

    if (IP_IS_OLST_OBJ(PObjParent)) {
	ListObjectInsert(PObjParent, ObjCount++, NULL);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to get close paren token from FILE f.				     M
*   This function invokes the parser's abort routine, if no close paren.     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:     A handler to the open stream.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   _IPGetCloseParenToken						     M
*****************************************************************************/
void _IPGetCloseParenToken(int Handler)
{
    char StringToken[LINE_LEN_LONG];

    if (_IPGetToken(Handler, StringToken) != IP_TOKEN_CLOSE_PAREN)
	_IPParserAbort(IP_ERR_CLOSE_PAREN_EXPECTED, StringToken);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to skip to the next closed parenthesis.                            M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:     A handler to the open stream.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:	TRUE, if found close paren.                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   _IPSkipToCloseParenToken						     M
*****************************************************************************/
int _IPSkipToCloseParenToken(int Handler)
{
    char StringToken[LINE_LEN_LONG];
    IPTokenType
	Token = IP_TOKEN_EOF;

    while (!InputEOF(Handler) &&
	  (Token = _IPGetToken(Handler, StringToken)) != IP_TOKEN_CLOSE_PAREN);

    return Token == IP_TOKEN_CLOSE_PAREN;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to get one numeric token into r.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:     A handler to the open stream.				     *
*   r:           Where numeric data should go to.                            *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetNumericToken(int Handler, RealType *r)
{
    char StringToken[LINE_LEN_LONG];

    _IPGetToken(Handler, StringToken);
    if (sscanf(StringToken, IP_IRIT_FLOAT_READ, r) != 1)
        _IPParserAbort(IP_ERR_NUMBER_EXPECTED, StringToken);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to read the content of a single object.                            *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:     A handler to the open stream.				     *
*   PObj:        Where to place the read object.                             *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void IritPrsrGetAuxObject(int Handler, IPObjectStruct *PObj)
{
    int	i, j, ErrLine;
    IPTokenType
	Token = IP_TOKEN_NONE;
    char *ErrStr, StringToken[LINE_LEN_LONG];
    CagdRType *Coords;
    IPPolygonStruct *PPolygon;
    CagdCrvStruct *PCurve;
    CagdSrfStruct *PSurface;
    TrimSrfStruct *PTrimSrf;
    TrivTVStruct *PTrivar;
    TrngTriangSrfStruct *PTriSrf;
    MdlModelStruct *PModel;

    ReallocNewTypeObject(PObj, IP_OBJ_UNDEF);

    while (_IPGetToken(Handler, StringToken) == IP_TOKEN_OPEN_PAREN) {
	switch (Token = _IPGetToken(Handler, StringToken)) {
	    case IP_TOKEN_POLYGON:
	    case IP_TOKEN_POLYLINE:
	    case IP_TOKEN_POINTLIST:
		ReallocNewTypeObject(PObj, IP_OBJ_POLY);
		PPolygon = IPAllocPolygon(0, NULL, NULL);
		switch (Token) {
		    case IP_TOKEN_POLYGON:
			IP_SET_POLYGON_OBJ(PObj);
			break;
		    case IP_TOKEN_POLYLINE:
			IP_SET_POLYLINE_OBJ(PObj);
			break;
		    case IP_TOKEN_POINTLIST:
			IP_SET_POINTLIST_OBJ(PObj);
			break;
		    default:
			_IPParserAbort(IP_ERR_UNDEF_EXPR_HEADER,
				       StringToken);
			break;
		}

		/* The following handle the optional attributes in struct.   */
		if (_IPGetToken(Handler, StringToken) == IP_TOKEN_OPEN_PAREN)
		    GetPolygonAttributes(PPolygon, Handler);
		else
		    _IPUnGetToken(Handler, StringToken);

		/* The following handles reading the vertices. */
		GetPointData(Handler, PPolygon, IP_IS_POLYGON_OBJ(PObj));

		if (IP_IS_POLYGON_OBJ(PObj)) {
		    if (!IP_HAS_PLANE_POLY(PPolygon))
			IritPrsrUpdatePolyPlane(PPolygon);

		    IritPrsrUpdateVrtxNrml(PPolygon, PPolygon -> Plane);
		}

		PPolygon -> Pnext = PObj -> U.Pl;
		PObj -> U.Pl = PPolygon;
		break;
	    case IP_TOKEN_SURFACE:
		ReallocNewTypeObject(PObj, IP_OBJ_SURFACE);
		ErrLine = _IPStream[Handler].LineNum;
		PSurface = CagdSrfReadFromFile2(Handler, &ErrStr, &ErrLine);
		_IPStream[Handler].LineNum = ErrLine;

		if (ErrStr != NULL) {
		    _IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		if (PSurface != NULL) {
		    PSurface -> Pnext = PObj -> U.Srfs;
		    PObj -> U.Srfs = PSurface;
		}
		break;
	    case IP_TOKEN_CURVE:
		ReallocNewTypeObject(PObj, IP_OBJ_CURVE);
		ErrLine = _IPStream[Handler].LineNum;
		PCurve = CagdCrvReadFromFile2(Handler, &ErrStr, &ErrLine);
		_IPStream[Handler].LineNum = ErrLine;

		if (ErrStr != NULL) {
		    _IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		if (PCurve != NULL) {
		    PCurve -> Pnext = PObj -> U.Crvs;
		    PObj -> U.Crvs = PCurve;
		}
		break;
	    case IP_TOKEN_TRIMSRF:
		ReallocNewTypeObject(PObj, IP_OBJ_TRIMSRF);
		ErrLine = _IPStream[Handler].LineNum;
		PTrimSrf = TrimReadTrimmedSrfFromFile2(Handler, TRUE,
						       &ErrStr, &ErrLine);
		_IPStream[Handler].LineNum = ErrLine;

		if (ErrStr != NULL) {
		    _IPParserAbort(IP_ERR_TRIM_LIB_ERR, ErrStr);
		    break;
		}

		if (PTrimSrf != NULL) {
		    PTrimSrf -> Pnext = PObj -> U.TrimSrfs;
		    PObj -> U.TrimSrfs = PTrimSrf;
		}
		break;
	    case IP_TOKEN_TRIVAR:
		ReallocNewTypeObject(PObj, IP_OBJ_TRIVAR);
		ErrLine = _IPStream[Handler].LineNum;
		PTrivar = TrivTVReadFromFile2(Handler, &ErrStr, &ErrLine);
		_IPStream[Handler].LineNum = ErrLine;

		if (ErrStr != NULL) {
		    _IPParserAbort(IP_ERR_TRIV_LIB_ERR, ErrStr);
		    break;
		}

		if (PTrivar != NULL) {
		    PTrivar -> Pnext = PObj -> U.Trivars;
		    PObj -> U.Trivars = PTrivar;
		}
		break;
	    case IP_TOKEN_TRISRF:
		ReallocNewTypeObject(PObj, IP_OBJ_TRISRF);
		ErrLine = _IPStream[Handler].LineNum;
		PTriSrf = TrngTriSrfReadFromFile2(Handler, &ErrStr, &ErrLine);
		_IPStream[Handler].LineNum = ErrLine;

		if (ErrStr != NULL) {
		    _IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		if (PTriSrf != NULL) {
		    PTriSrf -> Pnext = PObj -> U.TriSrfs;
		    PObj -> U.TriSrfs = PTriSrf;
		}
		break;
	    case IP_TOKEN_MODEL:
		ReallocNewTypeObject(PObj, IP_OBJ_MODEL);
		ErrLine = _IPStream[Handler].LineNum;
		PModel = MdlReadModelFromFile2(Handler, TRUE,
					       &ErrStr, &ErrLine);
		_IPStream[Handler].LineNum = ErrLine;

		if (ErrStr != NULL) {
		    _IPParserAbort(IP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		if (PModel != NULL) {
		    PModel -> Pnext = PObj -> U.Mdls;
		    PObj -> U.Mdls = PModel;
		}
		break;
	    case IP_TOKEN_NUMBER:
		ReallocNewTypeObject(PObj, IP_OBJ_NUMERIC);
		GetNumericToken(Handler, &PObj -> U.R);
		_IPGetCloseParenToken(Handler);
		break;
	    case IP_TOKEN_STRING:
		ReallocNewTypeObject(PObj, IP_OBJ_STRING);
		_IPGetToken(Handler, PObj -> U.Str);
		_IPGetCloseParenToken(Handler);
		break;
	    case IP_TOKEN_POINT:
		ReallocNewTypeObject(PObj, IP_OBJ_POINT);
		for (i = 0; i < 3; i++)
		    GetNumericToken(Handler, &PObj -> U.Pt[i]);
		_IPGetCloseParenToken(Handler);
		break;
	    case IP_TOKEN_VECTOR:
		ReallocNewTypeObject(PObj, IP_OBJ_VECTOR);
		for (i = 0; i < 3; i++)
		    GetNumericToken(Handler, &PObj -> U.Vec[i]);
		_IPGetCloseParenToken(Handler);
		break;
	    case IP_TOKEN_PLANE:
		ReallocNewTypeObject(PObj, IP_OBJ_PLANE);
		for (i = 0; i < 4; i++)
		    GetNumericToken(Handler, &PObj -> U.Plane[i]);
		_IPGetCloseParenToken(Handler);
		break;
	    case IP_TOKEN_MATRIX:
		ReallocNewTypeObject(PObj, IP_OBJ_MATRIX);
		for (i = 0; i < 4; i++)
		    for (j = 0; j < 4; j++)
			GetNumericToken(Handler, &(*PObj -> U.Mat)[i][j]);
		_IPGetCloseParenToken(Handler);

		if (strnicmp(PObj -> Name, "VIEW_MAT", 8) == 0) {
		    IritPrsrWasViewMat = TRUE;
		    MAT_COPY(IritPrsrViewMat, PObj -> U.Mat);
		}
		else if (strnicmp(PObj -> Name, "PRSP_MAT", 8) == 0) {
		    IritPrsrWasPrspMat = TRUE;
		    MAT_COPY(IritPrsrPrspMat, PObj -> U.Mat);
		}
		break;
	    case IP_TOKEN_INSTANCE:
		ReallocNewTypeObject(PObj, IP_OBJ_INSTANCE);
		_IPGetToken(Handler, StringToken);
		PObj -> U.Instance -> Name = IritStrdup(StringToken);
		for (i = 0; i < 4; i++)
		    for (j = 0; j < 4; j++)
			GetNumericToken(Handler,
					&PObj -> U.Instance -> Mat[i][j]);
		_IPGetCloseParenToken(Handler);
		break;
	    case IP_TOKEN_CTLPT:
		ReallocNewTypeObject(PObj, IP_OBJ_CTLPT);
		_IPGetToken(Handler, StringToken);

		i = atoi(&StringToken[1]);
		if ((StringToken[0] == 'P' || StringToken[0] == 'E' ) &&
		    i > 0 && i < 6) {
		   j = StringToken[0] == 'E';
		   PObj -> U.CtlPt.PtType = CAGD_MAKE_PT_TYPE(!j, i);
		}
		else {
		    _IPParserAbort(IP_ERR_PT_TYPE_EXPECTED, StringToken);
		    i = j = 0;
		    break;
		}

		Coords = PObj -> U.CtlPt.Coords;
		for ( i += 1 - j; i > 0; i--)
		    GetNumericToken(Handler, &Coords[j++]);
		_IPGetCloseParenToken(Handler);
		break;
	    default:
		_IPParserAbort(IP_ERR_UNDEF_EXPR_HEADER, StringToken);
		break;
	} /* Of switch. */
    } /* Of while. */

    _IPUnGetToken(Handler, StringToken);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to unget one token (on stack of UNGET_STACK_SIZE levels!)	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:       A handler to the open stream.			     M
*   StringToken:   Token to unget                                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   _IPUnGetToken							     M
*****************************************************************************/
void _IPUnGetToken(int Handler, char *StringToken)
{
    if (_IPStream[Handler].TokenStackPtr >= UNGET_STACK_SIZE)
	 _IPParserAbort(IP_ERR_STACK_OVERFLOW, "");

    strcpy(_IPStream[Handler].TokenStack[_IPStream[Handler].TokenStackPtr++],
	   StringToken);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to unget a single character from input stream.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:   A handler to the open stream.				     M
*   c:         Character to unget.                                           M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrInputUnGetC, files, parser                                       M
*****************************************************************************/
void IritPrsrInputUnGetC(int Handler, char c)
{
    _IPStream[Handler].UnGetChar = c;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to get a single character from input stream.			     *
*   If input returns EOF block until new input arrives (can happen if        *
* reading from a non io blocked socket).				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:   A handler to the open stream.				     *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:       Read character.                                               *
*****************************************************************************/
static int InputGetC(int Handler)
{
    int c;

    if (_IPStream[Handler].UnGetChar >= 0) {
	c = _IPStream[Handler].UnGetChar;

	_IPStream[Handler].UnGetChar = -1;
    }
    else if (_IPStream[Handler].f != NULL) {
	c = getc(_IPStream[Handler].f);
    }
    else {
	while ((c = SocReadCharNonBlock(Handler)) == EOF)
	    IritSleep(10);
    }

    if (c < ' ' && c > 0 && c != '\n' && c != '\r' && c != '\t')
        _IPParserAbort(IP_ERR_BIN_IN_TEXT, "Is it a binary file!?");

    return c;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to test for EOF condition in input stream.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:   A handler to the open stream.				     *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:      TRUE if EOF detected.                                          *
*****************************************************************************/
static int InputEOF(int Handler)
{
    if (_IPStream[Handler].f != NULL)
	return feof(_IPStream[Handler].f);
    else
	return FALSE;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to get the next token out of the input file f.		     *
*   Returns TRUE if !InputEOF and the next token found in StringToken.	     *
*   StringToken must be allocated before calling this routine!		     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:      A handler to the open stream.				     *
*   StringToken:  String token will be placed herein.                        *
*   Quoted:       If we detected a quoated string: "xxx yyy".                *
*                                                                            *
* RETURN VALUE:                                                              *
*   int:          TRUE if successful.                                        *
*****************************************************************************/
static int GetStringToken(int Handler, char *StringToken, int *Quoted)
{
    int	len;
    char *LocalStringToken,
	c = EOF;

    *Quoted = FALSE;

    if (_IPStream[Handler].TokenStackPtr) { /*	get first the unget token */
	strcpy(StringToken, _IPStream[Handler].TokenStack[--_IPStream[Handler].
							       TokenStackPtr]);
	return TRUE;
    }
    /* skip white spaces: */
    while ((!InputEOF(Handler)) &&
	   (((c = InputGetC(Handler)) == ' ') || (c == '\t') || (c == '\n')) &&
	   (c != (char) EOF))
	if (c == '\n')
	    _IPStream[Handler].LineNum++;		 /* Count the lines. */

    LocalStringToken = StringToken;
    if (c == '[')		      /* Its a token by	itself so return it. */
	*LocalStringToken++ = c;	      /* Copy the token	into string. */
    else {
	if (!InputEOF(Handler) && (c != (char) EOF)) {
	    if (c == '"') {
		*Quoted = TRUE;
		while ((!InputEOF(Handler)) &&
		       ((c = InputGetC(Handler)) != '"') &&
		       (c != '\n') &&
		       (c != (char) EOF)) {
		    *LocalStringToken++ = c;      /* Copy the quoted string. */
		    if (c == '\\') {
			/* Next character is quoted - copy verbatim. */
			*--LocalStringToken = c = InputGetC(Handler);
			LocalStringToken++;
		    }
		}
	    }
	    else {
		do
		    *LocalStringToken++ = c;  /* Copy the token into string. */
		while ((!InputEOF(Handler)) &&
		       ((c = InputGetC(Handler)) != ' ') &&
		       (c != '\t') &&
		       (c != '\n') &&
		       (c != (char) EOF));
	    }
	    if (!InputEOF(Handler) && c == '\n')
	        IritPrsrInputUnGetC(Handler, c);      /* Save for next time. */
	}
    }
    *LocalStringToken =	0;					 /* Put	eos. */

    /* The following handles the spacial case were we have XXXX] - we must   */
    /* split it	into two token XXXX and	], _IPUnGetToken(']') & return XXXX: */
    if (!*Quoted &&
	(StringToken[len = strlen(StringToken) - 1] == ']') &&
	(len > 0)) {
	/* Return CloseParan */
	_IPUnGetToken(Handler, &StringToken[len]);	 /* Save next token. */
	StringToken[len] = 0;			/* Set end of string on	"]". */
    }

    return !InputEOF(Handler) && (c != (char) EOF);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to get the next token out of the input file f as token number.     M
*   StringToken must be allocated before calling this routine!		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:       A handler to the open stream.			     M
*   StringToken:   String token will be placed herein.                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPTokenType:   Token as a numeral.                                       M
*                                                                            *
* KEYWORDS:                                                                  M
*   _IPGetToken						       		     M
*****************************************************************************/
IPTokenType _IPGetToken(int Handler, char *StringToken)
{
    static IPTokenType IntTokens[] = {
	IP_TOKEN_OPEN_PAREN,
	IP_TOKEN_CLOSE_PAREN,
	IP_TOKEN_E1,
	IP_TOKEN_P1,
	IP_TOKEN_E2,
	IP_TOKEN_P2,
	IP_TOKEN_E3,
	IP_TOKEN_P3,
	IP_TOKEN_E4,
	IP_TOKEN_P4,
	IP_TOKEN_E5,
	IP_TOKEN_P5,
	IP_TOKEN_NUMBER,
	IP_TOKEN_STRING,
	IP_TOKEN_POINT,
	IP_TOKEN_VECTOR,
	IP_TOKEN_MATRIX,
	IP_TOKEN_CTLPT,
	IP_TOKEN_VERTEX,
	IP_TOKEN_POLYGON,
	IP_TOKEN_POLYLINE,
	IP_TOKEN_POINTLIST,
	IP_TOKEN_OBJECT,
	IP_TOKEN_COLOR,
	IP_TOKEN_RGB,
	IP_TOKEN_INTERNAL,
	IP_TOKEN_NORMAL,
	IP_TOKEN_PLANE,
	IP_TOKEN_CURVE,
	IP_TOKEN_SURFACE,
	IP_TOKEN_BEZIER,
	IP_TOKEN_BSPLINE,
	IP_TOKEN_POWER,
	IP_TOKEN_TRIVAR,
	IP_TOKEN_PTYPE,
	IP_TOKEN_NUM_PTS,
	IP_TOKEN_ORDER,
	IP_TOKEN_KV,
	IP_TOKEN_KVP,
	IP_TOKEN_TRIMMDL,
	IP_TOKEN_TRIMSRF,
	IP_TOKEN_TRIMCRV,
	IP_TOKEN_TRIMCRVSEG,
	IP_TOKEN_INSTANCE,
	IP_TOKEN_TRISRF,
	IP_TOKEN_MODEL,
	IP_TOKEN_MDLTSEG,
	IP_TOKEN_MDLTSRF,
	IP_TOKEN_MDLLOOP,

	IP_TOKEN_NONE
    };
    static char *StrTokens[] = {
	"[",
	"]",
	"E1",
	"P1",
	"E2",
	"P2",
	"E3",
	"P3",
	"E4",
	"P4",
	"E5",
	"P5",
	"NUMBER",
	"STRING",
	"POINT",
	"VECTOR",
	"MATRIX",
	"CTLPT",
	"VERTEX",
	"POLYGON",
	"POLYLINE",
	"POINTLIST",
	"OBJECT",
	"COLOR",
	"RGB",
	"INTERNAL",
	"NORMAL",
	"PLANE",
	"CURVE",
	"SURFACE",
	"BEZIER",
	"BSPLINE",
	"POWER",
	"TRIVAR",
	"PTYPE",
	"NUMPTS",
	"ORDER",
	"KV",
	"KVP",
	"TRIMMODEL",
	"TRIMSRF",
	"TRIMCRV",
	"TRIMCRVSEG",
	"INSTANCE",
	"TRISRF",
	"MODEL",
	"MDLTSEG",
	"MDLTSRF",
	"MDLLOOP",

	NULL
    };
    int i, Quoted;

    if (!GetStringToken(Handler, StringToken, &Quoted))
	return IP_TOKEN_EOF;

    if (Quoted)
	return IP_TOKEN_QUOTED;

    for (i = 0; StrTokens[i] != NULL; i++)
	if (stricmp(StringToken, StrTokens[i]) == 0)
	    return IntTokens[i];

    return IP_TOKEN_OTHER;			  /* Must be number or name. */
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
*   The first '[' was already read.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   PVertex:    Where attributes should go to.                               *
*   Handler:    A handler to the open stream.				     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetVertexAttributes(IPVertexStruct *PVertex, int Handler)
{
    int i;
    RealType Len;
    char StringToken[LINE_LEN_LONG];

    do {
	switch (_IPGetToken(Handler, StringToken)) {
	    case IP_TOKEN_INTERNAL:
		_IPGetCloseParenToken(Handler);
		IP_SET_INTERNAL_VRTX(PVertex);
		break;
	    case IP_TOKEN_NORMAL:
		/* The following handles reading 3 coord. of vertex normal. */
		for (i = 0; i < 3; i++)
		    GetNumericToken(Handler, &PVertex -> Normal[i]);

		/* Make sure it is normalized. */
		Len = PT_LENGTH(PVertex -> Normal);
		if (Len > 0) {
		    for (i = 0; i < 3; i++)
			PVertex -> Normal[i] /= Len;
		    IP_SET_NORMAL_VRTX(PVertex);
		}
		_IPGetCloseParenToken(Handler);
		break;
	    default:
		GetGenericAttribute(&PVertex -> Attrs, Handler, StringToken);
		break;
	}
    }
    while (_IPGetToken(Handler, StringToken) == IP_TOKEN_OPEN_PAREN);

    PVertex -> Attrs = AttrReverseAttributes(PVertex -> Attrs);

    _IPUnGetToken(Handler, StringToken);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
*   The first '[' was already read.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   PPolygon:   Where attributes should go to.                               *
*   Handler:    A handler to the open stream.				     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetPolygonAttributes(IPPolygonStruct *PPolygon, int Handler)
{
    int i;
    RealType Len;
    char StringToken[LINE_LEN_LONG];

    do {
	switch (_IPGetToken(Handler, StringToken)) {
	    case IP_TOKEN_PLANE:
		/* The following handles reading of 4 coord. of plane eqn.. */
		for (i = 0; i < 4; i++)
		    GetNumericToken(Handler, &PPolygon -> Plane[i]);

		/* Make sure it is normalized. */
		Len = PT_LENGTH(PPolygon -> Plane);
		if (Len > 0)
		    for (i = 0; i < 4; i++)
			PPolygon -> Plane[i] /= Len;
		else
		    _IPParserAbort(IP_ERR_DEGEN_NORMAL, "");

		_IPGetCloseParenToken(Handler);
		IP_SET_PLANE_POLY(PPolygon);
		break;
	    default:
		GetGenericAttribute(&PPolygon -> Attrs, Handler, StringToken);
		break;
	}
    }
    while (_IPGetToken(Handler, StringToken) == IP_TOKEN_OPEN_PAREN);

    PPolygon -> Attrs = AttrReverseAttributes(PPolygon -> Attrs);

    _IPUnGetToken(Handler, StringToken);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
*   The first '[' was already read.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   PObject:    Where attributes should go to.                               *
*   Handler:    A handler to the open stream.				     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetObjectAttributes(IPObjectStruct *PObject, int Handler)
{
    int	i;
    char StringToken[LINE_LEN_LONG];

    do {
	switch (_IPGetToken(Handler, StringToken)) {
	    case IP_TOKEN_COLOR:
		_IPGetToken(Handler, StringToken);
		if (sscanf(StringToken, "%d", &i) != 1)
		    _IPParserAbort(IP_ERR_NUMBER_EXPECTED, StringToken);
		_IPGetCloseParenToken(Handler);
		AttrSetObjectColor(PObject, i);
		break;
	    default:
		GetGenericAttribute(&PObject -> Attrs, Handler, StringToken);
		break;
	}
    }
    while (_IPGetToken(Handler, StringToken) == IP_TOKEN_OPEN_PAREN);

    if (AttrGetObjectColor(PObject) == IP_ATTR_NO_COLOR)
    	AttrSetObjectColor(PObject, IP_LOAD_COLOR);

    PObject -> Attrs = AttrReverseAttributes(PObject -> Attrs);

    _IPUnGetToken(Handler, StringToken);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to read one generic attribute.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Attrs:    Where to place the read attribute.                             *
*   Handler:  A handler to the open stream.				     *
*   Name:     Name of attribute.                                             *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetGenericAttribute(IPAttributeStruct **Attrs,
				int Handler,
				char *Name)
{
    int Token;
    char StringToken[LINE_LEN_LONG];

    if ((Token = _IPGetToken(Handler, StringToken)) == IP_TOKEN_CLOSE_PAREN) {
	AttrSetStrAttrib(Attrs, Name, "");
    }
    else if (Token == IP_TOKEN_QUOTED) {
	AttrSetStrAttrib(Attrs, Name, StringToken);

	_IPSkipToCloseParenToken(Handler);
    }
    else if (Token == IP_TOKEN_OPEN_PAREN) {
	IPObjectStruct
	    *PObj = IPAllocObject("", IP_OBJ_UNDEF, NULL);

	_IPUnGetToken(Handler, StringToken);
	IritPrsrGetAllObjects(Handler, PObj, 10);
	PObj = EliminateDegenLists(PObj);

	AttrSetObjAttrib(Attrs, Name, PObj, FALSE);

	_IPSkipToCloseParenToken(Handler);
    }
    else {
	int i;
	RealType d;

	for (i = strlen(StringToken) - 1; i >= 0; i--) {
	    if (!(isdigit(StringToken[i]) ||
		  StringToken[i] == 'e' ||
		  StringToken[i] == 'E' ||
		  StringToken[i] == '.' ||
		  StringToken[i] == '+' ||
		  StringToken[i] == '-'))
		break;
	}
	if (i < 0 && sscanf(StringToken, IP_IRIT_FLOAT_READ, &d) == 1) {
	    if (d == (int) d)
		AttrSetIntAttrib(Attrs, Name, (int) d);
	    else
		AttrSetRealAttrib(Attrs, Name, d);
	}
	else
	    AttrSetStrAttrib(Attrs, Name, StringToken);

	_IPSkipToCloseParenToken(Handler);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to read poly vertex information.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Handler:     A handler to the open stream.				     *
*   PPolygon:    Where vertices are to be placed.                            *
*   IsPolygon:   Should we expect a polygon or a polyline? a pointlist?      *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetPointData(int Handler, IPPolygonStruct *PPolygon, int IsPolygon)
{
    int i, j, Length;
    char StringToken[LINE_LEN_LONG];
    IPVertexStruct *V,
	*VTail = NULL;

    if (_IPGetToken(Handler, StringToken) != IP_TOKEN_OTHER ||
	sscanf(StringToken, "%d", &Length) != 1)
	_IPParserAbort(IP_ERR_NUMBER_EXPECTED, StringToken);

    for (i = 0; i < Length; i++) {
	if (_IPGetToken(Handler, StringToken) != IP_TOKEN_OPEN_PAREN)
	    _IPParserAbort(IP_ERR_OPEN_PAREN_EXPECTED, StringToken);

	V = IPAllocVertex(0, NULL, NULL);

	/* The following handle the optional attributes in struct. */
	if (_IPGetToken(Handler, StringToken) == IP_TOKEN_OPEN_PAREN)
	    GetVertexAttributes(V, Handler);
	else
	    _IPUnGetToken(Handler, StringToken);

	for (j = 0; j < 3; j++)				/* Read coordinates. */
	    GetNumericToken(Handler, &V -> Coord[j]);

	_IPGetCloseParenToken(Handler);

	if (VTail == NULL)
	    PPolygon -> PVertex = VTail = V;
	else {
	    VTail -> Pnext = V;
	    VTail = V;
	}
    }

    if (_IritPrsrPolyListCirc && IsPolygon)
	VTail -> Pnext = PPolygon -> PVertex;

    _IPGetCloseParenToken(Handler);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Concatenate all freeform objects in FreeForms into a single list.          M
*                                                                            *
* PARAMETERS:                                                                M
*   FreeForms:  Freeform geometry to process.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   concatenated linked list.                            M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrConcatFreeForm, conversion                                       M
*****************************************************************************/
IPObjectStruct *IritPrsrConcatFreeForm(IritPrsrFreeFormStruct *FreeForms)
{
    IPObjectStruct *ObjLast,
	*Objs = NULL,
	*CrvObjs = FreeForms -> CrvObjs,
	*SrfObjs = FreeForms -> SrfObjs,
	*TrimSrfObjs = FreeForms -> TrimSrfObjs,
	*TrivarObjs = FreeForms -> TrivarObjs,
	*TriSrfObjs = FreeForms -> TriSrfObjs,
	*ModelObjs = FreeForms -> ModelObjs;

    if (CrvObjs != NULL) {
	ObjLast = IritPrsrGetLastObj(CrvObjs);
	ObjLast -> Pnext = Objs;
	Objs = CrvObjs;
    }
    if (SrfObjs != NULL) {
	ObjLast = IritPrsrGetLastObj(SrfObjs);
	ObjLast -> Pnext = Objs;
	Objs = SrfObjs;
    }
    if (TrimSrfObjs != NULL) {
	ObjLast = IritPrsrGetLastObj(TrimSrfObjs);
	ObjLast -> Pnext = Objs;
	Objs = TrimSrfObjs;
    }
    if (TrivarObjs != NULL) {
	ObjLast = IritPrsrGetLastObj(TrivarObjs);
	ObjLast -> Pnext = Objs;
	Objs = TrivarObjs;
    }
    if (TriSrfObjs != NULL) {
	ObjLast = IritPrsrGetLastObj(TriSrfObjs);
	ObjLast -> Pnext = Objs;
	Objs = TriSrfObjs;
    }
    if (ModelObjs != NULL) {
	ObjLast = IritPrsrGetLastObj(ModelObjs);
	ObjLast -> Pnext = Objs;
	Objs = ModelObjs;
    }

    return Objs;
}

#ifdef DEBUG

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Dummy function to link at debugging time.                               *
*                                                                            *
* PARAMETERS:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
void IritPrsrDbg(void)
{
    CagdDbg(NULL);
    TrimDbg(NULL);
    TrivDbg(NULL);
    TrngDbg(NULL);
    IritPrsrStderrObject(NULL);
}

#endif /* DEBUG */
