Metropoli BBS
VIEWER: dataprsr.c MODE: TEXT (ASCII)
/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
* Data File Interpreter module - handle data files - to/from geom objects    *
*****************************************************************************/

#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 "program.h"
#include "cagd_lib.h"
#include "allocate.h"
#include "attribut.h"
#include "dataprsr.h"			    /* Visible to the other modules. */
#include "geomat3d.h"
#include "objects.h"
#include "windows.h"
#include "graphgen.h"

#define UNGET_STACK_SIZE	5		     /* Internal stack size. */

typedef enum {			  /* List of all possible tokens enumerated. */
    TOKEN_NONE,

    TOKEN_OPEN_PAREN,
    TOKEN_CLOSE_PAREN,

    TOKEN_E2,
    TOKEN_P2,
    TOKEN_E3,
    TOKEN_P3,

    TOKEN_NUMBER,
    TOKEN_STRING,
    TOKEN_VECTOR,
    TOKEN_MATRIX,
    TOKEN_CTLPT,
    TOKEN_VERTEX,
    TOKEN_POLYGON,
    TOKEN_POLYLINE,
    TOKEN_OBJECT,
    TOKEN_COLOR,
    TOKEN_INTERNAL,
    TOKEN_NORMAL,
    TOKEN_PLANE,
    TOKEN_CURVE,
    TOKEN_SURFACE,

    TOKEN_OTHER	= 100,			/* Probably names & numbers. */
    TOKEN_EOF = -1
} TokenType;

typedef enum {			 /* Possible error code during data parsing. */
    DP_NO_ERR = 0,

    DP_ERR_NUMBER_EXPECTED,
    DP_ERR_OPEN_PAREN_EXPECTED,
    DP_ERR_CLOSE_PAREN_EXPECTED,
    DP_ERR_LIST_COMP_UNDEF,
    DP_ERR_UNDEF_EXPR_HEADER,
    DP_ERR_PT_TYPE_EXPECTED,
    DP_ERR_OBJECT_EMPTY,
    DP_ERR_EMPTY_NAME,
    DP_ERR_OPEN_FAILED,
    DP_ERR_MIXED_TYPES,
    DP_STR_NOT_IN_QUOTES,
    DP_ERR_CAGD_LIB_ERR,

    DP_WRN_OBJ_NAME_TRUNC = 100
} DataPrsrErrType;

static int  DPGlblLineCount = 0;     /* Used to locate errors in input file. */
static DataPrsrErrType DPGlblParserError = DP_NO_ERR;   /* Last err # found. */
static char DPGlblTokenError[LINE_LEN]; /* Last token where error was found. */
static jmp_buf LclLongJumpBuffer;		   /* Used in error traping. */
static int  GlblToken =	0,	      /* Used by the parser, to unget token. */
	    GlblLineCount = 1;	     /* Used to locate errors in input file. */
static char GlblStringToken[UNGET_STACK_SIZE][LINE_LEN];/* Save unget tokens.*/
static FILE *OutputFile = NULL;

static void UnGetToken(char *StringToken);
static void GetStringToken(FILE *f, char *StringToken);
static TokenType GetToken(FILE *f, char *StringToken);
static void GetVertexAttributes(VertexStruct *PVertex, FILE *f,
							      int *HasNormal);
static void GetPolygonAttributes(PolygonStruct *PPolygon, FILE *f,
								int *HasPlane);
static void GetObjectAttributes(ObjectStruct *PObject, FILE *f);
static void GetPointData(FILE *f, PolygonStruct *PPolygon, int IsPolyline,
							int *HasNormal);
static void DPUpdatePolyPlane(PolygonStruct *PPoly);
static void ParserError(DataPrsrErrType ErrNum, char *Msg);
static void DataPrsrPutAllObjects(ObjectStruct *PObj, int Indent);
static char *Real2Str(RealType R);
static void DataPrsrGetAllObjects(FILE *f, ObjectStruct *PObjParent);
static void GetCloseParenToken(FILE *f);
static void GetNumericToken(FILE *f, RealType *r);
static void DataPrsrGetAuxObject(FILE *f, ObjectStruct *PObj);

#ifdef USE_VARARGS
static void IFprintf(int Indent, char *va_alist, ...);
#else
static void IFprintf(int Indent, char *Format, ...);
#endif /* USE_VARARGS */

