Metropoli BBS
VIEWER: convdata.c MODE: TEXT (ASCII)
/*****************************************************************************
* CONVDATA.C is an unsupported tool to help convert old (*.ply) data files   *
* to the new (*.dat) format. This tool will be retired in future releases.   *
*									     *
* To compile:								     *
*									     *
*	Under MSDOS, using Borland/Turbo C: 'tcc -ml convdata.c'	     *
*									     *
*	Under UNIX: 'cc -i convdata convdata.c'				     *
*									     *
* Usage:								     *
*									     *
*	convdata < oldfile.ply > newfile.dat				     *
*									     *
*	or								     *
*									     *
*	convdata oldfile.ply > newfile.dat				     *
*****************************************************************************/

#ifdef __MSDOS__
#include <stdlib.h>
#include <conio.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>

#ifdef NO_VOID_PTR
#define VoidPtr		char *
#else
#define VoidPtr		void *
#endif /* NO_VOID_PTR */

#ifndef LINE_LEN
#define LINE_LEN	255
#endif  LINE_LEN

#define NAME_LEN	6

#ifndef TRUE
#define TRUE		1
#define FALSE		0
#endif

#ifndef NULL
#define NULL		0
#endif

#define FILE_TYPE_DATA	1
#define FILE_TYPE_VIEW	2

#define GEN_COPY(Dest, Src, Size)       memcpy(Dest, Src, Size)

#define	UNGET_STACK_SIZE	5

/*****************************************************************************
* Tokens definitions goes into here                                          *
*****************************************************************************/
#define TOKEN_OPEN_PAREN	1
#define TOKEN_CLOSE_PAREN	2

#define TOKEN_NUMBER		10 /* Not used as number & names are decoded */
				    /* according to their places in grammer. */

#define TOKEN_VERTEX	20
#define TOKEN_POLYGON	21
#define TOKEN_POLYLINE	22
#define TOKEN_POINTLIST	23
#define TOKEN_OBJECT	24
#define TOKEN_COLOR	25
#define TOKEN_INTERNAL	26
#define TOKEN_NORMAL	27
#define TOKEN_PLANE	28
#define TOKEN_CBEZIER	29
#define TOKEN_SBEZIER	30
#define TOKEN_CNURB	31
#define TOKEN_SNURB	32

#define TOKEN_OTHER	100			/* Probably names & numbers. */
#define TOKEN_EOF	-1

#define VERTEX_ENTRY	0x0001		      /* Entry type, up to 16 types. */
#define POLYGON_ENTRY	0x0002
#define POLYLINE_ENTRY	0x0004
#define OBJECT_ENTRY	0x0008
#define COLOR_ENTRY	0x0010

#define POLYGON		1
#define POLYLINE	2
#define POINTLIST	3

/*****************************************************************************
* Parser error codes are following:                                          *
*****************************************************************************/
#define P_ERR_NUMBER_EXPECTED		1
#define P_ERR_OPEN_PAREN_EXPECTED	2
#define P_ERR_CLOSE_PAREN_EXPECTED	3
#define P_ERR_LIST_COMP_UNDEF		4
#define P_ERR_UNDEF_EXPR_HEADER		5
#define P_ERR_NAME_TOO_LONG		6
#define P_ERR_PT_TYPE_EXPECTED		7

#define P_WRN_OBJ_NAME_TRUNC		100

/*****************************************************************************
* And some more definitions ...                                              *
*****************************************************************************/

typedef unsigned char Byte;

typedef struct BinTree {	   /* The entries are saved as binary trees. */
    struct BinTree *right, *left;		      /* Classic, isnt it !? */
    union {
	VoidPtr		      PVoid;
	struct VertexStruct  *PVertex;
	struct PolygonStruct *PPolygon;
	struct ObjectStruct  *PObject;
    } Data;
    char Name[NAME_LEN];
    Byte EntryType;				/* Kind of pointer in union. */
    Byte Used;	     /* TRUE if allready has been used (multi - referenced). */
} BinTree;

typedef struct FileDescription {  /* Each data file loaded gets such struct. */
    BinTree *VertexPointer,	        /* Pointers on the binary trees ...  */
	    *PolygonPointer,
	    *ObjectPointer;
} FileDescription;

