/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * Module:	sml.c
 * Synopsis:	simplified markup language (SML) support
 * Taxonomy:	project private
 * Debug flag:	sml
 * Description:
 *
 *   This module implements methods that support the processing of a
 *   simplified markup language (SML). Objects that contain SML data
 *   can be created and manipulated, and SML can be imported into
 *   internal SML objects or exported from internal SML objects.
 *
 * Public Methods:
 *
 *   smlAddTag - Add new tag object into existing tag object
 *   smlConvertStringToTag - Convert string into tag object
 *   smlConvertTagToString - Convert a tag object into a string
 *		representation of the XML
 *   smlDbgPrintTag - Print a representation of an XML tag if debugging
 *   smlDelParam - Delete a parameter from a tag object
 *   smlDelTag - Delete element from tag object
 *   smlDup - Duplicate a tag object
 *   smlFindAndDelTag - Delete a tag if found in tag object
 *   smlFreeTag - Free a tag object and all its contents when no
 *		longer needed
 *   smlFstatCompareEq - Compare file status information
 *   smlGetElementName - Return a tag's element name
 *   smlGetNumParams - Get number of parameters set in tag
 *   smlGetParam - Get a parameter from a tag
 *   smlGetParamF - Get a formatted parameter from a tag
 *   smlGetParamByTag - Get a parameter by tag and index
 *   smlGetParamByTagParam Get parameter given tag name, index,
 *		parameter name, and value
 *   smlGetParamName - Get the name of a tag parameter given its index
 *   smlGetParam_r - Get a parameter from a tag into fixed buffer
 *   smlGetTag - Get an element from a tag
 *   smlGetTagByName - Get an element given a name and an index
 *   smlGetTagByTagParam - Get element given tag name, index, parameter name,
 *		and value
 *   smlGetVerbose - get current verbose mode setting
 *   smlLoadTagFromFile - Load a file into a tag object
 *   smlNewTag - Create a new (empty) tag object
 *   smlParamEq - Determine if parameter is equal to a specified value
 *   smlParamEqF - Determine if parameter is equal to a specified value
 *   smlPrintTag - Print a simple XML representation of a tag to stderr
 *   smlReadOneTag - read one complete tag from a datastream
 *   smlReadTagFromDs - read tag object from datastream
 *   smlSetFileStatInfo - encode file status information into tag
 *   smlSetVerbose - set/clear verbose mode for debugging output
 *   smlSetParam - Set parameter value in tag object
 *   smlSetParamF - Set parameter value in tag object
 *   smlWriteTagToDs - Write an XML representation of a tag to a datastream
 *   smlWriteTagToFd - Write an XML representation of a tag to an open file
 *		descriptor
 *   smlWriteTagToFile - Write an XML representation of a tag to a file
 */

/*
 * Unix includes
 */

#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <libintl.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/statvfs.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <strings.h>

/*
 * liblu Includes
 */

#include "libinst.h"
#include "messages.h"

/* Should be defined by cc -D */
#if	!defined(TEXT_DOMAIN)
#define	TEXT_DOMAIN "SYS_TEST"
#endif

/*
 * Private Method Forward Declarations
 */

/*PRINTFLIKE2*/
static void	_smlLogMsg(LogMsgType a_type, const char *a_format, ...);

static int	_smlReadTag(SML_TAG **r_tag, char **a_str, char *parent);

static int	_smlWriteSimpleTag(char **a_str,
				SML_TAG *tag);

static int	_smlWriteParamValue(char **a_str, char *value);

static void		_smlFreeTag(SML_TAG *tag);

static char		*_sml_fileStatInfoTag = "File-Stat-Info";

static boolean_t	verbose = B_FALSE;

/*
 *
 * This definition controls the maximum size of any individual sml
 * component, such as a tag name, tag *value*, etc. The code should
 * someday be revised to dynamically allocate whatever memory is needed
 * to hold such components while parsing, but that exercise is left for
 * another day. Any component that exceeds this length is silently
 * truncated...
 */

#define	MAX_SML_COMPONENT_LENGTH	16384

/*
 * Public Methods
 */

/*
 * Name:	smlAddTag
 * Description:	Add new tag object into existing tag object
 * Arguments:	r_tag - [RO, *RW] - (SML_TAG **)
 *			Pointer to handle to the tag object to update
 *			The handle may be updated if the tag object is
 *			moved in memory
 *		a_index - [RO] - (int)
 *			Add the tag after the "n"th tag in the tag object
 *			-1 == add the tag to the end of the tag object
 *			0 == add the tag to the beginning of the tag object
 *		a_subTag - [RO, *RW] - (SML_TAG *)
 *			The tag to add to 'tag'
 * Returns:	SML_TAG *
 *			The location within "r_tag" where "a_subTag"
 *			has been added - this is the handle into the r_tag
 *			object to the tag that was just added
 * Errors:	If the tag object cannot be updated, the process exits
 */

SML_TAG *
smlAddTag(SML_TAG **r_tag, int a_index, SML_TAG *a_subTag)
{
	SML_TAG	*tag;

	/* entry assertions */

	assert(SML_TAG__ISVALID(a_subTag));
	assert(SML_TAG__R_ISVALID(r_tag));

	/* if no tag to update specified, ignore request */

	tag = *r_tag;
	if (tag == SML_TAG__NULL) {
		return (tag);
	}

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ADD_TAG,
		a_subTag->name, tag->name);

	/* if index is out of range or -1, append to tag object */

	if ((a_index > tag->tags_num) || (a_index == -1)) {
		a_index = tag->tags_num;
	}

	/* bump number of tags in tag object */

	tag->tags_num++;

	/* expand tag object to hold new subtag */

	tag->tags = (SML_TAG *)realloc(tag->tags,
		sizeof (SML_TAG) * tag->tags_num);

	/* if not appending, adjust tag object to hold new subtag */

	if (a_index < (tag->tags_num - 1)) {
		(void) memmove(&(tag->tags[a_index + 1]), &(tag->tags[a_index]),
			sizeof (SML_TAG) * (tag->tags_num - a_index - 1));
	}

	/* copy new subtag into correct location in tag object */

	(void) memcpy(&(tag->tags[a_index]), a_subTag,
		sizeof (SML_TAG));

	return (&(tag->tags[a_index]));
}

/*
 * Name:	smlDelTag
 * Description:	Delete element from tag object
 * Arguments:	tag - [RO, *RW] - (SML_TAG *)
 *			The tag object to update
 *		sub_tag - [RO, *RW] - (SML_TAG *)
 *			Element to be removed from the tag object
 * Returns:	void
 *			The sub_tag is removed from the tag object
 * NOTE:	The sub-tag and all elements contained within it are deallocated
 *		the sub-tag is no longer valid when this method returns
 */

void
smlDelTag(SML_TAG *tag, SML_TAG *sub_tag)
{
	int	index;

	/* entry assertions */

	assert(SML_TAG__ISVALID(sub_tag));

	/* if no tag to update specified, ignore request */

	if (tag == SML_TAG__NULL) {
		return;
	}

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DEL_TAG,
		sub_tag->name, tag->name);

	/* if tag object is empty, ignore request */

	if (tag->tags_num == 0) {
		return;
	}

	/* determine index into tag object of element to remove */
	for (index = 0; index < tag->tags_num; index++) {
		if (sub_tag == &tag->tags[index]) {
			break;
		}
	}

	/* if element not found in tag, ignore request */

	if (index >= tag->tags_num) {
		return;
	}

	/* free up the subtag to be deleted */

	_smlFreeTag(sub_tag);

	/*
	 * if not removing last element, collapse tag object removing
	 * target element
	 */

	if (index < (tag->tags_num - 1)) {
		(void) memmove(&(tag->tags[index]), &(tag->tags[index + 1]),
			sizeof (SML_TAG) *(tag->tags_num - index - 1));
	}

	/* one less tag object in tag */

	tag->tags_num --;

	/*
	 * If only one tag left, then delete entire tag structure
	 * otherwise reallocate removing unneeded entry
	 */

	if (tag->tags_num > 0) {
		/* realloc removing last element in tag object */

		tag->tags = (SML_TAG *)realloc(tag->tags,
			sizeof (SML_TAG) *tag->tags_num);
	} else {
		tag->tags = SML_TAG__NULL;
	}
}

/*
 * Name:	smlFreeTag
 * Description:	Free a tag object and all its contents when no longer needed
 * Arguments:	tag - [RO, *RW] - (SML_TAG *)
 *			The tag object to be deleted
 * Returns:	void
 *			The tag object and all its contents are deallocated
 */

void
smlFreeTag(SML_TAG *tag)
{
	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));

	/* entry debugging info */

	if (tag->name != (char *)NULL) {
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_FREE_TAG,
			(unsigned long)tag, tag->name);
	}

	/* free the tag object contents */

	_smlFreeTag(tag);

	/* free the tag object handle */

	bzero(tag, sizeof (SML_TAG));
	free(tag);
}

/*
 * Name:	smlGetNumParams
 * Synopsis:	Get number of parameters set in tag
 * Description:	Return the number of parameters set in a tag
 * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the # params from
 * Returns:	int
 *			Number of parameters set in tag
 *			0 = no parameters are set
 */

int
smlGetNumParams(SML_TAG *a_tag)
{
	return (a_tag ? a_tag->params_num : 0);
}


/*
 * Name:	smlGetParam_r
 * Description:	Get a parameter from a tag into a buffer of fixed size
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the parameter from
 *		name - [RO, *RO] - (char *)
 *			Name of the parameter to retrieve
 *		buf - [RO, *RW] - (char *)
 *			Location of buffer to contain results
 *		bufLen - [RO, *RO] - (int)
 *			Maximum bytes available in buffer to contain results
 * Returns:	void
 */

