/*****************************************************************************
* 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__ */

#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. */

static IPObjectStruct *EliminateDegenLists(IPObjectStruct *PObj);
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);

/*****************************************************************************
* 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
*   See also functions IritPrsrSetPolyListCirc, IritPrsrSetFlattenObjects,   M
* and IritPrsrSetReadOneObject.						     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
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrOpenDataFile, files, parser                                      M
*****************************************************************************/
int IritPrsrOpenDataFile(char *FileName, int Read, int Messages)
{
    FILE *f;
    int ReadWriteBinary = IritPrsrSenseBinaryFile(FileName),
	IsPipe = FALSE;
    char *p;

    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;
	    }
	}
    }

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

    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 TURE, 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 (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
* Routine to read the data from	a given	file.				     M
*   Returns NULL if EOF was reached or error occured.			     M
*   See also functions IritPrsrSetPolyListCirc, IritPrsrSetFlattenObjects,   M
* and IritPrsrSetReadOneObject.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   Handler:     A handler to the open stream.				     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Read object, or NULl if failed.                      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, NULL);

    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
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetPolyListCirc, files, parser                                   M
*****************************************************************************/
void IritPrsrSetPolyListCirc(int Circ)
{
    _IritPrsrPolyListCirc = Circ;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Controls the hierarchy flattening of a read object.       		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Circ:     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 way the Ascii parser handle multiple objects in a file.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Circ:     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
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrSetFlattenObjects, files, parser                                 M
*****************************************************************************/
void IritPrsrSetReadOneObject(int OneObject)
{
    _IritPrsrReadOneObject = OneObject;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Propagate attributes from list objects down into their elements.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:     To propagate down Attrs attributes.                            M
*   Attrs:    Attributes to propagate.                                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrPropagateAttrs, attributes, files, parser                        M
*****************************************************************************/
void IritPrsrPropagateAttrs(IPObjectStruct *PObj, IPAttributeStruct *Attrs)
{
    IPAttributeStruct *Attr;

    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 (!AttrFindAttribute(Attrs, Attr -> Name)) {
		IPAttributeStruct
		    *TmpAttr = AttrCopyOneAttribute(Attr);

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

	for (i = 0; (PTmp = ListObjectGet(PObj, i)) != NULL; i++)
	    IritPrsrPropagateAttrs(PTmp, Attrs);

	AttrFreeAttributes(&Attrs);
    }
    else {
	/* Regular object - add to its attribute list every attribute in     */
	/* Attrs that is not found in its attribute list.		     */
	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.     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      Object(s) to flatten out.                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *:   Flattened hierarchy.                                 M
*                                                                            *
* KEYWORDS:                                                                  M
*   IritPrsrFlattenTree                                                      M
*****************************************************************************/
IPObjectStruct *IritPrsrFlattenTree(IPObjectStruct *PObj)
{
    IritPrsrFreeFormStruct IPFreeForm;

    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++) {
	    /* 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;

	    PTmp2 = IritPrsrFlattenTree(PTmp);
	    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;
	IPFreeForm.SrfObjs = NULL;
	IPFreeForm.TrimSrfObjs = NULL;
	IPFreeForm.TrivarObjs = NULL;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_SRF_OBJ(PObj)) {
	IPFreeForm.CrvObjs = NULL;
	IPFreeForm.SrfObjs = PObj;
	IPFreeForm.TrimSrfObjs = NULL;
	IPFreeForm.TrivarObjs = NULL;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRIMSRF_OBJ(PObj)) {
	IPFreeForm.CrvObjs = NULL;
	IPFreeForm.SrfObjs = NULL;
	IPFreeForm.TrimSrfObjs = PObj;
	IPFreeForm.TrivarObjs = NULL;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else if (IP_IS_TRIVAR_OBJ(PObj)) {
	IPFreeForm.CrvObjs = NULL;
	IPFreeForm.SrfObjs = NULL;
	IPFreeForm.TrimSrfObjs = NULL;
	IPFreeForm.TrivarObjs = PObj;
	return IritPrsrProcessFreeForm(&IPFreeForm);
    }
    else {
	return PObj;
    }
}

/*****************************************************************************
* 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);
		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_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;

    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, 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_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 (stricmp(PObj -> Name, "VIEW_MAT") == 0) {
		    IritPrsrWasViewMat = TRUE;
		    MAT_COPY(IritPrsrViewMat, PObj -> U.Mat);
		}
		else if (stricmp(PObj -> Name, "PRSP_MAT") == 0) {
		    IritPrsrWasPrspMat = TRUE;
		    MAT_COPY(IritPrsrPrspMat, PObj -> U.Mat);
		}
		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 int 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,

	0
    };
    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",

	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);

    _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);

    _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);

    _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_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, 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;

    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;
    }

    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);
    IritPrsrStderrObject(NULL);
}

#endif /* DEBUG */