/* Use the following structure if linear list operations are to be performed */
/* on VertexStruct/PolygonStruct/ObjectStruct (Virtual functions...).	     */
typedef struct LinearListStruct {
    struct LinearListStruct *Pnext;
} LinearListStruct;

typedef struct VertexStruct {
    struct VertexStruct *Pnext;
    float Coord[3];					   /* The 3D coords. */
    float Normal[3];					   /* The 3D normal. */
    Byte Transform;
    Byte Internal;		       /* If edge is Internal (IRIT output). */
    Byte HasNormal;			    /* If edge has a normal defined. */
} VertexStruct;

typedef struct PolygonStruct {
    struct PolygonStruct *Pnext;
    VertexStruct *PVertex;			    /* The polygon vertices. */
    float Plane[4];			      /* The Polygon plane equation. */
    Byte PolyType;		     /* One of POLYGON, POLYLINE, POINTLIST. */
    Byte HasPlane;			  /* If polygon has a plane defined. */
} PolygonStruct;

typedef struct ObjectStruct {
    struct ObjectStruct *Pnext;
    PolygonStruct *PPolygon;			    /* The objects polygons. */
    int Color;
} ObjectStruct;

/* And function prototypes: */

FileDescription *GetDataFile(FILE *f);
void GetViewFile(FILE *f, int FileExists);
BinTree *GetBinTree(char *RecName, BinTree *Tree);

extern unsigned int _stklen = 32766;	     /* Increase default stack size. */

static int  GlblToken =	0,	      /* Used by the parser, to unget token. */
	    GlblLineCount = 1,	     /* Used to locate errors in input file. */
	    GlblParserError = 0;	    /* Save last error number found. */
static char GlblStringToken[UNGET_STACK_SIZE][LINE_LEN];/* Save unget tokens.*/
static struct FileDescription *Descriptor;

char *Real2Str(double R);
static struct BinTree *AllocBinTree(int Entry, VoidPtr Data);
static void UnGetToken(char *StringToken);
static void GetStringToken(FILE *f, char *StringToken);
static int GetToken(FILE *f, char *StringToken);
static void GetVertexAttributes(VertexStruct *PVertex, FILE *f);
static void GetPolygonAttributes(PolygonStruct *PPolygon, FILE *f);
static void GetObjectAttributes(ObjectStruct *PObject, FILE *f);
static void EliminateComments(FILE *f);
static void ParserError(int ErrNum, char *Msg);
static void InsertBinTree(BinTree **Tree, BinTree *PNewRecord);
static LinearListStruct *GetNameFromFD(char *Name, FileDescription *FD,
							int EntryTypes);
static VoidPtr GetLinList(FILE *f, FileDescription *FD, int EntryTypes);
static char *MyMalloc(unsigned size);
static void MyExit(int ExitCode);

/*****************************************************************************
*									     *
*****************************************************************************/
void main(int argc, char **argv)
{
    FILE *f = NULL;

    if (argc == 2 && (f = fopen(argv[1], "rt")) != NULL)
	GetDataFile(f);
    else GetDataFile(stdin);

    if (f) fclose(f);

    MyExit(0);

    printf("%lf", exp((double) argc));
}

/*****************************************************************************
* Dump out the new format (simpler, isnt it!?).				     *
*****************************************************************************/
void DumpOutObject(BinTree *T)
{
    int i;
    struct ObjectStruct *PObject = T -> Data.PObject;
    struct PolygonStruct *PPolygon = PObject -> PPolygon;
    struct VertexStruct *PVertex;

    printf("[OBJECT [COLOR %d] %s\n", PObject -> Color, T -> Name);

    while (PPolygon) {
	for (i = 0, PVertex = PPolygon -> PVertex;
	     PVertex != NULL;
	     i++, PVertex = PVertex -> Pnext);

	printf("    [%s", PPolygon -> PolyType == POLYLINE ? "POLYLINE" :
		PPolygon -> PolyType == POLYGON ? "POLYGON" : "POINTLIST");
	if (PPolygon -> HasPlane)
	    printf(" [PLANE %s %s %s %s]",
		Real2Str(PPolygon -> Plane[0]),
		Real2Str(PPolygon -> Plane[1]),
		Real2Str(PPolygon -> Plane[2]),
		Real2Str(PPolygon -> Plane[3]));
	printf(" %d\n", i);

	PVertex = PPolygon -> PVertex;
	while (PVertex) {
	    printf("\t[");
	    if (PVertex -> Internal)
		printf("[INTERNAL] ");
	    if (PVertex -> HasNormal)
		printf("[NORMAL %s %s %s] ",
		    Real2Str(PVertex->Normal[0]),
		    Real2Str(PVertex->Normal[1]),
		    Real2Str(PVertex->Normal[2]));
	    printf("%s %s %s]\n",
		    Real2Str(PVertex->Coord[0]),
		    Real2Str(PVertex->Coord[1]),
		    Real2Str(PVertex->Coord[2]));

	    PVertex = PVertex -> Pnext;
	}

	printf("    ]\n");
	PPolygon = PPolygon -> Pnext;
    }
    printf("] /* OBJECT %s */\n", T -> Name);
}

