#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#ifdef _WIN32
#include <io.h>
#define open _open
#define O_RDONLY _O_RDONLY
#define read _read
#define close _close
#define creat _creat
#else
#include <unistd.h>
#include <netinet/in.h>
#endif

#include <cdevDirectoryTable.h>

#ifdef __VMS
#define DIRECTORY_CHAR ']'
#define DIRECTORY_SELF "[]"
#elif defined (_WIN32)
#define DIRECTORY_CHAR 0x5c
#define DIRECTORY_SELF ".\\"
#else
#define DIRECTORY_CHAR '/'
#define DIRECTORY_SELF "./"
#endif

// *****************************************************************************
// * cdevReportError :
// *	This is the default error reporting mechanism...
// *****************************************************************************
int cdevReportError(int severity, char *name, void *, char *formatString, ...)
	{
	va_list argp;
	char msg[1024];
	char *p=msg;
	int status=0;
	
	if(severity >= 2)
		{
		sprintf(msg, "%s %s: ", 
		name, 
		(severity == 0?"Information":
		(severity == 1?"Warning":
		(severity == 2?"Error":
		(severity == 3?"Severe Error":
		"Event")))));
		p+=::strlen(msg);
		
		va_start(argp, formatString);
		status=vsprintf(p, formatString, argp);
		va_end(argp);
		
		#ifndef _WIN32
			fprintf(stderr, "%s\n", msg);
			fflush (stderr);
		#else
     	   HANDLE stderrHandle=GetStdHandle(STD_ERROR_HANDLE);
     	   if(stderrHandle != INVALID_HANDLE_VALUE)
     	       {
     	       int nReady=strlen(msg);
     	       unsigned long nWritten=0;
            
     	       WriteFile(stderrHandle, msg, nReady, &nWritten, NULL);
     	       WriteFile(stderrHandle, "\n", 1, &nWritten, NULL);
     	       }		
		#endif
		}
	return status;
	}

// *****************************************************************************
// * These are the current and historical binary magic numbers for DDL files.
// *****************************************************************************
long cdevDirectoryTable::BinaryMagic_1_4 = 0xcde5dd1f;
long cdevDirectoryTable::BinaryMagic_1_5 = 0xcde5dd20;
long cdevDirectoryTable::BinaryMagic     = 0xcde5dd21;

// *****************************************************************************
// * getLongFromStream :
// *	This is a convenience function to extract a non-aligned four-byte long
// *	from a character string.
// *****************************************************************************
inline long getLongFromStream ( char * ptr )
	{
	long val;
	memcpy(&val, ptr, 4);
	return ntohl(val);
	}

// *****************************************************************************
// * assimilateTags :
// *	This is a convenience function that allows me to pull the service data
// *	from the comma delimitted list into an array.
// *****************************************************************************
inline int assimilateTags ( char *& ptr, char ** & tags, int & nTags )
	{
	int result = 0;
	char * tPtr;

	if(ptr && *ptr=='{')
		{
		for(ptr++; isspace(*ptr); ptr++);

		if(*ptr!='}')
			{
			tPtr = ptr;
			do	{
				if(*tPtr==',' || *tPtr=='}') nTags++;
				} while(*tPtr!='}' && *(tPtr++));
				
			if(*tPtr!='}') result = -1;
			else	{
				tags = new char *[nTags];
				memset(tags, 0, nTags*sizeof(char *));

				for(int idx=0; *ptr && idx<nTags; )
					{
					while(isspace(*ptr)) ptr++;
					tPtr=ptr;
					while(*tPtr && *tPtr!=',' && *tPtr!='}') tPtr++;
					if(tPtr==ptr) nTags--;
					else	{
						char temp   = *tPtr;
						*tPtr       = 0;
						tags[idx]   = strdup(ptr);
						*tPtr       = temp;
						ptr         = tPtr+1;
						tPtr        = tags[idx]+strlen(tags[idx])-1;
						while(isspace(*tPtr) && tPtr>tags[idx])
							{
							*tPtr = 0;
							tPtr--;
							}
						idx++;
						}
					}
				}
			}
		}

	return result;
	}


// *****************************************************************************
// * cdevDirectoryTable::load :
// *	This method allows the caller to load a DDL file (binary or ASCII).
// *****************************************************************************
int cdevDirectoryTable::load ( char * inputFile )
	{
	int fd;
	int result = CDEV_SUCCESS;
	
	if((fd = open(inputFile, O_RDONLY))==-1)
		{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Could not open CDEV DDL file %s", inputFile);
		result = CDEV_ERROR;
		}
	else	{
		long magicNum = 0;

		read(fd, &magicNum, sizeof(magicNum));
		magicNum = ntohl(magicNum);
		close(fd);
		
		if(magicNum==BinaryMagic_1_4)
			{
			cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
					"Cannot load a Binary DDL created by CDEV 1.4");
			result = CDEV_ERROR;
			}
		else if(magicNum==BinaryMagic_1_5)
			{
			cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
					"Cannot load a Binary DDL created by CDEV 1.5");
			result = CDEV_ERROR;
			}
		else if(magicNum==BinaryMagic) result = binaryLoad(inputFile);
		else result = asciiLoad(inputFile);
		}
	return result;
	}
	

// *****************************************************************************
// * cdevDirectoryTable::asciiLoad :
// *	This method will load the contents of an ASCII Text DDL file into the
// *	cdevDirectory object.
// *****************************************************************************
int cdevDirectoryTable::asciiLoad ( char * inputFile )
	{
	int    result = CDEV_SUCCESS;
	char   ddlDir  [1024];
	char   ddlFile [255];
	char * buf = NULL;
	int    buflen = 0;
	int    cursor = 0;
	
	if(!inputFile || !*inputFile)
		{
		cdevReportError(
			CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
			"CDEV DDL file has not been specified");
		result = CDEV_ERROR;
		}
	else	{
		strcpy(ddlDir, inputFile);
		char * ptr = strrchr(ddlDir, DIRECTORY_CHAR);
		if(ptr==NULL)
			{
			strcpy(ddlFile, inputFile);
			strcpy(ddlDir, DIRECTORY_SELF);
			}
		else	{
			ptr++;
			strcpy(ddlFile, ptr);
			*ptr = 0;
			}
		result = loadText(ddlDir, ddlFile, buf, buflen, cursor);
		}

	if(result==CDEV_SUCCESS) result = asciiBufferLoad(buf);
	if(buf) free(buf);	

	return result;
	}
	
// *****************************************************************************
// * cdevDirectoryTable::asciiBufferLoad :
// *	This method will load the contents of an ASCII Text DDL file into the
// *	cdevDirectory object.
// *****************************************************************************
int cdevDirectoryTable::asciiBufferLoad ( char * buf )
	{
	int result = CDEV_SUCCESS;

	if(!buf || !*buf)
		{
		cdevReportError(
			CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
			"NULL buffer provided to DDL loader");
		result = CDEV_ERROR;
		}
	else	{
		readServices    (buf);
		readClasses     (buf);
		readDevices     (buf);
		readAliases     (buf);
		readCollections (buf);
		}

	return result;
	}


