/*****************************************************************************
*   Module to handle libraries (first part - file and io).		     *
*									     *
* Written by:  Gershon Elber			IBM PC Ver 1.0,	Oct. 1989    *
*****************************************************************************/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <dos.h>
#include <dir.h>
#include <alloc.h>
#include <time.h>
#include "Program.h"
#include "Director.h"
#include "PriorQue.h"
#include "EELibs.h"
#include "EELibsL.h"
#include "EEModify.h"
#include "EERedraw.h"
#include "EEString.h"
#include "EELayer.h"
#include "Primary.h"
FILE *debug;
LibraryStruct
    *LibraryList = NULL;		    /* All part libs are saved here. */
static IntrCursorShapeStruct Cursor = {
    INTR_CURSOR_ARROW, 0, 0, 0, 0, FALSE, NULL, FALSE
};

static char *GetLine(FILE *f, char *Line, int *LineNum);
static PriorQue *LoadLibraryAux(FILE *f, int *NumOfParts);
static char *GetPinsEntry(FILE *f, char *Line, int *LineNum, int NumOfPins);
static int *GetMultiEntry(FILE *f, char *Line, int *LineNum, int NumOfUnits,
							int PinsPerUnit);
static LibraryDrawEntryStruct *GetDrawEntry(FILE *f, char *Line, int *LineNum,
			BooleanType *HasLines, LibraryEntryStruct *LibEntry);
static void UpdateBBox(LibraryEntryStruct *LibEntry, int x, int y);
static void FreeLibraryEntry(LibraryEntryStruct *Entry);
static LibraryStruct *FindLibrary(char *Name);