/*****************************************************************************
* Routine to print the data from given object into given file FileName.	     *
* If FileName is NULL or empty, print to screen using WndwInputWindowPutStr. *
*****************************************************************************/
void DataPrsrPutObject(char *FileName, ObjectStruct *PObj)
{
    char *Pchar;
    char FullFileName[LINE_LEN];

    DPGlblParserError = DP_NO_ERR;

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

    if (FileName && (int) strlen(FileName) > 0) {
    	if ((Pchar = strchr(FileName, '.')) != NULL)
	    *Pchar = 0;			 /* Make sure no file type is given. */
    	if (strlen(FileName) == 0) ParserError(DP_ERR_EMPTY_NAME, "");

    	strcpy(FullFileName, FileName);
    	strcat(FullFileName, ".dat");

    	if ((OutputFile = fopen(FullFileName, "w")) == NULL)
    	    ParserError(DP_ERR_OPEN_FAILED, FullFileName);
    }
    else
	OutputFile = NULL;

    DataPrsrPutAllObjects(PObj, 0);

    if (OutputFile) {
	fclose(OutputFile);
	OutputFile = NULL;
    }

    return;
}

/*****************************************************************************
* Routine to read the data from	a given	file.				     *
*****************************************************************************/
ObjectStruct *DataPrsrGetObjects(char *FileName)
{
    char FullFileName[LINE_LEN];
    FILE
	*f = NULL;
    ObjectStruct *PObj, *PTmp;

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

    if (strlen(FileName) == 0) ParserError(DP_ERR_EMPTY_NAME, "");

    strcpy(FullFileName, FileName);
    if (strrchr(FullFileName, '.') == NULL) strcat(FullFileName, ".dat");

    if ((f = fopen(FullFileName, "r")) == NULL)
	ParserError(DP_ERR_OPEN_FAILED, FullFileName);

    GlblToken =	0;			 /* Used in UnGetToken token buffer. */
    DPGlblParserError = DP_NO_ERR;			    /* Reset errors. */
    GlblLineCount = 1;				      /* Reset line counter. */

    PObj = AllocObject("", OBJ_LIST_OBJ, NULL);
    DataPrsrGetAllObjects(f, PObj);

    if (PObj -> ObjType == OBJ_LIST_OBJ &&
	PObj -> U.PObjList[1] == NULL)
    {
	/* Only one object in list - return the object instead. */
	PTmp = PObj -> U.PObjList[0];
	PObj -> U.PObjList[0] = NULL;
	MyFree((char *) PObj, ALLOC_OBJECT);
	PObj = PTmp;
    }

    fclose(f);
    return PObj;
}

/*****************************************************************************
* Same as fprintf but with indentation. Prints into global OutputFile unless *
* it is NULL in which it prints to screen using WndwInputWindowPutStr	     *
*****************************************************************************/
#ifdef USE_VARARGS
static void IFprintf(int Indent, char *va_alist, ...)
{
    char *Format, Line[LINE_LEN_LONG];
    int i;
    va_list ArgPtr;

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

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

    for (i = 0; i < Indent; i++) Line[i] = ' ';
    vsprintf(&Line[Indent], Format, ArgPtr);
    va_end(ArgPtr);

    if (OutputFile != NULL)
	fputs(Line, OutputFile);
    else
	WndwInputWindowPutStr(Line);
}