// *****************************************************************************
// * cdevDirectoryTable::binaryDump :
// *	This method will write the binary contents of the cdevDirectoryTable to
// *	an output file.
// *****************************************************************************
void cdevDirectoryTable::binaryDump ( char * outputFile )
	{
	cdevServiceDefinition *sPtr = NULL;
	cdevClassDefinition   *cPtr = NULL;
	cdevElementDefinition *ePtr = NULL;
	cdevDeviceDefinition  *dPtr = NULL;
	cdevAliasDefinition   *aPtr = NULL;
	
	int    fd = -1;	
	long   i  = 0;

	// *********************************************************************
	// * Open the output file.
	// *********************************************************************
#ifdef _WIN32
	if((fd = creat(	outputFile, _S_IREAD | _S_IWRITE))==-1)
#else
	if((fd = creat(	outputFile, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH))==-1)
#endif
		{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Failed to open binary output file to write");
		return;
		}
	
	// *********************************************************************
	// * Write the magic number to the output file.
	// *********************************************************************
	i = htonl(BinaryMagic);
	write(fd, &i, sizeof(i));
	 
	// *********************************************************************
	// * Write the service definitions to the output file.
	// *********************************************************************

	// Count the number of services.
	for(i=0, sPtr=serviceHead; sPtr!=NULL; sPtr = sPtr->getNext(), i++);

	// Write the service count to the output file.
	i = htonl(i);
	write(fd, &i, sizeof(i));
	
	// Write the individual services to the output file.
	for(sPtr=serviceHead; sPtr!=NULL; sPtr = sPtr->getNext())
		{
		// Write the service name.
		write(fd, sPtr->getName(), strlen(sPtr->getName())+1);

		// Write the number of tags.
		i = htonl(sPtr->getTagCnt());
		write(fd, &i, sizeof(i));

		// Write each tag.
		int     tagCnt = sPtr->getTagCnt();
		char ** tags   = sPtr->getTags();
		for(i=0; i<tagCnt; i++)
			{
			if(tags[i]!=NULL) 
				write(fd, tags[i], strlen(tags[i])+1);
			else	write(fd, "\0", 1);
			}
		}
	
	// *********************************************************************
	// * Write the class definitions to the output file.
	// *********************************************************************

	// Count the number of classes (not including the collectionClass)
	for(i=0, cPtr=classHead; cPtr!=NULL; cPtr = cPtr->getNext())
		{
		if(cPtr!=collectionClass) i++;
		}
		
	// Write the class count to the output file.
	i = htonl(i);
	write(fd, &i, sizeof(i));
	
	// Write the individual classes to the output file.
	for(cPtr=classHead; cPtr!=NULL; cPtr = cPtr->getNext())
		{
		// Go to the next entry if the current entry is the 
		// collectionClass
		if(cPtr == collectionClass) continue;

		// Write the class name.
		write(fd, cPtr->getName(), strlen(cPtr->getName())+1);
		
		// Write the parent class name (if any)
		if(cPtr->getParent()) write(fd, cPtr->getParent()->getName(), strlen(cPtr->getParent()->getName())+1);
		else write(fd, "\0", 1);
		
		// Count the number of verbs
		for(i=0, ePtr=cPtr->getVerbs(); ePtr!=NULL; ePtr=ePtr->getNext(), i++); 
		
		// Write the verb count
		i = htonl(i);
		write(fd, &i, sizeof(i));
		
		// Write each verb
		for(ePtr = cPtr->getVerbs(); ePtr!=NULL; ePtr=ePtr->getNext())
			{
			// Write the verb name
			write(fd, ePtr->getName(), strlen(ePtr->getName())+1);
			
			// Write the service name
			if(ePtr->getService()) write(fd, ePtr->getService()->getName(), strlen(ePtr->getService()->getName())+1);
			else write(fd, "\0", 1);
			
			// Write the number of serviceData items
			i = htonl(ePtr->getDataCnt());
			write(fd, &i, sizeof(i));
			
			// Write each serviceData item
			int     nItems = ePtr->getDataCnt();
			char ** items  = ePtr->getServiceData();
			for(i=0; i<nItems; i++)
				{
				if(items[i]!=NULL) 
					write(fd, items[i], strlen(items[i])+1);
				else	write(fd, "\0", 1);
				}
			}

		// Count the number of attributes
		for(i=0, ePtr=cPtr->getAttributes(); ePtr!=NULL; ePtr=ePtr->getNext(), i++); 
		
		// Write the attribute count
		i = htonl(i);
		write(fd, &i, sizeof(i));
		
		// Write each attribute
		for(ePtr = cPtr->getAttributes(); ePtr!=NULL; ePtr=ePtr->getNext())
			{
			// Write the attribute name
			write(fd, ePtr->getName(), strlen(ePtr->getName())+1);
			
			// Write the service name
			if(ePtr->getService()) write(fd, ePtr->getService()->getName(), strlen(ePtr->getService()->getName())+1);
			else write(fd, "\0", 1);
			
			// Write the number of serviceData items
			i = htonl(ePtr->getDataCnt());
			write(fd, &i, sizeof(i));
			
			// Write each serviceData item
			int     nItems = ePtr->getDataCnt();
			char ** items  = ePtr->getServiceData(); 
			for(i=0; i<nItems; i++)
				{
				if(items[i]!=NULL) 
					write(fd, items[i], strlen(items[i])+1);
				else	write(fd, "\0", 1);
				}
			}
		
		// Count the number of messages
		for(i=0, ePtr=cPtr->getMessages(); ePtr!=NULL; ePtr=ePtr->getNext(), i++); 
		
		// Write the message count
		i = htonl(i);
		write(fd, &i, sizeof(i));
		
		// Write each message
		for(ePtr = cPtr->getMessages(); ePtr!=NULL; ePtr=ePtr->getNext())
			{
			// Write the message name
			write(fd, ePtr->getName(), strlen(ePtr->getName())+1);
			
			// Write the service name
			if(ePtr->getService()) write(fd, ePtr->getService()->getName(), strlen(ePtr->getService()->getName())+1);
			else write(fd, "\0", 1);
			
			// Write the number of serviceData items
			i = htonl(ePtr->getDataCnt());
			write(fd, &i, sizeof(i));
			
			// Write each serviceData item
			int     nItems = ePtr->getDataCnt();
			char ** items  = ePtr->getServiceData();
			for(i=0; i<nItems; i++)
				{
				if(items[i]!=NULL) 
					write(fd, items[i], strlen(items[i])+1);
				else	write(fd, "\0", 1);
				}
			}
		
		// Count the number of devices
		for(i=0, dPtr = cPtr->getDevices(); dPtr!=NULL; dPtr = dPtr->getNext(), i++);
		
		// Write the number of devices
		i = htonl(i);
		write(fd, &i, sizeof(i));
		
		// Write each device
		for(dPtr = cPtr->getDevices(); dPtr!=NULL; dPtr = dPtr->getNext())
			{
			if(dPtr->getName() && *dPtr->getName())
				{
				write(fd, dPtr->getName(), strlen(dPtr->getName())+1);
				if(dPtr->getSubstituteName() && 
				  *dPtr->getSubstituteName() && 
				   dPtr->getSubstituteName()!=dPtr->getName())
					{
					write(fd, dPtr->getSubstituteName(), strlen(dPtr->getSubstituteName())+1);
					}
				else write(fd, "\0", 1);
				}
			else write(fd, "\0", 1);
			}
		}

	// *********************************************************************
	// * Write the alias definitions to the output file
	// *********************************************************************
	
	// Count the number of aliases
	for(i=0, aPtr = aliasHead; aPtr!=NULL; aPtr = aPtr->getNext(), i++);
	
	// Write the number of aliases.
	i = htonl(i);
	write(fd, &i, sizeof(i));
	
	// Write each alias to the output file
	for(aPtr = aliasHead; aPtr!=NULL; aPtr = aPtr->getNext())
		{
		// Write the alias name.
		write(fd, aPtr->getName(), strlen(aPtr->getName())+1);

		// Write the device name.
		write(fd, aPtr->getDevice().getName(), strlen(aPtr->getDevice().getName())+1);
		}

	// *********************************************************************
	// * Write the collection definitions to the output file.
	// *********************************************************************
	
	cdevCollectionDefinition * clPtr;
	//  Count the number of collections
	for(i=0, clPtr = collectionHead; clPtr!=NULL; clPtr = clPtr->getNext(), i++);
	
	//  Write the number of collections.
	i = htonl(i);
	write(fd, &i, sizeof(i));
	
	for(clPtr = collectionHead; clPtr!=NULL; clPtr = clPtr->getNext())
		{
		// Write the collection name
		write(fd, clPtr->getName(), strlen(clPtr->getName())+1);

		// Write the number of devices in the collection
		i = htonl(clPtr->getDeviceCnt());
		write(fd, &i, sizeof(i));
	
		// Write the devices
		char ** devices   = clPtr->getDevices();
		int     deviceCnt = clPtr->getDeviceCnt();
		for(i=0; i<deviceCnt; i++)
			{
			write(fd, devices[i], strlen(devices[i])+1);
			}
		}
	
	
	// *********************************************************************
	// * Close the output file.
	// *********************************************************************
	close(fd);
	}
	

