#ifdef _WIN32
#include <regexp.h>
#include <io.h>
#else
#include <sys/types.h>
#include <regex.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#endif
#include <cdevDirectoryTool.h>
#include <cdevDirectoryTable.h>


// *****************************************************************************
// * class QuickListNode :
// *	This node class is used to temporarily store results that are retrieved
// *	from the cdevDirectoryTable prior to placing them into an array.
// *****************************************************************************
class QuickListNode
{
friend class QuickList;

private:
	char                 * data;
	QuickListNode        * next;	
	static QuickListNode * freeList;

public:

	QuickListNode( void ) 
		: data(NULL), next(NULL)
		{
		}
		
	QuickListNode( char *str ) 
		: data(NULL), next(NULL) 
		{
		data = str;
		}

	~QuickListNode ( void )
		{
		data = NULL;
		next = NULL;
		}

	void * operator new    ( size_t );
	void   operator delete ( void * item );
};

QuickListNode * QuickListNode::freeList = NULL;

void * QuickListNode::operator new ( size_t )
	{
	QuickListNode * result = NULL;
	
	if(freeList==NULL)
		{
		QuickListNode * pile = ::new QuickListNode[64];
		for(int i=0; i<63; i++) pile[i].next = &pile[i+1];
		freeList = pile;
		}
	result   = freeList;
	freeList = freeList->next;
	return result;
	}

void QuickListNode::operator delete ( void * item )
	{
	QuickListNode * node = (QuickListNode *)item;
	node->next = freeList;
	freeList   = node;
	}

// *****************************************************************************
// * class QuickList :
// *	This node class is used to temporarily store results that are retrieved
// *	from the cdevDirectoryTable prior to placing them into an array.
// *****************************************************************************
class QuickList
{
private:
	QuickListNode * cursor;
	QuickListNode * head;
	QuickListNode * tail;
	int             itemCnt;
		
public:
	QuickList ( void ) : cursor(NULL), head(NULL), tail(NULL), itemCnt(0) 
		{
		}

	~QuickList ( void )
		{
		while(head!=NULL)
			{
			QuickListNode * x = head;
			head = head->next;
			delete x;
			}
		}

	char * first ( void ) 
		{ 
		cursor = head; 
		return cursor?cursor->data:(char *)NULL;
		}

	char * operator ++ ( void )
		{
		if(cursor) cursor=cursor->next;
		return cursor?cursor->data:(char *)NULL;
		}

	char * operator ++ ( int )
		{
		if(cursor) cursor=cursor->next;
		return cursor?cursor->data:(char *)NULL;
		}

	char * data ( void )
		{
		return cursor?cursor->data:(char *)NULL;
		}		

	void   add  ( char * data )
		{
		if(data) {
			if(tail)
				{
				tail->next = new QuickListNode(data);
				tail       = tail->next;
				itemCnt++;
				}
			else	{
				head = new QuickListNode(data);
				tail = head;
				itemCnt++;
				}
			}
		}
	
	int count ( void ) 
		{ 
		return itemCnt; 
		}

	char ** array ( void )
		{
		char ** ptr = NULL;
		if(itemCnt && head) {
			ptr = new char * [itemCnt+1];
			QuickListNode  * node;
			int i = 0;
			for(node = head; node!=NULL; node=node->next)
				{
				ptr[i++] = node->data;
				} 
			ptr[i] = NULL;
			}
		return ptr;
		}	
};



// *****************************************************************************
// * cdevDirectoryTool::cdevDirectoryTool :
// *	This is the constructor for the cdevDirectoryTool object.  It will 
// *	create a cdevDirectoryTable and use the file specified by CDEVDDL to 
// *	populate it with information.
// *****************************************************************************
cdevDirectoryTool::cdevDirectoryTool ( void )
	: cdevDirectoryTable()
	{
	}

// *****************************************************************************
// * cdevDirectoryTool::~cdevDirectoryTool :
// *	This is the destructor for the cdevDirectoryTool object.  It will delete
// *	the underlying cdevDirectoryTable.
// *****************************************************************************
cdevDirectoryTool::~cdevDirectoryTool ( void )
	{
	}