/*****************************************************************************
* Routine to print the data from given geometry object.			     *
*****************************************************************************/
static void DataPrsrPutAllObjects(ObjectStruct *PObj, int Indent)
{
    int i;
    char
	*ErrStr = NULL;
    CagdRType *Coords;
    PolygonStruct *PPolygon;
    VertexStruct *PVertex;

    if (IS_GEOM_OBJ(PObj)) {
	AttributeStruct
	    *Attr = &(PObj -> U.Attr);

	IFprintf(Indent, "[OBJECT [COLOR %d]", GetObjectColor(PObj));

	if (Attr -> NumStrAttribs > 0) {
	    IFprintf(0, "\n");
	    for (i = 0; i < Attr -> NumStrAttribs; i++)
	        IFprintf(Indent + 8,
		     i == Attr -> NumStrAttribs - 1 ? "[%s %s]" : "[%s %s]\n",
		     Attr -> StrAttrName[i], Attr -> StrAttrData[i]);
	    IFprintf(0, " %s\n",
		     strlen(PObj -> Name) ? PObj -> Name : "NONE");
	}
	else
    	    IFprintf(0, " %s\n",
		     strlen(PObj -> Name) ? PObj -> Name : "NONE");
    }
    else
	IFprintf(Indent, "[OBJECT %s\n",
	         strlen(PObj -> Name) ? PObj -> Name : "NONE");

    Indent += 4;

    switch (PObj -> ObjType) {
	case POLY_OBJ:
	    for (PPolygon = PObj -> U.Pl.P;
		 PPolygon != NULL;
		 PPolygon = PPolygon -> Pnext) {
		if (PPolygon -> V == NULL)
		    FatalError("Dump: Attemp to dump empty polygon, exit\n");
		for (PVertex = PPolygon -> V -> Pnext, i = 1;
		     PVertex != PPolygon -> V && PVertex != NULL;
		     PVertex = PVertex -> Pnext, i++);
		if (IS_POLYLINE_OBJ(PObj))
		    IFprintf(Indent, "[POLYLINE %d\n", i);
		else
		    IFprintf(Indent, "[POLYGON [PLANE %s %s %s %s] %d\n",
			Real2Str(PPolygon -> Plane[0]),
			Real2Str(PPolygon -> Plane[1]),
			Real2Str(PPolygon -> Plane[2]),
			Real2Str(PPolygon -> Plane[3]), i);

		PVertex = PPolygon -> V;
		do {		     /* Assume at least one edge in polygon! */
		    if (IS_POLYLINE_OBJ(PObj))
			IFprintf(Indent + 4, "[%s%s %s %s]\n",
			    IS_INTERNAL_EDGE(PVertex) ? "[INTERNAL] " : "",
			    Real2Str(PVertex -> Pt[0]),
			    Real2Str(PVertex -> Pt[1]),
			    Real2Str(PVertex -> Pt[2]));
		    else
			IFprintf(Indent + 4, "[%s[NORMAL %s %s %s] %s %s %s]\n",
			    IS_INTERNAL_EDGE(PVertex) ? "[INTERNAL] " : "",
			    Real2Str(PVertex -> Normal[0]),
			    Real2Str(PVertex -> Normal[1]),
			    Real2Str(PVertex -> Normal[2]),
			    Real2Str(PVertex -> Pt[0]),
			    Real2Str(PVertex -> Pt[1]),
			    Real2Str(PVertex -> Pt[2]));

		    PVertex = PVertex -> Pnext;
		}
		while (PVertex != PPolygon -> V && PVertex != NULL);
		IFprintf(Indent, "]\n");	       /* Close the polygon. */
	    }
	    break;
	case NUMERIC_OBJ:
	    IFprintf(Indent, "[NUMBER %s]\n", Real2Str(PObj -> U.R));
	    break;
	case VECTOR_OBJ:
	    IFprintf(Indent, "[VECTOR %s %s %s]\n",
		     Real2Str(PObj -> U.Vec[0]),
		     Real2Str(PObj -> U.Vec[1]),
		     Real2Str(PObj -> U.Vec[2]));
	    break;
	case CTLPT_OBJ:
	    Coords = PObj -> U.CtlPt.Coords;
	    switch (PObj -> U.CtlPt.PtType) {
		case CAGD_PT_E2_TYPE:
	    	    IFprintf(Indent, "[CTLPT %s %s %s]\n", "E2",
			     Real2Str(Coords[1]),
			     Real2Str(Coords[2]));
		    break;
		case CAGD_PT_P2_TYPE:
	    	    IFprintf(Indent, "[CTLPT %s %s %s %s]\n", "P2",
	                     Real2Str(Coords[0]),
			     Real2Str(Coords[1]),
			     Real2Str(Coords[2]));
		    break;
		case CAGD_PT_E3_TYPE:
		    IFprintf(Indent, "[CTLPT %s %s %s %s]\n", "E3",
			     Real2Str(Coords[1]),
			     Real2Str(Coords[2]),
			     Real2Str(Coords[3]));
		    break;
		case CAGD_PT_P3_TYPE:
	    	    IFprintf(Indent, "[CTLPT %s %s %s %s %s]\n", "P3",
			     Real2Str(Coords[0]),
			     Real2Str(Coords[1]),
			     Real2Str(Coords[2]),
			     Real2Str(Coords[3]));
		    break;
		default:
		    WndwInputWindowPutStr("Dump: Undefined point type");
		    break;
	    }
	    break;
	case MATRIX_OBJ:
	    IFprintf(Indent, "[MATRIX\n");
	    for (i = 0; i < 4; i++)
		IFprintf(Indent + 8, "%s %s %s %s%s\n",
			 Real2Str(PObj -> U.Mat[i][0]),
			 Real2Str(PObj -> U.Mat[i][1]),
			 Real2Str(PObj -> U.Mat[i][2]),
			 Real2Str(PObj -> U.Mat[i][3]),
			 i == 3 ? "]" : "");
	    break;
	case STRING_OBJ:
	    IFprintf(Indent, "[STRING \"%s\"]\n", PObj -> U.Str);
	    break;
	case OBJ_LIST_OBJ:
	    for (i = 0; PObj -> U.PObjList[i] != NULL; i++)
		DataPrsrPutAllObjects(PObj -> U.PObjList[i], Indent);
	    break;
	case CURVE_OBJ:
    	    CagdCrvWriteToFile2(PObj -> U.Crv.Crv, OutputFile, Indent, NULL, &ErrStr);
	    break;
	case SURFACE_OBJ:
	    CagdSrfWriteToFile2(PObj -> U.Srf.Srf, OutputFile, Indent, NULL, &ErrStr);
	    break;
	default:
	    WndwInputWindowPutStr(
		"Dump: Attemp to dump undefine object type.");
	    break;
    }

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

    if (ErrStr != NULL)	ParserError(DP_ERR_CAGD_LIB_ERR, ErrStr);
}