// *****************************************************************************
// * cdevDirectoryTable::binaryLoad :
// *	This method will load a binary DDL file that was generated using the
// *	binaryDump method.
// *****************************************************************************
int cdevDirectoryTable::binaryLoad ( char * inputFile )
	{
	long   i         = 0;
	int    result    = CDEV_SUCCESS;
	int    fd        = -1;
	char * dataBlock = NULL;
	int    dataLen   = 0;

	if((fd = open(inputFile, O_RDONLY))==-1)
		{
		cdevReportError (CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				 "Unable to load binary DDL file %s", inputFile);
		result = CDEV_ERROR;
		}
	else 	{
		dataLen = (int)lseek(fd, 0L, SEEK_END);
		lseek(fd, 0L, SEEK_SET);
		read(fd, &i, sizeof(i));
		if(ntohl(i)!=BinaryMagic)
			{
			cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
					"Invalid magic number in binary DDL file %s", 
					inputFile);
			result = CDEV_ERROR;
			}
		}
	
	if(result == CDEV_SUCCESS)
		{
		char * ptr;

		// *************************************************************
		// * Allocate the data block.
		// *************************************************************
		dataBlock = new char [dataLen];

		// *************************************************************
		// * Read the input file into the data block.
		// *************************************************************
		read(fd, dataBlock, dataLen);
		ptr = dataBlock;
		
		// *************************************************************
		// * Read the service count from the data block.
		// *************************************************************
		int svcCnt = (int)getLongFromStream(ptr);
		ptr+=sizeof(long);
		
		// *************************************************************
		// * Read the service definitions from the data block.
		// *************************************************************
		for(svcCnt--; svcCnt>=0; svcCnt--)
			{
			char *  svcName = NULL;
			int     nTags   = 0;
			char ** tags    = 0;
			
			// *****************************************************
			// * Get the service name from the head of the stream
			// *****************************************************
			svcName = strdup(ptr);
			ptr    += strlen(ptr)+1;
			
			// *****************************************************
			// * Read the number of tags from the head of the stream
			// *****************************************************
			nTags = (int)getLongFromStream(ptr);
			ptr  += sizeof(long);
			
			// *****************************************************
			// * If the tag count is greater than 0, read the tags 
			// * into an array.
			// *****************************************************
			if(nTags>0)
				{
				tags = new char *[nTags];
				for(i=0; i<nTags; i++) 
					{
					tags[i] = strdup(ptr);
					ptr += strlen(ptr)+1;
					}
				}

			// *****************************************************
			// * Create the new cdevServiceDefinition within the
			// * cdevDirectoryTable.
			// *****************************************************
			if(addService(svcName, tags, nTags)!=CDEV_SUCCESS)
				{
				if(svcName) delete svcName;
				if(tags && nTags) 
					{
					for(nTags--; nTags>=0; nTags--) delete tags[nTags];
					}
				if(tags) delete tags;
				}
			}

		// *************************************************************
		// * Read the class count from the data block.
		// *************************************************************
		int classCnt = (int)getLongFromStream(ptr);
		ptr+=sizeof(long);

		// *************************************************************
		// * Read the class definitions from the data block.
		// *************************************************************
		for(; classCnt>0; classCnt--)
			{
			char                  * className  = NULL;
			cdevClassDefinition   * parent     = NULL;
			cdevElementDefinition * verbs      = NULL;
			cdevElementDefinition * attributes = NULL;
			cdevElementDefinition * messages   = NULL;
			cdevElementDefinition * tail       = NULL;
			
			// *****************************************************
			// * Get the service name from the head of the stream
			// *****************************************************
			className = strdup(ptr);
			ptr      += strlen(ptr)+1;
			
			// *****************************************************
			// * Read the parent class from the data block.
			// *****************************************************
			parent = findClass(ptr);
			ptr   += strlen(ptr)+1;

			// *****************************************************
			// * Read the number of verbs from the data block.
			// *****************************************************
			int verbCnt = (int)getLongFromStream(ptr);
			ptr        += sizeof(long);
			tail        = NULL;
			
			// *****************************************************
			// * If the verbCnt is greater than 0, then begin 
			// * reading verbs into the verbs list.
			// *****************************************************
			for( ; verbCnt>0; verbCnt--)
				{
				// Get the verb name
				char * verbName = strdup(ptr);
				ptr            += strlen(ptr)+1;
				
				// Get the service definition
				cdevServiceDefinition * svcDef = findService(ptr);
				ptr+=strlen(ptr)+1;
				
				// Get the number of tags
				int nTags = (int)getLongFromStream(ptr);
				ptr      += sizeof(long);
				
				// Allocate the array of tags if necessary
				char ** tags = nTags?new char *[nTags]:(char **)NULL;
				
				// Read each of the tags into the tags array
				for(i=0; i<nTags; i++)
					{
					tags[i] = strdup(ptr);
					ptr+=strlen(ptr)+1;
					}

				// Add the new element definition to the end of the list
				if(tail == NULL)
					{
					verbs = new cdevElementDefinition(verbName, svcDef, tags, nTags);
					tail  = verbs;
					}
				else	{
					tail->setNext(new cdevElementDefinition(verbName, svcDef, tags, nTags));
					tail = tail->getNext();
					} 
				}

			// *****************************************************
			// * Read the number of attributes from the data block.
			// *****************************************************
			int attributeCnt = (int)getLongFromStream(ptr);
			ptr             += sizeof(long);
			tail             = NULL;
			
			// *****************************************************
			// * If the attributeCnt is greater than 0, then begin 
			// * reading attributes into the attributes list.
			// *****************************************************
			for( ; attributeCnt>0; attributeCnt--)
				{
				// Get the attribute name
				char * attributeName = strdup(ptr);
				ptr            += strlen(ptr)+1;
				
				// Get the service definition
				cdevServiceDefinition * svcDef = findService(ptr);
				ptr+=strlen(ptr)+1;
				
				// Get the number of tags
				int nTags = (int)getLongFromStream(ptr);
				ptr      += sizeof(long);
				
				// Allocate the array of tags if necessary
				char ** tags = nTags?new char *[nTags]:(char **)NULL;
				
				// Read each of the tags into the tags array
				for(i=0; i<nTags; i++)
					{
					tags[i] = strdup(ptr);
					ptr+=strlen(ptr)+1;
					}

				// Add the new element definition to the end of the list
				if(tail == NULL)
					{
					attributes = new cdevElementDefinition(attributeName, svcDef, tags, nTags);
					tail  = attributes;
					}
				else	{
					tail->setNext(new cdevElementDefinition(attributeName, svcDef, tags, nTags));
					tail = tail->getNext();
					} 
				}

			// *****************************************************
			// * Read the number of messages from the data block.
			// *****************************************************
			int messageCnt = (int)getLongFromStream(ptr);
			ptr             += sizeof(long);
			tail             = NULL;
			
			// *****************************************************
			// * If the messageCnt is greater than 0, then begin 
			// * reading messages into the messages list.
			// *****************************************************
			for( ; messageCnt>0; messageCnt--)
				{
				// Get the message name
				char * messageName = strdup(ptr);
				ptr            += strlen(ptr)+1;
				
				// Get the service definition
				cdevServiceDefinition * svcDef = findService(ptr);
				ptr+=strlen(ptr)+1;
				
				// Get the number of tags
				int nTags = (int)getLongFromStream(ptr);
				ptr      += sizeof(long);
				
				// Allocate the array of tags if necessary
				char ** tags = nTags?new char *[nTags]:(char **)NULL;
				
				// Read each of the tags into the tags array
				for(i=0; i<nTags; i++)
					{
					tags[i] = strdup(ptr);
					ptr+=strlen(ptr)+1;
					}

				// Add the new element definition to the end of the list
				if(tail == NULL)
					{
					messages = new cdevElementDefinition(messageName, svcDef, tags, nTags);
					tail  = messages;
					}
				else	{
					tail->setNext(new cdevElementDefinition(messageName, svcDef, tags, nTags));
					tail = tail->getNext();
					} 
				}
		
			// *****************************************************
			// * Create the new cdevClassDefinition within the
			// * cdevDirectoryTable.
			// *****************************************************
			cdevClassDefinition   * target;
			cdevClassDefinition   * actual;

			target = new cdevClassDefinition (*this, className, parent, verbs, attributes, messages);
			if(addClass(target)!=CDEV_SUCCESS)
				{
				actual = findClass(className);
				delete target;
				}
			else actual = target;

			// *****************************************************
			// * Read the table of devices that are associated with
			// * the class.
			// *****************************************************
			int nDevices = (int)getLongFromStream(ptr);
			ptr         += sizeof(long);
			
			for( ; nDevices>0; nDevices--)
				{
				if(*ptr)
					{
					char * d = strdup(ptr);
					char * s = NULL;
					
					ptr+=strlen(ptr)+1;
					if(*ptr) s = strdup(ptr);
					
					if(actual->addDevice(d, s)!=CDEV_SUCCESS)
						{
						delete d;
						if(s) delete s;
						}
					}
				ptr+=strlen(ptr)+1;
				}
			}
		
		// *************************************************************
		// * Read the table of aliases
		// *************************************************************
		int aliasCnt = (int)getLongFromStream(ptr);
		ptr         += sizeof(long);
		
		for ( ; aliasCnt>0; aliasCnt--)
			{
			char * alias = strdup(ptr);
			ptr         += strlen(ptr)+1;

			cdevDeviceDefinition * device = (cdevDeviceDefinition *)deviceHash.find(ptr);
			ptr += strlen(ptr)+1;
			
			if(*alias && device) addAlias(alias, *device);
			else delete alias;
			}

		// *********************************************************************
		// * Read the table of collections definitions
		// *********************************************************************
		int collectionCnt = (int)getLongFromStream(ptr);
		ptr              += sizeof(long);	
		
		for( ; collectionCnt>0; collectionCnt--)
			{
			int     nDevices   = 0;
			char ** devices    = NULL;
			char  * collection = strdup(ptr);
			ptr               += strlen(ptr)+1;

			nDevices = (int)getLongFromStream(ptr);
			ptr     += sizeof(long);	
			
			if(nDevices)
				{
				devices = new char *[nDevices];
				for(int x=0; x<nDevices; x++)
					{
					devices[x] = strdup(ptr);
					ptr       += strlen(ptr)+1;
					}
				}

			if(collection && *collection && devices && nDevices>0)
				{
				addCollection(collection, devices, nDevices);
				}
			else	{
				if(collection) delete collection;
				if(nDevices>0 && devices) 
					{
					while(--nDevices>=0) delete devices[nDevices];
					}
				if(devices) delete devices;
				}
			}
		}

	close (fd);
	if(dataBlock) delete dataBlock;
	return result;
	}	