// *****************************************************************************
// * cdevDirectoryTool::query :
// *	This method allows the caller to query the cdevDirectoryTable for 
// *	all devices that are in a specified class.  The returned list may
// *	be narowed by providing a regular expression that will be used to 
// *	match the device.
// *****************************************************************************
int cdevDirectoryTool::query (cdevDirectoryTable &table, cdevData *in, cdevData *out)
	{
	int        result = CDEV_SUCCESS;
	char       device    [255];
	char       classname [255];

	QuickList  devices;

	char    ** classes   = NULL;
	size_t     nClasses  = 0;
	
	if(!out) return CDEV_ERROR;
	
	// *********************************************************************
	// * Empty the outbound cdevData object prior to starting.
	// *********************************************************************
	out->remove();
	
	// *********************************************************************
	// * Prior to descending into the bowels of this code, determine if
	// * the caller is asking for the contents of a collection device...
	// *********************************************************************
	if(in)
		{
		*classname = 0;
		*device    = 0;
		in->get("class", classname, 255);
		in->get("device", device, 255);
		
		// *************************************************************
		// * If the class is not specified and the device is specified,
		// * determine if the device is an instance of a collection.
		// *************************************************************
		if(*classname==0 && *device!=0)
			{
			cdevCollectionDefinition * def = 
				(cdevCollectionDefinition *)table.getCollectionHash().find(device);
				
			// *****************************************************
			// * If the device is an instance of a collection, 
			// * extract the names of its component devices.
			// *****************************************************
			if(def!=NULL)
				{
				out->insert("value", def->getDevices(), def->getDeviceCnt());
				return CDEV_SUCCESS;
				}
			}
		}

	// *********************************************************************
	// * Call the queryClasses method to locate all of the class names
	// * that are associated with this request.
	// *********************************************************************
	if((result=queryClasses(table, in, out))!=CDEV_SUCCESS) return result;

	out->getElems("value", &nClasses);
	classes = new char *[nClasses];
	out->get     ("value", classes);

	// *********************************************************************
	// * Set the default values for the device.
	// *********************************************************************
	sprintf(device, ".*");
	
	// *********************************************************************
	// * Read the actual values of device and className from the 
	// * cdevData object if specified.
	// *********************************************************************
	if(in)	in->get("device", device,     255);

	// *********************************************************************
	// * Generate an operating system specific regexp structure.
	// *********************************************************************
	#if defined(__hpux) || defined (solaris) || defined (__linux) || defined (AIX)
		regex_t *regexp=0;
		if((regexp=(regex_t *) malloc((size_t) sizeof(regex_t))) == 0)
			{
			cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
					"malloc() failed, memory error!");
			result = CDEV_ERROR;
			}
		else if(regcomp(regexp, device, REG_EXTENDED) != 0)
			{
			cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
					"Bad regular expression: %s", device);
			result = CDEV_ERROR;
			}
	#else
		regexp *regexp=0;
		if((regexp=regcomp(device))==0)
			{
			cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
					"Bad regular expression: %s", device);
			result = CDEV_ERROR;
			}
	#endif    

	// *********************************************************************
	// * Walk through the list and locate all devices that match the 
	// * regular expression.
	// *********************************************************************
	if(result==CDEV_SUCCESS)
		{
		for(int i = 0; i<nClasses; i++)
			{
			cdevClassDefinition * Class;

			if((Class=(cdevClassDefinition *)table.getClassHash().find(classes[i]))!=NULL)
				{
				cdevDeviceDefinition * def;
				for(def = Class->getDevices(); def!=NULL; def = def->getNext())
					{
					#if defined (__hpux) || defined (solaris) || defined (__linux) || defined (AIX)
						if(regexec(regexp, def->getName(), 0, 0, 0) == 0)
					#else
						if(regexec(regexp, def->getName()) != 0)
					#endif
						{
						devices.add(def->getName());
						}
					}
				}
			}
		}
	// *********************************************************************
	// * If device names were collected, then place them into the outbound
	// * data object.  Otherwise, set the result to CDEV_NOTFOUND.
	// *********************************************************************
	if(result==CDEV_SUCCESS)
		{
		if(devices.count()>0)
			{
			char ** array = devices.array();
			out->insert("value", array);
			delete array;
			}
		else	{
			out->remove();
			result = CDEV_NOTFOUND;
			}
		}
		
	// *********************************************************************
	// * Release the regular expression variable and return the result.
	// *********************************************************************
	if(regexp)
		{
#if !defined (__VMS) && !defined (_WIN32)
		regfree(regexp);
#endif
		free   (regexp);
		}
	if(classes)
		{
		while(nClasses>0) delete classes[--nClasses];
		delete classes;
		}
	return result;
	}
	
	