/*****************************************************************************
* Convert a real number into a string.					     *
* The routine maintains 6 different buffers simultanuously so up to 6 calls  *
* can be issued from same printf...					     *
*****************************************************************************/
static char *Real2Str(RealType R)
{
    static int j, k,
	i = 0;
    static char Buffer[6][LINE_LEN_SHORT], Line[LINE_LEN];

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

#   ifdef DOUBLE
	sprintf(Buffer[i], "%-8.6lg", R);
#   else
	sprintf(Buffer[i], "%-8.6g", R);
#   endif /* DOUBLE */

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

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

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

/*****************************************************************************
* Routine to read the geometry data from a given file. Reads "[OBJECT ..."   *
* prefixes only and invoke the auxiliary routine.			     *
* Note objects may be recursively defined.				     *
*****************************************************************************/
static void DataPrsrGetAllObjects(FILE *f, ObjectStruct *PObjParent)
{
    char StringToken[LINE_LEN];
    TokenType Token;
    int ObjCount = 0,
	Quit = FALSE;
    ObjectStruct *PObj, *OldPObj;

    while (!Quit) {
    	while ((Token = GetToken(f, StringToken)) != TOKEN_OPEN_PAREN &&
	       Token != TOKEN_CLOSE_PAREN &&
	       Token != TOKEN_EOF);

	if (Token == TOKEN_CLOSE_PAREN || Token == TOKEN_EOF)
	{
	    if (Token == TOKEN_CLOSE_PAREN)
		UnGetToken(StringToken);
	    Quit = TRUE;
	    break;
	}

	switch (GetToken(f, StringToken)) {
	    case TOKEN_OBJECT:
	        PObjParent -> ObjType = OBJ_LIST_OBJ;

		PObj = AllocObject("", UNDEF_OBJ, NULL);

		/* The following handle optional attributes in record. */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetObjectAttributes(PObj, f);
		else
		{
		    SetObjectColor(PObj, GlblLoadColor);
		    UnGetToken(StringToken);
		}

		if (GetToken(f, StringToken) == TOKEN_OTHER &&
		    strcmp(StringToken, "NONE") != 0)
		    strcpy(PObj -> Name, StringToken);

		DataPrsrGetAllObjects(f, PObj);

		GetCloseParenToken(f);

		if (PObj -> ObjType == UNDEF_OBJ)
		    ParserError(DP_ERR_OBJECT_EMPTY, "");

		PObjParent -> U.PObjList[ObjCount++] = PObj;

		if ((int) strlen(PObj -> Name) > 0) {
		    if ((OldPObj = GetObject(PObj -> Name)) != NULL)
			DeleteObject(OldPObj, TRUE);
		    InsertObject(PObj);
		    PObj -> Count++;
		}
		break;
	    default:
		UnGetToken(StringToken);
		UnGetToken("[");
		DataPrsrGetAuxObject(f, PObjParent);
		Quit = TRUE;
		break;
	}
    }

    if (PObjParent -> ObjType == UNDEF_OBJ)
	ParserError(DP_ERR_OBJECT_EMPTY, "");
    else if (PObjParent -> ObjType == OBJ_LIST_OBJ)
	PObjParent -> U.PObjList[ObjCount++] = NULL;
}

/*****************************************************************************
* Routine to get close paren token from f.				     *
*****************************************************************************/
static void GetCloseParenToken(FILE *f)
{
    char StringToken[LINE_LEN];

    if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN)
	ParserError(DP_ERR_CLOSE_PAREN_EXPECTED, StringToken);
}

/*****************************************************************************
* Routine to get one numeric token into r.				     *
*****************************************************************************/
static void GetNumericToken(FILE *f, RealType *r)
{
    char StringToken[LINE_LEN];

    GetToken(f, StringToken);
#   ifdef DOUBLE
	if (sscanf(StringToken, "%lf", r) != 1)
#   else
	if (sscanf(StringToken, "%f", r) != 1)
#   endif /* DOUBLE */
	    ParserError(DP_ERR_NUMBER_EXPECTED, StringToken);
}