// *****************************************************************************
// * cdevDirectoryTable::loadText :
// *	This method is used to read a DDL file into a single buffer.  This 
// *	buffer may then be parsed into the cdevDirectoryTable structure.
// *****************************************************************************
int cdevDirectoryTable::loadText ( char * includePath, char * filename, char * &buf, int &buflen, int &cursor)
	{
	static int depth = 0;
	
	FILE * fp  = NULL;
	char * ptr = NULL;
	char  fname[1024];
		
	// *********************************************************************
	// * Increment the depth variable to indicate recursion into function.
	// *********************************************************************
	depth++;
	
	// *********************************************************************
	// * Create the exact path from the includePath and filename.
	// *********************************************************************
	sprintf(fname, "%s%s", includePath, filename);
	
	// *********************************************************************
	// * Create initial NULL terminated buffer if one does not exist.
	// *********************************************************************
	if(buf==NULL || buflen==0)
		{
		buflen   = 8192;
		buf      = (char *)malloc(buflen*sizeof(char));
		cursor   = 1;
		strcpy(buf, "\n\0");
		}
	
	if((fp = fopen(fname, "r"))!=NULL)
		{
		int fileSize;
		int dataSize;
		
		fseek(fp, 0L, SEEK_END);
		fileSize = (int)ftell(fp);
		fseek(fp, 0L, SEEK_SET);

		dataSize = strlen(buf);
		if(dataSize+fileSize > buflen)
			{
			buflen = (dataSize+fileSize)*2;
			buf = (char *)realloc(buf, buflen*sizeof(char));
			}

		memmove(buf+cursor+fileSize, buf+cursor, (dataSize-cursor)+1);
		fread  (buf+cursor, fileSize, 1, fp);
		fclose (fp);

		// *************************************************************
		// * Remove all of the comments from the input buffer.
		// *************************************************************
		compressComments(filename, buf+cursor);
					
		// *************************************************************
		// * Start locating and processing includes.  Note: because it
		// * recursively calls this method to read in include files, the
		// * ptr must be recalculated prior to each search.
		// *************************************************************
		while ((ptr = strstr(buf+cursor, "#include"))!=NULL)
			{
			char *fnamePtr = ptr+8;

			// *****************************************************
			// * The cursor should point to the current position.
			// *****************************************************
			cursor = ptr-buf;
			
			while(isspace(*fnamePtr)) fnamePtr++;

			if(*fnamePtr=='<')
				{
				fnamePtr++;
				for(ptr=fnamePtr; *ptr && *ptr!='>' && *ptr!='\n'; ptr++);
				if (*ptr=='>')
					{
					*ptr=0;
					strcpy(fname, fnamePtr);
					fnamePtr = fname;
					*ptr='>';
					}
				else	{
					cdevReportError(
						CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
						"Closing angle brace missing on #include in file %s",
						filename);
					fnamePtr = NULL;
					}
				}
			else	{
				cdevReportError(
					CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
					"Opening angle brace missing on #include in file %s",
					filename);
				fnamePtr = NULL;
				}

			// *****************************************************
			// * Remove the include directive from the data file.
			// *****************************************************
			if(ptr = strchr(ptr, '\n')) 
				{
				strcpy(buf+cursor, ptr+1);
				}
			else *(buf+cursor) = 0;
			
			// *****************************************************
			// * Load the include file to the current cursor 
			// * position.
			// *****************************************************
			loadText(includePath, fnamePtr, buf, buflen, cursor);
			}
		}
	else	{
		cdevReportError(
			CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
			"Could not find #include file %s", filename);
		}
		
	// *********************************************************************
	// * Only compress the spaces after all of the data has been loaded.
	// *********************************************************************
	if(--depth==0) compressSpaces  (filename, buf);
	return *buf?CDEV_SUCCESS:CDEV_ERROR;
	}

