OpenSolaris_b135/lib/libpkg/common/pkgstr.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2009 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 */


/*
 * Module:	pkgstr.c
 * Synopsis:	general string services
 * Taxonomy:	project private
 * Debug Flag:	str
 * Description:
 *
 *   This module implements general string utility services
 *
 * Public Methods:
 *
 *   pkgstrAddToken - Add a token to a string
 *   pkgstrContainsToken - Determine if a string contains a specified token
 *   pkgstrConvertPathToBasename - Return copy of base name in path string
 *   pkgstrConvertPathToDirname - Return copy of directory name in path string
 *   pkgstrConvertUllToTimeString_r - convert unsigned long long to time string
 *   pkgstrExpandTokens - Expand tokens from string appending tokens to another
 *   pkgstrGetToken - Get a token from a string
 *   pkgstrGetToken_r - Get a token from a string into a fixed buffer
 *   pkgstrLocatePathBasename - Locate position of base name in path string
 *   pkgstrNumTokens - Determine number of tokens in string
 *   pkgstrPrintf - Create a string from a printf style format and arguments
 *   pkgstrPrintf_r - Create a string from a printf style format and arguments
 *			into a fixed buffer
 *   pkgstrRemoveToken - Remove a token from a string
 *   pkgstrRemoveLeadingWhitespace - remove leading whitespace from string
 *   pkgstrScaleNumericString - Convert unsigned long long to human
 *	readable form
 */

/*
 * Unix Includes
 */

#define	__EXTENSIONS__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <limits.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <libintl.h>
#include <ctype.h>
#include <unistd.h>
#include <strings.h>
#include <stdarg.h>

/*
 * pkglib Includes
 */

#include "pkglib.h"
#include "pkgstrct.h"
#include "libintl.h"
#include "pkglocale.h"

/*
 * External definitions
 */

/*
 * Public methods
 */

/*
 * Name:	pkgstrRemoveLeadingWhitespace
 * Synopsis:	Remove leading whitespace from string
 * Description:	Remove all leading whitespace characters from a string
 * Arguments:	a_str - [RO, *RW] - (char **)
 *			Pointer to handle to string (in allocated storage) to
 *			remove all leading whitespace from
 * Returns:	void
 *			The input string is modified as follows:
 *			== (char *)NULL:
 *				- input string was (char *)NULL
 *				- input string is all whitespace
 *			!= (char *)NULL:
 *				- copy of input string with leading
 *				  whitespace removed
 * CAUTION:	The input string must be allocated space (via mem* or
 *		pkgstr* methods) - it must not be a static or inline
 *		character string
 * NOTE:	The input string a_str will be freed with 'free'
 *		if it is all whitespace, or if it contains any leading
 *		whitespace characters
 * 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
 */

void
pkgstrRemoveLeadingWhitespace(char **a_str)
{
	char	*o_str;

	/* entry assertions */

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

	/* if string is null, just return */

	if (*a_str == (char *)NULL) {
		return;
	}
	o_str = *a_str;

	/* if string is empty, deallocate and return NULL */

	if (*o_str == '\0') {
		/* free string - handle is reset to NULL by free */
		free(*a_str);
		*a_str = (char *)NULL;
		return;
	}

	/* if first character is not a space, just return */

	if (!isspace(*o_str)) {
		return;
	}

	/* advance past all space characters */

	while ((*o_str != '\0') && (isspace(*o_str))) {
		o_str++;
	}

	/* if string was all space characters, deallocate and return NULL */

	if (*o_str == '\0') {
		/* free string - *a_str is reset to NULL by free */
		free(*a_str);
		*a_str = (char *)NULL;
		return;
	}

	/* have non-space/null byte, return dup, deallocate original */

	o_str = strdup(o_str);
	assert(o_str != (char *)NULL);
	if (o_str != (char *)NULL) {
		free(*a_str);
		*a_str = o_str;
	}
}

unsigned long
pkgstrNumTokens(char *a_string, char *a_separators)
{
	int	index;

	if (a_string == (char *)NULL) {
		return (0);
	}

	if (*a_string == '\0') {
		return (0);
	}

	for (index = 0 ; ; index ++) {
		char *p;

		p = pkgstrGetToken((char *)NULL, a_string, index, a_separators);
		if (p == (char *)NULL) {
			return (index);
		}
		free(p);
	}
}