/*****************************************************************************
* Routine to read the geometry data from a given file.			     *
*****************************************************************************/
static void DataPrsrGetAuxObject(FILE *f, ObjectStruct *PObj)
{
    int	i, j, ErrLine, HasPlane, HasNormal;
    char *p1, *p2, *ErrStr, StringToken[LINE_LEN];
    CagdRType *Coords;
    VertexStruct *PVertex;
    PolygonStruct *PPolygon;
    CagdCrvStruct *PCurve;
    CagdSrfStruct *PSurface;

    while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN) {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_POLYLINE:
		switch (PObj -> ObjType) {
		    case POLY_OBJ:
			if (!IS_POLYLINE_OBJ(PObj))
			    ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = POLY_OBJ;
			PObj -> U.Pl.P = NULL;
			SET_POLYLINE_OBJ(PObj);
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}

		PPolygon = AllocPolygon(0, 0, NULL, NULL);

		/* The following handle the optional attributes in struct.   */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetPolygonAttributes(PPolygon, f, &HasPlane);
		else
		    UnGetToken(StringToken);

		/* The following handles reading the vertices. */
		GetPointData(f, PPolygon, TRUE, &HasNormal);

		PPolygon -> Pnext = PObj -> U.Pl.P;
		PObj -> U.Pl.P = PPolygon;
		break;
	    case TOKEN_POLYGON:
		switch (PObj -> ObjType) {
		    case POLY_OBJ:
			if (IS_POLYLINE_OBJ(PObj))
			    ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = POLY_OBJ;
			PObj -> U.Pl.P = NULL;
			RST_POLYLINE_OBJ(PObj);
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}

		PPolygon = AllocPolygon(0, 0, NULL, NULL);

		/* The following handle the optional attributes in struct.   */
		HasPlane = HasNormal = FALSE;
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetPolygonAttributes(PPolygon, f, &HasPlane);
		else
		    UnGetToken(StringToken);

		/* The following handles reading the vertices. */
		GetPointData(f, PPolygon, FALSE, &HasNormal);

		if (!HasPlane) DPUpdatePolyPlane(PPolygon);
		if (!HasNormal) {
		    /* Update all normals to be the same as plane eqn. */
		    PVertex = PPolygon -> V;
		    do {
			PT_COPY(PVertex -> Normal, PPolygon -> Plane);
			PVertex = PVertex -> Pnext;
		    }
		    while (PVertex != PPolygon -> V && PVertex != NULL);
		}
		PPolygon -> Pnext = PObj -> U.Pl.P;
		PObj -> U.Pl.P = PPolygon;
		break;
	    case TOKEN_NUMBER:
		switch (PObj -> ObjType) {
		    case NUMERIC_OBJ:
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = NUMERIC_OBJ;
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}
		GetNumericToken(f, &PObj -> U.R);
		GetCloseParenToken(f);
		break;
	    case TOKEN_STRING:
		switch (PObj -> ObjType) {
		    case STRING_OBJ:
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = STRING_OBJ;
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}
		GetToken(f, StringToken);
		if ((p1 = strchr(StringToken, '"')) == NULL ||
		    (p2 = strrchr(StringToken, '"')) == p1)
		    ParserError(DP_STR_NOT_IN_QUOTES, StringToken);

		/* Convert the end quote to EOS and copy string. */
		*p2 = 0;
		strcpy(PObj -> U.Str, &p1[1]);

		GetCloseParenToken(f);
		break;
	    case TOKEN_VECTOR:
		switch (PObj -> ObjType) {
		    case VECTOR_OBJ:
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = VECTOR_OBJ;
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}
		for (i = 0; i < 3; i++)
		    GetNumericToken(f, &PObj -> U.Vec[i]);
		GetCloseParenToken(f);
		break;
	    case TOKEN_MATRIX:
		switch (PObj -> ObjType) {
		    case MATRIX_OBJ:
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = MATRIX_OBJ;
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}
		for (i = 0; i < 4; i++)
		    for (j = 0; j < 4; j++)
			GetNumericToken(f, &PObj -> U.Mat[i][j]);
		GetCloseParenToken(f);
		break;
	    case TOKEN_CTLPT:
		switch (PObj -> ObjType) {
		    case CTLPT_OBJ:
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = CTLPT_OBJ;
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}

		switch (GetToken(f, StringToken)) {
		    case TOKEN_E2:
			PObj -> U.CtlPt.PtType = CAGD_PT_E2_TYPE;
			j = 2;
			i = 1;
			break;
		    case TOKEN_P2:
			PObj -> U.CtlPt.PtType = CAGD_PT_P2_TYPE;
			j = 3;
			i = 0;
			break;
		    case TOKEN_E3:
			PObj -> U.CtlPt.PtType = CAGD_PT_E3_TYPE;
			j = 3;
			i = 1;
			break;
		    case TOKEN_P3:
			PObj -> U.CtlPt.PtType = CAGD_PT_P3_TYPE;
			j = 4;
			i = 0;
			break;
		    default:
			WndwInputWindowPutStr("Read: Undefined point type");
			break;
		}

		Coords = PObj -> U.CtlPt.Coords;
		for ( ; j > 0; i++, j--)
		    GetNumericToken(f, &Coords[i]);
		GetCloseParenToken(f);
		break;
	    case TOKEN_SURFACE:
		switch (PObj -> ObjType) {
		    case SURFACE_OBJ:
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = SURFACE_OBJ;
			PObj -> U.Srf.Srf = NULL;
			PObj -> U.Srf.PLPolys = NULL;
			PObj -> U.Srf.CtlMesh = NULL;
			PObj -> U.Srf.Polygons = NULL;
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}

		ErrLine = GlblLineCount;
		PSurface = CagdSrfReadFromFile2(f, &ErrStr, &ErrLine);
		GlblLineCount = ErrLine;
		if (ErrStr != NULL) {
		    ParserError(DP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		PSurface -> Pnext = PObj -> U.Srf.Srf;
		PObj -> U.Srf.Srf = PSurface;
		break;
	    case TOKEN_CURVE:
		switch (PObj -> ObjType) {
		    case CURVE_OBJ:
			break;
		    case UNDEF_OBJ:
			PObj -> ObjType = CURVE_OBJ;
			PObj -> U.Crv.Crv = NULL;
			PObj -> U.Crv.PLPolys = NULL;
			PObj -> U.Crv.CtlPoly = NULL;
			break;
		    default:
			ParserError(DP_ERR_MIXED_TYPES, "");
			break;
		}

		ErrLine = GlblLineCount;
		PCurve = CagdCrvReadFromFile2(f, &ErrStr, &ErrLine);
		GlblLineCount = ErrLine;
		if (ErrStr != NULL) {
		    ParserError(DP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		PCurve -> Pnext = PObj -> U.Crv.Crv;
		PObj -> U.Crv.Crv = PCurve;
		break;
	    default:
		ParserError(DP_ERR_UNDEF_EXPR_HEADER, StringToken);
		break;
	} /* Of switch. */
    } /* Of while. */

    UnGetToken(StringToken);
}

/*****************************************************************************
*   Routine to unget one token (on stack of UNGET_STACK_SIZE levels!)	     *
*****************************************************************************/
static void UnGetToken(char *StringToken)
{
    if (GlblToken >= UNGET_STACK_SIZE)
	 FatalError("Parser Internal stack overflow...\n");

    strcpy(GlblStringToken[GlblToken], StringToken);
    GlblToken++;  /* GlblToken exists - Something in it (no overflow check). */
}

/*****************************************************************************
*   Routine to get the next token out of the input file	f.		     *
* Returns the next token found,	as StringToken.				     *
* Note:	StringToken must be allocated before calling this routine!	     *
*****************************************************************************/
static void GetStringToken(FILE *f, char *StringToken)
{
    int	len;
    char c, *LocalStringToken;

    if (GlblToken) { /*	get first the unget token */
	GlblToken--;
	strcpy(StringToken, GlblStringToken[GlblToken]);
	return;
    }
    /* skip white spaces: */
    while ((!feof(f))
	 && (((c = getc(f)) == ' ') || (c == '\t') || (c == '\n')))
	    if (c == '\n') GlblLineCount++;		 /* Count the lines. */

    LocalStringToken = StringToken;
    if (c == '[')		      /* Its a token by	itself so return it. */
	*LocalStringToken++ = c;	      /* Copy the token	into string. */
    else {
	if (!feof(f))
	     do	*LocalStringToken++ = c;      /* Copy the token	into string. */
	     while ((!feof(f)) &&
		      ((c = getc(f)) !=	' ') &&	(c != '\t') && (c != '\n'));
	if (c == '\n') ungetc(c, f);	 /* Save it to be counted 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	], UnGetToken(']') and return XXXX:  */
    if ((StringToken[len = strlen(StringToken)-1] == ']') && (len > 0))	{
	/* Return CloseParan */
	UnGetToken(&StringToken[len]);			 /* Save next token. */
	StringToken[len] = 0;			/* Set end of string on	"]". */
    }
}

/*****************************************************************************
*   Routine to get the next token out of the input file	f as token number.   *
* Note:	StringToken must be allocated before calling this routine!	     *
*****************************************************************************/
static TokenType GetToken(FILE *f, char *StringToken)
{
    static int IntTokens[] = {
	TOKEN_OPEN_PAREN,
	TOKEN_CLOSE_PAREN,
	TOKEN_VERTEX,
	TOKEN_POLYGON,
	TOKEN_POLYLINE,
	TOKEN_OBJECT,
	TOKEN_COLOR,
	TOKEN_INTERNAL,
	TOKEN_NORMAL,
	TOKEN_PLANE,
	TOKEN_CURVE,
	TOKEN_SURFACE,
	TOKEN_E2,
	TOKEN_P2,
	TOKEN_E3,
	TOKEN_P3,
	TOKEN_NUMBER,
	TOKEN_STRING,
	TOKEN_VECTOR,
	TOKEN_MATRIX,
	TOKEN_CTLPT,
	0
    };
    static char *StrTokens[] = {
	"[",
	"]",
	"VERTEX",
	"POLYGON",
	"POLYLINE",
	"OBJECT",
	"COLOR",
	"INTERNAL",
	"NORMAL",
	"PLANE",
	"CURVE",
	"SURFACE",
	"E2",
	"P2",
	"E3",
	"P3",
	"NUMBER",
	"STRING",
	"VECTOR",
	"MATRIX",
	"CTLPT",
	NULL
    };
    int i;

    GetStringToken(f, StringToken);

    if (feof(f)) return TOKEN_EOF;

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

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

/*****************************************************************************
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
* Note the '[' was allready read.					     *
* Current supported attributes: [INTERNAL] - internal edge (IRIT output).    *
*****************************************************************************/
static void GetVertexAttributes(VertexStruct *PVertex, FILE *f,
							      int *HasNormal)
{
    int i;
    char StringToken[LINE_LEN];

    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_INTERNAL:
		if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN)
		    ParserError(DP_ERR_CLOSE_PAREN_EXPECTED, StringToken);
		SET_INTERNAL_EDGE(PVertex);
		break;
	    case TOKEN_NORMAL:
		/* The following handles reading 3 coord. of vertex normal. */
		for (i = 0; i < 3; i++)
		    GetNumericToken(f, &PVertex -> Normal[i]);

		GetCloseParenToken(f);
		*HasNormal = TRUE;
		break;
	    default: /* Ignore this option! */
		while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN);
		break;
	}
    }
    while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN);

    UnGetToken(StringToken);
}