void
smlGetParam_r(SML_TAG *tag, char *name, char *buf, int bufLen)
{
	int	k;

	/* entry assertions */

	assert(name != (char *)NULL);
	assert(*name != '\0');
	assert(buf != (char *)NULL);
	assert(bufLen > 0);

	/* terminate the buffer */

	buf[0] = '\0';
	buf[bufLen-1] = '\0';

	bzero(buf, bufLen);

	/* if no tag specified, return NULL */

	if (tag == SML_TAG__NULL) {
		return;
	}

	/* if no parameters in tag, return NULL */

	if (tag->params == NULL) {
		return;
	}

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
		name, tag->name);

	/* scan tag object looking for specified parameter */

	for (k = 0; k < tag->params_num; k++) {
		assert(tag->params[k].name != (char *)NULL);
		assert(tag->params[k].value != (char *)NULL);
		if (streq(tag->params[k].name, name)) {
			_smlLogMsg(LOG_MSG_DEBUG,
				DBG_SML_GOT_PARAM,
				tag->name, name, tag->params[k].value);
			(void) strncpy(buf, tag->params[k].value, bufLen-1);
			return;
		}
	}

	/* parameter not found - return */
}

/*
 * Name:	smlGetParam
 * Description:	Get a parameter from a tag
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the parameter from
 *		name - [RO, *RO] - (char *)
 *			Name of the parameter to retrieve
 * Returns:	char *
 *			Value of the specified parameter
 *			== (char *)NULL if the parameter does not exist
 * NOTE:    	Any parameter returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the parameter is no longer needed.
 */

char *
smlGetParam(SML_TAG *tag, char *name)
{
	int	k;

	/* entry assertions */

	assert(name != (char *)NULL);
	assert(*name != '\0');

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, "get param param <%s>", name);

	/* if no tag specified, return NULL */

	if (tag == SML_TAG__NULL) {
		return ((char *)NULL);
	}

	/* if no parameters in tag, return NULL */

	if (tag->params == NULL) {
		return ((char *)NULL);
	}

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
		name, tag->name);

	/* scan tag object looking for specified parameter */

	for (k = 0; k < tag->params_num; k++) {
		assert(tag->params[k].name != (char *)NULL);
		assert(tag->params[k].value != (char *)NULL);
		if (streq(tag->params[k].name, name)) {
			_smlLogMsg(LOG_MSG_DEBUG,
				DBG_SML_GOT_PARAM,
				tag->name, name, tag->params[k].value);
			return (strdup(tag->params[k].value));
		}
	}

	/* parameter not found - return NULL */

	return ((char *)NULL);
}

/*
 * Name:	smlGetParamName
 * Description:	Get the name of a tag parameter given its index
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the parameter name from
 *		index - [RO] - (int)
 *			Index of parameter name to return
 * Returns:	char *
 *			Name of 'index'th parameter
 *			== (char *)NULL if no such parameter exists in tag
 * NOTE:    	Any parameter name returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the parameter name is no longer needed.
 */

char *
smlGetParamName(SML_TAG *tag, int index)
{
	/* if no tag specified, return NULL */

	if (tag == NULL) {
		return ((char *)NULL);
	}

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_NAME,
		tag->name, index);

	/* if no parameters in tag, return NULL */

	if (tag->params == NULL) {
		return ((char *)NULL);
	}

	/* if index not within range, return NULL */

	if (index >= tag->params_num) {
		return ((char *)NULL);
	}

	/* index within range - return parameter name */

	assert(tag->params[index].name != (char *)NULL);
	assert(tag->params[index].value != (char *)NULL);

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GOT_PARAM_NAME,
		tag->name, index, tag->params[index].name);

	return (strdup(tag->params[index].name));
}

/*
 * Name:	smlGetParamByTag
 * Synopsis:	Get a parameter value from a tag by name and index
 * Description:	Call to look for a parameter value from a tag with
 *		a given name with a parameter of a given name
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the parameter
 *		index - [RO] - (int)
 *			Index of nth tag by name to look for
 *		tagName - [RO, *RO] - (char *)
 *			Name of tag to look for
 *		paramName - [RO, *RO] - (char *)
 *			Name of parameter to return value of
 * Returns:	char *
 *			== (char *)NULL - no parameter value set
 *			!= (char *)NULL - value of parameter set
 */

char *
smlGetParamByTag(SML_TAG *tag, int index,
	char *tagName, char *paramName)
{
	SML_TAG	*rtag;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(tagName != (char *)NULL);
	assert(*tagName != '\0');
	assert(paramName != (char *)NULL);
	assert(*paramName != '\0');

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_BY_TAG,
		tagName, index, paramName);

	/* find the requested tag by name  and index */

	rtag = smlGetTagByName(tag, index, tagName);
	if (rtag == SML_TAG__NULL) {
		return ((char *)NULL);
	}

	return (smlGetParam(rtag, paramName));
}

/*
 * Name:	smlGetTagByTagParam
 * Synopsis:	Get element given tag name, index, parameter name, and value
 * Description:	Call to look for a tag with a given nae, that has a parameter
 *		of a given name with a specified value
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the element from
 *		index - [RO] - (int)
 *			Index of nth name to return
 *		tagName - [RO, *RO] - (char *)
 *			Tag name to look up
 *		paramName - [RO, *RO] - (char *)
 *			Parameter name to look up
 *		paramValue - [RO, *RO] - (char *)
 *			Parameter value to match
 * Returns:	SML_TAG *
 *			The 'index'th occurance of element 'name' with
 *			a parameter 'name' with value specified
 *			== SML_TAG__NULL if no such element exists
 */

SML_TAG *
smlGetTagByTagParam(SML_TAG *tag, int index,
	char *tagName, char *paramName, char *paramValue)
{
	int		ti;		/* tag structure index */

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(tagName != (char *)NULL);
	assert(*tagName != '\0');
	assert(paramName != (char *)NULL);
	assert(*paramName != '\0');
	assert(paramValue != (char *)NULL);
	assert(*paramValue != '\0');

	/* if tag has no elements, return NULL */

	if (tag->tags == NULL) {
		return (SML_TAG__NULL);
	}

	/*
	 * Search algorithm:
	 *  -> search tag structure; for each tag with element == "tagName":
	 *  -> search tag parameters; if parameter name == "paramName"
	 *  -> if parameter value != "paramValue"; to next tag
	 *  -> if parameter value == "paramValue":
	 *  -> if not the "index"th paramValue found; to next tag
	 *  -> return tag found
	 */

	for (ti = 0; ti < tag->tags_num; ti++) {
		int	pi;	/* parameter structure index */

		/* if tag element does not match, go on to next tag */

		if (strcmp(tag->tags[ti].name, tagName)) {
			continue;
		}

		/* element matches: search for specified parameter name/value */

		for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
			assert(tag->tags[ti].params[pi].name != (char *)NULL);
			assert(tag->tags[ti].params[pi].value != (char *)NULL);

			/* if parameter name doesnt match to next parameter */

			if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
				continue;
			}

			/* if parameter value doesnt match to next tag */

			if (strcmp(tag->tags[ti].params[pi].value,
				paramValue)) {
				break;
			}

			/*
			 * found element/paramname/paramvalue:
			 * -> if this is not the 'index'th one, go to next tag
			 */

			if (index-- != 0) {
				break;
			}

			/*
			 * found specified element/paramname/paramvalue:
			 * -> return the tag found
			 */

			return (&tag->tags[ti]);
		}

	}

	/* no such element found - return NULL */

	return (SML_TAG__NULL);
}

/*
 * Name:	smlGetParamByTagParam
 * Synopsis:	Get parameter given tag name, index, parameter name, and value
 * Description:	Call to return the value of a parameter from a tag of a
 *		given name, with a parameter of a given name with a
 *		specified value
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the element from
 *		index - [RO] - (int)
 *			Index of nth name to return
 *		tagName - [RO, *RO] - (char *)
 *			Tag name to look up
 *		paramName - [RO, *RO] - (char *)
 *			Parameter name to look up
 *		paramValue - [RO, *RO] - (char *)
 *			Parameter value to match
 *		paramReturn - [RO, *RO] - (char *)
 *			Parameter name to return the value of
 * Returns:	char *
 *			The value of parameter 'paramReturn' from the
 *			The 'index'th occurance of element 'name' with
 *			a parameter 'name' with value specified
 *			== (char *)NULL if no such parameter exists
 */

char *
smlGetParamByTagParam(SML_TAG *tag, int index,
	char *tagName, char *paramName, char *paramValue, char *paramReturn)
{
	int		ti;		/* tag structure index */

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(tagName != (char *)NULL);
	assert(*tagName != '\0');
	assert(paramName != (char *)NULL);
	assert(*paramName != '\0');
	assert(paramValue != (char *)NULL);
	assert(*paramValue != '\0');
	assert(paramReturn != (char *)NULL);
	assert(*paramReturn != '\0');

	/* if tag has no elements, return NULL */

	if (tag->tags == NULL) {
		return ((char *)NULL);
	}

	/*
	 * Search algorithm:
	 *  -> search tag structure; for each tag with element == "tagName":
	 *  -> search tag parameters; if parameter name == "paramName"
	 *  -> if parameter value != "paramValue"; to next tag
	 *  -> if parameter value == "paramValue":
	 *  -> if not the "index"th paramValue found; to next tag
	 *  -> return value of "paramReturn"
	 */

	for (ti = 0; ti < tag->tags_num; ti++) {
		int	pi;	/* parameter structure index */

		/* if tag element does not match, go on to next tag */

		if (strcmp(tag->tags[ti].name, tagName)) {
			continue;
		}

		/* element matches: search for specified parameter name/value */

		for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
			assert(tag->tags[ti].params[pi].name != (char *)NULL);
			assert(tag->tags[ti].params[pi].value != (char *)NULL);

			/* if parameter name doesnt match to next parameter */

			if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
				continue;
			}

			/* if parameter value doesnt match to next tag */

			if (strcmp(tag->tags[ti].params[pi].value,
				paramValue)) {
				break;
			}

			/*
			 * found element/paramname/paramvalue:
			 * -> if this is not the 'index'th one, go to next tag
			 */

			if (index-- != 0) {
				break;
			}

			/*
			 * found specified element/paramname/paramvalue:
			 * -> return parameter requested
			 */

			return (smlGetParam(&tag->tags[ti], paramReturn));
		}

	}

	/* no such element found - return NULL */

	return ((char *)NULL);
}