/*
 * Name:	pkgstrPrintf_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
pkgstrPrintf_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:	pkgstrPrintf
 * 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
 *		VARG_LIST - [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 *
pkgstrPrintf(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);
	assert(vres < LINE_MAX);

	/* allocate storage to hold the message */

	rstr = (char *)calloc(1, vres+2);
	assert(rstr != (char *)NULL);
	if (rstr == (char *)NULL) {
		return ((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(vres < LINE_MAX);
	assert(*rstr != '\0');

	/* return the results */

	return (rstr);
}

/*
 * Name:	pkgstrExpandTokens
 * Synopsis:	Expand tokens from string appending tokens to another
 * Description:	Given a string and a list of one or more separators,
 *		expand each token from the string and append those tokens
 *		to a string that is in allocated space - create new string
 *		if no string to append to exists.
 * Arguments:	a_old - [RO, *RW] - (char **)
 *			- Pointer to handle to string to append token to
 *			  == (char *)NULL - new string is created
 *		a_separator - [RO, *RO] - (char *)
 *			- separator to end tokens returned
 *		a_separators - [RO, *RO] - (char *)
 *			- String containing one or more characters that
 *			  can separate one "token" from a_string from another
 * Returns:	void
 * NOTE:    	Any token string returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the token string is no longer needed.
 */

void
pkgstrExpandTokens(char **a_old, char *a_string, char a_separator,
	char *a_separators)
{
	int		i;
	char		sep[2] = {'\0', '\0'};

	/* convert single separator character into character string */

	sep[0] = a_separator;

	/*
	 * iterate extracting tokens from the source string and adding
	 * those tokens to the target string when the tokens are not
	 * already present in the target string
	 */

	for (i = 0; ; i++) {
		char	*p;

		/* extract the next matching token from the source string */

		p = pkgstrGetToken((char *)NULL, a_string, i, a_separators);

		/* return if no token is available */

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

		/*
		 * obtained token from source string: if the token is not
		 * in the target string, add the token to the target string
		 */

		if (pkgstrContainsToken(*a_old, p, sep) == B_FALSE) {
			pkgstrAddToken(a_old, p, *sep);
		}

		/* free up temporary storage used by token from source string */

		free(p);
	}
	/*NOTREACHED*/
}


/*
 * Name:	pkgstrGetToken
 * Synopsis:	Get a separator delimited token from a string
 * Description:	Given a string and a list of one or more separators,
 *		return the position specified token (sequence of one or
 *		more characters that do not include any of the separators)
 * Arguments:	r_sep - [*RW] - (char *)
 *			- separator that ended the token returned
 *			- NOTE: this is a pointer to a "char", e.g.:
 *				- char a;
 *				- pkgstrGetToken(&a, ...)
 *		a_string - [RO, *RO] - (char *)
 *			- pointer to string to extract token from
 *		a_index - [RO, *RO] - (int)
 *			- Index of token to return; '0' is first matching
 *			  token, '1' is second matching token, etc.
 *		a_separators - [RO, *RO] - (char *)
 *			- String containing one or more characters that
 *			  can separate one "token" from another
 * Returns:	char *
 *			== (char *)NULL - no token matching criteria found
 *			!= (char *)NULL - token matching criteria
 * NOTE:    	Any token string returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the token string is no longer needed.
 */

char *
pkgstrGetToken(char *r_sep, char *a_string, int a_index, char *a_separators)
{
	char	*p;
	char	*q;
	char	*lasts;

	/* entry assertions */

	assert(a_string != (char *)NULL);
	assert(a_index >= 0);
	assert(a_separators != (char *)NULL);
	assert(*a_separators != '\0');

	/* if returned separator requested, reset to null until token found */

	if (r_sep != (char *)NULL) {
		*r_sep = '\0';
	}

	/* duplicate original string before breaking down into tokens */

	p = strdup(a_string);
	assert(p != (char *)NULL);
	if (p == (char *)NULL) {
		return ((char *)NULL);
	}
	lasts = p;

	/* scan for separators and return 'index'th token found */

	while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
		/* retrieve separator if requested */

		if (r_sep != (char *)NULL) {
			char	*x;

			x = strpbrk(a_string, a_separators);
			if (x) {
				*r_sep = *x;
			}
		}

		/* if this is the 'index'th token requested return it */

		if (a_index-- == 0) {
			char	*tmp;

			/* duplicate token into its own storage */

			tmp = strdup(q);
			assert(tmp != (char *)NULL);
			if (tmp == (char *)NULL) {
				return ((char *)NULL);
			}

			/* free up copy of original input string */

			free(p);

			/* return token found */

			return (tmp);
		}
	}

	/*
	 * token not found
	 */

	/* free up copy of original input string */

	free(p);

	/* return NULL pointer (token not found) */

	return ((char *)NULL);
}

/*
 * Name:	pkgstrGetToken
 * Synopsis:	Get separator delimited token from a string into a fixed buffer
 * Description:	Given a string and a list of one or more separators,
 *		return the position specified token (sequence of one or
 *		more characters that do not include any of the separators)
 *		into a specified buffer of a fixed maximum size
 * Arguments:	r_sep - [*RW] - (char *)
 *			- separator that ended the token returned
 *			- NOTE: this is a pointer to a "char", e.g.:
 *				- char a;
 *				- pkgstrGetToken(&a, ...)
 *		a_string - [RO, *RO] - (char *)
 *			- pointer to string to extract token from
 *		a_index - [RO, *RO] - (int)
 *			- Index of token to return; '0' is first matching
 *			  token, '1' is second matching token, etc.
 *		a_separators - [RO, *RO] - (char *)
 *			- String containing one or more characters that
 *			  can separate one "token" from another
 *		a_buf - [RO, *RW] - (char *)
 *			- Pointer to buffer used as storage space for the
 *			  returned token - the returned token is always
 *			  null terminated
 *			  a_buf[0] == '\0' - no token meeting criteria found
 *			  a_buf[0] != '\0' - token meeting criteria returned
 *		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
 *			  token is always null terminated
 * Returns:	void
 */

void
pkgstrGetToken_r(char *r_sep, char *a_string, int a_index,
	char *a_separators, char *a_buf, int a_bufLen)
{
	char	*p;
	char	*q;
	char	*lasts;

	/* entry assertions */

	assert(a_string != (char *)NULL);
	assert(a_index >= 0);
	assert(a_separators != (char *)NULL);
	assert(*a_separators != '\0');
	assert(a_buf != (char *)NULL);
	assert(a_bufLen > 0);

	/* reset returned separator */

	if (r_sep != (char *)NULL) {
		*r_sep = '\0';
	}

	/* zero out contents of return buffer */

	bzero(a_buf, a_bufLen);

	/* duplicate original string before breaking down into tokens */

	p = strdup(a_string);
	assert(p != (char *)NULL);
	if (p == (char *)NULL) {
		return;
	}
	lasts = p;

	/* scan for separators and return 'index'th token found */

	while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
		/* retrieve separator if requested */

		if (r_sep != (char *)NULL) {
			char	*x;
			x = strpbrk(a_string, a_separators);
			if (x) {
				*r_sep = *x;
			}
		}

		/* if this is the 'index'th token requested return it */

		if (a_index-- == 0) {
			/* copy as many characters as possible to return buf */

			(void) strncpy(a_buf, q, a_bufLen-1);
			break;
		}
	}

	/* free up copy of original input string */

	free(p);
}