/*****************************************************************************
* Convert a real number into a string.					     *
*****************************************************************************/
char *Real2Str(double R)
{
    static int i = 0;
    static char Buffer[10][LINE_LEN];
    int j, k;

    sprintf(Buffer[i], "%-8.6lg", R);

    for (k = 0; !isdigit(Buffer[i][k]) && k < LINE_LEN; k++);
    if (k >= LINE_LEN) {
	fprintf(stderr, "Conversion of real number failed.\n");
	MyExit(3);
    }

    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) % 10;
    return Buffer[j];
}

/*****************************************************************************
* Routine to read the data from	a given	file and update	the data structures  *
* Descriptor (which returned) from it.					     *
* If MoreFlag is on then more printed staff will be given...		     *
*****************************************************************************/
FileDescription *GetDataFile(FILE *f)
{
    int	i;
    char StringToken[LINE_LEN];
    struct VertexStruct	 *PVertex;
    struct PolygonStruct *PPolygon;
    struct ObjectStruct	 *PObject;
    struct BinTree	 *PBinTree;

    GlblToken =	0;

    Descriptor = (FileDescription *) MyMalloc(sizeof(FileDescription));
    /* As all the trees	are empty set the following: */
    Descriptor -> VertexPointer	   =
    Descriptor -> PolygonPointer   =
    Descriptor -> ObjectPointer	   = (BinTree *) NULL;

    GlblLineCount = 1;				      /* Reset line counter. */
    EliminateComments(f);		 /* Skip all comments including '['. */
    while (!feof(f)) {
	GlblParserError = 0;				    /* Reset errors. */
	switch (GetToken(f, StringToken)) {
	    case TOKEN_VERTEX:
		PVertex = (VertexStruct *) MyMalloc(sizeof(VertexStruct));
		PVertex -> Transform = FALSE; /* Flag if vertex transformed. */
		PVertex -> Internal = FALSE;  /* Flag if vertex is internal. */
		PVertex -> HasNormal = FALSE;  /* Flag if vertex has normal. */
		PBinTree = AllocBinTree(VERTEX_ENTRY, (VoidPtr) PVertex);
		GetToken(f, PBinTree -> Name);		    /* Get the name. */
		if (strlen(PBinTree -> Name) >= NAME_LEN)
		    ParserError(P_ERR_NAME_TOO_LONG, PBinTree -> Name);
		/* The following handle the optional attributes in record.   */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetVertexAttributes(PVertex, f);	  /* Get attributes. */
		else UnGetToken(StringToken);
		/* The following handles reading of 3 coord. of vertex.      */
		for (i=0; i<3 ;i++) {
		    GetToken(f, StringToken);
		    if (sscanf(StringToken, "%f", &PVertex -> Coord[i]) != 1)
			ParserError(P_ERR_NUMBER_EXPECTED, StringToken);
		}
		if ((i=GetToken(f, StringToken)) != TOKEN_CLOSE_PAREN)
			    ParserError(P_ERR_CLOSE_PAREN_EXPECTED, StringToken);
		if (!GlblParserError)
			 InsertBinTree(&Descriptor -> VertexPointer, PBinTree);
		break;
	     case TOKEN_POLYGON:
		PPolygon = (PolygonStruct *) MyMalloc(sizeof(PolygonStruct));
		PPolygon -> PolyType = POLYGON;
		PPolygon -> HasPlane = FALSE;
		PBinTree = AllocBinTree(POLYGON_ENTRY, (VoidPtr) PPolygon);
		GetToken(f, PBinTree -> Name);		    /* Get the name. */
		if (strlen(PBinTree -> Name) >= NAME_LEN)
		    ParserError(P_ERR_NAME_TOO_LONG, PBinTree -> Name);
		/* The following handle the optional attributes in record. */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetPolygonAttributes(PPolygon, f);
		else UnGetToken(StringToken);
		/* The following handles reading the members list. */
		PPolygon -> PVertex = (VertexStruct *)
			  GetLinList(f, Descriptor, VERTEX_ENTRY);
		if (!GlblParserError)
		       InsertBinTree(&Descriptor -> PolygonPointer, PBinTree);
		break;
	     case TOKEN_POLYLINE:
		PPolygon = (PolygonStruct *) MyMalloc(sizeof(PolygonStruct));
		PPolygon -> PolyType = POLYLINE;
		PPolygon -> HasPlane = FALSE;
		PBinTree = AllocBinTree(POLYGON_ENTRY, (VoidPtr) PPolygon);
		GetToken(f, PBinTree -> Name);		    /* Get the name. */
		if (strlen(PBinTree -> Name) >= NAME_LEN)
		    ParserError(P_ERR_NAME_TOO_LONG, PBinTree -> Name);
		/* The following handle the optional attributes in record. */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetPolygonAttributes(PPolygon, f);
		else UnGetToken(StringToken);
		/* The following handles reading the members list. */
		PPolygon -> PVertex = (VertexStruct *)
			GetLinList(f, Descriptor, VERTEX_ENTRY);
		if (!GlblParserError)
		       InsertBinTree(&Descriptor -> PolygonPointer, PBinTree);
		break;
	     case TOKEN_POINTLIST:
		PPolygon = (PolygonStruct *) MyMalloc(sizeof(PolygonStruct));
		PPolygon -> PolyType = POINTLIST;
		PPolygon -> HasPlane = FALSE;
		PBinTree = AllocBinTree(POLYGON_ENTRY, (VoidPtr) PPolygon);
		GetToken(f, PBinTree -> Name);		    /* Get the name. */
		if (strlen(PBinTree -> Name) >= NAME_LEN)
		    ParserError(P_ERR_NAME_TOO_LONG, PBinTree -> Name);
		/* The following handle the optional attributes in record. */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetPolygonAttributes(PPolygon, f);
		else UnGetToken(StringToken);
		/* The following handles reading the members list. */
		PPolygon -> PVertex = (VertexStruct *)
			GetLinList(f, Descriptor, VERTEX_ENTRY);
		if (!GlblParserError)
		       InsertBinTree(&Descriptor -> PolygonPointer, PBinTree);
		break;
	     case TOKEN_OBJECT:
		PObject = (ObjectStruct *) MyMalloc(sizeof(ObjectStruct));
		PObject-> Color = 1;
		PBinTree = AllocBinTree(OBJECT_ENTRY, (VoidPtr) PObject);
		GetToken(f, PBinTree -> Name);		    /* Get the name. */
		if (strlen(PBinTree -> Name) >= NAME_LEN) {
		    /* Although this may be considered an error, no one      */
		    /* reference objects, so we can simply truncate it.      */
		    PBinTree -> Name[NAME_LEN - 1] = 0;
		}
		/* The following handle the optional attributes in record. */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
					GetObjectAttributes(PObject, f);
		else UnGetToken(StringToken);
		/* The following handles reading the polygon list.	     */
		/* Note an object might be created from Polygons only.	     */
		PObject -> PPolygon = (PolygonStruct *)
				GetLinList(f, Descriptor, POLYGON_ENTRY);
		if (!GlblParserError)
			InsertBinTree(&Descriptor -> ObjectPointer, PBinTree);
		DumpOutObject(PBinTree);
		break;
	    default:
		ParserError(P_ERR_UNDEF_EXPR_HEADER, StringToken);
		break;
	} /* Of switch. */
	if (!feof(f)) EliminateComments(f);  /* Skip comments including '['. */
    } /* Of while !eof. */

    return Descriptor;
}