/*
 * Name:	smlGetElementName
 * Description:	Return the name of a given tag
 * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the element name from
 * Returns:	char *
 *			Value of name of specified tag
 * NOTE:    	Any name string returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the name string is no longer needed.
 */

char *
smlGetElementName(SML_TAG *a_tag)
{
	/* entry assertions */

	assert(SML_TAG__ISVALID(a_tag));
	assert(a_tag->name != (char *)NULL);
	assert(*a_tag->name != '\0');

	/* return the tag name */

	return (strdup(a_tag->name));
}

/*
 * Name:	smlGetTag
 * Description:	Get an element from a tag
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the element from
 *		index - [RO] - (int)
 *			Index of element to return
 * Returns:	SML_TAG *
 *			The 'index'th element from the specified tag
 *			== SML_TAG__NULL if no such tag or element
 */

SML_TAG *
smlGetTag(SML_TAG *tag, int index)
{
	/* if no tag specified, return NULL */

	if (tag == NULL) {
		return (SML_TAG__NULL);
	}

	/* if tag has no elements, return NULL */

	if (tag->tags == NULL) {
		return (SML_TAG__NULL);
	}

	/* if index not within range, return NULL */

	if (tag->tags_num <= index) {
		return (SML_TAG__NULL);
	}

	/* index within range, return element specified */

	assert(SML_TAG__ISVALID(&tag->tags[index]));

	return (&tag->tags[index]);
}

/*
 * Name:	smlGetTagByName
 * Description:	Get an element given a name and an index
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the element from
 *		index - [RO] - (int)
 *			Index of nth name to return
 *		name - [RO, *RO] - (char *)
 *			Tag name to look up
 * Returns:	SML_TAG *
 *			The 'index'th occurance of element 'name'
 *			== SML_TAG__NULL if no such element exists
 */

SML_TAG *
smlGetTagByName(SML_TAG *tag, int index, char *name)
{
	int k;

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_TAG_BY_NAME, name, index);

	/* if no tag specified, return NULL */

	if (tag == NULL) {
		return (SML_TAG__NULL);
	}

	/* if this tag is the one mentioned, return it */

	if (streq(tag->name, name) && (index == 0)) {
		return (tag);
	}

	/* if tag has no elements, return NULL */

	if (tag->tags == NULL) {
		return (SML_TAG__NULL);
	}

	/* if index out of range, return NULL */

	if (tag->tags_num <= index) {
		return (SML_TAG__NULL);
	}

	/* index within range - search for specified element */

	for (k = 0; k < tag->tags_num; k++) {
		if (streq(tag->tags[k].name, name)) {
			if (index == 0) {
				assert(SML_TAG__ISVALID(&tag->tags[k]));
				return (&tag->tags[k]);
			} else {
				index--;
			}
		}
	}

	/* no such element found - return NULL */

	return (SML_TAG__NULL);
}

/*
 * Name:	smlConvertStringToTag
 * Description:	Convert string into tag object
 * Arguments:	err - [RO, *RW] (LU_ERR)
 *			Error object - used to contain any errors encountered
 *			and return those errors to this methods caller
 *		r_tag - [RW, *RW] - (SML_TAG **)
 *			Pointer to handle to place new tag object
 *		str - [RO, *RO] - (char *)
 *			String object to convert to tag object
 * Returns:	int
 *			RESULT_OK - string converted to tag object
 *			RESULT_ERR - problem converting string to tag object
 * NOTE:    	Any tag object returned is placed in new storage for the
 *		calling method. The caller must use 'smlFreeTag' to dispose
 *		of the storage once the tag object name is no longer needed.
 */

int
smlConvertStringToTag(SML_TAG **r_tag, char *str)
{
	int	r;
	SML_TAG	*tag = SML_TAG__NULL;
	SML_TAG	*tmp_tag;

	/* entry assertions */

	assert(SML_TAG__R_ISVALID(r_tag));
	assert(str != (char *)NULL);
	assert(*str != '\0');

	tag = smlNewTag("tagfile");

	for (;;) {
		r = _smlReadTag(&tmp_tag, &str, NULL);
		if (r != RESULT_OK) {
			smlFreeTag(tag);
			return (r);
		}
		if (tmp_tag == SML_TAG__NULL) {
			if (*str != '\0') {
				continue;
			}
			_smlLogMsg(LOG_MSG_DEBUG,
				DBG_SML_LOADED_TAGS_FROM_STR,
				(unsigned long)tag, tag->name);
			*r_tag = tag;
			return (RESULT_OK);
		}
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_IN_TOP_TAG,
			tmp_tag->name);
		tag->tags_num++;
		tag->tags = (SML_TAG *)realloc(tag->tags,
			sizeof (SML_TAG) *tag->tags_num);
		(void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
			sizeof (SML_TAG));
	}
}

/*
 * Name:	smlReadOneTag
 * Description:	read one complete tag from a datastream
 * Arguments:	err - [RO, *RW] (LU_ERR)
 *			Error object - used to contain any errors encountered
 *			and return those errors to this methods caller
 *		r_tag - [RW, *RW] - (SML_TAG **)
 *			Pointer to handle to place new tag object
 *			== SML_TAG__NULL if empty tag found (not an error)
 *		ds - [RO, *RO] - (LU_DS)
 *			Handle to datastream to read tag from
 * Returns:	int
 *			RESULT_OK - tag successfully read
 *			RESULT_ERR - problem reading tag
 * NOTE:    	Any tag object returned is placed in new storage for the
 *		calling method. The caller must use 'smlFreeTag' to dispose
 *		of the storage once the tag object name is no longer needed.
 */

int
smlReadOneTag(SML_TAG **r_tag, char *a_str)
{
	int	r;

	/* entry assertions */

	assert(SML_TAG__R_ISVALID(r_tag));
	assert(a_str != (char *)NULL);

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG, a_str);

	/* reset return tag */

	*r_tag = SML_TAG__NULL;

	/* read tag from datastream, no parent tag to attach it to */

	r = _smlReadTag(r_tag, &a_str, NULL);
	if (r != RESULT_OK) {
		_smlLogMsg(LOG_MSG_ERR, ERR_SML_CANNOT_READ_TAG);
		return (r);
	}

	if (*r_tag != SML_TAG__NULL) {
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ONE_TAG_READ,
			(unsigned long)*r_tag,
			(*r_tag)->name ? (*r_tag)->name : "<no name>");
	} else {
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG_NOTAG);
	}

	/* exit debugging info */

	return (RESULT_OK);
}

/*
 * Name:	smlNewTag
 * Description:	Create a new (empty) tag object
 * Arguments:	name - [RO, *RO] - (char *)
 *			Name of tag; NULL to give the tag no name
 * Returns:	SML_TAG *
 *			Tag object created
 * NOTE:    	Any tag object returned is placed in new storage for the
 *		calling method. The caller must use 'smlFreeTag' to dispose
 *		of the storage once the tag object name is no longer needed.
 * Errors:	If the tag object cannot be created, the process exits
 */

SML_TAG *
smlNewTag(char *name)
{
	SML_TAG	*tag;

	/* entry assertions */

	assert((name == (char *)NULL) || (*name != '\0'));

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATE_NEW_TAG_OBJECT,
		name ? name : "<no name>");

	/* allocate zeroed storage for the tag object */

	tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
	assert(tag != SML_TAG__NULL);

	/* if name is provided, duplicate and assign it */

	if (name != (char *)NULL) {
		tag->name = strdup(name);
	}

	/* exit assertions */

	assert(SML_TAG__ISVALID(tag));

	/* exit debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATED_NEW_TAG_OBJECT,
		(unsigned long)tag, name ? name : "<no name>");

	return (tag);
}

/*
 * Name:	smlConvertTagToString
 * Description:	Convert a tag object into a string representation of the XML
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to convert to a string
 * Returns:	char *
 *			String representation (in XML) of tag object
 *			== (char *)NULL if conversion is not possible
 * NOTE:    	Any string returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the string is no longer needed.
 */

char *
smlConvertTagToString(SML_TAG *tag)
{
	char		*str = (char *)NULL;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));

	/* convert the tag object into the datastream */

	(void) _smlWriteSimpleTag(&str, tag);

	assert(str != (char *)NULL);
	assert(*str != '\0');

	/* return the results */

	return (str);
}

/*
 * Name:	smlDbgPrintTag
 * Synopsis:	Print a representation of an XML tag if debugging
 * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
 *			Pointer to tag structure to dump
 *		a_format - [RO, RO*] (char *)
 *			printf-style format for debugging message to be output
 *		VARG_LIST - [RO] (?)
 *			arguments as appropriate to 'format' specified
 * Returns:	void
 *			If one of the debugging flags is set, the hexdump
 *			is output.
 */

/*PRINTFLIKE2*/
void
smlDbgPrintTag(SML_TAG *a_tag, char *a_format, ...)
{
	va_list	ap;
	size_t		vres = 0;
	char		bfr[1];
	char		*rstr = (char *)NULL;

	/* entry assertions */

	assert(a_format != (char *)NULL);
	assert(*a_format != '\0');
	assert(SML_TAG__ISVALID(a_tag));

	/*
	 * output the message header
	 */

	/* determine size of the message in bytes */

	va_start(ap, a_format);
	vres = vsnprintf(bfr, 1, a_format, ap);
	va_end(ap);

	assert(vres > 0);

	/* allocate storage to hold the message */

	rstr = (char *)calloc(1, vres+2);
	assert(rstr != (char *)NULL);

	/* generate the results of the printf conversion */

	va_start(ap, a_format);
	vres = vsnprintf(rstr, vres+1, a_format, ap);
	va_end(ap);

	assert(vres > 0);
	assert(*rstr != '\0');

	_smlLogMsg(LOG_MSG_DEBUG, "%s", rstr);
	free(rstr);

	/* convert the tag into a string to be printed */

	rstr = smlConvertTagToString(a_tag);
	if (rstr != (char *)NULL) {
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_PRINTTAG, a_tag->name,
			strlen(rstr), rstr);
	}
	free(rstr);
}