/*
 * Name:	pkgstrAddToken
 * Synopsis:	Add a token to a string
 * Description:	Append a token (sequence of one or more characters) to a
 *		string that is in allocated space - create new string if
 *		no string to append to exists
 * Arguments:	a_old - [RO, *RW] - (char **)
 *			- Pointer to handle to string to append token to
 *			  == (char *)NULL - new string is created
 *		a_new - [RO, *RO] - (char *)
 *			- Pointer to string representing token to append
 *			  to the end of the "a_old" string
 *			  == (char *)NULL - no action is performed
 *			  a_new[0] == '\0' - no action is performed
 *		a_separator - [RO, *RO] - (char)
 *			- One character placed between the old (existing)
 *			  string and the new token to be added IF the old
 *			  string exists and is not empty (zero length)
 * Returns:	void
 * CAUTION:	The old (existing) string must be allocated space (via lu_mem*
 *		or pkgstr* methods) - it must not be a static or inline
 *		character string
 * NOTE:	The old (existing) string may be freed with 'free'
 *		if a token is appended to it
 * NOTE:    	Any string returned in 'a_old' is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the token string is no longer needed.
 */

void
pkgstrAddToken(char **a_old, char *a_new, char a_separator)
{
	/* entry assertions */

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

	/* if token to add is null, just return */

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

	/* if token to add is empty (zero length), just return */

	if (*a_new == '\0') {
		return;
	}

	/* make sure that new token does not contain the separator */

	assert(strchr(a_new, (int)a_separator) == (char *)NULL);

	/* if old string is empty (zero length), deallocate */

	if ((*a_old != (char *)NULL) && ((*a_old)[0] == '\0')) {
		/* *a_old is set to NULL by free */
		free(*a_old);
		*a_old = (char *)NULL;
	}

	/* if old string is exists, append separator and token */

	if (*a_old != (char *)NULL) {
		char *p;
		p = pkgstrPrintf("%s%c%s", *a_old, a_separator, a_new);
		free(*a_old);
		*a_old = p;
		return;
	}

	/* old string does not exist - return duplicate of token */

	assert(*a_old == (char *)NULL);
	*a_old = strdup(a_new);
	assert(*a_old != (char *)NULL);
}