// *****************************************************************************
// * cdevDirectoryTool::queryClass :
// *	This method allows the caller to query the cdevDirectoryTable for 
// *	the name of the class that a specific device belongs to.
// *****************************************************************************
int cdevDirectoryTool::queryClass (cdevDirectoryTable &table, cdevData *in, cdevData *out)
	{
	int result = CDEV_SUCCESS;
	char device[255];

	*device = 0;

	if(out==NULL) result = CDEV_INVALIDARG;
	else	{
		out->remove();
	
		if(!in || in->get("device", device, 255)!=CDEV_SUCCESS)
			{
			result = CDEV_INVALIDARG;
			}
		else	{
			cdevDeviceDefinition * def = 
				(cdevDeviceDefinition *)
					table.getDeviceHash().find(device);
			if(def==NULL) result = CDEV_NOTFOUND;
			else	{
				out->insert("value", def->getClass().getName());
				result = CDEV_SUCCESS;
				}
			}
		}

	return result;
	}


// *****************************************************************************
// * cdevDirectoryTool::queryClasses :
// *	Finds all classes that match the specified regular expression.
// *****************************************************************************
int cdevDirectoryTool::queryClasses (cdevDirectoryTable &table, cdevData *in, cdevData *out)
	{
	cdevClassDefinition * def;
	int                   result = CDEV_SUCCESS;
	QuickList             classes;
	char                  classname[255];
	
	// *********************************************************************
	// * If the outbound cdevData object is not specified return CDEV_ERROR.
	// *********************************************************************
	if(!out) return CDEV_ERROR;
	
	// *********************************************************************
	// * Obtain the class name using a default if necessary.
	// *********************************************************************
	*classname = 0;	
	if(in) in->get("class", classname, 255);
	
	// *********************************************************************
	// * If the name represents a specific class name, then locate all
	// * other classes that inherit from that class and add them to the
	// * list.
	// *********************************************************************
	if((def=(cdevClassDefinition *)table.getClassHash().find(classname))!=NULL)
		{
		cdevClassDefinition * cur;
		for(cur=table.getClasses(); cur!=NULL; cur=cur->getNext())
			{
			if(cur->isA(classname)) classes.add(cur->getName());
			}
		}
	// *********************************************************************
	// * Return the name of all implementation classes.  That is all classes
	// * that have device instances created from them.
	// *********************************************************************
	else if(*classname==0)
		{
		cdevClassDefinition * cur;
		for(cur=table.getClasses(); cur!=NULL; cur=cur->getNext())
			{
			if(cur->getDevices()!=NULL) classes.add(cur->getName());
			}
		}
	// *********************************************************************
	// Otherwise, use regular expression comparison to locate all matches.
	// *********************************************************************
	else	{
		// *************************************************************
		// * Generate an operating system specific regexp structure.
		// *************************************************************
		#if defined(__hpux) || defined (solaris) || defined (__linux) || defined (AIX)
			regex_t *regexp=0;
			if((regexp=(regex_t *) malloc((size_t) sizeof(regex_t))) == 0)
				{
				cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
						"malloc() failed, memory error!");
				result = CDEV_ERROR;
				}
			else if(regcomp(regexp, classname, REG_EXTENDED) != 0)
				{
				cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
						"Bad regular expression: %s", classname);
				result = CDEV_ERROR;
				}
		#else
			regexp *regexp=0;
			if((regexp=regcomp(classname))==0)
				{
				cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL, 
						"Bad regular expression: %s", classname);
				result = CDEV_ERROR;
				}
		#endif    
	
		if(result==CDEV_SUCCESS)
			{
			StringHashIterator iter(&table.getClassHash());
			char             * key;
		
			for(iter.first(); (key=iter.key())!=NULL; iter++)
				{
				#if defined (__hpux) || defined (solaris) || defined (__linux) || defined (AIX)
					if(regexec(regexp, key, 0, 0, 0) == 0)
				#else
					if(regexec(regexp, key) != 0)
				#endif
					{
					classes.add(key);
					}
				}
			}

		// *************************************************************
		// * Release the regular expression variable.
		// *************************************************************
		if(regexp)
			{
#if !defined (__VMS) && !defined (_WIN32)
			regfree(regexp);
#endif
			free   (regexp);
			}
		}	

	// *********************************************************************
	// * If class names were collected, then place them into the outbound
	// * data object.  Otherwise, set the result to CDEV_NOTFOUND.
	// *********************************************************************
	if(result==CDEV_SUCCESS)
		{
		if(classes.count()>0)
			{
			char ** array = classes.array();
			out->insert("value", array);
			delete array;
			}
		else result = CDEV_NOTFOUND;
		}
		
	return result;
	}