/*
 * Name:	smlDelParam
 * Description:	Delete a parameter from a tag object
 * Arguments:	tag - [RO, *RW] - (SML_TAG *)
 *			The tag object to delete the parameter from
 *		name - [RO, *RO] - (char *)
 *			The parameter to delete from the tag object
 * Returns:	void
 *			If the parameter exists, it is deleted from the tag
 */

void
smlDelParam(SML_TAG *tag, char *name)
{
	int k;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(tag->name != (char *)NULL);
	assert(name != NULL);
	assert(*name != '\0');

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DELETE_PARAM,
		tag->name, name);

	/* if tag has no parameters, nothing to delete */

	if (tag->params == NULL) {
		_smlLogMsg(LOG_MSG_DEBUG,
			DBG_SML_DELETE_PARAM_NO_PARAMS);
		return;
	}

	assert(tag->params_num > 0);

	/* search the tag for the parameter */

	for (k = 0; k < tag->params_num; k++) {
		if (streq(tag->params[k].name, name)) {
			break;
		}
	}

	/* if the parameter was not found, nothing to delete */

	if (k >= tag->params_num) {
		_smlLogMsg(LOG_MSG_DEBUG,
			DBG_SML_DELETE_PARAM_NOT_FOUND,
			name);
		return;
	}

	/* parameter found - indicate deleted */

	assert(tag->params[k].name != (char *)NULL);
	assert(tag->params[k].value != (char *)NULL);

	_smlLogMsg(LOG_MSG_DEBUG,
		DBG_SML_DELETE_PARAM_FOUND,
		name, tag->params[k].value);

	/* free up storage fro parameter */

	free(tag->params[k].name);
	free(tag->params[k].value);

	/* if not at end, compact parameter storage */

	if (k < (tag->params_num -1)) {
		(void) memmove(&(tag->params[k]), &(tag->params[k + 1]),
			sizeof (SML_PARAM) *(tag->params_num - k - 1));
	}

	/* one less parameter object in tag */

	tag->params_num --;

	/*
	 * If only one parameter left, then delete entire parameter storage,
	 * otherwise reallocate removing unneeded entry
	 */

	if (tag->params_num > 0) {
		/* realloc removing last element in tag object */

		tag->params = (SML_PARAM *)
			realloc(tag->params,
			sizeof (SML_PARAM) *tag->params_num);
	} else {
		tag->params = (SML_PARAM *)NULL;
	}
}

/*
 * Name:	smlSetParamF
 * Description:	Set formatted parameter value in tag object
 * Arguments:	tag - [RO, *RW] - (SML_TAG *)
 *			The tag object to set the parameter in
 *		name - [RO, *RO] - (char *)
 *			The parameter to add to the tag object
 *		format - [RO, RO*] (char *)
 *			printf-style format to create parameter value from
 *		... - [RO] (?)
 *			arguments as appropriate to 'format' specified
 * Returns:	void
 *			The parameter value is set in the tag object
 *			according to the results of the format string
 *			and arguments
 */

/*PRINTFLIKE3*/
void
smlSetParamF(SML_TAG *tag, char *name, char *format, ...)
{
	va_list	ap;
	size_t		vres = 0;
	char		*bfr = NULL;
	char		fbfr[1];

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(name != (char *)NULL);
	assert(*name != '\0');
	assert(format != NULL);
	assert(*format != '\0');

	/* determine size of the parameter name in bytes */

	va_start(ap, format);
	vres = vsnprintf(fbfr, 1, format, ap);
	va_end(ap);

	assert(vres > 0);

	/* allocate storage to hold the message */

	bfr = (char *)calloc(1, vres+2);
	assert(bfr != (char *)NULL);

	/* generate the parameter name and store it in the allocated storage */

	va_start(ap, format);
	vres = vsnprintf(bfr, vres+1, format, ap);
	va_end(ap);

	assert(vres > 0);
	assert(*bfr != '\0');

	/* add the parameter to the tag */

	smlSetParam(tag, name, bfr);

	/* free up temporary storage and return */

	free(bfr);
}

/*
 * Name:	smlGetParam
 * Description:	Get a format-generated parameter from a tag
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to obtain the parameter from
 *		format - [RO, RO*] (char *)
 *			printf-style format for parameter name to be
 *			looked up to be formatted
 *		... - [RO] (?)
 *			arguments as appropriate to 'format' specified
 * Returns:	char *
 *			Value of the specified parameter
 *			== (char *)NULL if the parameter does not exist
 * NOTE:    	Any parameter returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the parameter is no longer needed.
 */

/*PRINTFLIKE2*/
char *
smlGetParamF(SML_TAG *tag, char *format, ...)
{
	va_list	ap;
	size_t		vres = 0;
	char		*bfr = NULL;
	char		fbfr[1];
	char		*p;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(format != NULL);
	assert(*format != '\0');

	/* determine size of the parameter name in bytes */

	va_start(ap, format);
	vres = vsnprintf(fbfr, 1, format, ap);
	va_end(ap);

	assert(vres > 0);

	/* allocate storage to hold the message */

	bfr = (char *)calloc(1, vres+2);
	assert(bfr != (char *)NULL);

	/* generate the parameter name and store it in the allocated storage */

	va_start(ap, format);
	vres = vsnprintf(bfr, vres+1, format, ap);
	va_end(ap);

	assert(vres > 0);
	assert(*bfr != '\0');

	/* add the parameter to the tag */

	p = smlGetParam(tag, bfr);

	/* free up temporary storage and return */

	free(bfr);

	return (p);
}

/*
 * Name:	smlSetParam
 * Description:	Set parameter value in tag object
 * Arguments:	tag - [RO, *RW] - (SML_TAG *)
 *			The tag object to set the parameter in
 *		name - [RO, *RO] - (char *)
 *			The parameter to add to the tag object
 *		value - [RO, *RO] - (char *)
 *			The value of the parameter to set in the tag object
 * Returns:	void
 *			The parameter value is set in the tag object
 */

void
smlSetParam(SML_TAG *tag, char *name, char *value)
{
	SML_PARAM *parameter;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(name != (char *)NULL);
	assert(*name != '\0');
	assert(value != (char *)NULL);

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_SET_PARAM,
		tag->name, name, value);

	/* if parameters exist, see if modifying existing parameter */

	if (tag->params != NULL) {
		int k;
		for (k = 0; k < tag->params_num; k++) {
			assert(tag->params[k].name != (char *)NULL);
			assert(tag->params[k].value != (char *)NULL);

			/* if name does not match, skip */

			if (!streq(tag->params[k].name, name)) {
				continue;
			}

			/* found parameter - if value is same, leave alone */

			if (streq(tag->params[k].value, value)) {
				_smlLogMsg(LOG_MSG_DEBUG,
					DBG_SML_SET_PARAM_LEAVE_ALONE,
					tag->params[k].value);
				return;
			}

			/* exists and has different value - change */

			_smlLogMsg(LOG_MSG_DEBUG,
				DBG_SML_SET_PARAM_MODIFY,
				tag->params[k].value);
				free(tag->params[k].value);
				tag->params[k].value = strdup(value);
				return;
		}
	}

	/* not modifying existing - add new parameter */

	_smlLogMsg(LOG_MSG_DEBUG,
		DBG_SML_SET_PARAM_CREATE_NEW);

	parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
	bzero(parameter, sizeof (SML_PARAM));
	parameter->name = strdup(name);
	parameter->value = strdup(value);

	tag->params_num++;
	tag->params = (SML_PARAM *)realloc(tag->params,
			sizeof (SML_PARAM) *tag->params_num);
	(void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
			sizeof (SML_PARAM));
	free(parameter);
}

/*
 * Name:	smlParamEqF
 * Description:	Determine if parameter is equal to a specified formatted value
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to look for the parameter to compare
 *		findTag - [RO, *RO] - (char *)
 *			Tag within tag object to look for the parameter in
 *		findParam - [RO, *RO] - (char *)
 *			Parameter within tag to look for
 *		format - [RO, RO*] (char *)
 *			printf-style format for value to be compared against
 *			parameter value
 *		... - [RO] (?)
 *			arguments as appropriate to 'format' specified to
 *			generate the value to compare parameter with
 * Returns:	boolean_t
 *			B_TRUE - the parameter exists and matches given value
 *			B_FALSE - parameter does not exist or does not match
 */

/*PRINTFLIKE4*/
boolean_t
smlParamEqF(SML_TAG *tag, char *findTag, char *findParam, char *format, ...)
{
	va_list	ap;
	size_t		vres = 0;
	char		*bfr = NULL;
	char		fbfr[1];
	boolean_t	b;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(format != NULL);
	assert(*format != '\0');

	/* determine size of the parameter value in bytes */

	va_start(ap, format);
	vres = vsnprintf(fbfr, 1, format, ap);
	va_end(ap);

	assert(vres > 0);

	/* allocate storage to hold the message */

	bfr = (char *)calloc(1, vres+2);
	assert(bfr != (char *)NULL);

	/* generate the parameter value and store it in the allocated storage */

	va_start(ap, format);
	vres = vsnprintf(bfr, vres+1, format, ap);
	va_end(ap);

	assert(vres > 0);
	assert(*bfr != '\0');

	/* add the parameter to the tag */

	b = smlParamEq(tag, findTag, findParam, bfr);

	/* free up temporary storage and return */

	free(bfr);

	return (b);
}

/*
 * Name:	smlParamEq
 * Description:	Determine if parameter is equal to a specified value
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to look for the parameter to compare
 *		findTag - [RO, *RO] - (char *)
 *			Tag within tag object to look for the parameter in
 *		findParam - [RO, *RO] - (char *)
 *			Parameter within tag to look for
 *		str - [RO, *RO] - (char *)
 *			Value to compare parameter with
 * Returns:	boolean_t
 *			B_TRUE - the parameter exists and matches given value
 *			B_FALSE - parameter does not exist or does not match
 */