/*****************************************************************************
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
* Note the '[' was allready read.					     *
* Current supported attributes: [PLANE A B C D].			     *
*****************************************************************************/
static void GetPolygonAttributes(PolygonStruct *PPolygon, FILE *f,
								int *HasPlane)
{
    int i;
    char StringToken[LINE_LEN];

    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_PLANE:
		/* The following handles reading of 4 coord. of plane eqn.. */
		for (i = 0; i < 4; i++)
		    GetNumericToken(f, &PPolygon -> Plane[i]);
		GetCloseParenToken(f);
		*HasPlane = TRUE;
		break;
	    default:
		while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN);
		break;
	}
    }
    while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN);

    UnGetToken(StringToken);
}

/*****************************************************************************
* Routine to read from input file f the	following [ATTR ...] [ATTR ...].     *
* Note the '[' was allready read.					     *
* Current supported attributes: [COLOR C] - set color.			     *
* All other attributes are treated as String attributes.		     *
*****************************************************************************/
static void GetObjectAttributes(ObjectStruct *PObject, FILE *f)
{
    int	i;
    char StringToken[LINE_LEN], Data[LINE_LEN], Name[LINE_LEN];

    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_COLOR:
		GetToken(f, StringToken);
		if (sscanf(StringToken, "%d", &i) != 1)
		    ParserError(DP_ERR_NUMBER_EXPECTED, StringToken);
		GetCloseParenToken(f);
		SetObjectColor(PObject, i);
		break;
	    default:
		strcpy(Name, StringToken);
		strcpy(Data, " ");
		while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN) {
		    strcat(Data, StringToken);
		    strcat(Data, " ");
		}
		SetObjectStrAttrib(PObject, Name, Data);
		break;
	}
    }
    while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN);

    UnGetToken(StringToken);
}