/*
 * Name:	pkgstrContainsToken
 * Synopsis:	Does a given string contain a specified substring
 * Description:	Determine if a given substring exists in a larger string
 * Arguments:	a_string - [RO, *RO] - (char *)
 *			Pointer to string to look for substring in
 *		a_token - [RO, *RO] - (char *)
 *			Pointer to substring to look for in larger string
 * Results:	boolean_t
 *			B_TRUE - substring exists in larger string
 *			B_FALSE - substring does NOT exist in larger string
 * NOTE:	The substring must match on a "token" basis; that is, the
 *		substring must exist in the larger string delineated with
 *		either spaces or tabs to match.
 */

boolean_t
pkgstrContainsToken(char *a_string, char *a_token, char *a_separators)
{
	char	*lasts;
	char	*current;
	char	*p;

	/* entry assertions */

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

	/* if token is not supplied, return false */

	if (a_token == (char *)NULL) {
		return (B_FALSE);
	}

	/* if no string provided, return false */

	if (a_string == (char *)NULL) {
		return (B_FALSE);
	}

	/* if string empty (zero length), return false */

	if (*a_string == '\0') {
		return (B_FALSE);
	}

	/* duplicate larger string because strtok_r changes it */

	p = strdup(a_string);
	assert(p != (char *)NULL);
	if (p == (char *)NULL) {
		return (B_FALSE);
	}

	lasts = p;

	/* scan each token looking for a match */

	while ((current = strtok_r((char *)NULL, a_separators, &lasts)) !=
			(char *)NULL) {
		if (streq(current, a_token)) {
			free(p);
			return (B_TRUE);
		}
	}

	/* free up temporary storage */

	free(p);

	/* not found */

	return (B_FALSE);
}

/*
 * Name:	pkgstrRemoveToken
 * Synopsis:	Remove a token from a string
 * Description:	Remove a token (sequence of one or more characters) from a
 *		string that is in allocated space
 * Arguments:	r_string - [RO, *RW] - (char **)
 *			- Pointer to handle to string to remove token from
 *		a_token - [RO, *RO] - (char *)
 *			Pointer to token (substring) to look for and remove
 *			from r_string provided
 *		a_separators - [RO, *RO] - (char *)
 *			- String containing one or more characters that
 *			  separate one "token" from another in r_string
 *		a_index - [RO, *RO] - (int)
 *			- Index of token to remove; '0' is first matching
 *			  token, '1' is second matching token, etc.
 * Returns:	void
 * CAUTION:	The input string must be allocated space (via lu_mem* or
 *		pkgstr* methods) - it must not be a static or inline
 *		character string
 * NOTE:	The input string r_string will be freed with 'free'
 *		if the token to be removed is found
 * NOTE:    	Any token string returned is placed in new storage for the
 *		calling method. The caller must use 'free' to dispose
 *		of the storage once the token string is no longer needed.
 * Errors:	If the new token string cannot be created, the process exits
 */