// *****************************************************************************
// * cdevDirectoryTool::queryCollection :
// *	Finds all devices that are part of a collection.
// *****************************************************************************
int cdevDirectoryTool::queryCollection (cdevDirectoryTable &table, cdevData *in, cdevData *out)
	{
	int        result = CDEV_SUCCESS;
	char       device    [255];
	
	if(!out) return CDEV_INVALIDARG;
	
	// *********************************************************************
	// * Empty the outbound cdevData object prior to starting.
	// *********************************************************************
	out->remove();
	
	if(in)
		{
		*device    = 0;
		in->get("device", device, 255);
		
		if(*device)
			{
			cdevCollectionDefinition * def = 
				(cdevCollectionDefinition *)table.getCollectionHash().find(device);
				
			// *****************************************************
			// * If the device is an instance of a collection, 
			// * extract the names of its component devices.
			// *****************************************************
			if(def!=NULL)
				{
				if(def->getDeviceCnt()>0)
					out->insert("value", def->getDevices(), def->getDeviceCnt());
				result = CDEV_SUCCESS;
				}
			else result = CDEV_NOTFOUND;
			}
		else result = CDEV_INVALIDARG;
		}
	else result = CDEV_INVALIDARG;
	return result;
	}

// *****************************************************************************
// * cdevDirectoryTool::queryElements :
// *	This method allows the caller to query the cdevDirectoryTable for 
// *	the particular element type that is associated with a device or class.
// *****************************************************************************
int cdevDirectoryTool::queryElements (	cdevDirectoryTable &table, 
					cdevDirectoryTable::ElementType type, 
					cdevData *in, cdevData *out)
	{
	int                      result     = CDEV_SUCCESS;
	cdevClassDefinition    * classDef   = NULL;
	cdevElementDefinition ** elementDef = NULL;
	int                      nElements  = 0;
	char                     device   [255];
	char                     className[255];

	*device    = 0;
	*className = 0;
	 
	if(out==NULL) result = CDEV_INVALIDARG;
	else	{
		out->remove();
	
		if(!in || 
		   (in->get("device", device, 255)!=CDEV_SUCCESS) &&
		    in->get("class",  className, 255)!=CDEV_SUCCESS)
			{
			result = CDEV_INVALIDARG;
			}
		else if(*device)
			{
			cdevDeviceDefinition * def = 
				(cdevDeviceDefinition *)
					table.getDeviceHash().find(device);

			if(def==NULL) result = CDEV_NOTFOUND;
			else classDef = &def->getClass();
			}
		else	{
			classDef = (cdevClassDefinition *)
				table.getClassHash().find(className);
			if(classDef==NULL) result = CDEV_NOTFOUND;
			}
		}

	if(classDef!=NULL)
		{
		if(classDef->getElements(type, elementDef, nElements)>0)
			{
			int     i   = 0;
			char ** ptr = new char *[nElements+1];

			for(i=0; i<nElements; i++)
				{
				ptr[i] = elementDef[i]->getName();
				}
			ptr[i] = NULL;

			out->insert("value", ptr);
			delete ptr;
			delete elementDef;
			}
		else	{
			result = CDEV_NOTFOUND;
			if(elementDef) delete elementDef;
			}
		}
		
	return result;
	}

// *****************************************************************************
// * cdevDirectoryTool::queryVerbs :
// *	This method allows the caller to query the cdevDirectoryTable for 
// *	all verbs that are associated with a device or class.
// *****************************************************************************
int cdevDirectoryTool::queryVerbs (	cdevDirectoryTable &table, 
					cdevData *in, cdevData *out)
	{
	return queryElements(table, cdevDirectoryTable::VERB, in, out);
	}
	