// *****************************************************************************
// * cdevDirectoryTable::compressComments :
// *	This method will remove all comments from the input buffer.
// *****************************************************************************
void cdevDirectoryTable::compressComments ( char * filename, char * buf )
	{
	// *************************************************************
	// * Remove all comment blocks.
	// *************************************************************
	char *ptr = buf;
	while((ptr = strstr(ptr, "/*"))!=NULL)
		{
		char *ePtr = strstr(ptr, "*/");
		if(ePtr==NULL)
			{
			cdevReportError(
				CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
				"Unterminated comment block in file %s",
				filename);
			*ptr = 0;
			}
		else	strcpy(ptr, ePtr+2);
		}

	// *************************************************************
	// * Remove all comment lines.
	// *************************************************************
	ptr = buf;
	while((ptr = strstr(ptr, "//"))!=NULL)
		{
		char *ePtr = strchr(ptr, '\n');
		if(ePtr!=NULL) strcpy(ptr, ePtr);
		else           *ptr ='\n';
		}

	}
	
// *****************************************************************************
// * cdevDirectoryTable::compressSpaces :
// *	This method will remove any extraneous spaces from the input buffer.
// *****************************************************************************
void cdevDirectoryTable::compressSpaces ( char *, char * buf )
	{
	// *************************************************************
	// * Remove all extraneous white space... remember, if you take
	// * it out here you won't have to parse it later...
	// *************************************************************
	char *ptr = buf;
	while(*(ptr = ptr+strcspn(ptr, " \t\n"))!=0)
		{
		if(*ptr=='\t') *ptr = ' ';
		if(isspace(*(ptr+1)))
			{
			char * ePtr = ptr+1;
			while(isspace(*ePtr))
				{
				if(*ePtr=='\n') *ptr='\n';
				ePtr++;
				}
			strcpy(ptr+1, ePtr);
			}
		else ptr++;
		}
	}


// *****************************************************************************
// * These are the definitive errors for the readServices method.
// *****************************************************************************
static char * readServiceErrors [] = 
	{
	"",
	"Service name is missing from definition",
	"Service definition is missing opening brace",
	"Invalid service format",
	"Tag definition is missing opening brace",
	"Tag definition is missing closing brace",
	"Service definition is missing closing brace"
	};

// *****************************************************************************
// * cdevDirectoryTable::readServices :
// *	This method will read all of the services that are defined in the
// *	specified input stream and will add them to the services entry of the
// *	cdevDirectoryTable.
// *****************************************************************************
void cdevDirectoryTable::readServices( char * buf )
	{
	char * tPtr = buf;
	char * ptr  = buf;
	
	enum {SUCCESS=0, 
	      SVC_NAME, 
	      SVC_OPEN_BRACE, 
	      SVC_FMT, 
	      TAG_OPEN_BRACE, 
	      TAG_CLOSE_BRACE,
	      SVC_CLOSE_BRACE} error;
	
	// *********************************************************************
	// * While locating the service keyword at the beginning of a line.
	// *********************************************************************
	while((ptr=strstr(ptr, "\nservice "))!=NULL)
		{
		char *  errorRestart = ptr+9;
		char *  name         = NULL;
		char ** tags         = NULL;
		int     nTags        = 0;
		char *  tPtr         = ptr+=9;

		error = SUCCESS;
		
		while(*tPtr && !isspace(*tPtr)) tPtr++;
		
		// *************************************************************
		// * If the service name was located, copy it to the name
		// * variable.  Otherwise, set the error code.
		// *************************************************************
		if(*tPtr)
			{
			*tPtr = 0;
			name  = strdup(ptr);
			*tPtr = ' ';
			}
		else error = SVC_NAME;
		
		// *************************************************************
		// * If no errors have occurred locate the opening brace.
		// *************************************************************
		if(!error && *(++tPtr)!='{') error = SVC_OPEN_BRACE;
		else for(ptr=tPtr+1; isspace(*ptr); ptr++);
		
		// *************************************************************
		// * If no errors have occured, find the tags entry (if present)
		// *************************************************************
		if(!error && *ptr=='}') error = SUCCESS;
		else if(!error && !strncmp(ptr, "tags", 4))
			{
			// *****************************************************
			// * Find the opening brace for the tag list.
			// *****************************************************
			for(ptr+=4; isspace(*ptr); ptr++);
			if(*ptr!='{') error = TAG_OPEN_BRACE;
			else 	{
				if(assimilateTags(ptr, tags, nTags)!=0)
					{
					error = TAG_CLOSE_BRACE;
					}
				}
			// *****************************************************
			// * If no errors have occurred find the closing brace
			// * for the service definition.
			// *****************************************************
			if(!error) for(ptr++; isspace(*ptr); ptr++);
			if(!error && *ptr!='}') error = SVC_CLOSE_BRACE;
			}
		else error = SVC_FMT;
		
		if(!error)
			{
			if(nTags==0 && tags!=NULL)
				{
				delete tags;
				tags = NULL;
				}
			addService(name, tags, nTags);
			}
		else	{
			cdevReportError(
				CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Service \"%s\" Error\n\t=> %s\n\t=> %s\n", name?name:"UNKNOWN",
				readServiceErrors[error],
				"Format is: service name { tags {tag1,tag2}}");
			
			if(name) delete name;
			if(tags && nTags) while(--nTags>=0) delete tags[nTags];
			if(tags) delete tags;
			ptr = errorRestart;
			}
		}			

	return;
	}