/*****************************************************************************
* Routine to allocate one BinTree Item:					     *
*****************************************************************************/
static struct BinTree *AllocBinTree(int Entry, VoidPtr Data)
{
    struct BinTree *PBinTree;

    PBinTree = (BinTree *) MyMalloc(sizeof(BinTree));
    PBinTree -> EntryType = Entry;
    PBinTree -> Used = FALSE;
    PBinTree -> Data.PVoid = Data;
    PBinTree -> right = PBinTree -> left = (BinTree *) NULL;

    return PBinTree;
}

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

    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 =	NULL;					 /* 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] = NULL;		/* Set end of string on	"]". */
    }
}

/*****************************************************************************
*   Routine to get the next token out of the input file	f as token number.   *
* Returns the next token number	found, with numeric result in NumericToken   *
* if TokenType is TOKEN_NUMBER.						     *
* Note:	StringToken must be allocated before calling this routine!	     *
*****************************************************************************/
static int GetToken(FILE *f, char *StringToken)
{
    GetStringToken(f, StringToken);

    if (feof(f))			     return TOKEN_EOF;

    if (!strcmp(StringToken, "["))	     return TOKEN_OPEN_PAREN;
    if (!strcmp(StringToken, "]"))	     return TOKEN_CLOSE_PAREN;

    if (!strcmp(StringToken, "VERTEX"))	     return TOKEN_VERTEX;
    if (!strcmp(StringToken, "POLYGON"))     return TOKEN_POLYGON;
    if (!strcmp(StringToken, "POLYLINE"))    return TOKEN_POLYLINE;
    if (!strcmp(StringToken, "POINTLIST"))   return TOKEN_POINTLIST;
    if (!strcmp(StringToken, "OBJECT"))	     return TOKEN_OBJECT;

    if (!strcmp(StringToken, "COLOR"))	     return TOKEN_COLOR;
    if (!strcmp(StringToken, "INTERNAL"))    return TOKEN_INTERNAL;
    if (!strcmp(StringToken, "NORMAL"))      return TOKEN_NORMAL;
    if (!strcmp(StringToken, "PLANE"))	     return TOKEN_PLANE;

    if (!strcmp(StringToken, "CBEZIER"))     return TOKEN_CBEZIER;
    if (!strcmp(StringToken, "SBEZIER"))     return TOKEN_SBEZIER;
    if (!strcmp(StringToken, "CNURB"))       return TOKEN_CNURB;
    if (!strcmp(StringToken, "SNURB"))       return TOKEN_SNURB;

    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 i;
    char StringToken[LINE_LEN];


    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_INTERNAL:
		PVertex -> Internal = TRUE;
		if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN)
		    ParserError(P_ERR_CLOSE_PAREN_EXPECTED, StringToken);
		break;
	    case TOKEN_NORMAL:
		PVertex -> HasNormal = TRUE;
		/* The following handles reading of 3 coord. of normal.	*/
		for (i=0; i<3 ;i++) {
		    GetToken(f, StringToken);
		    if (sscanf(StringToken, "%f", &PVertex -> Normal[i]) != 1)
			ParserError(P_ERR_NUMBER_EXPECTED, StringToken);
		}
		if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN)
		    ParserError(P_ERR_CLOSE_PAREN_EXPECTED, StringToken);
		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: None					     *