// *****************************************************************************
// * cdevDirectoryTool::queryAttributes :
// *	This method allows the caller to query the cdevDirectoryTable for 
// *	all verbs that are associated with a device or class.
// *****************************************************************************
int cdevDirectoryTool::queryAttributes (cdevDirectoryTable &table, 
					cdevData *in, cdevData *out)
	{
	return queryElements(table, cdevDirectoryTable::ATTRIBUTE, in, out);
	}

// *****************************************************************************
// * cdevDirectoryTool::queryMessages :
// *	This method allows the caller to query the cdevDirectoryTable for 
// *	all verbs that are associated with a device or class.
// *****************************************************************************
int cdevDirectoryTool::queryMessages (	cdevDirectoryTable &table, 
					cdevData *in, cdevData *out)
	{
	return queryElements(table, cdevDirectoryTable::MESSAGE, in, out);
	}
	
	
// *****************************************************************************
// * cdevDirectoryTool::service :
// *	This method allows the caller to query the cdevDirectoryTable for
// *	the service that will respond to a specified device and message.
// *****************************************************************************
int cdevDirectoryTool::service ( cdevDirectoryTable &table, cdevData *in, cdevData *out)
	{
	cdevDeviceDefinition     * def    = NULL;
	cdevRedirectorDefinition * redir  = NULL;
	cdevServiceDefinition    * svc    = NULL;
	int                        result = CDEV_SUCCESS;
	char                       device [255];
	char                       message[255];
	
	if(out) out->remove();
	*device  = 0;
	*message = 0;

	if(in==NULL || out==NULL)
		{
		result = CDEV_INVALIDARG;
		}
	else if(in->get("device", device, 255)!=CDEV_SUCCESS ||
	        in->get("message", message, 255)!=CDEV_SUCCESS ||
	        *device == 0 || *message == 0)
		{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Field device and message must be specified for service query");
		result = CDEV_INVALIDARG;
		}
	else if((def = (cdevDeviceDefinition *)table.getDeviceHash().find(device))==NULL)
		{
		cdevReportError(CDEV_SEVERITY_WARN, "CDEV Directory", NULL,
				"Device %s is not listed in the CDEV Directory", 
				device);
		result = CDEV_NOTFOUND;
		}
	else if((redir = (cdevRedirectorDefinition *)def->getClass().getRedirector().find(message))==NULL)
		{
		cdevReportError(CDEV_SEVERITY_WARN, "CDEV Directory", NULL,
				"Message %s IS NOT supported by device %s",
				message, device);
		result = CDEV_NOTFOUND;
		}
	else if((svc = redir->getService()) == NULL)
		{
		cdevReportError(CDEV_SEVERITY_WARN, "CDEV Directory", NULL,
				"No service associated with device \"%s\" message \"%s\"",
				device, message);
		result =  CDEV_NOTFOUND;
		}
	else
		{
		char serviceName[255];
		sprintf(serviceName, "%sService", svc->getName());
		out->insert("value", serviceName);
		result = CDEV_SUCCESS;
		}

	return result;
	}