// *****************************************************************************
// * These are the definitive errors for the readClasses method.
// *****************************************************************************
static char * readClassErrors [] = 
	{
	"",
	"Class name is missing from definition",
	"Specified parent class is not defined",
	"Parent class definition is formatted wrong",
	"Class definition is missing opening brace",
	"Class definition is missing closing brace",
	"Class definition has no content"
	};

// *****************************************************************************
// * cdevDirectoryTable::readClasses :
// *	This method will read all of the classes that are defined in the
// *	specified input stream and will add them to the classes entry of the
// *	cdevDirectoryTable.
// *****************************************************************************
void cdevDirectoryTable::readClasses( char * buf )
	{
	char   parentName[255];
	char * ptr = buf;
	char * tPtr;
	
	enum {SUCCESS=0, 
	      CLASS_NAME, 
	      CLASS_NO_PARENT,
	      CLASS_PARENT_FMT,
	      CLASS_OPEN_BRACE,
	      CLASS_CLOSE_BRACE,
	      CLASS_EMPTY} error;

	while((ptr = strstr(ptr, "\nclass "))!=NULL)
		{
		char                  * errorRestart   = ptr+=7;
		char                  * name           = NULL;
		cdevClassDefinition   * newClass       = NULL;
		cdevClassDefinition   * parent         = NULL;
		char                  * startClassCode = NULL;
		char                  * endClassCode   = NULL;
		
		error       = SUCCESS;
		*parentName = 0;

		// *************************************************************
		// * Reposition ptr to point at the first space after the class
		// * name.  If a space exists after the class name, then copy
		// * the class name to the name variable, otherwise, report an
		// * error.
		// *************************************************************
		for(tPtr=ptr; *ptr && !isspace(*ptr); ptr++);
		if(*ptr) 
			{
			*ptr = 0;
			name = strdup(tPtr);
			*ptr = ' ';
			}
		else error = CLASS_NAME;
		
		if(!error)
			{
			// *****************************************************
			// * Reposition the ptr to the first occurence of a 
			// * colon or an opening brace after the class name.
			// ***************************************************** 
			while(*ptr && *ptr!=':' && *ptr!='{') ptr++;

			// *****************************************************
			// * If a colon was found, then it indicates that the 
			// * class is a sub-class of another predefined class.
			// * Read the name of the parent class into the 
			// * parentName variable.  If the specified parent class
			// * has not already been defined, then report an error.
			// *****************************************************
			if(*ptr==':')
				{
				for(ptr++; isspace(*ptr); ptr++);
				if(*ptr)
					{
					for(tPtr=ptr; *ptr && !isspace(*ptr); ptr++);
					if(*ptr)
						{
						*ptr = 0;
						strcpy(parentName, tPtr);
						*ptr = ' ';
						parent = findClass(parentName);
						if(parent==NULL) error = CLASS_NO_PARENT;
						}
					while(*ptr && *ptr!='{') ptr++;
					}
				else error = CLASS_PARENT_FMT;
				}
			}
		
		// *************************************************************
		// * The opening brace indicates the beginning of the actual
		// * class definition.  From here the method will determine the
		// * location of the class's closing brace and will begin 
		// * assimilating the definitions for verbs, attributes and 
		// * messages that are inside.
		// *************************************************************
		if(!error && *ptr=='{')
			{
			int    count = 1;
			startClassCode = ++ptr;
			endClassCode   = startClassCode;
			
			while(count>0)
				{
				if(*endClassCode=='{')      count++;
				else if(*endClassCode=='}') count--;
				else if(*endClassCode==0)   count=0;
				if(count) endClassCode++;
				}
			if(*endClassCode!='}') error = CLASS_CLOSE_BRACE;
			}
		else if(!error) error = CLASS_OPEN_BRACE;

		// *************************************************************
		// * At this point the class, parent class and extent of the
		// * class definition is known and we can create the class
		// * object and populate it with elements.
		// *************************************************************
		if(!error)
			{
			newClass = new cdevClassDefinition(*this, name, parent);
			
			if(endClassCode!=startClassCode)
				{
				readElements(*newClass, VERB,      startClassCode, endClassCode);
				readElements(*newClass, ATTRIBUTE, startClassCode, endClassCode);
				readElements(*newClass, MESSAGE,   startClassCode, endClassCode);
				}
				
			addClass(newClass);
			}
		else	{
			cdevReportError(
				CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Class \"%s\" Error\n\t=> %s\n\t=> %s\n", name?name:"UNKNOWN",
				readClassErrors[error],
				"Format is: class name : parent { ... }");
			
			if(name) delete name;
			ptr = errorRestart;
			}
		}			

	return;
	}


// *****************************************************************************
// * Type names that are associated with the various element types.
// *****************************************************************************
static char * readElementTypeNames[] = 
	{
	"verb",
	"attribute",
	"message"
	};

// *****************************************************************************
// * These are the definitive errors for the readElements method.
// *****************************************************************************
static char * readElementErrors [] = 
	{
	"",
	"opening brace is missing",
	"definition has no content",
	"definition is missing service name",
	"definition references an undefined service",
	"requires a service definition",
	"service data closing brace is missing",
	"definition has invalid format"
	};