boolean_t
smlParamEq(SML_TAG *tag, char *findTag, char *findParam, char *str)
{
	SML_TAG	*rtag;
	char		*rparm;
	boolean_t	answer;

	/* entry assertions */

	assert(str != (char *)NULL);
	assert(findParam != (char *)NULL);
	assert(findTag != (char *)NULL);
	assert(SML_TAG__ISVALID(tag));

	/* look for the specified tag - if not found, return false */

	rtag = smlGetTagByName(tag, 0, findTag);
	if (rtag == SML_TAG__NULL) {
		return (B_FALSE);
	}

	/* look for the specified parameter - if not found, return false */

	rparm = smlGetParam(rtag, findParam);
	if (rparm == (char *)NULL) {
		return (B_FALSE);
	}

	/* parameter found - compare against given value */

	answer = strcasecmp(str, rparm);

	/* free up parameter storage */

	free(rparm);

	/* return results of comparison */

	return (answer == 0 ? B_TRUE : B_FALSE);
}

/*
 * Name:	smlFindAndDelTag
 * Description:	Delete a tag if found in tag object
 * Arguments:	tag - [RO, *RW] - (SML_TAG *)
 *			The tag object to delete the tag from
 *		findTag - [RO, *RO] - (char *)
 *			Tag within tag object to delete
 * Returns:	boolean_t
 *			B_TRUE - tag found and deleted
 *			B_FALSE - tag not found
 */

boolean_t
smlFindAndDelTag(SML_TAG *tag, char *findTag)
{
	SML_TAG	*rtag = SML_TAG__NULL;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));
	assert(findTag != (char *)NULL);
	assert(*findTag != '\0');

	/* find the specified tag - if not found, return false */

	rtag = smlGetTagByName(tag, 0, findTag);
	if (rtag == SML_TAG__NULL) {
		return (B_FALSE);
	}

	/* tag found - delete it and return true */

	smlDelTag(tag, rtag);

	return (B_TRUE);
}

/*
 * Name:	smlDup
 * Description:	Duplicate a tag object
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to duplicate
 * Returns:	SML_TAG *
 *			A handle to a complete duplicate of the tag provided
 * NOTE:    	Any tag object returned is placed in new storage for the
 *		calling method. The caller must use 'smlFreeTag' to dispose
 *		of the storage once the tag object name is no longer needed.
 * Errors:	If the tag object cannot be duplicated, the process exits
 */

SML_TAG *
smlDup(SML_TAG *tag)
{
	SML_TAG	*rtag = SML_TAG__NULL;
	int		i;

	/* entry assertions */

	assert(SML_TAG__ISVALID(tag));

	/* allocate zeroed storage for the tag object */

	rtag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
	assert(rtag != SML_TAG__NULL);

	/* duplicate all parameters of the tag */

	rtag->name = (tag->name ? strdup(tag->name) : (char *)NULL);
	rtag->params_num = tag->params_num;
	if (tag->params != (SML_PARAM *)NULL) {
		rtag->params = (SML_PARAM *)
			calloc(1, sizeof (SML_PARAM)*rtag->params_num);
		bzero(rtag->params, sizeof (SML_PARAM)*rtag->params_num);
		for (i = 0; i < rtag->params_num; i++) {
			rtag->params[i].name = tag->params[i].name ?
				strdup(tag->params[i].name) :
					(char *)NULL;
			rtag->params[i].value = tag->params[i].value ?
				strdup(tag->params[i].value) :
					(char *)NULL;
		}
	}

	/* duplicate all elements of the tag */

	rtag->tags_num = tag->tags_num;

	if (tag->tags != SML_TAG__NULL) {
		rtag->tags = (SML_TAG *)
			calloc(1, sizeof (SML_TAG)*rtag->tags_num);
		bzero(rtag->tags, sizeof (SML_TAG)*rtag->tags_num);
		for (i = 0; i < rtag->tags_num; i++) {
			SML_TAG *stag;
			stag = smlDup(&tag->tags[i]);
			(void) memcpy(&rtag->tags[i], stag,
				sizeof (SML_TAG));
			free(stag);
		}
	}

	/* exit assertions */

	assert(SML_TAG__ISVALID(rtag));

	/* return */

	return (rtag);
}

/*
 * Name:	smlSetFileStatInfo
 * Description;	Given a file status structure and path name, encode the
 *		structure and place it and the name into the specified tag
 *		in a "_sml_fileStatInfoTag" (private) element
 * Arguments:	tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to deposit the information into
 *		statbuf - [RO, *RO] - (struct stat *)
 *			Pointer to file status structure to encode
 *		path - [RO, *RO] - (char *)
 *			Pointer to path name of file to encode
 * Returns:	void
 *			The information is placed into the specified tag object
 */

void
smlSetFileStatInfo(SML_TAG **tag, struct stat *statbuf, char *path)
{
	SML_TAG	*rtag;

	/* entry assertions */

	assert(SML_TAG__R_ISVALID(tag));
	assert(SML_TAG__ISVALID(*tag));
	assert(statbuf != (struct stat *)NULL);

	/* if stat info exists, delete it */

	(void) smlFindAndDelTag(*tag, _sml_fileStatInfoTag);

	/* create the file stat info inside of the top level tag */

	assert(smlGetTagByName(*tag, 0, _sml_fileStatInfoTag)
							== SML_TAG__NULL);
	rtag = smlNewTag(_sml_fileStatInfoTag);
	assert(SML_TAG__ISVALID(rtag));
	(void) smlAddTag(tag, 0, rtag);
	free(rtag);

	/* obtain handle on newly created file stat info tag */

	rtag = smlGetTagByName(*tag, 0, _sml_fileStatInfoTag);
	assert(SML_TAG__ISVALID(rtag));

	/* add file info as parameters to the tag */

	if (path != (char *)NULL) {
		smlSetParam(rtag, "st_path", path);
	}

	smlSetParamF(rtag, "st_ino", "0x%llx",
		(unsigned long long)statbuf->st_ino);
	smlSetParamF(rtag, "st_mode", "0x%llx",
		(unsigned long long)statbuf->st_mode);
	smlSetParamF(rtag, "st_mtime", "0x%llx",
		(unsigned long long)statbuf->st_mtime);
	smlSetParamF(rtag, "st_ctime", "0x%llx",
		(unsigned long long)statbuf->st_ctime);
	smlSetParamF(rtag, "st_size", "0x%llx",
		(unsigned long long)statbuf->st_size);
}

/*
 * Name:	smlFstatCompareEQ
 * Description:	Given a file status structure and path name, look for the
 *		information placed into a tag object via smlSetFileStatInfo
 *		and if present compare the encoded information with the
 *		arguments provided
 * Arguments:	statbuf - [RO, *RO] - (struct stat *)
 *			Pointer to file status structure to compare
 *		tag - [RO, *RO] - (SML_TAG *)
 *			The tag object to compare against
 *		path - [RO, *RO] - (char *)
 *			Pointer to path name of file to compare
 * Returns:	boolean_t
 *			B_TRUE - both status structures are identical
 *			B_FALSE - the status structures are not equal
 */

boolean_t
smlFstatCompareEq(struct stat *statbuf, SML_TAG *tag, char *path)
{
	if (tag == SML_TAG__NULL) {
		return (B_FALSE);
	}

	assert(SML_TAG__ISVALID(tag));

	if (statbuf == (struct stat *)NULL) {
		return (B_FALSE);
	}

	if (path != (char *)NULL) {
		if (smlParamEq(tag,
			_sml_fileStatInfoTag, "st_path", path) != B_TRUE) {
			return (B_FALSE);
		}
	}

	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ino",
		"0x%llx", (unsigned long long)statbuf->st_ino) != B_TRUE) {
		return (B_FALSE);
	}

	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mode",
		"0x%llx", (unsigned long long)statbuf->st_mode) != B_TRUE) {
		return (B_FALSE);
	}

	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mtime",
		"0x%llx", (unsigned long long)statbuf->st_mtime) != B_TRUE) {
		return (B_FALSE);
	}

	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ctime",
		"0x%llx", (unsigned long long)statbuf->st_ctime) != B_TRUE) {
		return (B_FALSE);
	}

	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_size",
		"0x%llx", (unsigned long long)statbuf->st_size) != B_TRUE) {
		return (B_FALSE);
	}

	return (B_TRUE);
}

/*
 * Name:	set_verbose
 * Description:	Turns on verbose output
 * Scope:	public
 * Arguments:	verbose = B_TRUE indicates verbose mode
 * Returns:	none
 */
void
smlSetVerbose(boolean_t setting)
{
	verbose = setting;
}

/*
 * Name:	get_verbose
 * Description:	Returns whether or not to output verbose messages
 * Scope:	public
 * Arguments:	none
 * Returns:	B_TRUE - verbose messages should be output
 */
boolean_t
smlGetVerbose()
{
	return (verbose);
}

/*
 * Name:	sml_strPrintf
 * Synopsis:	Create string from printf style format and arguments
 * Description:	Call to convert a printf style format and arguments into a
 *		string of characters placed in allocated storage
 * Arguments:	format - [RO, RO*] (char *)
 *			printf-style format for string to be formatted
 *		... - [RO] (?)
 *			arguments as appropriate to 'format' specified
 * Returns:	char *
 *			A string representing the printf conversion results
 * NOTE:    	Any string returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the string is no longer needed.
 * Errors:	If the string cannot be created, the process exits
 */

/*PRINTFLIKE1*/
char *
sml_strPrintf(char *a_format, ...)
{
	va_list	ap;
	size_t		vres = 0;
	char		bfr[1];
	char		*rstr = (char *)NULL;

	/* entry assertions */

	assert(a_format != (char *)NULL);
	assert(*a_format != '\0');

	/* determine size of the message in bytes */

	va_start(ap, a_format);
	vres = vsnprintf(bfr, 1, a_format, ap);
	va_end(ap);

	assert(vres > 0);

	/* allocate storage to hold the message */

	rstr = (char *)calloc(1, vres+2);
	assert(rstr != (char *)NULL);

	/* generate the results of the printf conversion */

	va_start(ap, a_format);
	vres = vsnprintf(rstr, vres+1, a_format, ap);
	va_end(ap);

	assert(vres > 0);
	assert(*rstr != '\0');

	/* return the results */

	return (rstr);
}