void
pkgstrRemoveToken(char **r_string, char *a_token, char *a_separators,
	int a_index)
{
	char	*a_string;
	char	*copyString;
	char	sep = 0;
	int	copyLength;
	int	i;

	/* entry assertions */

	assert(r_string != (char **)NULL);
	assert(a_token != (char *)NULL);
	assert(*a_token != '\0');
	assert(a_separators != (char *)NULL);
	assert(*a_separators != '\0');

	/* simple case: input string is null; return empty string */

	a_string = *r_string;
	if (*a_string == '\0') {
		return;
	}

	/* simple case: token == input string; return empty string */

	if (streq(a_string, a_token)) {
		/* deallocate input string; free sets *r_string to NULL */

		free(*r_string);
		*r_string = (char *)NULL;
		return;
	}

	/* simple case: token not in input string: return */

	if (!pkgstrContainsToken(a_string, a_token, a_separators)) {
		return;
	}

	/*
	 * Pick apart the old string building the new one as we go along
	 * removing the first occurance of the token provided
	 */

	copyLength = (strlen(a_string)-strlen(a_token))+2;
	copyString = calloc(1, copyLength);
	assert(copyString != (char *)NULL);
	if (copyString == (char *)NULL) {
		return;
	}

	for (i = 0; ; i++) {
		char	*p;

		p = pkgstrGetToken(&sep, a_string, i, a_separators);
		if (p == (char *)NULL) {
			break;
		}

		if (streq(p, a_token) && (a_index-- == 0)) {
			continue;
		}

		if (*copyString) {
			assert(sep != '\0');
			(void) strncat(copyString, &sep, 1);
		}

		(void) strcat(copyString, p);
	}

	free(*r_string);
	assert(*copyString);
	*r_string = copyString;
}

/*
 * Name:	pkgstrScaleNumericString
 * Synopsis:	Convert unsigned long long to human readable form
 * Description:	Convert a string containing an unsigned long long representation
 *		and convert it into a human readable numeric string. The number
 *		is scaled down until it is small enough to be in a good human
 *		readable format i.e. in the range 0 thru scale-1.
 * Arguments:	a_buf - [RO, *RW] - (char *)
 *			Pointer to buffer containing string representation
 *			of unsigned long long to convert
 *		scale - [RO, *RO] - (unsigned long long)
 *			Value to scale the number into
 * Returns:	a_buf - contains human readable scaled representation of
 *			original value contained in the buffer
 * Note:	The value "(unsigned long long)-1" is a special case and
 *		is always converted to "-1".
 * Errors:	If the string cannot be created, the process exits
 */

void
pkgstrScaleNumericString(char *a_buf, unsigned long long scale)
{
static char		*M = " KMGTPE"; /* Measurement: */
					/* kilo, mega, giga, tera, peta, exa */

	unsigned long long number = 0;	/* convert this number */
	unsigned long long save = 0;
	char	*uom = M;    /* unit of measurement, initially ' ' (=M[0]) */

	/* entry assertions */

	assert(scale > (unsigned long long)0);
	assert(scale <=  (unsigned long long)1048576);

	/*
	 * Get the number - if no number of empty number, just return
	 */

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

	if (*a_buf == '\0') {
		(void) strcpy(a_buf, "0");
		return;
	}

	/* convert out the number from the input buffer */

	number = strtoull(a_buf, (char **)NULL, 10);

	/* if conversion error, return "-1" */

	if ((long long)number == (long long)-1) {
		(void) strcpy(a_buf, "-1");
		return;
	}

	/*
	 * Now have number as a count of scale units.
	 * Stop scaling when we reached exa-bytes, then something is
	 * probably wrong with our number (it is improbably large)
	 */

	while ((number >= scale) && (*uom != 'E')) {
		uom++; /* next unit of measurement */
		save = number;
		number = (number + (scale / 2)) / scale;
	}

	/* check if we should output a decimal place after the point */

	if (save && ((save / scale) < 10)) {
		/* sprintf() will round for us */
		float fnum = (float)save / scale;
		(void) sprintf(a_buf, "%4.1f%c", fnum, *uom);
	} else {
		(void) sprintf(a_buf, "%4llu%c", number, *uom);
	}
}

/*
 * Name:	pkgstrLocatePathBasename
 * Synopsis:	Locate position of base name in path string
 * Description:	Locate the base name (last path item) in a path and
 *		return a pointer to the first byte of the base name
 *		within the given path
 * Arguments:	a_path - [RO, *RO] - (char *)
 *			- Pointer to string representing path to scan
 * Returns:	char *
 *			- Pointer into string of first byte of path base name
 *			- == (char *)NULL - input path is (char *)NULL
 */

char *
pkgstrLocatePathBasename(char *a_path)
{
	char	*p;

	/* if path is NULL, return NULL */

	if (!a_path) {
		return (a_path);
	}

	/* locate last occurance of '/' in path */

	p = strrchr(a_path, '/');
	if (p != (char *)NULL) {
		/* base name located - return -> first byte */
		return (p+1);
	}

	/* no occurance of '/' - entry path must be basename */

	return (a_path);
}