/*****************************************************************************
* Routine to read poly* vertex information.				     *
*****************************************************************************/
static void GetPointData(FILE *f, PolygonStruct *PPolygon, int IsPolyline,
							   int *HasNormal)
{
    int i, j, Length;
    char StringToken[LINE_LEN];
    VertexStruct *V,
	*VTail = NULL;

    if (GetToken(f, StringToken) != TOKEN_OTHER ||
	sscanf(StringToken, "%d", &Length) != 1)
	ParserError(DP_ERR_NUMBER_EXPECTED, StringToken);

    for (i = 0; i < Length; i++) {
	if (GetToken(f, StringToken) != TOKEN_OPEN_PAREN)
	    ParserError(DP_ERR_OPEN_PAREN_EXPECTED, StringToken);

	V = AllocVertex(0, 0, NULL, NULL);

	/* The following handle the optional attributes in struct. */
	*HasNormal = FALSE;
	if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
	    GetVertexAttributes(V, f, HasNormal);
	else
	    UnGetToken(StringToken);

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

	GetCloseParenToken(f);

	if (!*HasNormal)
	    PT_COPY(V -> Normal, PPolygon -> Plane);

	if (VTail == NULL)
	    PPolygon -> V = VTail = V;
	else {
	    VTail -> Pnext = V;
	    VTail = V;
	}
    }
    VTail -> Pnext = IsPolyline ? NULL : PPolygon -> V;

    GetCloseParenToken(f);
}