/*
 * Name:	sml_strPrintf_r
 * Synopsis:	Create string from printf style format and arguments
 * Description:	Call to convert a printf style format and arguments into a
 *		string of characters placed in allocated storage
 * Arguments:	a_buf - [RO, *RW] - (char *)
 *			- Pointer to buffer used as storage space for the
 *			  returned string created
 *		a_bufLen - [RO, *RO] - (int)
 *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
 *			  bytes will be placed in 'a_buf' - the returned
 *			  string is always null terminated
 *		a_format - [RO, RO*] (char *)
 *			printf-style format for string to be formatted
 *		VARG_LIST - [RO] (?)
 *			arguments as appropriate to 'format' specified
 * Returns:	void
 */

/*PRINTFLIKE3*/
void
sml_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
{
	va_list	ap;
	size_t		vres = 0;

	/* entry assertions */

	assert(a_format != (char *)NULL);
	assert(*a_format != '\0');
	assert(a_buf != (char *)NULL);
	assert(a_bufLen > 1);

	/* generate the results of the printf conversion */

	va_start(ap, a_format);
	vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
	va_end(ap);

	assert(vres > 0);
	assert(vres < a_bufLen);

	a_buf[a_bufLen-1] = '\0';
}

/*
 * Name:	sml_XmlEncodeString
 * Description:	Given a plain text string, convert that string into one that
 *		encoded using the XML character reference encoding format.
 * Arguments:	a_plain_text_string	- [RO, *RO] (char *)
 *			The plain text string to convert (encode)
 * Returns:	char *
 *			The encoded form of the plain text string provided
 * NOTE:    	Any string returned is placed in new storage for the
 *		calling method. The caller must use 'lu_memFree' to dispose
 *		of the storage once the string is no longer needed.
 */

char *
sml_XmlEncodeString(char *a_plainTextString)
{
	char *stringHead;	/* -> start of string containing encoded data */
	long stringTail;	/* byte pos of first free byte in stringHead */
	long stringLength;	/* total bytes allocd starting at stringHead */
	char *p;		/* temp -> to retrieve bytes from src string */
	long textLength = 0;	/* length of the string to convert */

	/* entry assertions */

	assert(a_plainTextString != (char *)NULL);

	textLength = strlen(a_plainTextString);

	/* Allocate initial string buffer to hold results */

	stringLength = textLength*2;
	stringTail = 0;
	stringHead = (char *)calloc(1, (size_t)stringLength+2);
	assert(stringHead != (char *)NULL);

	/* Add in the encoded message text */

	for (p = a_plainTextString; textLength > 0; p++, textLength--) {
		/*
		 * Must have at least 12 bytes: this must be at least the
		 * maximum number of bytes that can be added for a single
		 * byte as the last byte of the stream. Assuming the byte
		 * needs to be encoded, it could be:
		 * &#xxxxxxxx;\0
		 * If not that many bytes left, grow the buffer.
		 */

		if ((stringLength-stringTail) < 12) {
			stringLength += (textLength*2)+12;
			stringHead =
				realloc(stringHead,
					(size_t)stringLength+2);
			assert(stringHead != (char *)NULL);
		}

		/*
		 * See if this byte is a 'printable 7-bit ascii value'.
		 * If so just add it to the new string; otherwise, must
		 * output an XML character value encoding for the byte.
		 */

		switch (*p) {
		case '!':
		case '#':
		case '%':
		case '\'':
		case '(':
		case ')':
		case '*':
		case '+':
		case ',':
		case '-':
		case '.':
		case '/':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case ':':
		case ';':
		case '<':
		case '=':
		case '>':
		case '?':
		case '@':
		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
		case 'G':
		case 'H':
		case 'I':
		case 'J':
		case 'K':
		case 'L':
		case 'M':
		case 'N':
		case 'O':
		case 'P':
		case 'Q':
		case 'R':
		case 'S':
		case 'T':
		case 'U':
		case 'V':
		case 'W':
		case 'X':
		case 'Y':
		case 'Z':
		case '[':
		case ']':
		case '^':
		case '_':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'h':
		case 'i':
		case 'j':
		case 'k':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'q':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
		case 'y':
		case 'z':
		case '{':
		case '|':
		case '}':
		case '~':
		case ' ':
			/*
			 * It is a printable 7-bit ascii character:
			 * just add it to the end of the new string.
			 */

			stringHead[stringTail++] = *p;
			break;
		default:
			/*
			 * It is not a printable 7-bit ascii character:
			 * add it as an xml character value encoding.
			 */

			stringTail += sprintf(&stringHead[stringTail], "&#%x;",
					(*p)&0xFF);
			break;
		}
	}

	/* Terminate the new string */

	stringHead[stringTail] = '\0';

	/* realloc the string so it is only as big as it needs to be */

	stringHead = realloc(stringHead, stringTail+1);
	assert(stringHead != (char *)NULL);

	return (stringHead);
}

/*
 * Name:	sml_XmlDecodeString
 * Description:	Given a string encoded using the XML character reference format,
 *		convert that string into a plain text (unencoded) string.
 * Arguments:	a_xml_encoded_string	- [RO, *RO] (char *)
 *			The XML encoded string to convert to plain text
 * Returns:	char *
 *			The unencoded (plain text) form of the encoded string
 * NOTE:    	Any string returned is placed in new storage for the
 *		calling method. The caller must use 'lu_memFree' to dispose
 *		of the storage once the string is no longer needed.
 */

char *
sml_XmlDecodeString(char *a_xmlEncodedString)
{
	char *s = NULL;		/* -> index into encoded bytes string */
	char *d = NULL;		/* -> index into decoded bytes string */
	char *rs = NULL;	/* -> string holding ref bytes allocated */
	char *ri = NULL;	/* -> index into string holding reference */
	long textLength = 0;	/* length of encoded string to decode */
	unsigned long rv = 0;	/* temp to hold scanf results of byte conv */
	char *i = NULL;		/* temp to hold strchr results */
	char *stringHead = NULL;	/* -> plain test buffer */
	ptrdiff_t tmpdiff;

	/*
	 * A finite state machine is used to convert the xml encoded string
	 * into plain text. The states of the machine are defined below.
	 */

	int fsmsState = -1;	/* Finite state machine state */
#define	fsms_text	0	/* Decoding plain text */
#define	fsms_seenAmp	1	/* Found & */
#define	fsms_seenPound	2	/* Found # following & */
#define	fsms_collect	3	/* Collecting character reference bytes */

	/* entry assertions */

	assert(a_xmlEncodedString != (char *)NULL);

	textLength = strlen(a_xmlEncodedString);

	/*
	 * Allocate string that can contain the decoded string.
	 * Since decoding always results in a shorter string (bytes encoded
	 * using the XML character reference are larger in the encoded form)
	 * we can allocate a string the same size as the encoded string.
	 */

	stringHead = (char *)calloc(1, textLength+1);
	assert(stringHead != (char *)NULL);

	/*
	 * Convert all bytes.
	 */

	/* Decoding plain text */
	fsmsState = fsms_text;

	for (s = a_xmlEncodedString, d = stringHead; textLength > 0;
		s++, textLength--) {
		switch (fsmsState) {
		case fsms_text:	/* Decoding plain text */
			if (rs != NULL) {
				free(rs);
				rs = NULL;
				ri = NULL;
			}
			if (*s == '&') {
				/* Found & */
				fsmsState = fsms_seenAmp;
				continue;
			}
			*d++ = *s;
			continue;

		case fsms_seenAmp:	/* Found & */
			if (*s == '#') {
				/* Found # following & */
				fsmsState = fsms_seenPound;
				continue;
			}
			fsmsState = fsms_text;	/* Decoding plain text */
			*d++ = '&';
			*d++ = *s;
			continue;

		case fsms_seenPound:		/* Found # following & */
			i = strchr(s, ';');
			if (i == NULL) {
				/* Decoding plain text */
				fsmsState = fsms_text;
				*d++ = '&';
				*d++ = '#';
				*d++ = *s;
				continue;
			}
			tmpdiff = (ptrdiff_t)i - (ptrdiff_t)s;
			rs = (char *)calloc(1, tmpdiff + 1);
			assert(rs != (char *)NULL);
			ri = rs;
			/* Collecting character reference bytes */
			fsmsState = fsms_collect;

			/*FALLTHRU*/

		/* Collecting character reference bytes */
		case fsms_collect:
			if (*s != ';') {
				switch (*s) {
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
				case 'a':
				case 'b':
				case 'c':
				case 'd':
				case 'e':
				case 'f':
				case 'A':
				case 'B':
				case 'C':
				case 'D':
				case 'E':
				case 'F':
					*ri++ = *s;
					break;
				default:
					*ri = '\0';
					*d++ = '&';
					*d++ = '#';
					tmpdiff = (ptrdiff_t)ri - (ptrdiff_t)rs;
					(void) strncpy(d, rs, tmpdiff-1);
					*d++ = *s;
					/* Decoding plain text */
					fsmsState = fsms_text;
					break;
				}
				continue;
			}
			*ri = '\0';
			if (sscanf(rs, "%lx", &rv) != 1) {
				*d++ = '?';
			} else {
				*d++ = (rv & 0xFF);
			}
			/* Decoding plain text */
			fsmsState = fsms_text;
		}
	}

	/* Done converting bytes - deallocate reference byte storage */

	free(rs);

	/* terminate the converted (plain text) string */

	*d = '\0';

	/* exit assertions */

	assert(stringHead != (char *)NULL);

	return (stringHead);
}

/*
 * Private Methods
 */

/*
 * Name:	_smlReadTag
 * Description:	read complete tag from a datastream
 * Arguments:	err - [RO, *RW] (LU_ERR)
 *			Error object - used to contain any errors encountered
 *			and return those errors to this methods caller
 *		r_tag - [RW, *RW] - (SML_TAG **)
 *			Pointer to handle to place new tag object
 *			== SML_TAG__NULL if empty tag found (not an error)
 *		ds - [RO, *RO] - (LU_DS)
 *			Handle to datastream to read tag from
 *		parent - [RO, *RO] - (char *)
 *			Name for parent of tag (NONE if top of tag)
 * Returns:	int
 *			RESULT_OK - tag successfully read
 *			RESULT_ERR - problem reading tag
 * NOTE:    	Any tag object returned is placed in new storage for the
 *		calling method. The caller must use 'smlFreeTag' to dispose
 *		of the storage once the tag object name is no longer needed.
 * Errors:	If the tag object cannot be duplicated, the process exits
 */