// *****************************************************************************
// * cdevDirectoryTool::serviceData :
// *	This method allows the caller to query the cdevDirectoryTable for
// *	the serviceData that is associated with a specific device/message
// *	combination.
// *****************************************************************************
int cdevDirectoryTool::serviceData ( cdevDirectoryTable &table, cdevData *in, cdevData *out)
	{
	cdevDeviceDefinition     * def    = NULL;
	cdevRedirectorDefinition * redir  = NULL;
	int                        result = CDEV_SUCCESS;
	char                       device [255];
	char                       message[255];
	
	if(out) out->remove();
	*device  = 0;
	*message = 0;
	
	if(in==NULL || out==NULL)
		{
		result = CDEV_INVALIDARG;
		}
	else if(in->get("device", device, 255)!=CDEV_SUCCESS ||
	        in->get("message", message, 255)!=CDEV_SUCCESS ||
		*device==0 || *message==0)
		{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Field device and message must be specified for serviceData query");
		result = CDEV_INVALIDARG;
		}
	else if((def = (cdevDeviceDefinition *)table.getDeviceHash().find(device))==NULL)
		{
		cdevReportError(CDEV_SEVERITY_WARN, "CDEV Directory", NULL,
				"Device %s is not listed in the CDEV Directory", 
				device);
		result = CDEV_NOTFOUND;
		}
	else if((redir = (cdevRedirectorDefinition *)def->getClass().getRedirector().find(message))==NULL)
		{
		cdevReportError(CDEV_SEVERITY_WARN, "CDEV Directory", NULL,
				"Message %s IS NOT supported by device %s",
				message, device);
		result = CDEV_NOTFOUND;
		}
	else	{
#if defined (__VMS) && !defined (_WIN32)
		const char ** serviceData = redir->getServiceData();
#else
		char **       serviceData = (char **)redir->getServiceData();
#endif
		int     dataCount   = redir->getDataCnt();
	
		for(int i=0; i<dataCount; i++)
			{
			char   tagName[255];
			char   tagValue[1024];
			
			const char * tag = serviceData[i];
			const char * eTag;
			const char * dTag;
			int          j;
			int          tagNum;
			
			*tagName  = 0;
			*tagValue = 0;

			while(isspace(*tag)) tag++;
			for(eTag = tag; *eTag && *eTag!='=' && !isspace(*eTag); eTag++);
			for(dTag = eTag; *dTag && (*dTag=='=' || isspace(*dTag)); dTag++);

			for(j=0; tag<eTag && j<254; tag++, j++) tagName[j] = *tag;
			tagName[j] = 0;

			for(j=0; *dTag && j<1024; dTag++)
				{
				if(*dTag=='<' && *(dTag+1)=='>')
					{
					strcpy(&tagValue[j], def->getSubstituteName());
					j+=strlen(def->getSubstituteName());
					dTag++;
					}
				else tagValue[j++] = *dTag;
				}
			tagValue[j] = 0;

			if(*tagName && *tagValue) 
				{
				if(cdevData::tagC2I(tagName, &tagNum)!=CDEV_SUCCESS)
					{
					cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
							"Attempt to insert unknown cdevData tag\n\t=> Tag Name : %s\n\t=> Tag Value: %s\n",
							tagName, tagValue);
					}
				else out->insert(tagName, tagValue);
				}
			}
		result = CDEV_SUCCESS;
		}

	return result;
	}


// *****************************************************************************
// * cdevDirectoryTool::validate :
// *	This method allows the caller to confirm that the specified 
// * 	device/class - mesage combination is valid.
// *****************************************************************************
int cdevDirectoryTool::validate ( cdevDirectoryTable &table, cdevData *in, cdevData *out)
	{
	int                        result = CDEV_SUCCESS;
	char                       className[255];
	char                       device   [255];
	char                       message  [255];
	char                       verb     [255];
	char                       attribute[255];
	
	if(out) out->remove();
	*device    = 0;
	*className = 0;
	*message   = 0;
	*verb      = 0;
	*attribute = 0;
	
	if(in==NULL || out==NULL)
		{
		result = CDEV_INVALIDARG;
		}
	else if((in->get("device", device, 255)!=CDEV_SUCCESS &&
	        in->get("class",  className, 255)!=CDEV_SUCCESS) ||
	        (*device==0 && *className==0))
		{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Field device or class must be specified for validate query");
		result = CDEV_INVALIDARG;
		}
	else if ((in->get("message", message, 255)!=CDEV_SUCCESS &&
	         (in->get("verb", verb, 255)!=CDEV_SUCCESS || 
	          in->get("attribute", attribute, 255)!=CDEV_SUCCESS)) ||
	         (*message==0 && (*verb==0 || *attribute==0)))
		{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"A valid message or verb/attribute must be specified for validate query");
		result = CDEV_INVALIDARG;
		}				
	else	{
		cdevDeviceDefinition     * deviceDef = NULL;
		cdevClassDefinition      * def       = NULL;
		
		if(*className==0)
			{
			deviceDef = (cdevDeviceDefinition *)table.getDeviceHash().find(device);
			if(deviceDef==NULL) out->insert("value", 0);
			else def   = &(deviceDef->getClass());
			}
		else if((def = (cdevClassDefinition *)table.getClassHash().find(className))==NULL)
			{
			out->insert("value", 0);
			}
		
		if(def!=NULL)	
			{
			if(*message==0) sprintf(message, "%s %s", verb, attribute);
			if(def->getRedirector().find(message)!=NULL)
				{
				out->insert("value", 1);
				}
			else	out->insert("value", 0);
			}
		}
	return result;
	}