/*****************************************************************************
*   Routine to update the Plane equation of the given polygon by the order   *
* of the first 3 vertices of that polygon.				     *
*****************************************************************************/
static void DPUpdatePolyPlane(PolygonStruct *PPoly)
{
    int i;
    VectorType V1, V2;
    RealType Len;
    VertexStruct *V;

    V = PPoly -> V;	PT_SUB(V1, V -> Pt, V -> Pnext -> Pt);
    V = V -> Pnext;	PT_SUB(V2, V -> Pt, V -> Pnext -> Pt);

    PPoly -> Plane[0] = V1[1] * V2[2] - V2[1] * V1[2];
    PPoly -> Plane[1] = V1[2] * V2[0] - V2[2] * V1[0];
    PPoly -> Plane[2] = V1[0] * V2[1] - V2[0] * V1[1];
    PPoly -> Plane[3] = (-DOT_PROD(PPoly -> Plane, PPoly -> V -> Pt));

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

/*****************************************************************************
* Routine to print pasring error according to ErrNum and set GlblParserError.*
*****************************************************************************/
static void ParserError(DataPrsrErrType ErrNum, char *Msg)
{
    DPGlblLineCount = GlblLineCount;
    DPGlblParserError = ErrNum;
    strcpy(DPGlblTokenError, Msg);	/* Keep the message in safe place... */

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

/*****************************************************************************
* Returns TRUE if error happened, FALSE otherwise.			     *
*   If error, then ErrorMsg is updated to point on static str describing it. *
*****************************************************************************/
int DataPrsrParseError(char **ErrorMsg)
{
    DataPrsrErrType Temp;
    char TempCopy[LINE_LEN];

    if ((Temp = DPGlblParserError) == DP_NO_ERR) return FALSE;

    strcpy(TempCopy, DPGlblTokenError);
    DPGlblParserError = DP_NO_ERR;

    switch (Temp) {
	case DP_ERR_NUMBER_EXPECTED:
	    sprintf(DPGlblTokenError, "Line %d: Numeric data expected - found %s",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_OPEN_PAREN_EXPECTED:
	    sprintf(DPGlblTokenError, "Line %d: '[' expected - found '%s'",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_CLOSE_PAREN_EXPECTED:
	    sprintf(DPGlblTokenError, "Line %d: ']' expected - found '%s'",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_LIST_COMP_UNDEF:
	    sprintf(DPGlblTokenError, "Line %d: Undefined list element - \"%s\"",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_UNDEF_EXPR_HEADER:
	    sprintf(DPGlblTokenError, "Line %d: Undefined TOKEN - \"%s\"",
		DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_PT_TYPE_EXPECTED:
	    sprintf(DPGlblTokenError, "Line %d: Point type expected",
		DPGlblLineCount);
	    break;
	case DP_ERR_OBJECT_EMPTY:
	    sprintf(DPGlblTokenError, "Line %d: Empty object found",
		DPGlblLineCount);
	    break;
	case DP_ERR_EMPTY_NAME:
	    sprintf(DPGlblTokenError, "Given file name is empty");
	    break;
	case DP_ERR_OPEN_FAILED:
	    sprintf(DPGlblTokenError, "Fail to open file - %s",	TempCopy);
	    break;
	case DP_ERR_MIXED_TYPES:
	    sprintf(DPGlblTokenError,
		    "Line %d: Mixed data types in same object",
		    DPGlblLineCount);
	    break;
	case DP_STR_NOT_IN_QUOTES:
	    sprintf(DPGlblTokenError,
		    "Line %d: String not in quotes (%s)",
		    DPGlblLineCount, TempCopy);
	    break;
	case DP_ERR_CAGD_LIB_ERR:
	    sprintf(DPGlblTokenError, "Line %d: %s",
		    DPGlblLineCount, TempCopy);
	    break;
	default:
	    sprintf(DPGlblTokenError,
		    "Line %d: Data file parser - undefined error",
		    DPGlblLineCount);
	    break;
    }

    *ErrorMsg = DPGlblTokenError;

    return TRUE;
}
[ RETURN TO DIRECTORY ]