static int
_smlReadTag(SML_TAG **r_tag, char **a_str, char *parent)
{
	int	r;
	SML_TAG	*tag;
	SML_TAG	*tmp_tag;
	char	name[MAX_SML_COMPONENT_LENGTH];
	int	pos = 0;
	int	c;
	char	*p = *a_str;

	/* entry assertions */

	assert(SML_TAG__R_ISVALID(r_tag));
	assert(a_str != (char **)NULL);

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_TAG,
		parent ? parent : "<<TOP TAG>>");

	/* reset return tag */

	*r_tag = SML_TAG__NULL;

	/* allocate zeroed storage for the tag object */

	tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
	assert(tag != SML_TAG__NULL);

	/* reset name accumulator storage */

	bzero(name, sizeof (name));

	/* ignore delimters before tag */

	for (;;) {
		/* read tag character - handle failure/EOF */

		if ((*p == '\0') || ((c = (*p++)) == '\0')) {
			if (parent == NULL) {
				_smlLogMsg(LOG_MSG_DEBUG,
					DBG_SML_READTAG_EXPECTED_EOF,
					p ? p : "?");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_OK);
			}

			/* EOF in middle of processing tag */

			_smlLogMsg(LOG_MSG_ERR,
				DBG_SML_READTAG_UNEXPECTED_EOF,
				p ? p : "?");
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* if beginning of tag, break out */

		if (c == '<') {
			break;
		}

		/* not tag beginning: ignore delimiters if not inside tag yet */

		if (parent == (char *)NULL) {
			/* ignore delimters */

			if (strchr(" \t", c) != (char *)NULL) {
				continue;
			}

			/* on blank lines, return no tag object */

			if (c == '\n') {
				_smlLogMsg(LOG_MSG_DEBUG,
					DBG_SML_READTAG_BLANKLINE,
					p ? p : "?");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_OK);
			}

			/* invalid character before tag start */

			_smlLogMsg(LOG_MSG_ERR, ERR_SML_READTAG_BAD_START_CHAR,
				c, (unsigned int)c);
			*a_str = p;
			return (RESULT_ERR);
		}
	}

	/*
	 * all delimiters have been ignored and opening tag character seen;
	 * process tag
	 */

	assert(c == '<');

	c = *p;
	if (*p != '\0') {
		p++;
	}

	/* handle EOF after tag opening character found */

	if (c == '\0') {
		_smlLogMsg(LOG_MSG_ERR,
			ERR_SML_EOF_BEFORE_TAG_NAME,
			parent ? parent : "<<NONE>>");
		smlFreeTag(tag);
		*a_str = p;
		return (RESULT_ERR);
	}

	/* is this a tag closure? */

	if (c == '/') {
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_START_CLOSE_TAG,
			parent ? parent : "<<NONE>>");

		for (;;) {
			/* get next character of tag name */

			c = *p;
			if (*p != '\0') {
				p++;
			}

			/* EOF inside tag name? */

			if (c == '\0') {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_CLOSE_TAG_EOF,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* tag close: break out of collection loop */

			if (c == '>') {
				break;
			}

			/* see if illegal character in tag name */

			/* CSTYLED */
			if (strchr("/ \t\n\":<?$'\\`!@#%^&*()+=|[]{};,", c)
				!= NULL) {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_CLOSE_TAG_ILLCHAR,
					c, (unsigned int)c, name);
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* valid character - add to name if room left */

			if (pos < sizeof (name)-1) {
				name[pos] = (c&0xFF);
				pos++;
			}

			assert(pos < sizeof (name));
		}

		/* close of tag found */

		assert(c == '>');

		/* is the tag empty? If so that's an error */

		if (*name == '\0') {
			_smlLogMsg(LOG_MSG_ERR,
				ERR_SML_READTAG_CLOSE_EMPTY_TAG);
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* if no parent, a close tag outside of any open tag */

		if (parent == (char *)NULL) {
			_smlLogMsg(LOG_MSG_ERR,
				ERR_SML_READTAG_CLOSE_NO_PARENT,
				name);
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* if not close to current parent, error */

		if (!streq(parent, name)) {
			_smlLogMsg(LOG_MSG_ERR,
				ERR_SML_READTAG_CLOSE_WRONG_TAG,
				name, parent);
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* close of current tag found - success */

		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READTAG_CLOSE_TAG,
			name);
		smlFreeTag(tag);
		*a_str = p;
		return (RESULT_OK);
	}

	/* not starting a close tag */

	assert(c != '/');
	assert(c != '<');

	/* at start of tag - input tag name */

	bzero(name, sizeof (name));
	pos = 0;

	for (;;) {

		/* EOF inside of tag name? */

		if (c == '\0') {
			_smlLogMsg(LOG_MSG_ERR,
				ERR_SML_READTAG_TAG_EOF,
				name, parent ? parent : "<<NONE>>");
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* if separator or end of line then tag name collected */

		if (strchr(" >\t\n", c) != NULL) {
			break;
		}

		/* see if illegal character in tag name */

		/*CSTYLED*/
		if (strchr("\":<>?$'\\`!@#%^&*()+=|[]{};,", c) != NULL) {
			_smlLogMsg(LOG_MSG_ERR,
				ERR_SML_READTAG_TAG_ILLCHAR,
				c, (unsigned int)c, name);
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* close current tag? */

		if (c == '/') {
			/* get next character of tag name */

			c = *p;
			if (*p != '\0') {
				p++;
			}

			/* tag close not found? */

			if (c != '>') {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_BADTAG_CLOSE,
					name, parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* is the tag empty? If so that's an error */

			if (*name == '\0') {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_EMPTY_TAG,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* tag closed */

			_smlLogMsg(LOG_MSG_DEBUG,
				DBG_SML_READTAG_CLOSED_TAG,
				name, parent ? parent : "<<NONE>>");

			tag->name = strdup(name);
			*r_tag = tag;
			*a_str = p;
			return (RESULT_OK);
		}

		/* valid character - add to name if room left */

		if (pos < sizeof (name)-1) {
			name[pos] = (c&0xFF);
			pos++;
		}

		assert(pos < sizeof (name));

		/* get next character to parse */

		c = *p;
		if (*p != '\0') {
			p++;
		}
	}

	/* have a valid tag name: <tagname */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_TAG_NAME,
		name, parent ? parent : "<<NONE>>");

	assert(*name != '\0');

	/* place tag name inside of tag object */

	tag->name = strdup(name);

	/* clear out name accumulator to get parameters */

	bzero(name, sizeof (name));
	pos = 0;

	/* input parameters */

	if (c != '>')
		for (;;) {

		char *pname;
		char *pvalue;
		SML_PARAM *parameter;

		/* pass spaces before parameter name */

		for (;;) {

			/* get next character of parameter name */

			c = *p;
			if (*p != '\0') {
				p++;
			}

			/* EOF inside parameter name? */

			if (c == '\0') {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_PARM_EOF,
					tag->name,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* if separator/end of line tag parameter collected */

			if (strchr(" \t\n", c) != NULL) {
				continue;
			}

			/* see if illegal character in parameter name */

			/*CSTYLED*/
			if (strchr("\":<?$'\\`!@#%^&*()+=|[]{};,.", c) !=
				(char *)NULL) {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_PARMNAME_ILLCHAR,
					c, (unsigned int)c, name, tag->name,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* tag close found? */

			if (c == '>') {
				break;
			}

			/* close tag found ? */

			if (c == '/') {
				c = *p;
				if (*p != '\0') {
					p++;
				}
				if (c == '>') {
					_smlLogMsg(LOG_MSG_DEBUG,
						DBG_SML_TAG_ONLY,
						tag->name);
					*r_tag = tag;
					*a_str = p;
					return (RESULT_OK);
				}

				/* / not followed by > */
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_BADPARMNAME_CLOSE,
					name, tag->name,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* valid character - add to name if room left */

			if (pos < sizeof (name)-1) {
				name[pos] = (c&0xFF);
				pos++;
			}

			assert(pos < sizeof (name));
			break;
		}

		if (c == '>') {
			break;
		}

		/* input parameter name */

		for (;;) {
			c = *p;
			if (*p != '\0') {
				p++;
			}

			/* EOF inside of parameter name? */

			if (c == '\0') {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_PARM_EOF,
					tag->name,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/*CSTYLED*/
			if (strchr("\t \n\":<>?$'\\`!@%^*()+|[]{},./", c) != NULL) {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_PARMNAME_ILLCHAR,
					c, (unsigned int)c, name, tag->name,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* name - value separator found ? */

			if (c == '=') {
				break;
			}

			/* valid character - add to name if room left */

			if (pos < sizeof (name)-1) {
				name[pos] = (c&0xFF);
				pos++;
			}

			assert(pos < sizeof (name));
		}

		/* is the parameter name empty? If so that's an error */

		if (*name == '\0') {
			_smlLogMsg(LOG_MSG_ERR,
				ERR_SML_READTAG_EMPTY_PARMNAME,
				tag->name, parent ? parent : "<<NONE>>");
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* have a parameter name */

		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_NAME,
			name, tag->name);

		/* duplicate (save) parameter name */

		pname = strdup(name);

		/* clear out name accumulator to get parameters */

		bzero(name, sizeof (name));
		pos = 0;

		c = *p;
		if (*p != '\0') {
			p++;
		}

		if (c != '"') {
			_smlLogMsg(LOG_MSG_ERR,
				ERR_SML_PARM_SEP_BAD,
				c, (unsigned int)c);
			free(pname);
			smlFreeTag(tag);
			*a_str = p;
			return (RESULT_ERR);
		}

		/* input parameter value */

		for (;;) {
			c = *p;
			if (*p != '\0') {
				p++;
			}

			/* EOF inside of parameter value? */

			if (c == '\0') {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_PARMVAL_EOF,
					pname, tag->name,
					parent ? parent : "<<NONE>>");
				smlFreeTag(tag);
				free(pname);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* close of parameter value? */

			if (c == '"') {
				break;
			}

			if (strchr("\n", c) != NULL) {
				_smlLogMsg(LOG_MSG_ERR,
					ERR_SML_READTAG_PARMVAL_NL,
					pname, tag->name,
					parent ? parent : "<<NONE>>");
				free(pname);
				smlFreeTag(tag);
				*a_str = p;
				return (RESULT_ERR);
			}

			/* valid character - add to value if room left */

			if (pos < sizeof (name)-1) {
				name[pos] = (c&0xFF);
				pos++;
			}

			assert(pos < sizeof (name));
		}

		/* got the value */

		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_VALUE,
			pname, name, tag->name);

		pvalue = sml_XmlDecodeString(name);
		bzero(name, sizeof (name));
		pos = 0;

		parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
		bzero(parameter, sizeof (SML_PARAM));
		parameter->name = pname;
		parameter->value = pvalue;
		tag->params_num++;
		tag->params = (SML_PARAM *)
			realloc(tag->params,
				sizeof (SML_PARAM) *tag->params_num);
		(void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
			sizeof (SML_PARAM));

		free(parameter);
		if (c == '>') {
			break;
		}
	}

	/* finished processing this tag element entry */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_TAG_HEAD_DONE,
		tag->name, parent ? parent : "<<NULL>>");

	tag->tags = NULL;

	while (((r = _smlReadTag(&tmp_tag, &p, tag->name))
		== RESULT_OK) && (tmp_tag != NULL)) {
		tag->tags_num++;
		tag->tags = (SML_TAG *)realloc(tag->tags,
			sizeof (SML_TAG) *tag->tags_num);
		(void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
			sizeof (SML_TAG));
		free(tmp_tag);
	}

	c = *p;
	if (*p != '\0') {
		p++;
	}

	*r_tag = tag;
	*a_str = p;
	return (r);
}

/*
 * Name:	_smlWriteParamValue
 * Description:	XML Encode a plain text parameter value and write to datastream
 * Arguments:	ds - [RO, *RO] - (LU_DS)
 *			Handle to datastream to write parameter value to
 *		value - [RO, *RO] - (char *)
 *			Parameter value to be encoded and written
 * Returns:	int
 *			RESULT_OK - tag successfully read
 *			RESULT_ERR - problem reading tag
 */

static int
_smlWriteParamValue(char **a_str, char *value)
{
	char		*ns;
	char		*p;

	/* entry assertions */

	assert(a_str != (char **)NULL);
	assert(value != (char *)NULL);

	/* xml encode the plain text string */

	p = sml_XmlEncodeString(value);
	assert(p != (char *)NULL);

	/* write the xml encoded parameter value to the datastream */

	ns = sml_strPrintf("%s\"%s\"", *a_str ? *a_str : "", p);

	/* free up xml encoded value storage */

	free(p);

	if (ns == NULL) {
		return (RESULT_ERR);
	}

	if (*a_str != NULL) {
		free(*a_str);
	}
	*a_str = ns;

	/* return results */

	return (RESULT_OK);
}

static int
_smlWriteSimpleTag(char **a_str, SML_TAG *tag)
{
	int	r;
	int 	k;
	char	*ns;
	char	*np0;
	char	*np1;

	if (tag == NULL) {
		return (RESULT_OK);
	}

	if (*a_str == NULL) {
		*a_str = strdup("");
	}

	if (tag->params_num == 0) {
		if (tag->tags_num == 0) {
			ns = sml_strPrintf("%s<%s/>\n", *a_str, tag->name);
			free(*a_str);
			*a_str = ns;
			return (RESULT_OK);
		} else {
			ns = sml_strPrintf("%s<%s>\n", *a_str, tag->name);
			if (ns == NULL) {
				return (RESULT_ERR);
			}
			free(*a_str);
			*a_str = ns;
		}
	} else {
		ns = sml_strPrintf("%s<%s %s=", *a_str ? *a_str : "", tag->name,
				tag->params[0].name);
		if (ns == NULL) {
			return (RESULT_ERR);
		}
		free(*a_str);
		*a_str = ns;

		np0 = NULL;
		r = _smlWriteParamValue(&np0, tag->params[0].value);
		if ((np0 == NULL) || (r != RESULT_OK)) {
			return (RESULT_ERR);
		}

		ns = sml_strPrintf("%s%s", *a_str, np0);
		if (ns == NULL) {
			return (RESULT_ERR);
		}

		free(np0);
		free(*a_str);
		*a_str = ns;

		for (k = 1; k < tag->params_num; k++) {
			np0 = sml_strPrintf(" %s=", tag->params[k].name);
			if (np0 == NULL) {
				return (RESULT_ERR);
			}
			np1 = NULL;
			r = _smlWriteParamValue(&np1, tag->params[k].value);
			if ((np1 == NULL) || (r != RESULT_OK)) {
				return (RESULT_ERR);
			}

			ns = sml_strPrintf("%s%s%s", *a_str, np0, np1);
			if (ns == NULL) {
				return (RESULT_ERR);
			}

			free(np0);
			free(np1);
			free(*a_str);
			*a_str = ns;
		}

		if (tag->tags_num == 0) {
			np0 = sml_strPrintf("/>\n");
			if (np0 == NULL) {
				return (RESULT_ERR);
			}
			ns = sml_strPrintf("%s%s", *a_str, np0);
			if (ns == NULL) {
				return (RESULT_ERR);
			}
			free(np0);
			free(*a_str);
			*a_str = ns;
		} else {
			np0 = sml_strPrintf(">\n");
			if (np0 == NULL) {
				return (RESULT_ERR);
			}
			ns = sml_strPrintf("%s%s", *a_str, np0);
			if (ns == NULL) {
				return (RESULT_ERR);
			}
			free(np0);
			free(*a_str);
			*a_str = ns;
		}
	}

	for (k = 0; k < tag->tags_num; k++) {
		r = _smlWriteSimpleTag(a_str, &(tag->tags[k]));
		if (r != RESULT_OK) {
			return (r);
		}
	}

	if (tag->tags_num > 0) {
		np0 = sml_strPrintf("</%s>\n", tag->name);
		if (np0 == NULL) {
			return (RESULT_ERR);
		}
		ns = sml_strPrintf("%s%s", *a_str ? *a_str : "", np0);
		if (ns == NULL) {
			return (RESULT_ERR);
		}
		free(np0);
		free(*a_str);
		*a_str = ns;
	}

	return (RESULT_OK);
}

static void
_smlFreeTag(SML_TAG *tag)
{
	int k;

	/* entry assertions */

	assert(tag != SML_TAG__NULL);

	/* entry debugging info */

	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG,
		(unsigned long)tag,
		tag->name ? tag->name : "<<NONE>>",
		tag->params_num, tag->tags_num);

	for (k = 0; k < tag->params_num; k++) {
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_NAME,
			(unsigned long)(&tag->params[k]),
			(unsigned long)(tag->params[k].name),
			tag->params[k].name);
		free(tag->params[k].name);
		tag->params[k].name = (char *)NULL;
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_VALUE,
			(unsigned long)(&tag->params[k]),
			(unsigned long)(tag->params[k].value),
			tag->params[k].value);
		free(tag->params[k].value);
		tag->params[k].value = (char *)NULL;
	}

	for (k = 0; k < tag->tags_num; k++) {
		_smlFreeTag(&tag->tags[k]);
	}

	if (tag->name != NULL) {
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG_NAME,
			(unsigned long)tag->name, tag->name);
		free(tag->name);
		tag->name = NULL;
	}


	if (tag->params != NULL) {
		assert(tag->params_num > 0);
		bzero(tag->params, sizeof (SML_PARAM)*tag->params_num);
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAMS,
					(unsigned long)tag->params);
		free(tag->params);
		tag->params = NULL;
		tag->params_num = 0;
	}

	if (tag->tags != NULL) {
		assert(tag->tags_num > 0);
		bzero(tag->tags, sizeof (SML_TAG)*tag->tags_num);
		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAGS,
			(unsigned long)tag->tags);
		free(tag->tags);
		tag->tags = NULL;
		tag->tags_num = 0;
	}
}

/*
 * Name:	log_msg
 * Description:	Outputs messages to logging facility.
 * Scope:	public
 * Arguments:	type - the severity of the message
 *		out - where to output the message.
 *		fmt - the printf format, plus its arguments
 * Returns:	none
 */

/*PRINTFLIKE2*/
static void
_smlLogMsg(LogMsgType a_type, const char *a_format, ...)
{
	va_list	ap;
	size_t		vres = 0;
	char		bfr[1];
	char		*rstr = (char *)NULL;
	FILE	*out;
	char	*prefix;

	switch (a_type) {
	case LOG_MSG_ERR:
	default:
		out = stderr;
		prefix = MSG_LOG_ERROR;
		break;
	case LOG_MSG_WRN:
		out = stderr;
		prefix = MSG_LOG_WARNING;
		break;
	case LOG_MSG_INFO:
		out = stdout;
		prefix = NULL;
		break;
	case LOG_MSG_DEBUG:
		if (!smlGetVerbose()) {
			/* no debug messages if not verbose mode */
			return;
		}
		out = stderr;
		prefix = MSG_LOG_DEBUG;
		break;
	}

	if (prefix != NULL) {
		(void) fprintf(out, "%s: ", prefix);
	}

	/* determine size of the message in bytes */

	va_start(ap, a_format);
	vres = vsnprintf(bfr, 1, a_format, ap);
	va_end(ap);

	/* allocate storage to hold the message */

	rstr = (char *)malloc(vres+2);

	/* generate the results of the printf conversion */

	va_start(ap, a_format);
	vres = vsnprintf(rstr, vres+1, a_format, ap);
	va_end(ap);

	if (fprintf(out, "%s\n", rstr) < 0) {
		/*
		 * nothing output, try stderr as a
		 * last resort
		 */
		(void) fprintf(stderr, ERR_LOG_FAIL, a_format);
	}

	free(rstr);
}