*****************************************************************************/
static void GetPolygonAttributes(PolygonStruct *PPolygon, FILE *f)
{
    int i;
    char StringToken[LINE_LEN];

    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_PLANE:
		PPolygon -> HasPlane = TRUE;
		/* The following handles reading of 4 coord. of plane eqn.. */
		for (i=0; i<4 ;i++) {
		    GetToken(f, StringToken);
		    if (sscanf(StringToken, "%f", &PPolygon -> Plane[i]) != 1)
			ParserError(P_ERR_NUMBER_EXPECTED, StringToken);
		}
		if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN)
		    ParserError(P_ERR_CLOSE_PAREN_EXPECTED, StringToken);
		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.			     *
*****************************************************************************/
static void GetObjectAttributes(ObjectStruct *PObject, FILE *f)
{
    int	i;
    char StringToken[LINE_LEN];

    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_COLOR:
		GetToken(f, StringToken);
		if (sscanf(StringToken, "%d", &i) != 1)
		    ParserError(P_ERR_NUMBER_EXPECTED, StringToken);
		if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN)
		    ParserError(P_ERR_CLOSE_PAREN_EXPECTED, StringToken);
		PObject -> Color = i;
		break;
	    default:
		while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN);
		break;
	}
    }
    while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN);

    UnGetToken(StringToken);
}