/*****************************************************************************
* Routine to load a library from the current directory.			     *
*****************************************************************************/
void LoadLibrary(void)
{
    int i, NumOfFiles, NumOfParts;
    char *p, LibName[FILE_NAME_LEN];
    FILE *f;
    FileNameType *FileNames;
    LibraryStruct *NewLib;
    PriorQue *Entries;

    if ((FileNames = GetFileNamesDir("*.lib", ".", &NumOfFiles)) != NULL) {
	qsort(FileNames, NumOfFiles, sizeof(FileNameType),
	      (int (*)(const void *, const void *)) strcmp);
	if ((i = IntrQueryList("Load Lib", (char **) FileNames,
			 sizeof(FileNameType), NumOfFiles, EEListNumDisplayed,
                         EEPopUpFrameColor, EEPopUpBackColor, EEPopUpForeColor,
                         EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                         INTR_WNDW_PLACE_CENTER)) < 0) {
	    MyFree((VoidPtr) FileNames);
	    return;
	}
	strcpy(LibName, FileNames[i]);
	if ((p = strchr(LibName, '.')) != NULL) p[0] = 0;
	if (FindLibrary(LibName) != NULL) {
	    /* Library by that name already exists - kill old one? */
	    if (IntrQueryYesNo("Library by that name exists, overwrite?",
		      EEPopUpFrameColor, EEPopUpBackColor, EEPopUpForeColor,
                      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                      INTR_WNDW_PLACE_CENTER)) {
		FreeLibrary(LibName);
	    }
	    else {
		MyFree((VoidPtr) FileNames);
		return;
	    }
	}
	f = fopen(FileNames[i], "rt");
	MyFree((VoidPtr) FileNames);
	if (f == NULL) {
	    IntrQueryContinue("Failed to open library", EEPopUpFrameColor,
            		      EEPopUpBackColor, EEPopUpForeColor,
                              EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    return;
	}
	if ((Entries = LoadLibraryAux(f, &NumOfParts)) != NULL) {
	    NewLib = (LibraryStruct *) MyMalloc(sizeof(LibraryStruct));
	    NewLib -> Entries = Entries;
	    NewLib -> NumOfParts = NumOfParts;
	    strcpy(NewLib -> Name, LibName);
	    NewLib -> Pnext = LibraryList;
	    LibraryList = NewLib;
	}
	fclose(f);
    }
    else {
	IntrQueryContinue("No libraries found", EEPopUpFrameColor,
            		  EEPopUpBackColor, EEPopUpForeColor,
                          EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
    }
}

/*****************************************************************************
* Routine to load the given library name. FullLibName should hold full path  *
* of file name to open, while LibName should hold only its name.	     *
* IF library already exists, it is NOT reloaded.			     *
*****************************************************************************/
void LoadLibraryName(char *FullLibName, char *LibName)
{
    int NumOfParts;
    FILE *f;
    LibraryStruct *NewLib;
    PriorQue *Entries;

    if (FindLibrary(LibName) != NULL) return;

    f = fopen(FullLibName, "rt");
    if (f == NULL) {
	IntrQueryContinue("Failed to open library", EEPopUpFrameColor,
            		  EEPopUpBackColor, EEPopUpForeColor,
                          EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	return;
    }
    if ((Entries = LoadLibraryAux(f, &NumOfParts)) != NULL) {
	NewLib = (LibraryStruct *) MyMalloc(sizeof(LibraryStruct));
	NewLib -> Entries = Entries;
	NewLib -> NumOfParts = NumOfParts;
	strcpy(NewLib -> Name, LibName);
	NewLib -> Pnext = LibraryList;
	LibraryList = NewLib;
    }
    fclose(f);
}

/*****************************************************************************
* Routine to load all libraries specified in LoadLibraryList which assumes   *
* to hold names of libraries to load, seperated by commas and with no lib    *
* extension. Note we dont use strtok as LoadLibraryName uses it internally.  *
*****************************************************************************/
void LoadLibraries(char *LoadLibraryList)
{
    char *p,*pcc, *NextLibName, Name[LINE_LEN_SHORT], Line[LINE_LEN],
	*LibName = LoadLibraryList;

    if (!strtok(LibName, "\n\r") || strlen(LibName) == 0) return;

    do {
	if ((NextLibName = strchr(LibName, ',')) != NULL) {
	    NextLibName[0] = 0;
	    NextLibName = &NextLibName[1];
	}

	strcpy(Name, LibName);
	if ((p = strrchr(Name, '.')) != NULL && strlen(p) < 5) *p = 0;
	strcat(Name, ".lib");
	p = searchpath(Name);
	if (p != NULL) {
	    IntrDrawMessage(p, EEPopUpForeColor, EEPopUpBackColor);
	    LoadLibraryName(p, LibName);
	    IntrEraseMessage();
	}
	else { 
		pcc = getenv("EEDLIB");
		if(pcc==NULL){
			fprintf(stderr,"EEDRAW: Libraries not found in search path, and EEDLIB not set\n");
			exit(1);
		}
		sprintf(Line,"%s\\%s",pcc,Name);
		p=Line;
		IntrDrawMessage(p, EEPopUpForeColor, EEPopUpBackColor);
		LoadLibraryName(p, LibName);
		IntrEraseMessage();
		
/*
            sprintf(Line, "Failed to find library \"%s\"", Name);
	    IntrQueryContinue(Line, EEPopUpFrameColor,
            		      EEPopUpBackColor, EEPopUpForeColor,
                              EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    return;
*/
	}

	LibName = NextLibName;
    }
    while (LibName != NULL);
}

/*****************************************************************************
* Routine to free a library from the current loaded libraries.		     *
* If LibName is NULL, the user is interactively requested for lib name.	     *
*****************************************************************************/
void FreeLibrary(char *LibName)
{
    int i,
	NumOfLibs = NumOfLibraries();
    char **Names;
    LibraryStruct *Lib, *TempLib;

    if (NumOfLibs == 0) {
	IntrQueryContinue("No libraries are loaded", EEPopUpFrameColor,
            		  EEPopUpBackColor, EEPopUpForeColor,
                          EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	return;
    }
    if (LibName == NULL) {		/* Need to ask for what lib to free: */
	Names = (char **) MyMalloc(sizeof(char *) * NumOfLibs);
	for (i = 0, Lib = LibraryList; Lib != NULL; Lib = Lib -> Pnext, i++)
	    Names[i] = Lib -> Name;
        i = IntrQueryList("Free Lib", Names, 0, NumOfLibs,
			  EEListNumDisplayed, EEPopUpFrameColor,
                          EEPopUpBackColor, EEPopUpForeColor,
                          EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	MyFree((VoidPtr) Names);

	if (i >= 0)
	    for (Lib = LibraryList; i > 0; i--, Lib = Lib -> Pnext);
	else
	    return;
    }
    else {			     /* Simply search for this library name: */
	for (Lib = LibraryList;
	     Lib != NULL && strcmp(LibName, Lib -> Name) != 0;
	     Lib = Lib -> Pnext);
	if (Lib == NULL) return;
    }

    if (Lib == LibraryList)
	LibraryList = Lib -> Pnext;
    else {
	for (TempLib = LibraryList;
	     TempLib -> Pnext != Lib;
	     TempLib = TempLib -> Pnext);
	TempLib -> Pnext = TempLib -> Pnext -> Pnext;
    }
    PQFreeFunc(Lib -> Entries, FreeLibraryEntry);
    MyFree((VoidPtr) Lib);
}

/*****************************************************************************
* Routine to return pointers to all library names.			     *
*****************************************************************************/
char **GetLibNames(void)
{
    int i,
	NumOfLibs = NumOfLibraries();
    char **Names;
    LibraryStruct *Lib;

    Names = (char **) MyMalloc(sizeof(char *) * (NumOfLibs + 1));
    for (i = 0, Lib = LibraryList; Lib != NULL; Lib = Lib -> Pnext, i++)
	Names[i] = Lib -> Name;
    Names[i] = NULL;

    return Names;
}

/*****************************************************************************
* Routine to read one line from given file.				     *
*****************************************************************************/
static char *GetLine(FILE *f, char *Line, int *LineNum)
{
    do {
	if (fgets(Line, LINE_LEN - 1, f) == NULL) return NULL;
	++*LineNum;
    }
    while (Line[0] == '#' || Line[0] == '\n' || Line[0] == 0);

    return Line;
}

/*****************************************************************************
* Routine to compare two LibraryEntryStruct for the PriorQue module.         *
* Comparison is based on Part name.					     *
*****************************************************************************/
int LibraryEntryCompare(LibraryEntryStruct *LE1,
			LibraryEntryStruct *LE2)
{
    return strcmp(LE1 -> Name, LE2 -> Name);
}

/*****************************************************************************
* Routine to load a library from given open file.			     *
*****************************************************************************/
static PriorQue *LoadLibraryAux(FILE *f, int *NumOfParts)
{
    int LineNum = 0;
    char Line[LINE_LEN_LONG], *p, *Name, *Prefix;
    BooleanType Res, HasLines;
    PriorQue
	*PQ = NULL;
    LibraryEntryStruct *LibEntry;

    *NumOfParts = 0;

    if (GetLine(f, Line, &LineNum) == NULL ||
	strncmp(Line, FILE_IDENT, sizeof(FILE_IDENT) - 1) != 0) {
	IntrQueryContinue("File is NOT EEDRAW library!", EEPopUpFrameColor,
		          EEPopUpBackColor, EEPopUpForeColor,
                          EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	return NULL;
    }

    PQInit(&PQ);
    PQCompFunc((PQCompFuncType) LibraryEntryCompare);

    while (GetLine(f, Line, &LineNum)) {
	p = strtok(Line, " \t\n");

	if (strcmp(p, "DEF") != 0) {
	    sprintf(Line, "DEF command expected in line %d, aborted.",
								LineNum);
	    IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);

	    return PQ;
	}
	else {
	    /* Read one DEF/ENDDEF part entry from library: */
	    LibEntry = (LibraryEntryStruct *)
				       MyMalloc(sizeof(LibraryEntryStruct));
	    LibEntry -> BBoxMinX = LibEntry -> BBoxMaxX =
	    LibEntry -> BBoxMinY = LibEntry -> BBoxMaxY = 0;
	    LibEntry -> Pins = NULL;
	    LibEntry -> Drawings = NULL;
	    LibEntry -> Multi = NULL;

	    if ((Name = strtok(NULL, " \t\n")) == NULL ||      /* Part name: */
		(Prefix = strtok(NULL, " \t\n")) == NULL ||  /* Prefix name: */
		(p = strtok(NULL, " \t\n")) == NULL ||         /* NumOfPins: */
		sscanf(p, "%d", &LibEntry -> NumOfPins) != 1 ||
		(p = strtok(NULL, " \t\n")) == NULL ||        /* TextInside: */
		sscanf(p, "%d", &LibEntry -> TextInside) != 1 ||
		(p = strtok(NULL, " \t\n")) == NULL ||	        /* DrawNums: */
		sscanf(p, "%d", &LibEntry -> DrawNums) != 1 ||
		(p = strtok(NULL, " \t\n")) == NULL ||        /* NumOfUnits: */
		sscanf(p, "%d", &LibEntry -> NumOfUnits) != 1 ||
		(LibEntry -> NumOfUnits > 0 &&
		 ((p = strtok(NULL, " \t\n")) == NULL ||     /* PinsPerUnit: */
		   sscanf(p, "%d", &LibEntry -> PinsPerUnit) != 1)) ||
		strlen(Name) > PART_NAME_LEN ||
		strlen(Prefix) > PREFIX_NAME_LEN) {
		sprintf(Line, "Wrong DEF format in line %d, aborted.",
								LineNum);
		IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
		FreeLibraryEntry(LibEntry);
		return PQ;
	    }
	    else {
		/* Copy part name and prefix. */
		strupr(Name);
		if ((LibEntry -> DrawName = Name[0] != '~') != FALSE)
		    strcpy(LibEntry -> Name, Name);
		else
		    strcpy(LibEntry -> Name, &Name[1]);
		if (strcmp(Prefix, "~") == 0)
		    LibEntry -> Prefix[0] = 0;
		else
		    strcpy(LibEntry -> Prefix, Prefix);
	    }

	    HasLines = FALSE;
	    while (TRUE) {
		GetLine(f, Line, &LineNum);
		p = strtok(Line, " \t\n");

		Res = TRUE;
		if (strcmp(p, "ENDDEF") == 0) {
		    ++*NumOfParts;
		    break;
		}
		else if (strcmp(p, "DRAW") == 0)
		    Res = (LibEntry -> Drawings =
			   GetDrawEntry(f, Line, &LineNum, &HasLines,
							   LibEntry)) != NULL;
		else if (strcmp(p, "PINS") == 0)
		    Res = (LibEntry -> Pins =
			   GetPinsEntry(f, Line, &LineNum, LibEntry -> NumOfPins))
								      != NULL;
		else if (strcmp(p, "MULTI") == 0) {
		    if (LibEntry -> NumOfUnits > 0)
			Res = (LibEntry -> Multi = GetMultiEntry(f, Line, &LineNum,
			       LibEntry -> NumOfUnits, LibEntry -> PinsPerUnit))
								      != NULL;
		    else {
			sprintf(Line, "MULTI found in DEF with #Units = 0 in line %d, aborted.",
								LineNum);
			IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
			FreeLibraryEntry(LibEntry);
			return PQ;
		    }
		}
		else {
		    sprintf(Line, "Undefined command \"%s\" in line %d, aborted.",
								p, LineNum);
		    IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
		    FreeLibraryEntry(LibEntry);
		    return PQ;
		}

		if (!Res) {		      /* Something went wrong there. */
		    FreeLibraryEntry(LibEntry);
		    return PQ;
		}
	    }
	    if (LibEntry -> Pins == NULL && HasLines) {
		sprintf(Line, "No PINS defined for part in line %d, aborted.",
								LineNum);
		IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
		MyFree((VoidPtr) LibEntry);
		return PQ;
	    }
	    if (LibEntry -> NumOfUnits > 0) {
		if (LibEntry -> Multi == NULL) {
		    sprintf(Line, "No MULTI defined for part in line %d, aborted.",
								LineNum);
		    IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
		    MyFree((VoidPtr) LibEntry);
		    return PQ;
		}
		if (LibEntry -> Drawings == NULL) {
		    sprintf(Line, "No DRAW defined for part in line %d, aborted.",
								LineNum);
		    IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
		    MyFree((VoidPtr) LibEntry);
		    return PQ;
		}
	    }
	    if (LibEntry -> Drawings == NULL) {
		/* Initialize BBox manually - its only a box with pins: */
		LibEntry -> BBoxMaxX = LibEntry -> BBoxMaxY =
		   PIN_WIDTH * LibEntry -> NumOfPins / 2 + PIN_LENGTH;
		LibEntry -> BBoxMinX = LibEntry -> BBoxMinY =
		    -LibEntry -> BBoxMaxX;
	    }

	    /* If we are here, this part is O.k. - put it in: */
	    PQInsert(&PQ, LibEntry);
	}
    }

    return PQ;
}

/*****************************************************************************
* Routine to load a PINS definition from given file. Note "PINS" line has    *
* been read already. Reads upto and include ENDPINS, or an error (NULL ret). *
*****************************************************************************/
static char *GetPinsEntry(FILE *f, char *Line, int *LineNum, int NumOfPins)
{
    int i;
    char *p, Pins[LINE_LEN_LONG];

    Pins[0] = 0;
    for (i = 0; i < NumOfPins; i++) {
	if (GetLine(f, Line, LineNum) == NULL) {
	    IntrQueryContinue("File ended prematurely", EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    return NULL;
	}

	p = strtok(Line, "\n");		     /* Remove the CR from line end. */
	if (strcmp(p, "ENDPINS") == 0) {
	    sprintf(Line, "ENDPINS too soon (not enough pins) in line %d, aborted.", *LineNum);
	    IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    return NULL;
	}
	if ((p[0] == '~' && strlen(p) == 1))	       /* Empty line ("~") ? */
	    strcat(Pins, "~");
	else
	    strcat(Pins, p);
	strcat(Pins, PIN_SEPERATOR);
	if (strlen(Pins) > LINE_LEN_LONG - 10) {
	    sprintf(Line, "Pin definitions are too long in line %d, aborted.",
								*LineNum);
	    IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    return NULL;
	}
    }

    if (GetLine(f, Line, LineNum) == NULL) {
	IntrQueryContinue("File ended prematurely", EEPopUpFrameColor,
		          EEPopUpBackColor, EEPopUpForeColor,
                	  EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	return NULL;
    }
    p = strtok(Line, " \t\n");
    if (strcmp(p, "ENDPINS") != 0) {
	sprintf(Line, "ENDPINS expected in line %d, aborted.", *LineNum);
	IntrQueryContinue(Line, EEPopUpFrameColor,
		          EEPopUpBackColor, EEPopUpForeColor,
                	  EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	return NULL;
    }

    return strdup(Pins);
}

/*****************************************************************************
* Routine to load a MULTI definition from given file. Note "MULTI" line has  *
* been read already. Reads upto and include ENDMULTI, or an error (NULL ret).*
*****************************************************************************/
static int *GetMultiEntry(FILE *f, char *Line, int *LineNum, int NumOfUnits,
							int PinsPerUnit)
{
    int i, j, *Array,
	Count = 0;
    char *p;

    Array = (int *) MyMalloc(sizeof(int) * NumOfUnits * PinsPerUnit);

    for (i = 0; i < NumOfUnits; i++) {
	if (GetLine(f, Line, LineNum) == NULL) {
	    IntrQueryContinue("File ended prematurely", EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    MyFree((VoidPtr) Array);
	    return NULL;
	}

	for (p = strtok(Line, " \t\n"), j = 0;
	     j < PinsPerUnit;
	     p = strtok(NULL, " \t\n"), j++) {
	    if (p == NULL ||
		sscanf(p, "%d", &Array[Count++]) != 1) {
		sprintf(Line, "MULTI has less pins than needed in line %d",
								*LineNum);
		IntrQueryContinue(Line, EEPopUpFrameColor,
		                  EEPopUpBackColor, EEPopUpForeColor,
                	          EEPopUpXorColor, EEWindowsFrameWidth,
                                  &Cursor, INTR_WNDW_PLACE_CENTER);
		MyFree((VoidPtr) Array);
		return NULL;
	    }
	}
    }

    if (GetLine(f, Line, LineNum) == NULL) {
	IntrQueryContinue("File ended prematurely", EEPopUpFrameColor,
		          EEPopUpBackColor, EEPopUpForeColor,
                	  EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	return NULL;
    }
    p = strtok(Line, " \t\n");
    if (strcmp(p, "ENDMULTI") != 0) {
	sprintf(Line, "ENDMULTI expected in line %d, aborted.", *LineNum);
	IntrQueryContinue(Line, EEPopUpFrameColor,
		          EEPopUpBackColor, EEPopUpForeColor,
                	  EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                          INTR_WNDW_PLACE_CENTER);
	return NULL;
    }

    return Array;
}

/*****************************************************************************
* Routine to load a DRAW definition from given file. Note "DRAW" line has    *
* been read already. Reads upto and include ENDDRAW, or an error (NULL ret). *
*****************************************************************************/
static LibraryDrawEntryStruct *GetDrawEntry(FILE *f, char *Line, int *LineNum,
			BooleanType *HasLines, LibraryEntryStruct *LibEntry)
{
    int i;
    char *p, Buffer[LINE_LEN_SHORT];
    RealType r;
    BooleanType
	Error = FALSE;
    LibraryDrawEntryStruct *Tail, *New,
	*Head = NULL;

    *HasLines = FALSE;

    while (TRUE) {
	if (GetLine(f, Line, LineNum) == NULL) {
	    IntrQueryContinue("File ended prematurely", EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth, &Cursor,
                              INTR_WNDW_PLACE_CENTER);
	    return Head;
	}

	if (strncmp(Line, "ENDDRAW", 7) == 0) break;

	New = (LibraryDrawEntryStruct *)
				   MyMalloc(sizeof(LibraryDrawEntryStruct));
	New -> Pnext = NULL;

	switch (Line[0]) {
	    case 'A': /* Arc */
		New -> DrawType = ARC_DRAW_TYPE;
		Error = sscanf(&Line[2], "%d %d %d %d %d %d",
		    &New -> Layer,
		    &New -> U.Arc.x, &New -> U.Arc.y, &New -> U.Arc.r,
		    &New -> U.Arc.t1, &New -> U.Arc.t2) != 6;
		New ->U.Arc.x *= LIB_SCALE_DRAW;
		New ->U.Arc.y *= LIB_SCALE_DRAW;
		New ->U.Arc.r *= LIB_SCALE_DRAW;
		NORMALIZE_ANGLE(New -> U.Arc.t1);
		NORMALIZE_ANGLE(New -> U.Arc.t2);
		if (New -> U.Arc.t1 > New -> U.Arc.t2)
		    New -> U.Arc.t2 += 360;
		New -> U.Arc.t1 += 1; /* Force arc of 180 degree to be less. */
		New -> U.Arc.t2 -= 1;

		/* Update the bbox: */
		UpdateBBox(LibEntry, New -> U.Arc.x - New -> U.Arc.r,
				     New -> U.Arc.y - New -> U.Arc.r);
		UpdateBBox(LibEntry, New -> U.Arc.x + New -> U.Arc.r,
				     New -> U.Arc.y + New -> U.Arc.r);
		break;
	    case 'C': /* Circle */
		New -> DrawType = CIRCLE_DRAW_TYPE;
		Error = sscanf(&Line[2], "%d %d %d %d",
		    &New -> Layer,
		    &New -> U.Circ.x, &New -> U.Circ.y, &New -> U.Circ.r) != 4;
		New ->U.Circ.x *= LIB_SCALE_DRAW;
		New ->U.Circ.y *= LIB_SCALE_DRAW;
		New ->U.Circ.r *= LIB_SCALE_DRAW;
		/* Update the bbox: */
		UpdateBBox(LibEntry, New -> U.Circ.x - New -> U.Circ.r,
				     New -> U.Circ.y - New -> U.Circ.r);
		UpdateBBox(LibEntry, New -> U.Circ.x + New -> U.Circ.r,
				     New -> U.Circ.y + New -> U.Circ.r);
		break;
	    case 'T': /* Text */
		New -> DrawType = TEXT_DRAW_TYPE;
		Error = sscanf(&Line[2], "%d %d %d %d %s",
			       &New -> Layer,
			       &New -> U.Text.x, &New -> U.Text.y,
			       &New -> U.Text.Horiz, Buffer) != 5;
		if (!Error) {			   /* Convert '~' to spaces. */
		    for (i = 0; i < strlen(Buffer); i++)
			if (Buffer[i] == '~') Buffer[i] = ' ';
		    New -> U.Text.Text = strdup(Buffer);
		}
		New ->U.Text.x *= LIB_SCALE_DRAW;
		New ->U.Text.y *= LIB_SCALE_DRAW;
		break;
	    case 'S': /* Square */
		New -> DrawType = SQUARE_DRAW_TYPE;
		Error = sscanf(&Line[2], "%d %d %d %d %d",
			       &New -> Layer,
			       &New -> U.Sqr.x1, &New -> U.Sqr.y1,
			       &New -> U.Sqr.x2, &New -> U.Sqr.y2) != 5;
		New ->U.Sqr.x1 *= LIB_SCALE_DRAW;
		New ->U.Sqr.y1 *= LIB_SCALE_DRAW;
		New ->U.Sqr.x2 *= LIB_SCALE_DRAW;
		New ->U.Sqr.y2 *= LIB_SCALE_DRAW;
		break;
	    case 'L': /* Line */
		*HasLines = TRUE;
		New -> DrawType = LINE_DRAW_TYPE;
		if ((i = sscanf(&Line[2], "%d %d %d %d %d %s",
				&New -> Layer,
				&New -> U.Line.x1, &New -> U.Line.y1,
				&New -> U.Line.x2, &New -> U.Line.y2,
				Buffer)) != 6)
		    i = sscanf(&Line[2], "%d %d %d %d %d",
			       &New -> Layer,
			       &New -> U.Line.x1, &New -> U.Line.y1,
			       &New -> U.Line.x2, &New -> U.Line.y2);
		Error = (i != 5 && i != 6) ||
			(New -> U.Line.x1 != New -> U.Line.x2 &&
			 New -> U.Line.y1 != New -> U.Line.y2);
		New ->U.Line.x1 *= LIB_SCALE_DRAW;
		New ->U.Line.y1 *= LIB_SCALE_DRAW;
		New ->U.Line.x2 *= LIB_SCALE_DRAW;
		New ->U.Line.y2 *= LIB_SCALE_DRAW;
		UpdateBBox(LibEntry, New -> U.Line.x1, New -> U.Line.y1);
		UpdateBBox(LibEntry, New -> U.Line.x2, New -> U.Line.y2);
		New -> U.Line.Invert = i == 5 && Buffer[0] == 'I';
		break;
	    case 'P': /* Polyline */

		New -> DrawType = POLYLINE_DRAW_TYPE;
		New -> U.Poly.PolyList = NULL;

 		p = strtok(&Line[4], " \t\n"); 



		if (sscanf(/*p*/&Line[2], "%d %d",
			 &New -> Layer, &New -> U.Poly.n) == 2 &&
		    New -> U.Poly.n > 0) {
		    New -> U.Poly.PolyList = (int *)
			MyMalloc(sizeof(int) * New -> U.Poly.n * 2);
		
		    for (i = 0; i < New -> U.Poly.n * 2 && !Error; i++) {
			p = strtok(NULL, " \t\n");
			Error = sscanf(p, "%d", &New -> U.Poly.PolyList[i]) !=
									1;
			New ->U.Poly.PolyList[i] *= LIB_SCALE_DRAW;
			if (i % 2 != 0)
			    UpdateBBox(LibEntry, New -> U.Poly.PolyList[i-1],
						 New -> U.Poly.PolyList[i]);
		    }
		    New -> U.Poly.Fill = (p = strtok(NULL, " \t\n")) != NULL &&
					 p[0] == 'F';
		}
		else
		    Error = TRUE;
		Error |= New -> U.Poly.Fill &&
		    (New -> U.Poly.PolyList[0] != New -> U.Poly.PolyList[i-2] ||
		     New -> U.Poly.PolyList[1] != New -> U.Poly.PolyList[i-1]);
		if (Error && New -> U.Poly.PolyList)
		    MyFree((VoidPtr) New -> U.Poly.PolyList);
		break;
	    default:
		sprintf(Line, "Undefined DRAW command in line %d, aborted.",
								*LineNum);
		IntrQueryContinue(Line, EEPopUpFrameColor,
		                  EEPopUpBackColor, EEPopUpForeColor,
                	          EEPopUpXorColor, EEWindowsFrameWidth,
                                  &Cursor, INTR_WNDW_PLACE_CENTER);
		return Head;
	}
	if (Error) {
	    sprintf(Line, "Error in %c DRAW command in line %d, aborted.",
							Line[0], *LineNum);
	    IntrQueryContinue(Line, EEPopUpFrameColor,
		              EEPopUpBackColor, EEPopUpForeColor,
                	      EEPopUpXorColor, EEWindowsFrameWidth,
                              &Cursor, INTR_WNDW_PLACE_CENTER);
	    MyFree((VoidPtr) New);
	    /* FLush till end of draw: */
	    do {
		if (GetLine(f, Line, LineNum) == NULL) {
		    IntrQueryContinue("File ended prematurely",
                                      EEPopUpFrameColor, EEPopUpBackColor,
                                      EEPopUpForeColor, EEPopUpXorColor,
                                      EEWindowsFrameWidth, &Cursor,
                                      INTR_WNDW_PLACE_CENTER);
		    return Head;
		}
	    }
	    while (strncmp(Line, "ENDDRAW", 7) != 0);
	    return Head;
	}
	else {
	    if (Head == NULL)
		Head = Tail = New;
	    else {
		Tail -> Pnext = New;
		Tail = New;
	    }
	}
    }

    /* Update the bbox to the maximum extrem as we may rotate the object. */
    r = MAX(ABS(LibEntry->BBoxMinX), ABS(LibEntry->BBoxMaxX));
    r = MAX(r, ABS(LibEntry->BBoxMinY));
    r = MAX(r, ABS(LibEntry->BBoxMaxY));
    LibEntry->BBoxMaxX = LibEntry->BBoxMaxY = r;
    LibEntry->BBoxMinX = LibEntry->BBoxMinY = -r;

    return Head;
}

/*****************************************************************************
* Routine to update LibEntry bounding box accrding to given x, y value.	     *
*****************************************************************************/
static void UpdateBBox(LibraryEntryStruct *LibEntry, int x, int y)
{
    if (LibEntry -> BBoxMinX > x) LibEntry -> BBoxMinX = x;
    if (LibEntry -> BBoxMaxX < x) LibEntry -> BBoxMaxX = x;
    if (LibEntry -> BBoxMinY > y) LibEntry -> BBoxMinY = y;
    if (LibEntry -> BBoxMaxY < y) LibEntry -> BBoxMaxY = y;
}

/*****************************************************************************
* Routine to free one library entry.					     *
*****************************************************************************/
static void FreeLibraryEntry(LibraryEntryStruct *Entry)
{
    LibraryDrawEntryStruct *TempDraw,
	*Drawings = Entry -> Drawings;

    if (Entry -> Pins) MyFree((VoidPtr) Entry -> Pins);
    if (Entry -> Multi) MyFree((VoidPtr) Entry -> Multi);
    while (Drawings) {
	TempDraw = Drawings;
	Drawings = Drawings -> Pnext;

	switch (TempDraw -> DrawType) {
	    case ARC_DRAW_TYPE:
	    case CIRCLE_DRAW_TYPE:
	    case SQUARE_DRAW_TYPE:
	    case LINE_DRAW_TYPE:
		break;
	    case TEXT_DRAW_TYPE:
		MyFree((VoidPtr) TempDraw -> U.Text.Text);
		break;
	    case POLYLINE_DRAW_TYPE:
		MyFree((VoidPtr) TempDraw -> U.Poly.PolyList);
		break;
	}
	MyFree((VoidPtr) TempDraw);
    }

    MyFree((VoidPtr) Entry);
}

/*****************************************************************************
* Routine to find the library given its name.				     *
*****************************************************************************/
static LibraryStruct *FindLibrary(char *Name)
{
    LibraryStruct
	*Lib = LibraryList;

    while (Lib) {
	if (strcmp(Name, Lib -> Name) == 0) return Lib;
	Lib = Lib -> Pnext;
    }
    return NULL;
}

/*****************************************************************************
* Routine to find the number of libraries currently loaded.		     *
*****************************************************************************/
int NumOfLibraries(void)
{
    int i;
    LibraryStruct
	*Lib = LibraryList;

    for (i = 0; Lib != NULL; Lib = Lib -> Pnext) i++;

    return i;
}