// *****************************************************************************
// * cdevDirectoryTable::readElements :
// *	This method is used to read a list of elements from the class definition
// *	that is in the character string buffer specified by start and end.
// *	If elements exist, then an cdevElementDefinition will be created and
// *	they will be inserted into the list.
// *****************************************************************************
void cdevDirectoryTable::readElements
	(cdevClassDefinition  &def, 
	 ElementType           type,
	 char                 *start, 
	 char                 *end)
	{
	char   oldEndChar;
	char * ptr = start;
	char   targetString[32];
	int    targetStringLen;

	enum	{
		SUCCESS = 0,
		ELEMENT_OPEN_BRACE,
		ELEMENT_EMPTY,
		ELEMENT_NO_SERVICE,
		ELEMENT_SERVICE_UNDEFINED,
		MESSAGE_NEEDS_SERVICE,
		SERVICE_DATA_CLOSE_BRACE,
		ELEMENT_FMT
		} error;
			
	// *********************************************************************
	// * Null terminate the end of the block in order to improve search.
	// *********************************************************************
	if(end)	{
		if(*end!='}') end++;
		oldEndChar = *end;
		*end = 0;
		}
		
	// *********************************************************************
	// * Create the string that represents the element definition and then 
	// * begin locating instances of the element within the data block.
	// *********************************************************************
	sprintf(targetString, "%ss", readElementTypeNames[type]);
	targetStringLen = strlen(targetString);
	
	while((ptr = strstr(ptr, targetString))!=NULL)
		{
		char * errorRestart = (ptr+=targetStringLen);

		error = SUCCESS;

		// *************************************************************
		// * The first character before and after the definition should 
		// * be some sort of white space...  Otherwise, it has read an 
		// * invalid entry.
		// *************************************************************
		if(isspace(*ptr) && isspace(*(ptr-targetStringLen-1)))
			{
			while(isspace(*ptr)) ptr++;
			if(*ptr!='{') error = ELEMENT_OPEN_BRACE;
			else for(ptr++; isspace(*ptr); ptr++);
			while (!error && *ptr && *ptr!='}')
				{
				char                    oldChar;
				char                  * tPtr       = ptr;
				char                  * name       = NULL;
				cdevServiceDefinition * service    = NULL;
				char                 ** tags       = NULL;
				int                     nTags      = 0;
				
				// *********************************************
				// * Read the name of the element into the name 
				// * variable
				// *********************************************
				while(*ptr && *ptr!=',' && *ptr!=';' && 
				      *ptr!='}' && !isspace(*ptr)) ptr++;
				oldChar = *ptr;
				*ptr = 0;
				if(strlen(tPtr)>0) name = strdup(tPtr);
				else error = ELEMENT_EMPTY;
				*ptr    = oldChar;
				
				// *********************************************
				// * Position to the beginning of the 
				// * serviceName.
				// *********************************************
				while(isspace(*ptr)) ptr++;
	
				// *********************************************
				// * If an opening brace is found at this point 
				// * it means that the developer entered the 
				// * service data without entering the service.
				// *********************************************
				if(*ptr=='{') error = ELEMENT_NO_SERVICE;
	
				// *********************************************
				// * If a servicename has been provided.
				// *********************************************
				if(!error && *ptr!=',' && *ptr!=';' && *ptr!='}')
					{
					tPtr = ptr;
					while(*ptr && *ptr!=',' && *ptr!=';' && 
					      *ptr!='}' && !isspace(*ptr)) ptr++;
					
					oldChar = *ptr;
					*ptr    = 0;
					service = findService(tPtr);
					*ptr    = oldChar;
	
					if(service==NULL) error = ELEMENT_SERVICE_UNDEFINED;
					else	{	
						// *****************************
						// * Walk to the beginning of 
						// * the next entry.
						// *****************************
						while(*ptr && isspace(*ptr)) ptr++;
						
						// *****************************
						// * If the next character is an 
						// * opening brace then the 
						// * service data can be read.
						// *****************************
						if(!error && *ptr=='{') 
							{
							if(assimilateTags(ptr, tags, nTags)!=0)
								{
								error = SERVICE_DATA_CLOSE_BRACE;
								}
							}
						}
					}
				// *********************************************
				// * If no errors have occured walk to the end 
				// * of the definition.
				// *********************************************
				if(!error)
					{
					while(*ptr && isspace(*ptr)) ptr++;
					if(*ptr=='}')
						{
						// Do nothing
						}
					else if(*ptr==',' || *ptr==';') 
						{
						for(ptr++; isspace(*ptr); ptr++);
						}
					else error = ELEMENT_FMT;
					}
	
				// *********************************************
				// * If the entry is a message and the developer
				// * has not specified a service, then it cannot 
				// * be added to the list.
				// *********************************************
				if(!error && type==MESSAGE && service==NULL) 
					error=MESSAGE_NEEDS_SERVICE;
				
				// *********************************************
				// * If the data was read, add it to the 
				// * definition, otherwise, report an error.
				// *********************************************
				if(!error)
					{
					if(nTags==0 && tags!=NULL)
						{
						delete tags;
						tags = NULL;
						}
					
					switch(type)
						{
						case VERB:
						def.addVerb(name, service, tags, nTags);
						break;
						
						case ATTRIBUTE:
						def.addAttribute(name, service, tags, nTags);
						break;
						
						case MESSAGE:
						def.addMessage(name, service, tags, nTags);
						break;
						}
					}
				else	{
					cdevReportError(
						CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
						"Class \"%s\" - %s \"%s\" Error\n\t=> %s %s\n\t=> Format is: %ss {name service {serviceData};}\n", 
						def.getName(), readElementTypeNames[type], name?name:"UNKNOWN",
						readElementTypeNames[type], readElementErrors[error],
						readElementTypeNames[type]);
				
					if(name)    delete name;
					if(tags && nTags) while(--nTags>=0) delete tags[nTags];
					if(tags) delete tags;
					ptr = errorRestart;
					}
				}
			}
		}

	*end = oldEndChar;
	}
	
	
// *****************************************************************************
// * cdevDirectoryTable::readDevices :
// *	This method is used to read all of the device instances that are defined
// *	within the CDEV DDL data.
// *****************************************************************************
void cdevDirectoryTable::readDevices ( char * buf )
	{
	cdevClassDefinition * def;

	for(def = classHead; def!=NULL; def = def->getNext())
		{
		char * ptr = buf;
		char   oldChar;
		char * name;
		char * substName;
		char * tPtr;
		char   target[255];
		int    targetLen;
		sprintf(target, "\n%s :", def->getName());
		targetLen = strlen(target);
		while((ptr = strstr(ptr, target))!=NULL)
			{
			ptr+=targetLen;
			while(*ptr && *ptr!=';') 
				{
				while(isspace(*ptr)) ptr++;
				tPtr = ptr;
				while(*ptr!=',' && *ptr!=';' && !isspace(*ptr)) ptr++;
				
				oldChar = *ptr;
				*ptr    = 0;
				if(strlen(tPtr)>0)
					{
					name      = strdup(tPtr);
					substName = NULL;
					*ptr      = oldChar;
					
					// *************************************
					// * Start looking to see if a 
					// * substitution name is provided with
					// * the device name.
					// *************************************
					for(tPtr = ptr; isspace(*tPtr); tPtr++);
					if(*tPtr=='{')
						{
						do { tPtr++; } while(isspace(*tPtr));
						for(ptr = tPtr; *ptr && !isspace(*ptr) && *ptr!='}'; ptr++);
						oldChar = *ptr;
						*ptr    = 0;
						if(strlen(tPtr)>0)
							{
							substName = strdup(tPtr);
							}
						*ptr = oldChar;
						while(*ptr && *ptr!='}') ptr++;
						}
					
					if(def->addDevice(name, substName)!=CDEV_SUCCESS) 
						{
						cdevReportError(
							CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
							"Device %s has already been defined\n", name); 
						delete name;
						delete substName;
						}
					}
				else *ptr = oldChar;

				if(*ptr!=';') ptr++;
				}
			if(*ptr!=';') 
				{
				cdevReportError(
					CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
					"Instance list for class %s unterminated\n\t=> Format is : class : instance1, instance2;", 
					def->getName());
				}
			}
		}
	}