// *****************************************************************************
// * cdevDirectoryTool::update :
// *	This method allows the caller to add new data to the cdevDirectoryTable.
// *****************************************************************************
int cdevDirectoryTool::update ( cdevDirectoryTable &table, cdevData *in, cdevData *)
	{
	int    result = CDEV_SUCCESS;
	size_t nElems = 0;
	
	if(in==NULL || 
	   in->getType("file")!=CDEV_STRING ||
	   in->getElems("file", &nElems)!=CDEV_SUCCESS ||
	   nElems!=1)
	   	{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"A valid file or data block must be specified");
		result = CDEV_INVALIDARG;
		}
	else	{
		char *ptr;
		in->find("file", (void *&)ptr);
		
		// *************************************************************
		// * If the specified data item is a file, read the file using
		// * the load method.
		// *************************************************************
#ifdef _WIN32
		if(!_access(ptr, 00)) table.load(ptr);
#else
		if(!access(ptr, 00)) table.load(ptr);
#endif

		// *************************************************************
		// * Otherwise, reformat the data stream to ensure that a
		// * carriage return exists at the beginning of the block and
		// * follows each open and close brace... Then use the  
		// * asciiBufferLoad method to read the data.
		// *************************************************************
		else	{
			char * pPtr = ptr;
			char * buf  = NULL;
			char * bPtr = NULL;
			int    cnt  = 1;
			
			while(*pPtr!=0) 
				{
				if(*pPtr=='}' || *pPtr=='{') cnt++;
				pPtr++;
				}
							
			buf       = new char[strlen(ptr)+cnt+1];
			bPtr      = buf;
			pPtr      = ptr;
			*(bPtr++) = '\n';
			while(*pPtr!=0)
				{
				*(bPtr++) = *pPtr;
				if(*pPtr=='{' || *pPtr=='}') *(bPtr++) = '\n';
				pPtr++;
				}
			*bPtr = 0;
			
			table.compressComments("<< User Input >>", buf);
			table.compressSpaces  ("<< User Input >>", buf);
		   	result = table.asciiBufferLoad(buf);

			delete buf;
			}
		}
	return result;
	}


// *****************************************************************************
// * cdevDirectoryTool::writeAscii :
// *	Outputs the contents of the cdevDirectoryTable specified by table to
// *	the ASCII file specified in the file tag.
// *****************************************************************************
int cdevDirectoryTool::writeAscii ( cdevDirectoryTable &table, cdevData *in, cdevData * )
	{
	int    result = CDEV_SUCCESS;
	char   filename[255];
	FILE * fp;	

	*filename = 0;
	
	if(in==NULL || 
	   in->get("file", filename, 255)!=CDEV_SUCCESS || 
	   *filename==0)
	   	{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"A valid filename must be specified");
		result = CDEV_INVALIDARG;
	   	}
	else if ((fp = fopen(filename, "w"))==NULL)
		{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"Could not open file %s to write", filename);
		result = CDEV_INVALIDARG;
		}	 
	else	{
		table.asciiDump(fp);
		fclose(fp);
		}

	return result;
	}

// *****************************************************************************
// * cdevDirectoryTool::writeBinary :
// *	Outputs the contents of the cdevDirectoryTable specified by table to
// *	the BINARY file specified in the file tag.
// *****************************************************************************
int cdevDirectoryTool::writeBinary ( cdevDirectoryTable &table, cdevData *in, cdevData * )
	{
	int    result = CDEV_SUCCESS;
	char   filename[255];

	*filename = 0;
	
	if(in==NULL || 
	   in->get("file", filename, 255)!=CDEV_SUCCESS || 
	   *filename==0)
	   	{
		cdevReportError(CDEV_SEVERITY_ERROR, "CDEV Directory", NULL,
				"A valid filename must be specified");
		result = CDEV_INVALIDARG;
	   	}
	else	table.binaryDump(filename);

	return result;
	}
