xref: /titanic_50/usr/src/cmd/svr4pkg/libinst/sml.c (revision 5d0e1406420f52cc4d3d0543044034c4894b5865)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 /*
29  * Module:	sml.c
30  * Synopsis:	simplified markup language (SML) support
31  * Taxonomy:	project private
32  * Debug flag:	sml
33  * Description:
34  *
35  *   This module implements methods that support the processing of a
36  *   simplified markup language (SML). Objects that contain SML data
37  *   can be created and manipulated, and SML can be imported into
38  *   internal SML objects or exported from internal SML objects.
39  *
40  * Public Methods:
41  *
42  *   smlAddTag - Add new tag object into existing tag object
43  *   smlConvertStringToTag - Convert string into tag object
44  *   smlConvertTagToString - Convert a tag object into a string
45  *		representation of the XML
46  *   smlDbgPrintTag - Print a representation of an XML tag if debugging
47  *   smlDelParam - Delete a parameter from a tag object
48  *   smlDelTag - Delete element from tag object
49  *   smlDup - Duplicate a tag object
50  *   smlFindAndDelTag - Delete a tag if found in tag object
51  *   smlFreeTag - Free a tag object and all its contents when no
52  *		longer needed
53  *   smlFstatCompareEq - Compare file status information
54  *   smlGetElementName - Return a tag's element name
55  *   smlGetNumParams - Get number of parameters set in tag
56  *   smlGetParam - Get a parameter from a tag
57  *   smlGetParamF - Get a formatted parameter from a tag
58  *   smlGetParamByTag - Get a parameter by tag and index
59  *   smlGetParamByTagParam Get parameter given tag name, index,
60  *		parameter name, and value
61  *   smlGetParamName - Get the name of a tag parameter given its index
62  *   smlGetParam_r - Get a parameter from a tag into fixed buffer
63  *   smlGetTag - Get an element from a tag
64  *   smlGetTagByName - Get an element given a name and an index
65  *   smlGetTagByTagParam - Get element given tag name, index, parameter name,
66  *		and value
67  *   smlGetVerbose - get current verbose mode setting
68  *   smlLoadTagFromFile - Load a file into a tag object
69  *   smlNewTag - Create a new (empty) tag object
70  *   smlParamEq - Determine if parameter is equal to a specified value
71  *   smlParamEqF - Determine if parameter is equal to a specified value
72  *   smlPrintTag - Print a simple XML representation of a tag to stderr
73  *   smlReadOneTag - read one complete tag from a datastream
74  *   smlReadTagFromDs - read tag object from datastream
75  *   smlSetFileStatInfo - encode file status information into tag
76  *   smlSetVerbose - set/clear verbose mode for debugging output
77  *   smlSetParam - Set parameter value in tag object
78  *   smlSetParamF - Set parameter value in tag object
79  *   smlWriteTagToDs - Write an XML representation of a tag to a datastream
80  *   smlWriteTagToFd - Write an XML representation of a tag to an open file
81  *		descriptor
82  *   smlWriteTagToFile - Write an XML representation of a tag to a file
83  */
84 
85 /*
86  * Unix includes
87  */
88 
89 #include <locale.h>
90 #include <signal.h>
91 #include <stdio.h>
92 #include <stdlib.h>
93 #include <libintl.h>
94 #include <stdarg.h>
95 #include <string.h>
96 #include <unistd.h>
97 #include <sys/statvfs.h>
98 #include <errno.h>
99 #include <assert.h>
100 #include <sys/types.h>
101 #include <sys/stat.h>
102 #include <fcntl.h>
103 #include <limits.h>
104 #include <strings.h>
105 
106 /*
107  * liblu Includes
108  */
109 
110 #include "libinst.h"
111 #include "messages.h"
112 
113 /* Should be defined by cc -D */
114 #if	!defined(TEXT_DOMAIN)
115 #define	TEXT_DOMAIN "SYS_TEST"
116 #endif
117 
118 /*
119  * Private Method Forward Declarations
120  */
121 
122 /*PRINTFLIKE2*/
123 static void	_smlLogMsg(LogMsgType a_type, const char *a_format, ...);
124 
125 static int	_smlReadTag(SML_TAG **r_tag, char **a_str, char *parent);
126 
127 static int	_smlWriteSimpleTag(char **a_str,
128 				SML_TAG *tag);
129 
130 static int	_smlWriteParamValue(char **a_str, char *value);
131 
132 static void		_smlFreeTag(SML_TAG *tag);
133 
134 static char		*_sml_fileStatInfoTag = "File-Stat-Info";
135 
136 static boolean_t	verbose = B_FALSE;
137 
138 /*
139  *
140  * This definition controls the maximum size of any individual sml
141  * component, such as a tag name, tag *value*, etc. The code should
142  * someday be revised to dynamically allocate whatever memory is needed
143  * to hold such components while parsing, but that exercise is left for
144  * another day. Any component that exceeds this length is silently
145  * truncated...
146  */
147 
148 #define	MAX_SML_COMPONENT_LENGTH	16384
149 
150 /*
151  * Public Methods
152  */
153 
154 /*
155  * Name:	smlAddTag
156  * Description:	Add new tag object into existing tag object
157  * Arguments:	r_tag - [RO, *RW] - (SML_TAG **)
158  *			Pointer to handle to the tag object to update
159  *			The handle may be updated if the tag object is
160  *			moved in memory
161  *		a_index - [RO] - (int)
162  *			Add the tag after the "n"th tag in the tag object
163  *			-1 == add the tag to the end of the tag object
164  *			0 == add the tag to the beginning of the tag object
165  *		a_subTag - [RO, *RW] - (SML_TAG *)
166  *			The tag to add to 'tag'
167  * Returns:	SML_TAG *
168  *			The location within "r_tag" where "a_subTag"
169  *			has been added - this is the handle into the r_tag
170  *			object to the tag that was just added
171  * Errors:	If the tag object cannot be updated, the process exits
172  */
173 
174 SML_TAG *
175 smlAddTag(SML_TAG **r_tag, int a_index, SML_TAG *a_subTag)
176 {
177 	SML_TAG	*tag;
178 
179 	/* entry assertions */
180 
181 	assert(SML_TAG__ISVALID(a_subTag));
182 	assert(SML_TAG__R_ISVALID(r_tag));
183 
184 	/* if no tag to update specified, ignore request */
185 
186 	tag = *r_tag;
187 	if (tag == SML_TAG__NULL) {
188 		return (tag);
189 	}
190 
191 	/* entry debugging info */
192 
193 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ADD_TAG,
194 		a_subTag->name, tag->name);
195 
196 	/* if index is out of range or -1, append to tag object */
197 
198 	if ((a_index > tag->tags_num) || (a_index == -1)) {
199 		a_index = tag->tags_num;
200 	}
201 
202 	/* bump number of tags in tag object */
203 
204 	tag->tags_num++;
205 
206 	/* expand tag object to hold new subtag */
207 
208 	tag->tags = (SML_TAG *)realloc(tag->tags,
209 		sizeof (SML_TAG) * tag->tags_num);
210 
211 	/* if not appending, adjust tag object to hold new subtag */
212 
213 	if (a_index < (tag->tags_num - 1)) {
214 		(void) memmove(&(tag->tags[a_index + 1]), &(tag->tags[a_index]),
215 			sizeof (SML_TAG) * (tag->tags_num - a_index - 1));
216 	}
217 
218 	/* copy new subtag into correct location in tag object */
219 
220 	(void) memcpy(&(tag->tags[a_index]), a_subTag,
221 		sizeof (SML_TAG));
222 
223 	return (&(tag->tags[a_index]));
224 }
225 
226 /*
227  * Name:	smlDelTag
228  * Description:	Delete element from tag object
229  * Arguments:	tag - [RO, *RW] - (SML_TAG *)
230  *			The tag object to update
231  *		sub_tag - [RO, *RW] - (SML_TAG *)
232  *			Element to be removed from the tag object
233  * Returns:	void
234  *			The sub_tag is removed from the tag object
235  * NOTE:	The sub-tag and all elements contained within it are deallocated
236  *		the sub-tag is no longer valid when this method returns
237  */
238 
239 void
240 smlDelTag(SML_TAG *tag, SML_TAG *sub_tag)
241 {
242 	int	index;
243 
244 	/* entry assertions */
245 
246 	assert(SML_TAG__ISVALID(sub_tag));
247 
248 	/* if no tag to update specified, ignore request */
249 
250 	if (tag == SML_TAG__NULL) {
251 		return;
252 	}
253 
254 	/* entry debugging info */
255 
256 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DEL_TAG,
257 		sub_tag->name, tag->name);
258 
259 	/* if tag object is empty, ignore request */
260 
261 	if (tag->tags_num == 0) {
262 		return;
263 	}
264 
265 	/* determine index into tag object of element to remove */
266 	for (index = 0; index < tag->tags_num; index++) {
267 		if (sub_tag == &tag->tags[index]) {
268 			break;
269 		}
270 	}
271 
272 	/* if element not found in tag, ignore request */
273 
274 	if (index >= tag->tags_num) {
275 		return;
276 	}
277 
278 	/* free up the subtag to be deleted */
279 
280 	_smlFreeTag(sub_tag);
281 
282 	/*
283 	 * if not removing last element, collapse tag object removing
284 	 * target element
285 	 */
286 
287 	if (index < (tag->tags_num - 1)) {
288 		(void) memmove(&(tag->tags[index]), &(tag->tags[index + 1]),
289 			sizeof (SML_TAG) *(tag->tags_num - index - 1));
290 	}
291 
292 	/* one less tag object in tag */
293 
294 	tag->tags_num --;
295 
296 	/*
297 	 * If only one tag left, then delete entire tag structure
298 	 * otherwise reallocate removing unneeded entry
299 	 */
300 
301 	if (tag->tags_num > 0) {
302 		/* realloc removing last element in tag object */
303 
304 		tag->tags = (SML_TAG *)realloc(tag->tags,
305 			sizeof (SML_TAG) *tag->tags_num);
306 	} else {
307 		tag->tags = SML_TAG__NULL;
308 	}
309 }
310 
311 /*
312  * Name:	smlFreeTag
313  * Description:	Free a tag object and all its contents when no longer needed
314  * Arguments:	tag - [RO, *RW] - (SML_TAG *)
315  *			The tag object to be deleted
316  * Returns:	void
317  *			The tag object and all its contents are deallocated
318  */
319 
320 void
321 smlFreeTag(SML_TAG *tag)
322 {
323 	/* entry assertions */
324 
325 	assert(SML_TAG__ISVALID(tag));
326 
327 	/* entry debugging info */
328 
329 	if (tag->name != (char *)NULL) {
330 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_FREE_TAG,
331 			(unsigned long)tag, tag->name);
332 	}
333 
334 	/* free the tag object contents */
335 
336 	_smlFreeTag(tag);
337 
338 	/* free the tag object handle */
339 
340 	bzero(tag, sizeof (SML_TAG));
341 	free(tag);
342 }
343 
344 /*
345  * Name:	smlGetNumParams
346  * Synopsis:	Get number of parameters set in tag
347  * Description:	Return the number of parameters set in a tag
348  * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
349  *			The tag object to obtain the # params from
350  * Returns:	int
351  *			Number of parameters set in tag
352  *			0 = no parameters are set
353  */
354 
355 int
356 smlGetNumParams(SML_TAG *a_tag)
357 {
358 	return (a_tag ? a_tag->params_num : 0);
359 }
360 
361 
362 /*
363  * Name:	smlGetParam_r
364  * Description:	Get a parameter from a tag into a buffer of fixed size
365  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
366  *			The tag object to obtain the parameter from
367  *		name - [RO, *RO] - (char *)
368  *			Name of the parameter to retrieve
369  *		buf - [RO, *RW] - (char *)
370  *			Location of buffer to contain results
371  *		bufLen - [RO, *RO] - (int)
372  *			Maximum bytes available in buffer to contain results
373  * Returns:	void
374  */
375 
376 void
377 smlGetParam_r(SML_TAG *tag, char *name, char *buf, int bufLen)
378 {
379 	int	k;
380 
381 	/* entry assertions */
382 
383 	assert(name != (char *)NULL);
384 	assert(*name != '\0');
385 	assert(buf != (char *)NULL);
386 	assert(bufLen > 0);
387 
388 	/* terminate the buffer */
389 
390 	buf[0] = '\0';
391 	buf[bufLen-1] = '\0';
392 
393 	bzero(buf, bufLen);
394 
395 	/* if no tag specified, return NULL */
396 
397 	if (tag == SML_TAG__NULL) {
398 		return;
399 	}
400 
401 	/* if no parameters in tag, return NULL */
402 
403 	if (tag->params == NULL) {
404 		return;
405 	}
406 
407 	/* entry debugging info */
408 
409 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
410 		name, tag->name);
411 
412 	/* scan tag object looking for specified parameter */
413 
414 	for (k = 0; k < tag->params_num; k++) {
415 		assert(tag->params[k].name != (char *)NULL);
416 		assert(tag->params[k].value != (char *)NULL);
417 		if (streq(tag->params[k].name, name)) {
418 			_smlLogMsg(LOG_MSG_DEBUG,
419 				DBG_SML_GOT_PARAM,
420 				tag->name, name, tag->params[k].value);
421 			(void) strncpy(buf, tag->params[k].value, bufLen-1);
422 			return;
423 		}
424 	}
425 
426 	/* parameter not found - return */
427 }
428 
429 /*
430  * Name:	smlGetParam
431  * Description:	Get a parameter from a tag
432  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
433  *			The tag object to obtain the parameter from
434  *		name - [RO, *RO] - (char *)
435  *			Name of the parameter to retrieve
436  * Returns:	char *
437  *			Value of the specified parameter
438  *			== (char *)NULL if the parameter does not exist
439  * NOTE:    	Any parameter returned is placed in new storage for the
440  *		calling method. The caller must use 'free' to dispose
441  *		of the storage once the parameter is no longer needed.
442  */
443 
444 char *
445 smlGetParam(SML_TAG *tag, char *name)
446 {
447 	int	k;
448 
449 	/* entry assertions */
450 
451 	assert(name != (char *)NULL);
452 	assert(*name != '\0');
453 
454 	/* entry debugging info */
455 
456 	_smlLogMsg(LOG_MSG_DEBUG, "get param param <%s>", name);
457 
458 	/* if no tag specified, return NULL */
459 
460 	if (tag == SML_TAG__NULL) {
461 		return ((char *)NULL);
462 	}
463 
464 	/* if no parameters in tag, return NULL */
465 
466 	if (tag->params == NULL) {
467 		return ((char *)NULL);
468 	}
469 
470 	/* entry debugging info */
471 
472 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
473 		name, tag->name);
474 
475 	/* scan tag object looking for specified parameter */
476 
477 	for (k = 0; k < tag->params_num; k++) {
478 		assert(tag->params[k].name != (char *)NULL);
479 		assert(tag->params[k].value != (char *)NULL);
480 		if (streq(tag->params[k].name, name)) {
481 			_smlLogMsg(LOG_MSG_DEBUG,
482 				DBG_SML_GOT_PARAM,
483 				tag->name, name, tag->params[k].value);
484 			return (strdup(tag->params[k].value));
485 		}
486 	}
487 
488 	/* parameter not found - return NULL */
489 
490 	return ((char *)NULL);
491 }
492 
493 /*
494  * Name:	smlGetParamName
495  * Description:	Get the name of a tag parameter given its index
496  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
497  *			The tag object to obtain the parameter name from
498  *		index - [RO] - (int)
499  *			Index of parameter name to return
500  * Returns:	char *
501  *			Name of 'index'th parameter
502  *			== (char *)NULL if no such parameter exists in tag
503  * NOTE:    	Any parameter name returned is placed in new storage for the
504  *		calling method. The caller must use 'free' to dispose
505  *		of the storage once the parameter name is no longer needed.
506  */
507 
508 char *
509 smlGetParamName(SML_TAG *tag, int index)
510 {
511 	/* if no tag specified, return NULL */
512 
513 	if (tag == NULL) {
514 		return ((char *)NULL);
515 	}
516 
517 	/* entry debugging info */
518 
519 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_NAME,
520 		tag->name, index);
521 
522 	/* if no parameters in tag, return NULL */
523 
524 	if (tag->params == NULL) {
525 		return ((char *)NULL);
526 	}
527 
528 	/* if index not within range, return NULL */
529 
530 	if (index >= tag->params_num) {
531 		return ((char *)NULL);
532 	}
533 
534 	/* index within range - return parameter name */
535 
536 	assert(tag->params[index].name != (char *)NULL);
537 	assert(tag->params[index].value != (char *)NULL);
538 
539 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GOT_PARAM_NAME,
540 		tag->name, index, tag->params[index].name);
541 
542 	return (strdup(tag->params[index].name));
543 }
544 
545 /*
546  * Name:	smlGetParamByTag
547  * Synopsis:	Get a parameter value from a tag by name and index
548  * Description:	Call to look for a parameter value from a tag with
549  *		a given name with a parameter of a given name
550  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
551  *			The tag object to obtain the parameter
552  *		index - [RO] - (int)
553  *			Index of nth tag by name to look for
554  *		tagName - [RO, *RO] - (char *)
555  *			Name of tag to look for
556  *		paramName - [RO, *RO] - (char *)
557  *			Name of parameter to return value of
558  * Returns:	char *
559  *			== (char *)NULL - no parameter value set
560  *			!= (char *)NULL - value of parameter set
561  */
562 
563 char *
564 smlGetParamByTag(SML_TAG *tag, int index,
565 	char *tagName, char *paramName)
566 {
567 	SML_TAG	*rtag;
568 
569 	/* entry assertions */
570 
571 	assert(SML_TAG__ISVALID(tag));
572 	assert(tagName != (char *)NULL);
573 	assert(*tagName != '\0');
574 	assert(paramName != (char *)NULL);
575 	assert(*paramName != '\0');
576 
577 	/* entry debugging info */
578 
579 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_BY_TAG,
580 		tagName, index, paramName);
581 
582 	/* find the requested tag by name  and index */
583 
584 	rtag = smlGetTagByName(tag, index, tagName);
585 	if (rtag == SML_TAG__NULL) {
586 		return ((char *)NULL);
587 	}
588 
589 	return (smlGetParam(rtag, paramName));
590 }
591 
592 /*
593  * Name:	smlGetTagByTagParam
594  * Synopsis:	Get element given tag name, index, parameter name, and value
595  * Description:	Call to look for a tag with a given nae, that has a parameter
596  *		of a given name with a specified value
597  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
598  *			The tag object to obtain the element from
599  *		index - [RO] - (int)
600  *			Index of nth name to return
601  *		tagName - [RO, *RO] - (char *)
602  *			Tag name to look up
603  *		paramName - [RO, *RO] - (char *)
604  *			Parameter name to look up
605  *		paramValue - [RO, *RO] - (char *)
606  *			Parameter value to match
607  * Returns:	SML_TAG *
608  *			The 'index'th occurance of element 'name' with
609  *			a parameter 'name' with value specified
610  *			== SML_TAG__NULL if no such element exists
611  */
612 
613 SML_TAG *
614 smlGetTagByTagParam(SML_TAG *tag, int index,
615 	char *tagName, char *paramName, char *paramValue)
616 {
617 	int		ti;		/* tag structure index */
618 
619 	/* entry assertions */
620 
621 	assert(SML_TAG__ISVALID(tag));
622 	assert(tagName != (char *)NULL);
623 	assert(*tagName != '\0');
624 	assert(paramName != (char *)NULL);
625 	assert(*paramName != '\0');
626 	assert(paramValue != (char *)NULL);
627 	assert(*paramValue != '\0');
628 
629 	/* if tag has no elements, return NULL */
630 
631 	if (tag->tags == NULL) {
632 		return (SML_TAG__NULL);
633 	}
634 
635 	/*
636 	 * Search algorithm:
637 	 *  -> search tag structure; for each tag with element == "tagName":
638 	 *  -> search tag parameters; if parameter name == "paramName"
639 	 *  -> if parameter value != "paramValue"; to next tag
640 	 *  -> if parameter value == "paramValue":
641 	 *  -> if not the "index"th paramValue found; to next tag
642 	 *  -> return tag found
643 	 */
644 
645 	for (ti = 0; ti < tag->tags_num; ti++) {
646 		int	pi;	/* parameter structure index */
647 
648 		/* if tag element does not match, go on to next tag */
649 
650 		if (strcmp(tag->tags[ti].name, tagName)) {
651 			continue;
652 		}
653 
654 		/* element matches: search for specified parameter name/value */
655 
656 		for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
657 			assert(tag->tags[ti].params[pi].name != (char *)NULL);
658 			assert(tag->tags[ti].params[pi].value != (char *)NULL);
659 
660 			/* if parameter name doesnt match to next parameter */
661 
662 			if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
663 				continue;
664 			}
665 
666 			/* if parameter value doesnt match to next tag */
667 
668 			if (strcmp(tag->tags[ti].params[pi].value,
669 				paramValue)) {
670 				break;
671 			}
672 
673 			/*
674 			 * found element/paramname/paramvalue:
675 			 * -> if this is not the 'index'th one, go to next tag
676 			 */
677 
678 			if (index-- != 0) {
679 				break;
680 			}
681 
682 			/*
683 			 * found specified element/paramname/paramvalue:
684 			 * -> return the tag found
685 			 */
686 
687 			return (&tag->tags[ti]);
688 		}
689 
690 	}
691 
692 	/* no such element found - return NULL */
693 
694 	return (SML_TAG__NULL);
695 }
696 
697 /*
698  * Name:	smlGetParamByTagParam
699  * Synopsis:	Get parameter given tag name, index, parameter name, and value
700  * Description:	Call to return the value of a parameter from a tag of a
701  *		given name, with a parameter of a given name with a
702  *		specified value
703  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
704  *			The tag object to obtain the element from
705  *		index - [RO] - (int)
706  *			Index of nth name to return
707  *		tagName - [RO, *RO] - (char *)
708  *			Tag name to look up
709  *		paramName - [RO, *RO] - (char *)
710  *			Parameter name to look up
711  *		paramValue - [RO, *RO] - (char *)
712  *			Parameter value to match
713  *		paramReturn - [RO, *RO] - (char *)
714  *			Parameter name to return the value of
715  * Returns:	char *
716  *			The value of parameter 'paramReturn' from the
717  *			The 'index'th occurance of element 'name' with
718  *			a parameter 'name' with value specified
719  *			== (char *)NULL if no such parameter exists
720  */
721 
722 char *
723 smlGetParamByTagParam(SML_TAG *tag, int index,
724 	char *tagName, char *paramName, char *paramValue, char *paramReturn)
725 {
726 	int		ti;		/* tag structure index */
727 
728 	/* entry assertions */
729 
730 	assert(SML_TAG__ISVALID(tag));
731 	assert(tagName != (char *)NULL);
732 	assert(*tagName != '\0');
733 	assert(paramName != (char *)NULL);
734 	assert(*paramName != '\0');
735 	assert(paramValue != (char *)NULL);
736 	assert(*paramValue != '\0');
737 	assert(paramReturn != (char *)NULL);
738 	assert(*paramReturn != '\0');
739 
740 	/* if tag has no elements, return NULL */
741 
742 	if (tag->tags == NULL) {
743 		return ((char *)NULL);
744 	}
745 
746 	/*
747 	 * Search algorithm:
748 	 *  -> search tag structure; for each tag with element == "tagName":
749 	 *  -> search tag parameters; if parameter name == "paramName"
750 	 *  -> if parameter value != "paramValue"; to next tag
751 	 *  -> if parameter value == "paramValue":
752 	 *  -> if not the "index"th paramValue found; to next tag
753 	 *  -> return value of "paramReturn"
754 	 */
755 
756 	for (ti = 0; ti < tag->tags_num; ti++) {
757 		int	pi;	/* parameter structure index */
758 
759 		/* if tag element does not match, go on to next tag */
760 
761 		if (strcmp(tag->tags[ti].name, tagName)) {
762 			continue;
763 		}
764 
765 		/* element matches: search for specified parameter name/value */
766 
767 		for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
768 			assert(tag->tags[ti].params[pi].name != (char *)NULL);
769 			assert(tag->tags[ti].params[pi].value != (char *)NULL);
770 
771 			/* if parameter name doesnt match to next parameter */
772 
773 			if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
774 				continue;
775 			}
776 
777 			/* if parameter value doesnt match to next tag */
778 
779 			if (strcmp(tag->tags[ti].params[pi].value,
780 				paramValue)) {
781 				break;
782 			}
783 
784 			/*
785 			 * found element/paramname/paramvalue:
786 			 * -> if this is not the 'index'th one, go to next tag
787 			 */
788 
789 			if (index-- != 0) {
790 				break;
791 			}
792 
793 			/*
794 			 * found specified element/paramname/paramvalue:
795 			 * -> return parameter requested
796 			 */
797 
798 			return (smlGetParam(&tag->tags[ti], paramReturn));
799 		}
800 
801 	}
802 
803 	/* no such element found - return NULL */
804 
805 	return ((char *)NULL);
806 }
807 
808 /*
809  * Name:	smlGetElementName
810  * Description:	Return the name of a given tag
811  * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
812  *			The tag object to obtain the element name from
813  * Returns:	char *
814  *			Value of name of specified tag
815  * NOTE:    	Any name string returned is placed in new storage for the
816  *		calling method. The caller must use 'free' to dispose
817  *		of the storage once the name string is no longer needed.
818  */
819 
820 char *
821 smlGetElementName(SML_TAG *a_tag)
822 {
823 	/* entry assertions */
824 
825 	assert(SML_TAG__ISVALID(a_tag));
826 	assert(a_tag->name != (char *)NULL);
827 	assert(*a_tag->name != '\0');
828 
829 	/* return the tag name */
830 
831 	return (strdup(a_tag->name));
832 }
833 
834 /*
835  * Name:	smlGetTag
836  * Description:	Get an element from a tag
837  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
838  *			The tag object to obtain the element from
839  *		index - [RO] - (int)
840  *			Index of element to return
841  * Returns:	SML_TAG *
842  *			The 'index'th element from the specified tag
843  *			== SML_TAG__NULL if no such tag or element
844  */
845 
846 SML_TAG *
847 smlGetTag(SML_TAG *tag, int index)
848 {
849 	/* if no tag specified, return NULL */
850 
851 	if (tag == NULL) {
852 		return (SML_TAG__NULL);
853 	}
854 
855 	/* if tag has no elements, return NULL */
856 
857 	if (tag->tags == NULL) {
858 		return (SML_TAG__NULL);
859 	}
860 
861 	/* if index not within range, return NULL */
862 
863 	if (tag->tags_num <= index) {
864 		return (SML_TAG__NULL);
865 	}
866 
867 	/* index within range, return element specified */
868 
869 	assert(SML_TAG__ISVALID(&tag->tags[index]));
870 
871 	return (&tag->tags[index]);
872 }
873 
874 /*
875  * Name:	smlGetTagByName
876  * Description:	Get an element given a name and an index
877  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
878  *			The tag object to obtain the element from
879  *		index - [RO] - (int)
880  *			Index of nth name to return
881  *		name - [RO, *RO] - (char *)
882  *			Tag name to look up
883  * Returns:	SML_TAG *
884  *			The 'index'th occurance of element 'name'
885  *			== SML_TAG__NULL if no such element exists
886  */
887 
888 SML_TAG *
889 smlGetTagByName(SML_TAG *tag, int index, char *name)
890 {
891 	int k;
892 
893 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_TAG_BY_NAME, name, index);
894 
895 	/* if no tag specified, return NULL */
896 
897 	if (tag == NULL) {
898 		return (SML_TAG__NULL);
899 	}
900 
901 	/* if this tag is the one mentioned, return it */
902 
903 	if (streq(tag->name, name) && (index == 0)) {
904 		return (tag);
905 	}
906 
907 	/* if tag has no elements, return NULL */
908 
909 	if (tag->tags == NULL) {
910 		return (SML_TAG__NULL);
911 	}
912 
913 	/* if index out of range, return NULL */
914 
915 	if (tag->tags_num <= index) {
916 		return (SML_TAG__NULL);
917 	}
918 
919 	/* index within range - search for specified element */
920 
921 	for (k = 0; k < tag->tags_num; k++) {
922 		if (streq(tag->tags[k].name, name)) {
923 			if (index == 0) {
924 				assert(SML_TAG__ISVALID(&tag->tags[k]));
925 				return (&tag->tags[k]);
926 			} else {
927 				index--;
928 			}
929 		}
930 	}
931 
932 	/* no such element found - return NULL */
933 
934 	return (SML_TAG__NULL);
935 }
936 
937 /*
938  * Name:	smlConvertStringToTag
939  * Description:	Convert string into tag object
940  * Arguments:	err - [RO, *RW] (LU_ERR)
941  *			Error object - used to contain any errors encountered
942  *			and return those errors to this methods caller
943  *		r_tag - [RW, *RW] - (SML_TAG **)
944  *			Pointer to handle to place new tag object
945  *		str - [RO, *RO] - (char *)
946  *			String object to convert to tag object
947  * Returns:	int
948  *			RESULT_OK - string converted to tag object
949  *			RESULT_ERR - problem converting string to tag object
950  * NOTE:    	Any tag object returned is placed in new storage for the
951  *		calling method. The caller must use 'smlFreeTag' to dispose
952  *		of the storage once the tag object name is no longer needed.
953  */
954 
955 int
956 smlConvertStringToTag(SML_TAG **r_tag, char *str)
957 {
958 	int	r;
959 	SML_TAG	*tag = SML_TAG__NULL;
960 	SML_TAG	*tmp_tag;
961 
962 	/* entry assertions */
963 
964 	assert(SML_TAG__R_ISVALID(r_tag));
965 	assert(str != (char *)NULL);
966 	assert(*str != '\0');
967 
968 	tag = smlNewTag("tagfile");
969 
970 	for (;;) {
971 		r = _smlReadTag(&tmp_tag, &str, NULL);
972 		if (r != RESULT_OK) {
973 			smlFreeTag(tag);
974 			return (r);
975 		}
976 		if (tmp_tag == SML_TAG__NULL) {
977 			if (*str != '\0') {
978 				continue;
979 			}
980 			_smlLogMsg(LOG_MSG_DEBUG,
981 				DBG_SML_LOADED_TAGS_FROM_STR,
982 				(unsigned long)tag, tag->name);
983 			*r_tag = tag;
984 			return (RESULT_OK);
985 		}
986 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_IN_TOP_TAG,
987 			tmp_tag->name);
988 		tag->tags_num++;
989 		tag->tags = (SML_TAG *)realloc(tag->tags,
990 			sizeof (SML_TAG) *tag->tags_num);
991 		(void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
992 			sizeof (SML_TAG));
993 	}
994 }
995 
996 /*
997  * Name:	smlReadOneTag
998  * Description:	read one complete tag from a datastream
999  * Arguments:	err - [RO, *RW] (LU_ERR)
1000  *			Error object - used to contain any errors encountered
1001  *			and return those errors to this methods caller
1002  *		r_tag - [RW, *RW] - (SML_TAG **)
1003  *			Pointer to handle to place new tag object
1004  *			== SML_TAG__NULL if empty tag found (not an error)
1005  *		ds - [RO, *RO] - (LU_DS)
1006  *			Handle to datastream to read tag from
1007  * Returns:	int
1008  *			RESULT_OK - tag successfully read
1009  *			RESULT_ERR - problem reading tag
1010  * NOTE:    	Any tag object returned is placed in new storage for the
1011  *		calling method. The caller must use 'smlFreeTag' to dispose
1012  *		of the storage once the tag object name is no longer needed.
1013  */
1014 
1015 int
1016 smlReadOneTag(SML_TAG **r_tag, char *a_str)
1017 {
1018 	int	r;
1019 
1020 	/* entry assertions */
1021 
1022 	assert(SML_TAG__R_ISVALID(r_tag));
1023 	assert(a_str != (char *)NULL);
1024 
1025 	/* entry debugging info */
1026 
1027 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG, a_str);
1028 
1029 	/* reset return tag */
1030 
1031 	*r_tag = SML_TAG__NULL;
1032 
1033 	/* read tag from datastream, no parent tag to attach it to */
1034 
1035 	r = _smlReadTag(r_tag, &a_str, NULL);
1036 	if (r != RESULT_OK) {
1037 		_smlLogMsg(LOG_MSG_ERR, ERR_SML_CANNOT_READ_TAG);
1038 		return (r);
1039 	}
1040 
1041 	if (*r_tag != SML_TAG__NULL) {
1042 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ONE_TAG_READ,
1043 			(unsigned long)*r_tag,
1044 			(*r_tag)->name ? (*r_tag)->name : "<no name>");
1045 	} else {
1046 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG_NOTAG);
1047 	}
1048 
1049 	/* exit debugging info */
1050 
1051 	return (RESULT_OK);
1052 }
1053 
1054 /*
1055  * Name:	smlNewTag
1056  * Description:	Create a new (empty) tag object
1057  * Arguments:	name - [RO, *RO] - (char *)
1058  *			Name of tag; NULL to give the tag no name
1059  * Returns:	SML_TAG *
1060  *			Tag object created
1061  * NOTE:    	Any tag object returned is placed in new storage for the
1062  *		calling method. The caller must use 'smlFreeTag' to dispose
1063  *		of the storage once the tag object name is no longer needed.
1064  * Errors:	If the tag object cannot be created, the process exits
1065  */
1066 
1067 SML_TAG *
1068 smlNewTag(char *name)
1069 {
1070 	SML_TAG	*tag;
1071 
1072 	/* entry assertions */
1073 
1074 	assert((name == (char *)NULL) || (*name != '\0'));
1075 
1076 	/* entry debugging info */
1077 
1078 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATE_NEW_TAG_OBJECT,
1079 		name ? name : "<no name>");
1080 
1081 	/* allocate zeroed storage for the tag object */
1082 
1083 	tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
1084 	assert(tag != SML_TAG__NULL);
1085 
1086 	/* if name is provided, duplicate and assign it */
1087 
1088 	if (name != (char *)NULL) {
1089 		tag->name = strdup(name);
1090 	}
1091 
1092 	/* exit assertions */
1093 
1094 	assert(SML_TAG__ISVALID(tag));
1095 
1096 	/* exit debugging info */
1097 
1098 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATED_NEW_TAG_OBJECT,
1099 		(unsigned long)tag, name ? name : "<no name>");
1100 
1101 	return (tag);
1102 }
1103 
1104 /*
1105  * Name:	smlConvertTagToString
1106  * Description:	Convert a tag object into a string representation of the XML
1107  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
1108  *			The tag object to convert to a string
1109  * Returns:	char *
1110  *			String representation (in XML) of tag object
1111  *			== (char *)NULL if conversion is not possible
1112  * NOTE:    	Any string returned is placed in new storage for the
1113  *		calling method. The caller must use 'free' to dispose
1114  *		of the storage once the string is no longer needed.
1115  */
1116 
1117 char *
1118 smlConvertTagToString(SML_TAG *tag)
1119 {
1120 	char		*str = (char *)NULL;
1121 
1122 	/* entry assertions */
1123 
1124 	assert(SML_TAG__ISVALID(tag));
1125 
1126 	/* convert the tag object into the datastream */
1127 
1128 	(void) _smlWriteSimpleTag(&str, tag);
1129 
1130 	assert(str != (char *)NULL);
1131 	assert(*str != '\0');
1132 
1133 	/* return the results */
1134 
1135 	return (str);
1136 }
1137 
1138 /*
1139  * Name:	smlDbgPrintTag
1140  * Synopsis:	Print a representation of an XML tag if debugging
1141  * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
1142  *			Pointer to tag structure to dump
1143  *		a_format - [RO, RO*] (char *)
1144  *			printf-style format for debugging message to be output
1145  *		VARG_LIST - [RO] (?)
1146  *			arguments as appropriate to 'format' specified
1147  * Returns:	void
1148  *			If one of the debugging flags is set, the hexdump
1149  *			is output.
1150  */
1151 
1152 /*PRINTFLIKE2*/
1153 void
1154 smlDbgPrintTag(SML_TAG *a_tag, char *a_format, ...)
1155 {
1156 	va_list	ap;
1157 	size_t		vres = 0;
1158 	char		bfr[1];
1159 	char		*rstr = (char *)NULL;
1160 
1161 	/* entry assertions */
1162 
1163 	assert(a_format != (char *)NULL);
1164 	assert(*a_format != '\0');
1165 	assert(SML_TAG__ISVALID(a_tag));
1166 
1167 	/*
1168 	 * output the message header
1169 	 */
1170 
1171 	/* determine size of the message in bytes */
1172 
1173 	va_start(ap, a_format);
1174 	vres = vsnprintf(bfr, 1, a_format, ap);
1175 	va_end(ap);
1176 
1177 	assert(vres > 0);
1178 
1179 	/* allocate storage to hold the message */
1180 
1181 	rstr = (char *)calloc(1, vres+2);
1182 	assert(rstr != (char *)NULL);
1183 
1184 	/* generate the results of the printf conversion */
1185 
1186 	va_start(ap, a_format);
1187 	vres = vsnprintf(rstr, vres+1, a_format, ap);
1188 	va_end(ap);
1189 
1190 	assert(vres > 0);
1191 	assert(*rstr != '\0');
1192 
1193 	_smlLogMsg(LOG_MSG_DEBUG, "%s", rstr);
1194 	free(rstr);
1195 
1196 	/* convert the tag into a string to be printed */
1197 
1198 	rstr = smlConvertTagToString(a_tag);
1199 	if (rstr != (char *)NULL) {
1200 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_PRINTTAG, a_tag->name,
1201 			strlen(rstr), rstr);
1202 	}
1203 	free(rstr);
1204 }
1205 
1206 /*
1207  * Name:	smlDelParam
1208  * Description:	Delete a parameter from a tag object
1209  * Arguments:	tag - [RO, *RW] - (SML_TAG *)
1210  *			The tag object to delete the parameter from
1211  *		name - [RO, *RO] - (char *)
1212  *			The parameter to delete from the tag object
1213  * Returns:	void
1214  *			If the parameter exists, it is deleted from the tag
1215  */
1216 
1217 void
1218 smlDelParam(SML_TAG *tag, char *name)
1219 {
1220 	int k;
1221 
1222 	/* entry assertions */
1223 
1224 	assert(SML_TAG__ISVALID(tag));
1225 	assert(tag->name != (char *)NULL);
1226 	assert(name != NULL);
1227 	assert(*name != '\0');
1228 
1229 	/* entry debugging info */
1230 
1231 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DELETE_PARAM,
1232 		tag->name, name);
1233 
1234 	/* if tag has no parameters, nothing to delete */
1235 
1236 	if (tag->params == NULL) {
1237 		_smlLogMsg(LOG_MSG_DEBUG,
1238 			DBG_SML_DELETE_PARAM_NO_PARAMS);
1239 		return;
1240 	}
1241 
1242 	assert(tag->params_num > 0);
1243 
1244 	/* search the tag for the parameter */
1245 
1246 	for (k = 0; k < tag->params_num; k++) {
1247 		if (streq(tag->params[k].name, name)) {
1248 			break;
1249 		}
1250 	}
1251 
1252 	/* if the parameter was not found, nothing to delete */
1253 
1254 	if (k >= tag->params_num) {
1255 		_smlLogMsg(LOG_MSG_DEBUG,
1256 			DBG_SML_DELETE_PARAM_NOT_FOUND,
1257 			name);
1258 		return;
1259 	}
1260 
1261 	/* parameter found - indicate deleted */
1262 
1263 	assert(tag->params[k].name != (char *)NULL);
1264 	assert(tag->params[k].value != (char *)NULL);
1265 
1266 	_smlLogMsg(LOG_MSG_DEBUG,
1267 		DBG_SML_DELETE_PARAM_FOUND,
1268 		name, tag->params[k].value);
1269 
1270 	/* free up storage fro parameter */
1271 
1272 	free(tag->params[k].name);
1273 	free(tag->params[k].value);
1274 
1275 	/* if not at end, compact parameter storage */
1276 
1277 	if (k < (tag->params_num -1)) {
1278 		(void) memmove(&(tag->params[k]), &(tag->params[k + 1]),
1279 			sizeof (SML_PARAM) *(tag->params_num - k - 1));
1280 	}
1281 
1282 	/* one less parameter object in tag */
1283 
1284 	tag->params_num --;
1285 
1286 	/*
1287 	 * If only one parameter left, then delete entire parameter storage,
1288 	 * otherwise reallocate removing unneeded entry
1289 	 */
1290 
1291 	if (tag->params_num > 0) {
1292 		/* realloc removing last element in tag object */
1293 
1294 		tag->params = (SML_PARAM *)
1295 			realloc(tag->params,
1296 			sizeof (SML_PARAM) *tag->params_num);
1297 	} else {
1298 		tag->params = (SML_PARAM *)NULL;
1299 	}
1300 }
1301 
1302 /*
1303  * Name:	smlSetParamF
1304  * Description:	Set formatted parameter value in tag object
1305  * Arguments:	tag - [RO, *RW] - (SML_TAG *)
1306  *			The tag object to set the parameter in
1307  *		name - [RO, *RO] - (char *)
1308  *			The parameter to add to the tag object
1309  *		format - [RO, RO*] (char *)
1310  *			printf-style format to create parameter value from
1311  *		... - [RO] (?)
1312  *			arguments as appropriate to 'format' specified
1313  * Returns:	void
1314  *			The parameter value is set in the tag object
1315  *			according to the results of the format string
1316  *			and arguments
1317  */
1318 
1319 /*PRINTFLIKE3*/
1320 void
1321 smlSetParamF(SML_TAG *tag, char *name, char *format, ...)
1322 {
1323 	va_list	ap;
1324 	size_t		vres = 0;
1325 	char		*bfr = NULL;
1326 	char		fbfr[1];
1327 
1328 	/* entry assertions */
1329 
1330 	assert(SML_TAG__ISVALID(tag));
1331 	assert(name != (char *)NULL);
1332 	assert(*name != '\0');
1333 	assert(format != NULL);
1334 	assert(*format != '\0');
1335 
1336 	/* determine size of the parameter name in bytes */
1337 
1338 	va_start(ap, format);
1339 	vres = vsnprintf(fbfr, 1, format, ap);
1340 	va_end(ap);
1341 
1342 	assert(vres > 0);
1343 
1344 	/* allocate storage to hold the message */
1345 
1346 	bfr = (char *)calloc(1, vres+2);
1347 	assert(bfr != (char *)NULL);
1348 
1349 	/* generate the parameter name and store it in the allocated storage */
1350 
1351 	va_start(ap, format);
1352 	vres = vsnprintf(bfr, vres+1, format, ap);
1353 	va_end(ap);
1354 
1355 	assert(vres > 0);
1356 	assert(*bfr != '\0');
1357 
1358 	/* add the parameter to the tag */
1359 
1360 	smlSetParam(tag, name, bfr);
1361 
1362 	/* free up temporary storage and return */
1363 
1364 	free(bfr);
1365 }
1366 
1367 /*
1368  * Name:	smlGetParam
1369  * Description:	Get a format-generated parameter from a tag
1370  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
1371  *			The tag object to obtain the parameter from
1372  *		format - [RO, RO*] (char *)
1373  *			printf-style format for parameter name to be
1374  *			looked up to be formatted
1375  *		... - [RO] (?)
1376  *			arguments as appropriate to 'format' specified
1377  * Returns:	char *
1378  *			Value of the specified parameter
1379  *			== (char *)NULL if the parameter does not exist
1380  * NOTE:    	Any parameter returned is placed in new storage for the
1381  *		calling method. The caller must use 'free' to dispose
1382  *		of the storage once the parameter is no longer needed.
1383  */
1384 
1385 /*PRINTFLIKE2*/
1386 char *
1387 smlGetParamF(SML_TAG *tag, char *format, ...)
1388 {
1389 	va_list	ap;
1390 	size_t		vres = 0;
1391 	char		*bfr = NULL;
1392 	char		fbfr[1];
1393 	char		*p;
1394 
1395 	/* entry assertions */
1396 
1397 	assert(SML_TAG__ISVALID(tag));
1398 	assert(format != NULL);
1399 	assert(*format != '\0');
1400 
1401 	/* determine size of the parameter name in bytes */
1402 
1403 	va_start(ap, format);
1404 	vres = vsnprintf(fbfr, 1, format, ap);
1405 	va_end(ap);
1406 
1407 	assert(vres > 0);
1408 
1409 	/* allocate storage to hold the message */
1410 
1411 	bfr = (char *)calloc(1, vres+2);
1412 	assert(bfr != (char *)NULL);
1413 
1414 	/* generate the parameter name and store it in the allocated storage */
1415 
1416 	va_start(ap, format);
1417 	vres = vsnprintf(bfr, vres+1, format, ap);
1418 	va_end(ap);
1419 
1420 	assert(vres > 0);
1421 	assert(*bfr != '\0');
1422 
1423 	/* add the parameter to the tag */
1424 
1425 	p = smlGetParam(tag, bfr);
1426 
1427 	/* free up temporary storage and return */
1428 
1429 	free(bfr);
1430 
1431 	return (p);
1432 }
1433 
1434 /*
1435  * Name:	smlSetParam
1436  * Description:	Set parameter value in tag object
1437  * Arguments:	tag - [RO, *RW] - (SML_TAG *)
1438  *			The tag object to set the parameter in
1439  *		name - [RO, *RO] - (char *)
1440  *			The parameter to add to the tag object
1441  *		value - [RO, *RO] - (char *)
1442  *			The value of the parameter to set in the tag object
1443  * Returns:	void
1444  *			The parameter value is set in the tag object
1445  */
1446 
1447 void
1448 smlSetParam(SML_TAG *tag, char *name, char *value)
1449 {
1450 	SML_PARAM *parameter;
1451 
1452 	/* entry assertions */
1453 
1454 	assert(SML_TAG__ISVALID(tag));
1455 	assert(name != (char *)NULL);
1456 	assert(*name != '\0');
1457 	assert(value != (char *)NULL);
1458 
1459 	/* entry debugging info */
1460 
1461 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_SET_PARAM,
1462 		tag->name, name, value);
1463 
1464 	/* if parameters exist, see if modifying existing parameter */
1465 
1466 	if (tag->params != NULL) {
1467 		int k;
1468 		for (k = 0; k < tag->params_num; k++) {
1469 			assert(tag->params[k].name != (char *)NULL);
1470 			assert(tag->params[k].value != (char *)NULL);
1471 
1472 			/* if name does not match, skip */
1473 
1474 			if (!streq(tag->params[k].name, name)) {
1475 				continue;
1476 			}
1477 
1478 			/* found parameter - if value is same, leave alone */
1479 
1480 			if (streq(tag->params[k].value, value)) {
1481 				_smlLogMsg(LOG_MSG_DEBUG,
1482 					DBG_SML_SET_PARAM_LEAVE_ALONE,
1483 					tag->params[k].value);
1484 				return;
1485 			}
1486 
1487 			/* exists and has different value - change */
1488 
1489 			_smlLogMsg(LOG_MSG_DEBUG,
1490 				DBG_SML_SET_PARAM_MODIFY,
1491 				tag->params[k].value);
1492 				free(tag->params[k].value);
1493 				tag->params[k].value = strdup(value);
1494 				return;
1495 		}
1496 	}
1497 
1498 	/* not modifying existing - add new parameter */
1499 
1500 	_smlLogMsg(LOG_MSG_DEBUG,
1501 		DBG_SML_SET_PARAM_CREATE_NEW);
1502 
1503 	parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
1504 	bzero(parameter, sizeof (SML_PARAM));
1505 	parameter->name = strdup(name);
1506 	parameter->value = strdup(value);
1507 
1508 	tag->params_num++;
1509 	tag->params = (SML_PARAM *)realloc(tag->params,
1510 			sizeof (SML_PARAM) *tag->params_num);
1511 	(void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
1512 			sizeof (SML_PARAM));
1513 	free(parameter);
1514 }
1515 
1516 /*
1517  * Name:	smlParamEqF
1518  * Description:	Determine if parameter is equal to a specified formatted value
1519  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
1520  *			The tag object to look for the parameter to compare
1521  *		findTag - [RO, *RO] - (char *)
1522  *			Tag within tag object to look for the parameter in
1523  *		findParam - [RO, *RO] - (char *)
1524  *			Parameter within tag to look for
1525  *		format - [RO, RO*] (char *)
1526  *			printf-style format for value to be compared against
1527  *			parameter value
1528  *		... - [RO] (?)
1529  *			arguments as appropriate to 'format' specified to
1530  *			generate the value to compare parameter with
1531  * Returns:	boolean_t
1532  *			B_TRUE - the parameter exists and matches given value
1533  *			B_FALSE - parameter does not exist or does not match
1534  */
1535 
1536 /*PRINTFLIKE4*/
1537 boolean_t
1538 smlParamEqF(SML_TAG *tag, char *findTag, char *findParam, char *format, ...)
1539 {
1540 	va_list	ap;
1541 	size_t		vres = 0;
1542 	char		*bfr = NULL;
1543 	char		fbfr[1];
1544 	boolean_t	b;
1545 
1546 	/* entry assertions */
1547 
1548 	assert(SML_TAG__ISVALID(tag));
1549 	assert(format != NULL);
1550 	assert(*format != '\0');
1551 
1552 	/* determine size of the parameter value in bytes */
1553 
1554 	va_start(ap, format);
1555 	vres = vsnprintf(fbfr, 1, format, ap);
1556 	va_end(ap);
1557 
1558 	assert(vres > 0);
1559 
1560 	/* allocate storage to hold the message */
1561 
1562 	bfr = (char *)calloc(1, vres+2);
1563 	assert(bfr != (char *)NULL);
1564 
1565 	/* generate the parameter value and store it in the allocated storage */
1566 
1567 	va_start(ap, format);
1568 	vres = vsnprintf(bfr, vres+1, format, ap);
1569 	va_end(ap);
1570 
1571 	assert(vres > 0);
1572 	assert(*bfr != '\0');
1573 
1574 	/* add the parameter to the tag */
1575 
1576 	b = smlParamEq(tag, findTag, findParam, bfr);
1577 
1578 	/* free up temporary storage and return */
1579 
1580 	free(bfr);
1581 
1582 	return (b);
1583 }
1584 
1585 /*
1586  * Name:	smlParamEq
1587  * Description:	Determine if parameter is equal to a specified value
1588  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
1589  *			The tag object to look for the parameter to compare
1590  *		findTag - [RO, *RO] - (char *)
1591  *			Tag within tag object to look for the parameter in
1592  *		findParam - [RO, *RO] - (char *)
1593  *			Parameter within tag to look for
1594  *		str - [RO, *RO] - (char *)
1595  *			Value to compare parameter with
1596  * Returns:	boolean_t
1597  *			B_TRUE - the parameter exists and matches given value
1598  *			B_FALSE - parameter does not exist or does not match
1599  */
1600 
1601 boolean_t
1602 smlParamEq(SML_TAG *tag, char *findTag, char *findParam, char *str)
1603 {
1604 	SML_TAG	*rtag;
1605 	char		*rparm;
1606 	boolean_t	answer;
1607 
1608 	/* entry assertions */
1609 
1610 	assert(str != (char *)NULL);
1611 	assert(findParam != (char *)NULL);
1612 	assert(findTag != (char *)NULL);
1613 	assert(SML_TAG__ISVALID(tag));
1614 
1615 	/* look for the specified tag - if not found, return false */
1616 
1617 	rtag = smlGetTagByName(tag, 0, findTag);
1618 	if (rtag == SML_TAG__NULL) {
1619 		return (B_FALSE);
1620 	}
1621 
1622 	/* look for the specified parameter - if not found, return false */
1623 
1624 	rparm = smlGetParam(rtag, findParam);
1625 	if (rparm == (char *)NULL) {
1626 		return (B_FALSE);
1627 	}
1628 
1629 	/* parameter found - compare against given value */
1630 
1631 	answer = strcasecmp(str, rparm);
1632 
1633 	/* free up parameter storage */
1634 
1635 	free(rparm);
1636 
1637 	/* return results of comparison */
1638 
1639 	return (answer == 0 ? B_TRUE : B_FALSE);
1640 }
1641 
1642 /*
1643  * Name:	smlFindAndDelTag
1644  * Description:	Delete a tag if found in tag object
1645  * Arguments:	tag - [RO, *RW] - (SML_TAG *)
1646  *			The tag object to delete the tag from
1647  *		findTag - [RO, *RO] - (char *)
1648  *			Tag within tag object to delete
1649  * Returns:	boolean_t
1650  *			B_TRUE - tag found and deleted
1651  *			B_FALSE - tag not found
1652  */
1653 
1654 boolean_t
1655 smlFindAndDelTag(SML_TAG *tag, char *findTag)
1656 {
1657 	SML_TAG	*rtag = SML_TAG__NULL;
1658 
1659 	/* entry assertions */
1660 
1661 	assert(SML_TAG__ISVALID(tag));
1662 	assert(findTag != (char *)NULL);
1663 	assert(*findTag != '\0');
1664 
1665 	/* find the specified tag - if not found, return false */
1666 
1667 	rtag = smlGetTagByName(tag, 0, findTag);
1668 	if (rtag == SML_TAG__NULL) {
1669 		return (B_FALSE);
1670 	}
1671 
1672 	/* tag found - delete it and return true */
1673 
1674 	smlDelTag(tag, rtag);
1675 
1676 	return (B_TRUE);
1677 }
1678 
1679 /*
1680  * Name:	smlDup
1681  * Description:	Duplicate a tag object
1682  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
1683  *			The tag object to duplicate
1684  * Returns:	SML_TAG *
1685  *			A handle to a complete duplicate of the tag provided
1686  * NOTE:    	Any tag object returned is placed in new storage for the
1687  *		calling method. The caller must use 'smlFreeTag' to dispose
1688  *		of the storage once the tag object name is no longer needed.
1689  * Errors:	If the tag object cannot be duplicated, the process exits
1690  */
1691 
1692 SML_TAG *
1693 smlDup(SML_TAG *tag)
1694 {
1695 	SML_TAG	*rtag = SML_TAG__NULL;
1696 	int		i;
1697 
1698 	/* entry assertions */
1699 
1700 	assert(SML_TAG__ISVALID(tag));
1701 
1702 	/* allocate zeroed storage for the tag object */
1703 
1704 	rtag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
1705 	assert(rtag != SML_TAG__NULL);
1706 
1707 	/* duplicate all parameters of the tag */
1708 
1709 	rtag->name = (tag->name ? strdup(tag->name) : (char *)NULL);
1710 	rtag->params_num = tag->params_num;
1711 	if (tag->params != (SML_PARAM *)NULL) {
1712 		rtag->params = (SML_PARAM *)
1713 			calloc(1, sizeof (SML_PARAM)*rtag->params_num);
1714 		bzero(rtag->params, sizeof (SML_PARAM)*rtag->params_num);
1715 		for (i = 0; i < rtag->params_num; i++) {
1716 			rtag->params[i].name = tag->params[i].name ?
1717 				strdup(tag->params[i].name) :
1718 					(char *)NULL;
1719 			rtag->params[i].value = tag->params[i].value ?
1720 				strdup(tag->params[i].value) :
1721 					(char *)NULL;
1722 		}
1723 	}
1724 
1725 	/* duplicate all elements of the tag */
1726 
1727 	rtag->tags_num = tag->tags_num;
1728 
1729 	if (tag->tags != SML_TAG__NULL) {
1730 		rtag->tags = (SML_TAG *)
1731 			calloc(1, sizeof (SML_TAG)*rtag->tags_num);
1732 		bzero(rtag->tags, sizeof (SML_TAG)*rtag->tags_num);
1733 		for (i = 0; i < rtag->tags_num; i++) {
1734 			SML_TAG *stag;
1735 			stag = smlDup(&tag->tags[i]);
1736 			(void) memcpy(&rtag->tags[i], stag,
1737 				sizeof (SML_TAG));
1738 			free(stag);
1739 		}
1740 	}
1741 
1742 	/* exit assertions */
1743 
1744 	assert(SML_TAG__ISVALID(rtag));
1745 
1746 	/* return */
1747 
1748 	return (rtag);
1749 }
1750 
1751 /*
1752  * Name:	smlSetFileStatInfo
1753  * Description;	Given a file status structure and path name, encode the
1754  *		structure and place it and the name into the specified tag
1755  *		in a "_sml_fileStatInfoTag" (private) element
1756  * Arguments:	tag - [RO, *RO] - (SML_TAG *)
1757  *			The tag object to deposit the information into
1758  *		statbuf - [RO, *RO] - (struct stat *)
1759  *			Pointer to file status structure to encode
1760  *		path - [RO, *RO] - (char *)
1761  *			Pointer to path name of file to encode
1762  * Returns:	void
1763  *			The information is placed into the specified tag object
1764  */
1765 
1766 void
1767 smlSetFileStatInfo(SML_TAG **tag, struct stat *statbuf, char *path)
1768 {
1769 	SML_TAG	*rtag;
1770 
1771 	/* entry assertions */
1772 
1773 	assert(SML_TAG__R_ISVALID(tag));
1774 	assert(SML_TAG__ISVALID(*tag));
1775 	assert(statbuf != (struct stat *)NULL);
1776 
1777 	/* if stat info exists, delete it */
1778 
1779 	(void) smlFindAndDelTag(*tag, _sml_fileStatInfoTag);
1780 
1781 	/* create the file stat info inside of the top level tag */
1782 
1783 	assert(smlGetTagByName(*tag, 0, _sml_fileStatInfoTag)
1784 							== SML_TAG__NULL);
1785 	rtag = smlNewTag(_sml_fileStatInfoTag);
1786 	assert(SML_TAG__ISVALID(rtag));
1787 	(void) smlAddTag(tag, 0, rtag);
1788 	free(rtag);
1789 
1790 	/* obtain handle on newly created file stat info tag */
1791 
1792 	rtag = smlGetTagByName(*tag, 0, _sml_fileStatInfoTag);
1793 	assert(SML_TAG__ISVALID(rtag));
1794 
1795 	/* add file info as parameters to the tag */
1796 
1797 	if (path != (char *)NULL) {
1798 		smlSetParam(rtag, "st_path", path);
1799 	}
1800 
1801 	smlSetParamF(rtag, "st_ino", "0x%llx",
1802 		(unsigned long long)statbuf->st_ino);
1803 	smlSetParamF(rtag, "st_mode", "0x%llx",
1804 		(unsigned long long)statbuf->st_mode);
1805 	smlSetParamF(rtag, "st_mtime", "0x%llx",
1806 		(unsigned long long)statbuf->st_mtime);
1807 	smlSetParamF(rtag, "st_ctime", "0x%llx",
1808 		(unsigned long long)statbuf->st_ctime);
1809 	smlSetParamF(rtag, "st_size", "0x%llx",
1810 		(unsigned long long)statbuf->st_size);
1811 }
1812 
1813 /*
1814  * Name:	smlFstatCompareEQ
1815  * Description:	Given a file status structure and path name, look for the
1816  *		information placed into a tag object via smlSetFileStatInfo
1817  *		and if present compare the encoded information with the
1818  *		arguments provided
1819  * Arguments:	statbuf - [RO, *RO] - (struct stat *)
1820  *			Pointer to file status structure to compare
1821  *		tag - [RO, *RO] - (SML_TAG *)
1822  *			The tag object to compare against
1823  *		path - [RO, *RO] - (char *)
1824  *			Pointer to path name of file to compare
1825  * Returns:	boolean_t
1826  *			B_TRUE - both status structures are identical
1827  *			B_FALSE - the status structures are not equal
1828  */
1829 
1830 boolean_t
1831 smlFstatCompareEq(struct stat *statbuf, SML_TAG *tag, char *path)
1832 {
1833 	if (tag == SML_TAG__NULL) {
1834 		return (B_FALSE);
1835 	}
1836 
1837 	assert(SML_TAG__ISVALID(tag));
1838 
1839 	if (statbuf == (struct stat *)NULL) {
1840 		return (B_FALSE);
1841 	}
1842 
1843 	if (path != (char *)NULL) {
1844 		if (smlParamEq(tag,
1845 			_sml_fileStatInfoTag, "st_path", path) != B_TRUE) {
1846 			return (B_FALSE);
1847 		}
1848 	}
1849 
1850 	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ino",
1851 		"0x%llx", (unsigned long long)statbuf->st_ino) != B_TRUE) {
1852 		return (B_FALSE);
1853 	}
1854 
1855 	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mode",
1856 		"0x%llx", (unsigned long long)statbuf->st_mode) != B_TRUE) {
1857 		return (B_FALSE);
1858 	}
1859 
1860 	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mtime",
1861 		"0x%llx", (unsigned long long)statbuf->st_mtime) != B_TRUE) {
1862 		return (B_FALSE);
1863 	}
1864 
1865 	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ctime",
1866 		"0x%llx", (unsigned long long)statbuf->st_ctime) != B_TRUE) {
1867 		return (B_FALSE);
1868 	}
1869 
1870 	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_size",
1871 		"0x%llx", (unsigned long long)statbuf->st_size) != B_TRUE) {
1872 		return (B_FALSE);
1873 	}
1874 
1875 	return (B_TRUE);
1876 }
1877 
1878 /*
1879  * Name:	set_verbose
1880  * Description:	Turns on verbose output
1881  * Scope:	public
1882  * Arguments:	verbose = B_TRUE indicates verbose mode
1883  * Returns:	none
1884  */
1885 void
1886 smlSetVerbose(boolean_t setting)
1887 {
1888 	verbose = setting;
1889 }
1890 
1891 /*
1892  * Name:	get_verbose
1893  * Description:	Returns whether or not to output verbose messages
1894  * Scope:	public
1895  * Arguments:	none
1896  * Returns:	B_TRUE - verbose messages should be output
1897  */
1898 boolean_t
1899 smlGetVerbose()
1900 {
1901 	return (verbose);
1902 }
1903 
1904 /*
1905  * Name:	sml_strPrintf
1906  * Synopsis:	Create string from printf style format and arguments
1907  * Description:	Call to convert a printf style format and arguments into a
1908  *		string of characters placed in allocated storage
1909  * Arguments:	format - [RO, RO*] (char *)
1910  *			printf-style format for string to be formatted
1911  *		... - [RO] (?)
1912  *			arguments as appropriate to 'format' specified
1913  * Returns:	char *
1914  *			A string representing the printf conversion results
1915  * NOTE:    	Any string returned is placed in new storage for the
1916  *		calling method. The caller must use 'free' to dispose
1917  *		of the storage once the string is no longer needed.
1918  * Errors:	If the string cannot be created, the process exits
1919  */
1920 
1921 /*PRINTFLIKE1*/
1922 char *
1923 sml_strPrintf(char *a_format, ...)
1924 {
1925 	va_list	ap;
1926 	size_t		vres = 0;
1927 	char		bfr[1];
1928 	char		*rstr = (char *)NULL;
1929 
1930 	/* entry assertions */
1931 
1932 	assert(a_format != (char *)NULL);
1933 	assert(*a_format != '\0');
1934 
1935 	/* determine size of the message in bytes */
1936 
1937 	va_start(ap, a_format);
1938 	vres = vsnprintf(bfr, 1, a_format, ap);
1939 	va_end(ap);
1940 
1941 	assert(vres > 0);
1942 
1943 	/* allocate storage to hold the message */
1944 
1945 	rstr = (char *)calloc(1, vres+2);
1946 	assert(rstr != (char *)NULL);
1947 
1948 	/* generate the results of the printf conversion */
1949 
1950 	va_start(ap, a_format);
1951 	vres = vsnprintf(rstr, vres+1, a_format, ap);
1952 	va_end(ap);
1953 
1954 	assert(vres > 0);
1955 	assert(*rstr != '\0');
1956 
1957 	/* return the results */
1958 
1959 	return (rstr);
1960 }
1961 
1962 /*
1963  * Name:	sml_strPrintf_r
1964  * Synopsis:	Create string from printf style format and arguments
1965  * Description:	Call to convert a printf style format and arguments into a
1966  *		string of characters placed in allocated storage
1967  * Arguments:	a_buf - [RO, *RW] - (char *)
1968  *			- Pointer to buffer used as storage space for the
1969  *			  returned string created
1970  *		a_bufLen - [RO, *RO] - (int)
1971  *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
1972  *			  bytes will be placed in 'a_buf' - the returned
1973  *			  string is always null terminated
1974  *		a_format - [RO, RO*] (char *)
1975  *			printf-style format for string to be formatted
1976  *		VARG_LIST - [RO] (?)
1977  *			arguments as appropriate to 'format' specified
1978  * Returns:	void
1979  */
1980 
1981 /*PRINTFLIKE3*/
1982 void
1983 sml_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
1984 {
1985 	va_list	ap;
1986 	size_t		vres = 0;
1987 
1988 	/* entry assertions */
1989 
1990 	assert(a_format != (char *)NULL);
1991 	assert(*a_format != '\0');
1992 	assert(a_buf != (char *)NULL);
1993 	assert(a_bufLen > 1);
1994 
1995 	/* generate the results of the printf conversion */
1996 
1997 	va_start(ap, a_format);
1998 	vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
1999 	va_end(ap);
2000 
2001 	assert(vres > 0);
2002 	assert(vres < a_bufLen);
2003 
2004 	a_buf[a_bufLen-1] = '\0';
2005 }
2006 
2007 /*
2008  * Name:	sml_XmlEncodeString
2009  * Description:	Given a plain text string, convert that string into one that
2010  *		encoded using the XML character reference encoding format.
2011  * Arguments:	a_plain_text_string	- [RO, *RO] (char *)
2012  *			The plain text string to convert (encode)
2013  * Returns:	char *
2014  *			The encoded form of the plain text string provided
2015  * NOTE:    	Any string returned is placed in new storage for the
2016  *		calling method. The caller must use 'lu_memFree' to dispose
2017  *		of the storage once the string is no longer needed.
2018  */
2019 
2020 char *
2021 sml_XmlEncodeString(char *a_plainTextString)
2022 {
2023 	char *stringHead;	/* -> start of string containing encoded data */
2024 	long stringTail;	/* byte pos of first free byte in stringHead */
2025 	long stringLength;	/* total bytes allocd starting at stringHead */
2026 	char *p;		/* temp -> to retrieve bytes from src string */
2027 	long textLength = 0;	/* length of the string to convert */
2028 
2029 	/* entry assertions */
2030 
2031 	assert(a_plainTextString != (char *)NULL);
2032 
2033 	textLength = strlen(a_plainTextString);
2034 
2035 	/* Allocate initial string buffer to hold results */
2036 
2037 	stringLength = textLength*2;
2038 	stringTail = 0;
2039 	stringHead = (char *)calloc(1, (size_t)stringLength+2);
2040 	assert(stringHead != (char *)NULL);
2041 
2042 	/* Add in the encoded message text */
2043 
2044 	for (p = a_plainTextString; textLength > 0; p++, textLength--) {
2045 		/*
2046 		 * Must have at least 12 bytes: this must be at least the
2047 		 * maximum number of bytes that can be added for a single
2048 		 * byte as the last byte of the stream. Assuming the byte
2049 		 * needs to be encoded, it could be:
2050 		 * &#xxxxxxxx;\0
2051 		 * If not that many bytes left, grow the buffer.
2052 		 */
2053 
2054 		if ((stringLength-stringTail) < 12) {
2055 			stringLength += (textLength*2)+12;
2056 			stringHead =
2057 				realloc(stringHead,
2058 					(size_t)stringLength+2);
2059 			assert(stringHead != (char *)NULL);
2060 		}
2061 
2062 		/*
2063 		 * See if this byte is a 'printable 7-bit ascii value'.
2064 		 * If so just add it to the new string; otherwise, must
2065 		 * output an XML character value encoding for the byte.
2066 		 */
2067 
2068 		switch (*p) {
2069 		case '!':
2070 		case '#':
2071 		case '%':
2072 		case '\'':
2073 		case '(':
2074 		case ')':
2075 		case '*':
2076 		case '+':
2077 		case ',':
2078 		case '-':
2079 		case '.':
2080 		case '/':
2081 		case '0':
2082 		case '1':
2083 		case '2':
2084 		case '3':
2085 		case '4':
2086 		case '5':
2087 		case '6':
2088 		case '7':
2089 		case '8':
2090 		case '9':
2091 		case ':':
2092 		case ';':
2093 		case '<':
2094 		case '=':
2095 		case '>':
2096 		case '?':
2097 		case '@':
2098 		case 'A':
2099 		case 'B':
2100 		case 'C':
2101 		case 'D':
2102 		case 'E':
2103 		case 'F':
2104 		case 'G':
2105 		case 'H':
2106 		case 'I':
2107 		case 'J':
2108 		case 'K':
2109 		case 'L':
2110 		case 'M':
2111 		case 'N':
2112 		case 'O':
2113 		case 'P':
2114 		case 'Q':
2115 		case 'R':
2116 		case 'S':
2117 		case 'T':
2118 		case 'U':
2119 		case 'V':
2120 		case 'W':
2121 		case 'X':
2122 		case 'Y':
2123 		case 'Z':
2124 		case '[':
2125 		case ']':
2126 		case '^':
2127 		case '_':
2128 		case 'a':
2129 		case 'b':
2130 		case 'c':
2131 		case 'd':
2132 		case 'e':
2133 		case 'f':
2134 		case 'g':
2135 		case 'h':
2136 		case 'i':
2137 		case 'j':
2138 		case 'k':
2139 		case 'l':
2140 		case 'm':
2141 		case 'n':
2142 		case 'o':
2143 		case 'p':
2144 		case 'q':
2145 		case 'r':
2146 		case 's':
2147 		case 't':
2148 		case 'u':
2149 		case 'v':
2150 		case 'w':
2151 		case 'x':
2152 		case 'y':
2153 		case 'z':
2154 		case '{':
2155 		case '|':
2156 		case '}':
2157 		case '~':
2158 		case ' ':
2159 			/*
2160 			 * It is a printable 7-bit ascii character:
2161 			 * just add it to the end of the new string.
2162 			 */
2163 
2164 			stringHead[stringTail++] = *p;
2165 			break;
2166 		default:
2167 			/*
2168 			 * It is not a printable 7-bit ascii character:
2169 			 * add it as an xml character value encoding.
2170 			 */
2171 
2172 			stringTail += sprintf(&stringHead[stringTail], "&#%x;",
2173 					(*p)&0xFF);
2174 			break;
2175 		}
2176 	}
2177 
2178 	/* Terminate the new string */
2179 
2180 	stringHead[stringTail] = '\0';
2181 
2182 	/* realloc the string so it is only as big as it needs to be */
2183 
2184 	stringHead = realloc(stringHead, stringTail+1);
2185 	assert(stringHead != (char *)NULL);
2186 
2187 	return (stringHead);
2188 }
2189 
2190 /*
2191  * Name:	sml_XmlDecodeString
2192  * Description:	Given a string encoded using the XML character reference format,
2193  *		convert that string into a plain text (unencoded) string.
2194  * Arguments:	a_xml_encoded_string	- [RO, *RO] (char *)
2195  *			The XML encoded string to convert to plain text
2196  * Returns:	char *
2197  *			The unencoded (plain text) form of the encoded string
2198  * NOTE:    	Any string returned is placed in new storage for the
2199  *		calling method. The caller must use 'lu_memFree' to dispose
2200  *		of the storage once the string is no longer needed.
2201  */
2202 
2203 char *
2204 sml_XmlDecodeString(char *a_xmlEncodedString)
2205 {
2206 	char *s = NULL;		/* -> index into encoded bytes string */
2207 	char *d = NULL;		/* -> index into decoded bytes string */
2208 	char *rs = NULL;	/* -> string holding ref bytes allocated */
2209 	char *ri = NULL;	/* -> index into string holding reference */
2210 	long textLength = 0;	/* length of encoded string to decode */
2211 	unsigned long rv = 0;	/* temp to hold scanf results of byte conv */
2212 	char *i = NULL;		/* temp to hold strchr results */
2213 	char *stringHead = NULL;	/* -> plain test buffer */
2214 	ptrdiff_t tmpdiff;
2215 
2216 	/*
2217 	 * A finite state machine is used to convert the xml encoded string
2218 	 * into plain text. The states of the machine are defined below.
2219 	 */
2220 
2221 	int fsmsState = -1;	/* Finite state machine state */
2222 #define	fsms_text	0	/* Decoding plain text */
2223 #define	fsms_seenAmp	1	/* Found & */
2224 #define	fsms_seenPound	2	/* Found # following & */
2225 #define	fsms_collect	3	/* Collecting character reference bytes */
2226 
2227 	/* entry assertions */
2228 
2229 	assert(a_xmlEncodedString != (char *)NULL);
2230 
2231 	textLength = strlen(a_xmlEncodedString);
2232 
2233 	/*
2234 	 * Allocate string that can contain the decoded string.
2235 	 * Since decoding always results in a shorter string (bytes encoded
2236 	 * using the XML character reference are larger in the encoded form)
2237 	 * we can allocate a string the same size as the encoded string.
2238 	 */
2239 
2240 	stringHead = (char *)calloc(1, textLength+1);
2241 	assert(stringHead != (char *)NULL);
2242 
2243 	/*
2244 	 * Convert all bytes.
2245 	 */
2246 
2247 	/* Decoding plain text */
2248 	fsmsState = fsms_text;
2249 
2250 	for (s = a_xmlEncodedString, d = stringHead; textLength > 0;
2251 		s++, textLength--) {
2252 		switch (fsmsState) {
2253 		case fsms_text:	/* Decoding plain text */
2254 			if (rs != NULL) {
2255 				free(rs);
2256 				rs = NULL;
2257 				ri = NULL;
2258 			}
2259 			if (*s == '&') {
2260 				/* Found & */
2261 				fsmsState = fsms_seenAmp;
2262 				continue;
2263 			}
2264 			*d++ = *s;
2265 			continue;
2266 
2267 		case fsms_seenAmp:	/* Found & */
2268 			if (*s == '#') {
2269 				/* Found # following & */
2270 				fsmsState = fsms_seenPound;
2271 				continue;
2272 			}
2273 			fsmsState = fsms_text;	/* Decoding plain text */
2274 			*d++ = '&';
2275 			*d++ = *s;
2276 			continue;
2277 
2278 		case fsms_seenPound:		/* Found # following & */
2279 			i = strchr(s, ';');
2280 			if (i == NULL) {
2281 				/* Decoding plain text */
2282 				fsmsState = fsms_text;
2283 				*d++ = '&';
2284 				*d++ = '#';
2285 				*d++ = *s;
2286 				continue;
2287 			}
2288 			tmpdiff = (ptrdiff_t)i - (ptrdiff_t)s;
2289 			rs = (char *)calloc(1, tmpdiff + 1);
2290 			assert(rs != (char *)NULL);
2291 			ri = rs;
2292 			/* Collecting character reference bytes */
2293 			fsmsState = fsms_collect;
2294 
2295 			/*FALLTHRU*/
2296 
2297 		/* Collecting character reference bytes */
2298 		case fsms_collect:
2299 			if (*s != ';') {
2300 				switch (*s) {
2301 				case '0':
2302 				case '1':
2303 				case '2':
2304 				case '3':
2305 				case '4':
2306 				case '5':
2307 				case '6':
2308 				case '7':
2309 				case '8':
2310 				case '9':
2311 				case 'a':
2312 				case 'b':
2313 				case 'c':
2314 				case 'd':
2315 				case 'e':
2316 				case 'f':
2317 				case 'A':
2318 				case 'B':
2319 				case 'C':
2320 				case 'D':
2321 				case 'E':
2322 				case 'F':
2323 					*ri++ = *s;
2324 					break;
2325 				default:
2326 					*ri = '\0';
2327 					*d++ = '&';
2328 					*d++ = '#';
2329 					tmpdiff = (ptrdiff_t)ri - (ptrdiff_t)rs;
2330 					(void) strncpy(d, rs, tmpdiff-1);
2331 					*d++ = *s;
2332 					/* Decoding plain text */
2333 					fsmsState = fsms_text;
2334 					break;
2335 				}
2336 				continue;
2337 			}
2338 			*ri = '\0';
2339 			if (sscanf(rs, "%lx", &rv) != 1) {
2340 				*d++ = '?';
2341 			} else {
2342 				*d++ = (rv & 0xFF);
2343 			}
2344 			/* Decoding plain text */
2345 			fsmsState = fsms_text;
2346 		}
2347 	}
2348 
2349 	/* Done converting bytes - deallocate reference byte storage */
2350 
2351 	free(rs);
2352 
2353 	/* terminate the converted (plain text) string */
2354 
2355 	*d = '\0';
2356 
2357 	/* exit assertions */
2358 
2359 	assert(stringHead != (char *)NULL);
2360 
2361 	return (stringHead);
2362 }
2363 
2364 /*
2365  * Private Methods
2366  */
2367 
2368 /*
2369  * Name:	_smlReadTag
2370  * Description:	read complete tag from a datastream
2371  * Arguments:	err - [RO, *RW] (LU_ERR)
2372  *			Error object - used to contain any errors encountered
2373  *			and return those errors to this methods caller
2374  *		r_tag - [RW, *RW] - (SML_TAG **)
2375  *			Pointer to handle to place new tag object
2376  *			== SML_TAG__NULL if empty tag found (not an error)
2377  *		ds - [RO, *RO] - (LU_DS)
2378  *			Handle to datastream to read tag from
2379  *		parent - [RO, *RO] - (char *)
2380  *			Name for parent of tag (NONE if top of tag)
2381  * Returns:	int
2382  *			RESULT_OK - tag successfully read
2383  *			RESULT_ERR - problem reading tag
2384  * NOTE:    	Any tag object returned is placed in new storage for the
2385  *		calling method. The caller must use 'smlFreeTag' to dispose
2386  *		of the storage once the tag object name is no longer needed.
2387  * Errors:	If the tag object cannot be duplicated, the process exits
2388  */
2389 
2390 static int
2391 _smlReadTag(SML_TAG **r_tag, char **a_str, char *parent)
2392 {
2393 	int	r;
2394 	SML_TAG	*tag;
2395 	SML_TAG	*tmp_tag;
2396 	char	name[MAX_SML_COMPONENT_LENGTH];
2397 	int	pos = 0;
2398 	int	c;
2399 	char	*p = *a_str;
2400 
2401 	/* entry assertions */
2402 
2403 	assert(SML_TAG__R_ISVALID(r_tag));
2404 	assert(a_str != (char **)NULL);
2405 
2406 	/* entry debugging info */
2407 
2408 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_TAG,
2409 		parent ? parent : "<<TOP TAG>>");
2410 
2411 	/* reset return tag */
2412 
2413 	*r_tag = SML_TAG__NULL;
2414 
2415 	/* allocate zeroed storage for the tag object */
2416 
2417 	tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
2418 	assert(tag != SML_TAG__NULL);
2419 
2420 	/* reset name accumulator storage */
2421 
2422 	bzero(name, sizeof (name));
2423 
2424 	/* ignore delimters before tag */
2425 
2426 	for (;;) {
2427 		/* read tag character - handle failure/EOF */
2428 
2429 		if ((*p == '\0') || ((c = (*p++)) == '\0')) {
2430 			if (parent == NULL) {
2431 				_smlLogMsg(LOG_MSG_DEBUG,
2432 					DBG_SML_READTAG_EXPECTED_EOF,
2433 					p ? p : "?");
2434 				smlFreeTag(tag);
2435 				*a_str = p;
2436 				return (RESULT_OK);
2437 			}
2438 
2439 			/* EOF in middle of processing tag */
2440 
2441 			_smlLogMsg(LOG_MSG_ERR,
2442 				DBG_SML_READTAG_UNEXPECTED_EOF,
2443 				p ? p : "?");
2444 			smlFreeTag(tag);
2445 			*a_str = p;
2446 			return (RESULT_ERR);
2447 		}
2448 
2449 		/* if beginning of tag, break out */
2450 
2451 		if (c == '<') {
2452 			break;
2453 		}
2454 
2455 		/* not tag beginning: ignore delimiters if not inside tag yet */
2456 
2457 		if (parent == (char *)NULL) {
2458 			/* ignore delimters */
2459 
2460 			if (strchr(" \t", c) != (char *)NULL) {
2461 				continue;
2462 			}
2463 
2464 			/* on blank lines, return no tag object */
2465 
2466 			if (c == '\n') {
2467 				_smlLogMsg(LOG_MSG_DEBUG,
2468 					DBG_SML_READTAG_BLANKLINE,
2469 					p ? p : "?");
2470 				smlFreeTag(tag);
2471 				*a_str = p;
2472 				return (RESULT_OK);
2473 			}
2474 
2475 			/* invalid character before tag start */
2476 
2477 			_smlLogMsg(LOG_MSG_ERR, ERR_SML_READTAG_BAD_START_CHAR,
2478 				c, (unsigned int)c);
2479 			*a_str = p;
2480 			return (RESULT_ERR);
2481 		}
2482 	}
2483 
2484 	/*
2485 	 * all delimiters have been ignored and opening tag character seen;
2486 	 * process tag
2487 	 */
2488 
2489 	assert(c == '<');
2490 
2491 	c = *p;
2492 	if (*p != '\0') {
2493 		p++;
2494 	}
2495 
2496 	/* handle EOF after tag opening character found */
2497 
2498 	if (c == '\0') {
2499 		_smlLogMsg(LOG_MSG_ERR,
2500 			ERR_SML_EOF_BEFORE_TAG_NAME,
2501 			parent ? parent : "<<NONE>>");
2502 		smlFreeTag(tag);
2503 		*a_str = p;
2504 		return (RESULT_ERR);
2505 	}
2506 
2507 	/* is this a tag closure? */
2508 
2509 	if (c == '/') {
2510 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_START_CLOSE_TAG,
2511 			parent ? parent : "<<NONE>>");
2512 
2513 		for (;;) {
2514 			/* get next character of tag name */
2515 
2516 			c = *p;
2517 			if (*p != '\0') {
2518 				p++;
2519 			}
2520 
2521 			/* EOF inside tag name? */
2522 
2523 			if (c == '\0') {
2524 				_smlLogMsg(LOG_MSG_ERR,
2525 					ERR_SML_READTAG_CLOSE_TAG_EOF,
2526 					parent ? parent : "<<NONE>>");
2527 				smlFreeTag(tag);
2528 				*a_str = p;
2529 				return (RESULT_ERR);
2530 			}
2531 
2532 			/* tag close: break out of collection loop */
2533 
2534 			if (c == '>') {
2535 				break;
2536 			}
2537 
2538 			/* see if illegal character in tag name */
2539 
2540 			/* CSTYLED */
2541 			if (strchr("/ \t\n\":<?$'\\`!@#%^&*()+=|[]{};,", c)
2542 				!= NULL) {
2543 				_smlLogMsg(LOG_MSG_ERR,
2544 					ERR_SML_READTAG_CLOSE_TAG_ILLCHAR,
2545 					c, (unsigned int)c, name);
2546 				smlFreeTag(tag);
2547 				*a_str = p;
2548 				return (RESULT_ERR);
2549 			}
2550 
2551 			/* valid character - add to name if room left */
2552 
2553 			if (pos < sizeof (name)-1) {
2554 				name[pos] = (c&0xFF);
2555 				pos++;
2556 			}
2557 
2558 			assert(pos < sizeof (name));
2559 		}
2560 
2561 		/* close of tag found */
2562 
2563 		assert(c == '>');
2564 
2565 		/* is the tag empty? If so that's an error */
2566 
2567 		if (*name == '\0') {
2568 			_smlLogMsg(LOG_MSG_ERR,
2569 				ERR_SML_READTAG_CLOSE_EMPTY_TAG);
2570 			smlFreeTag(tag);
2571 			*a_str = p;
2572 			return (RESULT_ERR);
2573 		}
2574 
2575 		/* if no parent, a close tag outside of any open tag */
2576 
2577 		if (parent == (char *)NULL) {
2578 			_smlLogMsg(LOG_MSG_ERR,
2579 				ERR_SML_READTAG_CLOSE_NO_PARENT,
2580 				name);
2581 			smlFreeTag(tag);
2582 			*a_str = p;
2583 			return (RESULT_ERR);
2584 		}
2585 
2586 		/* if not close to current parent, error */
2587 
2588 		if (!streq(parent, name)) {
2589 			_smlLogMsg(LOG_MSG_ERR,
2590 				ERR_SML_READTAG_CLOSE_WRONG_TAG,
2591 				name, parent);
2592 			smlFreeTag(tag);
2593 			*a_str = p;
2594 			return (RESULT_ERR);
2595 		}
2596 
2597 		/* close of current tag found - success */
2598 
2599 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READTAG_CLOSE_TAG,
2600 			name);
2601 		smlFreeTag(tag);
2602 		*a_str = p;
2603 		return (RESULT_OK);
2604 	}
2605 
2606 	/* not starting a close tag */
2607 
2608 	assert(c != '/');
2609 	assert(c != '<');
2610 
2611 	/* at start of tag - input tag name */
2612 
2613 	bzero(name, sizeof (name));
2614 	pos = 0;
2615 
2616 	for (;;) {
2617 
2618 		/* EOF inside of tag name? */
2619 
2620 		if (c == '\0') {
2621 			_smlLogMsg(LOG_MSG_ERR,
2622 				ERR_SML_READTAG_TAG_EOF,
2623 				name, parent ? parent : "<<NONE>>");
2624 			smlFreeTag(tag);
2625 			*a_str = p;
2626 			return (RESULT_ERR);
2627 		}
2628 
2629 		/* if separator or end of line then tag name collected */
2630 
2631 		if (strchr(" >\t\n", c) != NULL) {
2632 			break;
2633 		}
2634 
2635 		/* see if illegal character in tag name */
2636 
2637 		/*CSTYLED*/
2638 		if (strchr("\":<>?$'\\`!@#%^&*()+=|[]{};,", c) != NULL) {
2639 			_smlLogMsg(LOG_MSG_ERR,
2640 				ERR_SML_READTAG_TAG_ILLCHAR,
2641 				c, (unsigned int)c, name);
2642 			smlFreeTag(tag);
2643 			*a_str = p;
2644 			return (RESULT_ERR);
2645 		}
2646 
2647 		/* close current tag? */
2648 
2649 		if (c == '/') {
2650 			/* get next character of tag name */
2651 
2652 			c = *p;
2653 			if (*p != '\0') {
2654 				p++;
2655 			}
2656 
2657 			/* tag close not found? */
2658 
2659 			if (c != '>') {
2660 				_smlLogMsg(LOG_MSG_ERR,
2661 					ERR_SML_READTAG_BADTAG_CLOSE,
2662 					name, parent ? parent : "<<NONE>>");
2663 				smlFreeTag(tag);
2664 				*a_str = p;
2665 				return (RESULT_ERR);
2666 			}
2667 
2668 			/* is the tag empty? If so that's an error */
2669 
2670 			if (*name == '\0') {
2671 				_smlLogMsg(LOG_MSG_ERR,
2672 					ERR_SML_READTAG_EMPTY_TAG,
2673 					parent ? parent : "<<NONE>>");
2674 				smlFreeTag(tag);
2675 				*a_str = p;
2676 				return (RESULT_ERR);
2677 			}
2678 
2679 			/* tag closed */
2680 
2681 			_smlLogMsg(LOG_MSG_DEBUG,
2682 				DBG_SML_READTAG_CLOSED_TAG,
2683 				name, parent ? parent : "<<NONE>>");
2684 
2685 			tag->name = strdup(name);
2686 			*r_tag = tag;
2687 			*a_str = p;
2688 			return (RESULT_OK);
2689 		}
2690 
2691 		/* valid character - add to name if room left */
2692 
2693 		if (pos < sizeof (name)-1) {
2694 			name[pos] = (c&0xFF);
2695 			pos++;
2696 		}
2697 
2698 		assert(pos < sizeof (name));
2699 
2700 		/* get next character to parse */
2701 
2702 		c = *p;
2703 		if (*p != '\0') {
2704 			p++;
2705 		}
2706 	}
2707 
2708 	/* have a valid tag name: <tagname */
2709 
2710 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_TAG_NAME,
2711 		name, parent ? parent : "<<NONE>>");
2712 
2713 	assert(*name != '\0');
2714 
2715 	/* place tag name inside of tag object */
2716 
2717 	tag->name = strdup(name);
2718 
2719 	/* clear out name accumulator to get parameters */
2720 
2721 	bzero(name, sizeof (name));
2722 	pos = 0;
2723 
2724 	/* input parameters */
2725 
2726 	if (c != '>')
2727 		for (;;) {
2728 
2729 		char *pname;
2730 		char *pvalue;
2731 		SML_PARAM *parameter;
2732 
2733 		/* pass spaces before parameter name */
2734 
2735 		for (;;) {
2736 
2737 			/* get next character of parameter name */
2738 
2739 			c = *p;
2740 			if (*p != '\0') {
2741 				p++;
2742 			}
2743 
2744 			/* EOF inside parameter name? */
2745 
2746 			if (c == '\0') {
2747 				_smlLogMsg(LOG_MSG_ERR,
2748 					ERR_SML_READTAG_PARM_EOF,
2749 					tag->name,
2750 					parent ? parent : "<<NONE>>");
2751 				smlFreeTag(tag);
2752 				*a_str = p;
2753 				return (RESULT_ERR);
2754 			}
2755 
2756 			/* if separator/end of line tag parameter collected */
2757 
2758 			if (strchr(" \t\n", c) != NULL) {
2759 				continue;
2760 			}
2761 
2762 			/* see if illegal character in parameter name */
2763 
2764 			/*CSTYLED*/
2765 			if (strchr("\":<?$'\\`!@#%^&*()+=|[]{};,.", c) !=
2766 				(char *)NULL) {
2767 				_smlLogMsg(LOG_MSG_ERR,
2768 					ERR_SML_READTAG_PARMNAME_ILLCHAR,
2769 					c, (unsigned int)c, name, tag->name,
2770 					parent ? parent : "<<NONE>>");
2771 				smlFreeTag(tag);
2772 				*a_str = p;
2773 				return (RESULT_ERR);
2774 			}
2775 
2776 			/* tag close found? */
2777 
2778 			if (c == '>') {
2779 				break;
2780 			}
2781 
2782 			/* close tag found ? */
2783 
2784 			if (c == '/') {
2785 				c = *p;
2786 				if (*p != '\0') {
2787 					p++;
2788 				}
2789 				if (c == '>') {
2790 					_smlLogMsg(LOG_MSG_DEBUG,
2791 						DBG_SML_TAG_ONLY,
2792 						tag->name);
2793 					*r_tag = tag;
2794 					*a_str = p;
2795 					return (RESULT_OK);
2796 				}
2797 
2798 				/* / not followed by > */
2799 				_smlLogMsg(LOG_MSG_ERR,
2800 					ERR_SML_READTAG_BADPARMNAME_CLOSE,
2801 					name, tag->name,
2802 					parent ? parent : "<<NONE>>");
2803 				smlFreeTag(tag);
2804 				*a_str = p;
2805 				return (RESULT_ERR);
2806 			}
2807 
2808 			/* valid character - add to name if room left */
2809 
2810 			if (pos < sizeof (name)-1) {
2811 				name[pos] = (c&0xFF);
2812 				pos++;
2813 			}
2814 
2815 			assert(pos < sizeof (name));
2816 			break;
2817 		}
2818 
2819 		if (c == '>') {
2820 			break;
2821 		}
2822 
2823 		/* input parameter name */
2824 
2825 		for (;;) {
2826 			c = *p;
2827 			if (*p != '\0') {
2828 				p++;
2829 			}
2830 
2831 			/* EOF inside of parameter name? */
2832 
2833 			if (c == '\0') {
2834 				_smlLogMsg(LOG_MSG_ERR,
2835 					ERR_SML_READTAG_PARM_EOF,
2836 					tag->name,
2837 					parent ? parent : "<<NONE>>");
2838 				smlFreeTag(tag);
2839 				*a_str = p;
2840 				return (RESULT_ERR);
2841 			}
2842 
2843 			/*CSTYLED*/
2844 			if (strchr("\t \n\":<>?$'\\`!@%^*()+|[]{},./", c) != NULL) {
2845 				_smlLogMsg(LOG_MSG_ERR,
2846 					ERR_SML_READTAG_PARMNAME_ILLCHAR,
2847 					c, (unsigned int)c, name, tag->name,
2848 					parent ? parent : "<<NONE>>");
2849 				smlFreeTag(tag);
2850 				*a_str = p;
2851 				return (RESULT_ERR);
2852 			}
2853 
2854 			/* name - value separator found ? */
2855 
2856 			if (c == '=') {
2857 				break;
2858 			}
2859 
2860 			/* valid character - add to name if room left */
2861 
2862 			if (pos < sizeof (name)-1) {
2863 				name[pos] = (c&0xFF);
2864 				pos++;
2865 			}
2866 
2867 			assert(pos < sizeof (name));
2868 		}
2869 
2870 		/* is the parameter name empty? If so that's an error */
2871 
2872 		if (*name == '\0') {
2873 			_smlLogMsg(LOG_MSG_ERR,
2874 				ERR_SML_READTAG_EMPTY_PARMNAME,
2875 				tag->name, parent ? parent : "<<NONE>>");
2876 			smlFreeTag(tag);
2877 			*a_str = p;
2878 			return (RESULT_ERR);
2879 		}
2880 
2881 		/* have a parameter name */
2882 
2883 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_NAME,
2884 			name, tag->name);
2885 
2886 		/* duplicate (save) parameter name */
2887 
2888 		pname = strdup(name);
2889 
2890 		/* clear out name accumulator to get parameters */
2891 
2892 		bzero(name, sizeof (name));
2893 		pos = 0;
2894 
2895 		c = *p;
2896 		if (*p != '\0') {
2897 			p++;
2898 		}
2899 
2900 		if (c != '"') {
2901 			_smlLogMsg(LOG_MSG_ERR,
2902 				ERR_SML_PARM_SEP_BAD,
2903 				c, (unsigned int)c);
2904 			free(pname);
2905 			smlFreeTag(tag);
2906 			*a_str = p;
2907 			return (RESULT_ERR);
2908 		}
2909 
2910 		/* input parameter value */
2911 
2912 		for (;;) {
2913 			c = *p;
2914 			if (*p != '\0') {
2915 				p++;
2916 			}
2917 
2918 			/* EOF inside of parameter value? */
2919 
2920 			if (c == '\0') {
2921 				_smlLogMsg(LOG_MSG_ERR,
2922 					ERR_SML_READTAG_PARMVAL_EOF,
2923 					pname, tag->name,
2924 					parent ? parent : "<<NONE>>");
2925 				smlFreeTag(tag);
2926 				free(pname);
2927 				*a_str = p;
2928 				return (RESULT_ERR);
2929 			}
2930 
2931 			/* close of parameter value? */
2932 
2933 			if (c == '"') {
2934 				break;
2935 			}
2936 
2937 			if (strchr("\n", c) != NULL) {
2938 				_smlLogMsg(LOG_MSG_ERR,
2939 					ERR_SML_READTAG_PARMVAL_NL,
2940 					pname, tag->name,
2941 					parent ? parent : "<<NONE>>");
2942 				free(pname);
2943 				smlFreeTag(tag);
2944 				*a_str = p;
2945 				return (RESULT_ERR);
2946 			}
2947 
2948 			/* valid character - add to value if room left */
2949 
2950 			if (pos < sizeof (name)-1) {
2951 				name[pos] = (c&0xFF);
2952 				pos++;
2953 			}
2954 
2955 			assert(pos < sizeof (name));
2956 		}
2957 
2958 		/* got the value */
2959 
2960 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_VALUE,
2961 			pname, name, tag->name);
2962 
2963 		pvalue = sml_XmlDecodeString(name);
2964 		bzero(name, sizeof (name));
2965 		pos = 0;
2966 
2967 		parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
2968 		bzero(parameter, sizeof (SML_PARAM));
2969 		parameter->name = pname;
2970 		parameter->value = pvalue;
2971 		tag->params_num++;
2972 		tag->params = (SML_PARAM *)
2973 			realloc(tag->params,
2974 				sizeof (SML_PARAM) *tag->params_num);
2975 		(void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
2976 			sizeof (SML_PARAM));
2977 
2978 		free(parameter);
2979 		if (c == '>') {
2980 			break;
2981 		}
2982 	}
2983 
2984 	/* finished processing this tag element entry */
2985 
2986 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_TAG_HEAD_DONE,
2987 		tag->name, parent ? parent : "<<NULL>>");
2988 
2989 	tag->tags = NULL;
2990 
2991 	while (((r = _smlReadTag(&tmp_tag, &p, tag->name))
2992 		== RESULT_OK) && (tmp_tag != NULL)) {
2993 		tag->tags_num++;
2994 		tag->tags = (SML_TAG *)realloc(tag->tags,
2995 			sizeof (SML_TAG) *tag->tags_num);
2996 		(void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
2997 			sizeof (SML_TAG));
2998 		free(tmp_tag);
2999 	}
3000 
3001 	c = *p;
3002 	if (*p != '\0') {
3003 		p++;
3004 	}
3005 
3006 	*r_tag = tag;
3007 	*a_str = p;
3008 	return (r);
3009 }
3010 
3011 /*
3012  * Name:	_smlWriteParamValue
3013  * Description:	XML Encode a plain text parameter value and write to datastream
3014  * Arguments:	ds - [RO, *RO] - (LU_DS)
3015  *			Handle to datastream to write parameter value to
3016  *		value - [RO, *RO] - (char *)
3017  *			Parameter value to be encoded and written
3018  * Returns:	int
3019  *			RESULT_OK - tag successfully read
3020  *			RESULT_ERR - problem reading tag
3021  */
3022 
3023 static int
3024 _smlWriteParamValue(char **a_str, char *value)
3025 {
3026 	char		*ns;
3027 	char		*p;
3028 
3029 	/* entry assertions */
3030 
3031 	assert(a_str != (char **)NULL);
3032 	assert(value != (char *)NULL);
3033 
3034 	/* xml encode the plain text string */
3035 
3036 	p = sml_XmlEncodeString(value);
3037 	assert(p != (char *)NULL);
3038 
3039 	/* write the xml encoded parameter value to the datastream */
3040 
3041 	ns = sml_strPrintf("%s\"%s\"", *a_str ? *a_str : "", p);
3042 
3043 	/* free up xml encoded value storage */
3044 
3045 	free(p);
3046 
3047 	if (ns == NULL) {
3048 		return (RESULT_ERR);
3049 	}
3050 
3051 	if (*a_str != NULL) {
3052 		free(*a_str);
3053 	}
3054 	*a_str = ns;
3055 
3056 	/* return results */
3057 
3058 	return (RESULT_OK);
3059 }
3060 
3061 static int
3062 _smlWriteSimpleTag(char **a_str, SML_TAG *tag)
3063 {
3064 	int	r;
3065 	int 	k;
3066 	char	*ns;
3067 	char	*np0;
3068 	char	*np1;
3069 
3070 	if (tag == NULL) {
3071 		return (RESULT_OK);
3072 	}
3073 
3074 	if (*a_str == NULL) {
3075 		*a_str = strdup("");
3076 	}
3077 
3078 	if (tag->params_num == 0) {
3079 		if (tag->tags_num == 0) {
3080 			ns = sml_strPrintf("%s<%s/>\n", *a_str, tag->name);
3081 			free(*a_str);
3082 			*a_str = ns;
3083 			return (RESULT_OK);
3084 		} else {
3085 			ns = sml_strPrintf("%s<%s>\n", *a_str, tag->name);
3086 			if (ns == NULL) {
3087 				return (RESULT_ERR);
3088 			}
3089 			free(*a_str);
3090 			*a_str = ns;
3091 		}
3092 	} else {
3093 		ns = sml_strPrintf("%s<%s %s=", *a_str ? *a_str : "", tag->name,
3094 				tag->params[0].name);
3095 		if (ns == NULL) {
3096 			return (RESULT_ERR);
3097 		}
3098 		free(*a_str);
3099 		*a_str = ns;
3100 
3101 		np0 = NULL;
3102 		r = _smlWriteParamValue(&np0, tag->params[0].value);
3103 		if ((np0 == NULL) || (r != RESULT_OK)) {
3104 			return (RESULT_ERR);
3105 		}
3106 
3107 		ns = sml_strPrintf("%s%s", *a_str, np0);
3108 		if (ns == NULL) {
3109 			return (RESULT_ERR);
3110 		}
3111 
3112 		free(np0);
3113 		free(*a_str);
3114 		*a_str = ns;
3115 
3116 		for (k = 1; k < tag->params_num; k++) {
3117 			np0 = sml_strPrintf(" %s=", tag->params[k].name);
3118 			if (np0 == NULL) {
3119 				return (RESULT_ERR);
3120 			}
3121 			np1 = NULL;
3122 			r = _smlWriteParamValue(&np1, tag->params[k].value);
3123 			if ((np1 == NULL) || (r != RESULT_OK)) {
3124 				return (RESULT_ERR);
3125 			}
3126 
3127 			ns = sml_strPrintf("%s%s%s", *a_str, np0, np1);
3128 			if (ns == NULL) {
3129 				return (RESULT_ERR);
3130 			}
3131 
3132 			free(np0);
3133 			free(np1);
3134 			free(*a_str);
3135 			*a_str = ns;
3136 		}
3137 
3138 		if (tag->tags_num == 0) {
3139 			np0 = sml_strPrintf("/>\n");
3140 			if (np0 == NULL) {
3141 				return (RESULT_ERR);
3142 			}
3143 			ns = sml_strPrintf("%s%s", *a_str, np0);
3144 			if (ns == NULL) {
3145 				return (RESULT_ERR);
3146 			}
3147 			free(np0);
3148 			free(*a_str);
3149 			*a_str = ns;
3150 		} else {
3151 			np0 = sml_strPrintf(">\n");
3152 			if (np0 == NULL) {
3153 				return (RESULT_ERR);
3154 			}
3155 			ns = sml_strPrintf("%s%s", *a_str, np0);
3156 			if (ns == NULL) {
3157 				return (RESULT_ERR);
3158 			}
3159 			free(np0);
3160 			free(*a_str);
3161 			*a_str = ns;
3162 		}
3163 	}
3164 
3165 	for (k = 0; k < tag->tags_num; k++) {
3166 		r = _smlWriteSimpleTag(a_str, &(tag->tags[k]));
3167 		if (r != RESULT_OK) {
3168 			return (r);
3169 		}
3170 	}
3171 
3172 	if (tag->tags_num > 0) {
3173 		np0 = sml_strPrintf("</%s>\n", tag->name);
3174 		if (np0 == NULL) {
3175 			return (RESULT_ERR);
3176 		}
3177 		ns = sml_strPrintf("%s%s", *a_str ? *a_str : "", np0);
3178 		if (ns == NULL) {
3179 			return (RESULT_ERR);
3180 		}
3181 		free(np0);
3182 		free(*a_str);
3183 		*a_str = ns;
3184 	}
3185 
3186 	return (RESULT_OK);
3187 }
3188 
3189 static void
3190 _smlFreeTag(SML_TAG *tag)
3191 {
3192 	int k;
3193 
3194 	/* entry assertions */
3195 
3196 	assert(tag != SML_TAG__NULL);
3197 
3198 	/* entry debugging info */
3199 
3200 	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG,
3201 		(unsigned long)tag,
3202 		tag->name ? tag->name : "<<NONE>>",
3203 		tag->params_num, tag->tags_num);
3204 
3205 	for (k = 0; k < tag->params_num; k++) {
3206 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_NAME,
3207 			(unsigned long)(&tag->params[k]),
3208 			(unsigned long)(tag->params[k].name),
3209 			tag->params[k].name);
3210 		free(tag->params[k].name);
3211 		tag->params[k].name = (char *)NULL;
3212 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_VALUE,
3213 			(unsigned long)(&tag->params[k]),
3214 			(unsigned long)(tag->params[k].value),
3215 			tag->params[k].value);
3216 		free(tag->params[k].value);
3217 		tag->params[k].value = (char *)NULL;
3218 	}
3219 
3220 	for (k = 0; k < tag->tags_num; k++) {
3221 		_smlFreeTag(&tag->tags[k]);
3222 	}
3223 
3224 	if (tag->name != NULL) {
3225 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG_NAME,
3226 			(unsigned long)tag->name, tag->name);
3227 		free(tag->name);
3228 		tag->name = NULL;
3229 	}
3230 
3231 
3232 	if (tag->params != NULL) {
3233 		assert(tag->params_num > 0);
3234 		bzero(tag->params, sizeof (SML_PARAM)*tag->params_num);
3235 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAMS,
3236 					(unsigned long)tag->params);
3237 		free(tag->params);
3238 		tag->params = NULL;
3239 		tag->params_num = 0;
3240 	}
3241 
3242 	if (tag->tags != NULL) {
3243 		assert(tag->tags_num > 0);
3244 		bzero(tag->tags, sizeof (SML_TAG)*tag->tags_num);
3245 		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAGS,
3246 			(unsigned long)tag->tags);
3247 		free(tag->tags);
3248 		tag->tags = NULL;
3249 		tag->tags_num = 0;
3250 	}
3251 }
3252 
3253 /*
3254  * Name:	log_msg
3255  * Description:	Outputs messages to logging facility.
3256  * Scope:	public
3257  * Arguments:	type - the severity of the message
3258  *		out - where to output the message.
3259  *		fmt - the printf format, plus its arguments
3260  * Returns:	none
3261  */
3262 
3263 /*PRINTFLIKE2*/
3264 static void
3265 _smlLogMsg(LogMsgType a_type, const char *a_format, ...)
3266 {
3267 	va_list	ap;
3268 	size_t		vres = 0;
3269 	char		bfr[1];
3270 	char		*rstr = (char *)NULL;
3271 	FILE	*out;
3272 	char	*prefix;
3273 
3274 	switch (a_type) {
3275 	case LOG_MSG_ERR:
3276 	default:
3277 		out = stderr;
3278 		prefix = MSG_LOG_ERROR;
3279 		break;
3280 	case LOG_MSG_WRN:
3281 		out = stderr;
3282 		prefix = MSG_LOG_WARNING;
3283 		break;
3284 	case LOG_MSG_INFO:
3285 		out = stdout;
3286 		prefix = NULL;
3287 		break;
3288 	case LOG_MSG_DEBUG:
3289 		if (!smlGetVerbose()) {
3290 			/* no debug messages if not verbose mode */
3291 			return;
3292 		}
3293 		out = stderr;
3294 		prefix = MSG_LOG_DEBUG;
3295 		break;
3296 	}
3297 
3298 	if (prefix != NULL) {
3299 		(void) fprintf(out, "%s: ", prefix);
3300 	}
3301 
3302 	/* determine size of the message in bytes */
3303 
3304 	va_start(ap, a_format);
3305 	vres = vsnprintf(bfr, 1, a_format, ap);
3306 	va_end(ap);
3307 
3308 	/* allocate storage to hold the message */
3309 
3310 	rstr = (char *)malloc(vres+2);
3311 
3312 	/* generate the results of the printf conversion */
3313 
3314 	va_start(ap, a_format);
3315 	vres = vsnprintf(rstr, vres+1, a_format, ap);
3316 	va_end(ap);
3317 
3318 	if (fprintf(out, "%s\n", rstr) < 0) {
3319 		/*
3320 		 * nothing output, try stderr as a
3321 		 * last resort
3322 		 */
3323 		(void) fprintf(stderr, ERR_LOG_FAIL, a_format);
3324 	}
3325 
3326 	free(rstr);
3327 }
3328