/*****************************************************************************
* Routine to read the input tokens up to a '[' token - skip comments.	     *
* Note the routine reads the '[' token,	so next	is the expression itself.    *
*****************************************************************************/
static void EliminateComments(FILE *f)
{
    char StringToken[LINE_LEN];

    while ((!feof(f)) && (GetToken(f, StringToken) != TOKEN_OPEN_PAREN));
}

/*****************************************************************************
* Routine to print pasring error according to ErrNum and set GlblParserError.*
*****************************************************************************/
static void ParserError(int ErrNum, char *Msg)
{
    GlblParserError = TRUE;

    printf("Error in line %3d : ", GlblLineCount);

    switch (ErrNum) {
	case P_ERR_NUMBER_EXPECTED:
	    printf("Numeric data expected, ");
	    break;
	case P_ERR_OPEN_PAREN_EXPECTED:
	    printf("[ expected, ");
	    break;
	case P_ERR_CLOSE_PAREN_EXPECTED:
	    printf("] expected, ");
	    break;
	case P_ERR_LIST_COMP_UNDEF:
	    printf("List component undefined - ");
	    break;
	case P_ERR_UNDEF_EXPR_HEADER:
	    printf("Undefined expression header - ");
	    break;
	case P_ERR_NAME_TOO_LONG:
	    printf("Object Name too long (Max %d) - ", NAME_LEN - 1);
	    break;
	case P_ERR_PT_TYPE_EXPECTED:
	    printf("Point type expected, ");
	    break;
	default:
	    printf("Unknown error, ");
	    break;
    }
    printf("found %s.\n", Msg);
}

/*****************************************************************************
* Routine to insert new	element	into a binary tree. If the element already   *
* exists then the new one replace it.					     *
*****************************************************************************/
static void InsertBinTree(BinTree **Tree, BinTree *PNewRecord)
{
    int	Comparison;
    BinTree *PBin;

    if (*Tree == (BinTree *) NULL)   /*	Only might happen if the tree empty. */
	*Tree =	PNewRecord;
    else {			      /* Search for the new place to put it. */
	 /* Test for Match - if	so replace old by new: */
	if ((Comparison	= strcmp((*Tree) -> Name, PNewRecord -> Name)) == 0) {
	    PBin = *Tree;
	    *Tree = PNewRecord;					 /* Replace. */
	    switch (PBin -> EntryType) {      /* Free the data area (union). */
		case VERTEX_ENTRY:
		    free((char *) PBin -> Data.PVertex);
		    break;
		case POLYGON_ENTRY:
		    free((char *) PBin -> Data.PPolygon);
		    break;
		case OBJECT_ENTRY:
		    free((char *) PBin -> Data.PObject);
		    break;
		default:
		    /* Should not be, unless was not updated here... */
		    break;
	    }
	    free((char *) PBin);
	}
	else if	(Comparison > 0)	   /* Go to right side - its bigger. */
		if ((*Tree) -> right !=	(BinTree *) NULL)  /* Only if exist. */
		    InsertBinTree(&((*Tree) -> right), PNewRecord);
		else (*Tree) ->	right =	PNewRecord;  /*	Put record in place. */
	     else if ((*Tree) -> left != (BinTree *) NULL) /* Only if exist. */
		    InsertBinTree(&((*Tree) -> left), PNewRecord);/*Smaller. */
		  else (*Tree) -> left = PNewRecord; /*	Put record in place. */
    }
}

/*****************************************************************************
* Routine to Get an element from binary	tree. If the element is	found a	     *
* pointer to it	BinTree	record is return, NULL else...			     *
*****************************************************************************/
BinTree *GetBinTree(char *RecName, BinTree *Tree)
{
    int	Comparison;

    /* If the tree is empty - not found, return	NULL: */
    if (Tree ==	(BinTree *) NULL) return (BinTree *) NULL;

    /* Test for	Match -	if so return that record: */
    if ((Comparison = strcmp(Tree -> Name, RecName)) == 0)
	return Tree;			      /* Found it - so return it ... */
    else if (Comparison	> 0)
	      return GetBinTree(RecName, Tree -> right);
	 else return GetBinTree(RecName, Tree -> left);
}