// *****************************************************************************
// * These are the definitive errors for the readAliases method.
// *****************************************************************************
static char * readAliasErrors [] = 
	{
	"",
	"Alias name has not been specified",
	"Alias name has already been defined",
	"Aliased device has not been specified",
	"Aliased device has not been defined"
	};

// *****************************************************************************
// * cdevDirectoryTable::readAliases :
// *	This method is used to read all of the alias definitions within the 
// *	CDEV DDL data.
// *****************************************************************************
void cdevDirectoryTable::readAliases ( char * buf )
	{
	char                 * ptr = buf;
	
	enum	{
		SUCCESS = 0,
		ALIAS_NAME_NOT_SPECIFIED,
		ALIAS_ALREADY_DEFINED,
		ALIAS_DEVICE_NOT_SPECIFIED,
		ALIAS_DEVICE_NOT_DEFINED
		} error = SUCCESS;
		
	while((ptr = strstr(ptr, "\nalias "))!=NULL)
		{
		char   oldChar;
		char * name = NULL;
		char   deviceName[255];
		
		error       = SUCCESS;
		ptr        += 7;
		*deviceName = 0;
		
		while(isspace(*ptr) && *ptr!='\n') ptr++;	
		if(*ptr=='\n' || *ptr=='\0') error = ALIAS_NAME_NOT_SPECIFIED;
		else	{
			char *aPtr = ptr;
			
			while(*ptr && !isspace(*ptr)) ptr++;
			oldChar = *ptr;
			*ptr    = 0;
			name    = strdup(aPtr);
			*ptr    = oldChar;

			while(isspace(*ptr) && *ptr!='\n') ptr++;
			if(*ptr=='\n' || *ptr=='\0') error = ALIAS_DEVICE_NOT_SPECIFIED;
			else	{
				char                 *dPtr = ptr;
				cdevDeviceDefinition * def = NULL;

				while(*ptr && !isspace(*ptr)) ptr++;
				oldChar = *ptr;
				*ptr    = 0;
				strcpy(deviceName, dPtr);
				*ptr    = oldChar;
				def     = (cdevDeviceDefinition *)deviceHash.find(deviceName);
					
				if(def==NULL) error = ALIAS_DEVICE_NOT_DEFINED;
				else if(deviceHash.find(name)!=NULL) error = ALIAS_ALREADY_DEFINED;
				else addAlias (name, *def);
				}
			}
		if (error)
			{
			cdevReportError(
				CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Alias \"%s\" to device \"%s\" Error\n\t=> %s\n\t=> %s\n", 
				name?name:"UNKNOWN", *deviceName?deviceName:"UNKNOWN", 
				readAliasErrors[error],
				"Format is: alias aliasname devicename");
			if(name) delete name;
			}
		}
	}
	
// *****************************************************************************
// * These are the definitive errors for the readAliases method.
// *****************************************************************************
static char * readCollectionErrors [] = 
	{
	"",
	"Collection name has not been specified",
	"Collection definition is missing a colon",
	"Collection definition is missing a semi-colon",
	"Collection definition is empty"
	};

// *****************************************************************************
// * cdevDirectoryTable::readCollections :
// *	This method is used to read all of the collection definitions from the
// *	ASCII DDL file buffer.
// *****************************************************************************
void cdevDirectoryTable::readCollections ( char * buf )
	{
	char * ptr = buf;
	char * tPtr;
	
	enum {SUCCESS=0, 
	      COLLECTION_NAME, 
	      COLLECTION_NO_COLON,
	      COLLECTION_NO_SEMICOLON,
	      COLLECTION_EMPTY} error;

	while((ptr = strstr(ptr, "\ncollection "))!=NULL)
		{
		char                   * errorRestart = ptr+=12;
		char                   * name         = NULL;
		char                  ** devices      = NULL;
		int                      nDevices     = 0;

		error = SUCCESS;

		// *************************************************************
		// * Reposition ptr to point at the first space after the class
		// * name.  If a space exists after the name, then copy
		// * the name to the name variable, otherwise, report an error.
		// *************************************************************
		for(tPtr=ptr; *ptr && !isspace(*ptr); ptr++);
		if(*ptr) 
			{
			*ptr = 0;
			name = strdup(tPtr);
			*ptr = ' ';
			}
		else error = COLLECTION_NAME;
		
		if(!error)
			{
			// *****************************************************
			// * Reposition the ptr to the first occurence of a 
			// * colon after the collection name.
			// ***************************************************** 
			while(*ptr && isspace(*ptr)) ptr++;

			// *****************************************************
			// * If a colon was not found, then the definition is 
			// * formatted improperly and an error should be 
			// * reported.
			// *****************************************************
			if(*ptr!=':') error = COLLECTION_NO_COLON;
			else for(ptr++; isspace(*ptr); ptr++);
			}

		// *************************************************************
		// * Count the number of device names in the definition.
		// *************************************************************			
		char * deviceStart = ptr;
		while(!error && *ptr && *ptr!=';') 
			{
			while(isspace(*ptr)) ptr++;
			tPtr = ptr;
			while(*ptr!=',' && *ptr!=';' && !isspace(*ptr)) ptr++;

			char oldChar = *ptr;
			*ptr    = 0;
			if(strlen(tPtr)>0) nDevices++;
			*ptr = oldChar;
			if(*ptr!=';') ptr++;
			}
		
		// *************************************************************
		// * If the closing semicolon is missing then set the error flag
		// *************************************************************			
		if(!error && *ptr!=';') error = COLLECTION_NO_SEMICOLON;

		// *************************************************************
		// * Otherwise, begin assimilating the device names into the
		// * list.
		// *************************************************************
		else if(!error && nDevices>0)
			{
			ptr      = deviceStart;
			devices  = new char *[nDevices];
			nDevices = 0;

			while(*ptr && *ptr!=';')
				{
				while(isspace(*ptr)) ptr++;
				tPtr = ptr;
				while(*ptr!=',' && *ptr!=';' && !isspace(*ptr)) ptr++;

				char oldChar = *ptr;
				*ptr    = 0;
				if(strlen(tPtr)>0) devices[nDevices++] = strdup(tPtr);
				*ptr = oldChar;
				if(*ptr!=';') ptr++;
				}
			}
		
		// *************************************************************
		// * If no errors have occured, add the new 
		// * cdevCollectionDefinition to the list.
		// *************************************************************
		if(!error)
			{
			cdevCollectionDefinition * def = 
				new cdevCollectionDefinition(name, devices, nDevices);
				
			if(addCollection(def)!=CDEV_SUCCESS) delete def;
			}
		else	{
			cdevReportError(
				CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Collection \"%s\" Error\n\t=> %s\n\t=> %s\n", 
				name?name:"UNKNOWN", readCollectionErrors[error],
				"Format is: collection name : device1 device2;");

			if(name)    delete name;
			if(devices) delete devices;

			ptr = errorRestart;
			}
		}
	return;
	}