/*
 * Name:	pkgstrConvertPathToBasename
 * Synopsis:	Return copy of base name in path string
 * Description:	Locate the base name (last path item) in a path and
 *		return a copy of the base name in allocated storage
 * Arguments:	a_path - [RO, *RO] - (char *)
 *			- Pointer to string representing path to scan
 * Returns:	char *
 *			- String containing path base name
 *			- == (char *)NULL - input path is (char *)NULL
 * 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
 */

char *
pkgstrConvertPathToBasename(char *a_path)
{
	char	*p;

	/* if path is NULL, return NULL */

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

	/* if path is empty (zero length), return NULL */

	if (*a_path == '\0') {
		return ((char *)NULL);
	}

	/* locate last occurance of '/' in path */

	p = strrchr(a_path, '/');
	if (p == (char *)NULL) {
		/* no occurance of '/' - entry path must be basename */

		return (strdup(a_path));
	}

	/* base name located - return string from -> first byte */

	return (strdup(p+1));
}

/*
 * Name:	pkgstrConvertPathToDirname
 * Synopsis:	Return copy of directory in path string
 * Description:	Locate the directory name (everything but last path item) in a
 *		path and return a copy of the dir name in allocated storage
 * Arguments:	a_path - [RO, *RO] - (char *)
 *			- Pointer to string representing path to scan
 * Returns:	char *
 *			- String containing path directory name
 *			- == (char *)NULL - input path is (char *)NULL,
 *			  or a_path is empty (*a_path == '\0'), or the
 *			  a_path has no directory name in it.
 * 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
 */

char *
pkgstrConvertPathToDirname(char *a_path)
{
	char	*p;
	char	*retPath;

	/* if path is NULL, return NULL */

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

	/* if path is empty (zero length), return NULL */

	if (*a_path == '\0') {
		return ((char *)NULL);
	}

	/* locate last occurance of '/' in path */

	p = strrchr(a_path, '/');
	if (p == (char *)NULL) {
		/* no occurance of '/' - entire path must be basename */

		return ((char *)NULL);
	}

	/* duplicate original path */

	retPath = strdup(a_path);
	assert(retPath != (char *)NULL);
	if (retPath == (char *)NULL) {
		return ((char *)NULL);
	}

	/* remove all trailing '/'s from copy of path */

	for (p = strrchr(retPath, '/');	(p > retPath) && (*p == '/'); p--) {
		*p = '\0';
	}

	/* if entire path was '/'s, return null string - no directory present */

	if (*retPath == '\0') {
		free(retPath);
		return ((char *)NULL);
	}

	/* path has at least one non-'/' in it - return -> directory portion */

	return (retPath);
}

/*
 * Name:	pkgstrConvertUllToTimeString_r
 * Synopsis:	Convert an unsigned long long into a "time string"
 * Description:	Given an unsigned long long, return a "time string" which is a
 *		conversion of the unsigned long long interpreted as a number of
 *		nanoseconds into a "hour:minute:second.ns" ascii string
 * Arguments:	a_time - [RO, *RO] - (unsigned long long)n
 *			- value to convert
 *		a_buf - [RO, *RW] - (char *)
 *			- Pointer to buffer used as storage space for the
 *			  returned string
 *		a_bufLen - [RO, *RO] - (int)
 *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
 *			  bytes will be placed in 'a_buf'
 * Returns:	char *
 *			- String containing converted value
 * 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
 */

void
pkgstrConvertUllToTimeString_r(unsigned long long a_time,
	char *a_buf, int a_bufLen)
{
	unsigned long long	seconds;
	unsigned long long	minutes;
	unsigned long long	hours;
	unsigned long long	ns;

	/* entry assertions */

	assert(a_buf != (char *)NULL);
	assert(a_bufLen > 0);

	/* if time is 0, return immediate result */

	if (a_time == 0) {
		pkgstrPrintf_r(a_buf, a_bufLen, "%s", "0:00:00.000000000");
		return;
	}

	/* break out individual time components */

	ns = a_time % 1000000000ll;	/* nanoseconds left over from seconds */
	seconds = a_time / 1000000000ll;	/* total seconds */
	minutes = seconds / 60ll;	/* total minutes */
	seconds = seconds % 60ll;	/* seconds left over from minutes */
	hours = minutes / 60ll;		/* total hours */
	minutes = minutes % 60ll;	/* minutes left over from hours */

	/* return a converted string */

	pkgstrPrintf_r(a_buf, a_bufLen, "%llu:%02llu:%02llu.%09llu",
						hours, minutes, seconds, ns);
}