/*****************************************************************************
* Routine to search for	Name in	the trees, allowed by EntryTypes, of the     *
* file descrition FD. NULL returned if not found. The order of search is:    *
* VERTEX ,  POLYGON ,  OBJECT.						     *
* Once found, if was already used (multi-reference) it is copied fresh.	     *
*****************************************************************************/
static LinearListStruct *GetNameFromFD(char *Name, FileDescription *FD,
								int EntryTypes)
{
    BinTree *PBin;
    VertexStruct *PVertex;
    PolygonStruct *PPolygon;
    ObjectStruct *PObject;

    if (EntryTypes & VERTEX_ENTRY) {		  /* Check in vertices tree. */
	if ((PBin = GetBinTree(Name, FD -> VertexPointer)) != NULL) {
	    if (PBin -> Used) {
		PVertex = (VertexStruct *) MyMalloc(sizeof(VertexStruct));
		GEN_COPY(PVertex, PBin -> Data.PVertex, sizeof(VertexStruct));
		return (LinearListStruct *) PVertex;
	    }
	    else {
		PBin -> Used = TRUE;
		return (LinearListStruct *) PBin -> Data.PVertex;
	    }
	}
    }
    if (EntryTypes & POLYGON_ENTRY) {		   /* Check in polygon tree. */
	if ((PBin = GetBinTree(Name, FD -> PolygonPointer)) != NULL) {
	    if (PBin -> Used) {
		PPolygon = (PolygonStruct *) MyMalloc(sizeof(PolygonStruct));
		GEN_COPY(PPolygon, PBin -> Data.PPolygon, sizeof(PolygonStruct));
		return (LinearListStruct *) PPolygon;
	    }
	    else {
		PBin -> Used = TRUE;
		return (LinearListStruct *) PBin -> Data.PPolygon;
	    }
	}
    }
    if (EntryTypes & OBJECT_ENTRY) {		    /* Check in object tree. */
	if ((PBin = GetBinTree(Name, FD -> ObjectPointer)) != NULL) {
	    if (PBin -> Used) {
		PObject = (ObjectStruct *) MyMalloc(sizeof(ObjectStruct));
		GEN_COPY(PObject, PBin -> Data.PObject, sizeof(ObjectStruct));
		return (LinearListStruct *) PObject;
	    }
	    else {
		PBin -> Used = TRUE;
		return (LinearListStruct *) PBin -> Data.PObject;
	    }
	}
    }

    return NULL;					       /* Not found. */
}

/*****************************************************************************
* Routine to get linear	list of	names from file	f until	']' is detected.     *
* search for that names	in file	description FD unter the trees allowed	     *
* according to EntryTypes (1 bit per entry, see	?????Entry is parser.h).     *
* Create a linear list of pointers to them. Return that	linear list.	     *
*****************************************************************************/
static VoidPtr GetLinList(FILE *f, FileDescription *FD, int EntryTypes)
{
    char StringToken[LINE_LEN];
    struct LinearListStruct *PLinHead = NULL, *PLinTail = NULL, *PItem;

    while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN) {
	if ((PItem = GetNameFromFD(StringToken, FD, EntryTypes)) == NULL) {
	    ParserError(P_ERR_LIST_COMP_UNDEF, StringToken);/* Record undef. */
	    continue;			 /* To next component to search for. */
	}
	if (PLinHead ==	NULL)				/* Its first record. */
	    PLinHead = PLinTail	= PItem;
	else {					/* Its record in the middle. */
	    PLinTail ->	Pnext =	PItem;
	    PLinTail = PItem;
	}
    }
    if (PLinTail != NULL) PLinTail -> Pnext = NULL;	/* Mark end of list. */

    return (VoidPtr) PLinHead;
}

/*****************************************************************************
* My Routine to	allocate dynamic memory. All program requests must call this *
* routine (no direct call to malloc). Dies if no memory.		     *
*****************************************************************************/
static char *MyMalloc(unsigned size)
{
    char *p;

    if ((p = malloc(size)) != NULL) return p;

    printf("Not enough memory, exit\n");
    MyExit(2);

    return NULL;				    /* Make warnings silent. */
}

/*****************************************************************************
* MyExit routine. Note it might call to CloseGraph without calling	     *
* InitGraph(), or call MouseClose() without MouseInit() etc. and it is the   *
* responsibility of the individual modules to do nothing in these cases.     *
*****************************************************************************/
static void MyExit(int ExitCode)
{
    exit(ExitCode);
}
[ RETURN TO DIRECTORY ]