pkgstr.c revision 9781:ccf49524d5dc
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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28/*
29 * Module:	pkgstr.c
30 * Synopsis:	general string services
31 * Taxonomy:	project private
32 * Debug Flag:	str
33 * Description:
34 *
35 *   This module implements general string utility services
36 *
37 * Public Methods:
38 *
39 *   pkgstrAddToken - Add a token to a string
40 *   pkgstrContainsToken - Determine if a string contains a specified token
41 *   pkgstrConvertPathToBasename - Return copy of base name in path string
42 *   pkgstrConvertPathToDirname - Return copy of directory name in path string
43 *   pkgstrConvertUllToTimeString_r - convert unsigned long long to time string
44 *   pkgstrExpandTokens - Expand tokens from string appending tokens to another
45 *   pkgstrGetToken - Get a token from a string
46 *   pkgstrGetToken_r - Get a token from a string into a fixed buffer
47 *   pkgstrLocatePathBasename - Locate position of base name in path string
48 *   pkgstrNumTokens - Determine number of tokens in string
49 *   pkgstrPrintf - Create a string from a printf style format and arguments
50 *   pkgstrPrintf_r - Create a string from a printf style format and arguments
51 *			into a fixed buffer
52 *   pkgstrRemoveToken - Remove a token from a string
53 *   pkgstrRemoveLeadingWhitespace - remove leading whitespace from string
54 *   pkgstrScaleNumericString - Convert unsigned long long to human
55 *	readable form
56 */
57
58/*
59 * Unix Includes
60 */
61
62#define	__EXTENSIONS__
63
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67#include <libintl.h>
68#include <limits.h>
69#include <sys/types.h>
70#include <assert.h>
71#include <errno.h>
72#include <libintl.h>
73#include <ctype.h>
74#include <unistd.h>
75#include <strings.h>
76#include <stdarg.h>
77
78/*
79 * pkglib Includes
80 */
81
82#include "pkglib.h"
83#include "pkgstrct.h"
84#include "libintl.h"
85#include "pkglocale.h"
86
87/*
88 * External definitions
89 */
90
91/*
92 * Public methods
93 */
94
95/*
96 * Name:	pkgstrRemoveLeadingWhitespace
97 * Synopsis:	Remove leading whitespace from string
98 * Description:	Remove all leading whitespace characters from a string
99 * Arguments:	a_str - [RO, *RW] - (char **)
100 *			Pointer to handle to string (in allocated storage) to
101 *			remove all leading whitespace from
102 * Returns:	void
103 *			The input string is modified as follows:
104 *			== (char *)NULL:
105 *				- input string was (char *)NULL
106 *				- input string is all whitespace
107 *			!= (char *)NULL:
108 *				- copy of input string with leading
109 *				  whitespace removed
110 * CAUTION:	The input string must be allocated space (via mem* or
111 *		pkgstr* methods) - it must not be a static or inline
112 *		character string
113 * NOTE:	The input string a_str will be freed with 'free'
114 *		if it is all whitespace, or if it contains any leading
115 *		whitespace characters
116 * NOTE:    	Any string returned is placed in new storage for the
117 *		calling method. The caller must use 'free' to dispose
118 *		of the storage once the string is no longer needed.
119 * Errors:	If the string cannot be created, the process exits
120 */
121
122void
123pkgstrRemoveLeadingWhitespace(char **a_str)
124{
125	char	*o_str;
126
127	/* entry assertions */
128
129	assert(a_str != (char **)NULL);
130
131	/* if string is null, just return */
132
133	if (*a_str == (char *)NULL) {
134		return;
135	}
136	o_str = *a_str;
137
138	/* if string is empty, deallocate and return NULL */
139
140	if (*o_str == '\0') {
141		/* free string - handle is reset to NULL by free */
142		free(*a_str);
143		*a_str = (char *)NULL;
144		return;
145	}
146
147	/* if first character is not a space, just return */
148
149	if (!isspace(*o_str)) {
150		return;
151	}
152
153	/* advance past all space characters */
154
155	while ((*o_str != '\0') && (isspace(*o_str))) {
156		o_str++;
157	}
158
159	/* if string was all space characters, deallocate and return NULL */
160
161	if (*o_str == '\0') {
162		/* free string - *a_str is reset to NULL by free */
163		free(*a_str);
164		*a_str = (char *)NULL;
165		return;
166	}
167
168	/* have non-space/null byte, return dup, deallocate original */
169
170	o_str = strdup(o_str);
171	assert(o_str != (char *)NULL);
172	if (o_str != (char *)NULL) {
173		free(*a_str);
174		*a_str = o_str;
175	}
176}
177
178unsigned long
179pkgstrNumTokens(char *a_string, char *a_separators)
180{
181	int	index;
182
183	if (a_string == (char *)NULL) {
184		return (0);
185	}
186
187	if (*a_string == '\0') {
188		return (0);
189	}
190
191	for (index = 0 ; ; index ++) {
192		char *p;
193
194		p = pkgstrGetToken((char *)NULL, a_string, index, a_separators);
195		if (p == (char *)NULL) {
196			return (index);
197		}
198		free(p);
199	}
200}
201
202/*
203 * Name:	pkgstrPrintf_r
204 * Synopsis:	Create string from printf style format and arguments
205 * Description:	Call to convert a printf style format and arguments into a
206 *		string of characters placed in allocated storage
207 * Arguments:	a_buf - [RO, *RW] - (char *)
208 *			- Pointer to buffer used as storage space for the
209 *			  returned string created
210 *		a_bufLen - [RO, *RO] - (int)
211 *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
212 *			  bytes will be placed in 'a_buf' - the returned
213 *			  string is always null terminated
214 *		a_format - [RO, RO*] (char *)
215 *			printf-style format for string to be formatted
216 *		VARG_LIST - [RO] (?)
217 *			arguments as appropriate to 'format' specified
218 * Returns:	void
219 */
220
221/*PRINTFLIKE3*/
222void
223pkgstrPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
224{
225	va_list		ap;
226	size_t		vres = 0;
227
228	/* entry assertions */
229
230	assert(a_format != (char *)NULL);
231	assert(*a_format != '\0');
232	assert(a_buf != (char *)NULL);
233	assert(a_bufLen > 1);
234
235	/* generate the results of the printf conversion */
236
237	va_start(ap, a_format);
238	vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
239	va_end(ap);
240
241	assert(vres > 0);
242	assert(vres < a_bufLen);
243
244	a_buf[a_bufLen-1] = '\0';
245}
246
247/*
248 * Name:	pkgstrPrintf
249 * Synopsis:	Create string from printf style format and arguments
250 * Description:	Call to convert a printf style format and arguments into a
251 *		string of characters placed in allocated storage
252 * Arguments:	format - [RO, RO*] (char *)
253 *			printf-style format for string to be formatted
254 *		VARG_LIST - [RO] (?)
255 *			arguments as appropriate to 'format' specified
256 * Returns:	char *
257 *			A string representing the printf conversion results
258 * NOTE:    	Any string returned is placed in new storage for the
259 *		calling method. The caller must use 'free' to dispose
260 *		of the storage once the string is no longer needed.
261 * Errors:	If the string cannot be created, the process exits
262 */
263
264/*PRINTFLIKE1*/
265char *
266pkgstrPrintf(char *a_format, ...)
267{
268	va_list		ap;
269	size_t		vres = 0;
270	char		bfr[1];
271	char		*rstr = (char *)NULL;
272
273	/* entry assertions */
274
275	assert(a_format != (char *)NULL);
276	assert(*a_format != '\0');
277
278	/* determine size of the message in bytes */
279
280	va_start(ap, a_format);
281	vres = vsnprintf(bfr, 1, a_format, ap);
282	va_end(ap);
283
284	assert(vres > 0);
285	assert(vres < LINE_MAX);
286
287	/* allocate storage to hold the message */
288
289	rstr = (char *)calloc(1, vres+2);
290	assert(rstr != (char *)NULL);
291	if (rstr == (char *)NULL) {
292		return ((char *)NULL);
293	}
294
295	/* generate the results of the printf conversion */
296
297	va_start(ap, a_format);
298	vres = vsnprintf(rstr, vres+1, a_format, ap);
299	va_end(ap);
300
301	assert(vres > 0);
302	assert(vres < LINE_MAX);
303	assert(*rstr != '\0');
304
305	/* return the results */
306
307	return (rstr);
308}
309
310/*
311 * Name:	pkgstrExpandTokens
312 * Synopsis:	Expand tokens from string appending tokens to another
313 * Description:	Given a string and a list of one or more separators,
314 *		expand each token from the string and append those tokens
315 *		to a string that is in allocated space - create new string
316 *		if no string to append to exists.
317 * Arguments:	a_old - [RO, *RW] - (char **)
318 *			- Pointer to handle to string to append token to
319 *			  == (char *)NULL - new string is created
320 *		a_separator - [RO, *RO] - (char *)
321 *			- separator to end tokens returned
322 *		a_separators - [RO, *RO] - (char *)
323 *			- String containing one or more characters that
324 *			  can separate one "token" from a_string from another
325 * Returns:	void
326 * NOTE:    	Any token string returned is placed in new storage for the
327 *		calling method. The caller must use 'free' to dispose
328 *		of the storage once the token string is no longer needed.
329 */
330
331void
332pkgstrExpandTokens(char **a_old, char *a_string, char a_separator,
333	char *a_separators)
334{
335	int		i;
336	char		sep[2] = {'\0', '\0'};
337
338	/* convert single separator character into character string */
339
340	sep[0] = a_separator;
341
342	/*
343	 * iterate extracting tokens from the source string and adding
344	 * those tokens to the target string when the tokens are not
345	 * already present in the target string
346	 */
347
348	for (i = 0; ; i++) {
349		char	*p;
350
351		/* extract the next matching token from the source string */
352
353		p = pkgstrGetToken((char *)NULL, a_string, i, a_separators);
354
355		/* return if no token is available */
356
357		if (p == (char *)NULL) {
358			return;
359		}
360
361		/*
362		 * obtained token from source string: if the token is not
363		 * in the target string, add the token to the target string
364		 */
365
366		if (pkgstrContainsToken(*a_old, p, sep) == B_FALSE) {
367			pkgstrAddToken(a_old, p, *sep);
368		}
369
370		/* free up temporary storage used by token from source string */
371
372		free(p);
373	}
374	/*NOTREACHED*/
375}
376
377
378/*
379 * Name:	pkgstrGetToken
380 * Synopsis:	Get a separator delimited token from a string
381 * Description:	Given a string and a list of one or more separators,
382 *		return the position specified token (sequence of one or
383 *		more characters that do not include any of the separators)
384 * Arguments:	r_sep - [*RW] - (char *)
385 *			- separator that ended the token returned
386 *			- NOTE: this is a pointer to a "char", e.g.:
387 *				- char a;
388 *				- pkgstrGetToken(&a, ...)
389 *		a_string - [RO, *RO] - (char *)
390 *			- pointer to string to extract token from
391 *		a_index - [RO, *RO] - (int)
392 *			- Index of token to return; '0' is first matching
393 *			  token, '1' is second matching token, etc.
394 *		a_separators - [RO, *RO] - (char *)
395 *			- String containing one or more characters that
396 *			  can separate one "token" from another
397 * Returns:	char *
398 *			== (char *)NULL - no token matching criteria found
399 *			!= (char *)NULL - token matching criteria
400 * NOTE:    	Any token string returned is placed in new storage for the
401 *		calling method. The caller must use 'free' to dispose
402 *		of the storage once the token string is no longer needed.
403 */
404
405char *
406pkgstrGetToken(char *r_sep, char *a_string, int a_index, char *a_separators)
407{
408	char	*p;
409	char	*q;
410	char	*lasts;
411
412	/* entry assertions */
413
414	assert(a_string != (char *)NULL);
415	assert(a_index >= 0);
416	assert(a_separators != (char *)NULL);
417	assert(*a_separators != '\0');
418
419	/* if returned separator requested, reset to null until token found */
420
421	if (r_sep != (char *)NULL) {
422		*r_sep = '\0';
423	}
424
425	/* duplicate original string before breaking down into tokens */
426
427	p = strdup(a_string);
428	assert(p != (char *)NULL);
429	if (p == (char *)NULL) {
430		return ((char *)NULL);
431	}
432	lasts = p;
433
434	/* scan for separators and return 'index'th token found */
435
436	while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
437		/* retrieve separator if requested */
438
439		if (r_sep != (char *)NULL) {
440			char	*x;
441
442			x = strpbrk(a_string, a_separators);
443			if (x) {
444				*r_sep = *x;
445			}
446		}
447
448		/* if this is the 'index'th token requested return it */
449
450		if (a_index-- == 0) {
451			char	*tmp;
452
453			/* duplicate token into its own storage */
454
455			tmp = strdup(q);
456			assert(tmp != (char *)NULL);
457			if (tmp == (char *)NULL) {
458				return ((char *)NULL);
459			}
460
461			/* free up copy of original input string */
462
463			free(p);
464
465			/* return token found */
466
467			return (tmp);
468		}
469	}
470
471	/*
472	 * token not found
473	 */
474
475	/* free up copy of original input string */
476
477	free(p);
478
479	/* return NULL pointer (token not found) */
480
481	return ((char *)NULL);
482}
483
484/*
485 * Name:	pkgstrGetToken
486 * Synopsis:	Get separator delimited token from a string into a fixed buffer
487 * Description:	Given a string and a list of one or more separators,
488 *		return the position specified token (sequence of one or
489 *		more characters that do not include any of the separators)
490 *		into a specified buffer of a fixed maximum size
491 * Arguments:	r_sep - [*RW] - (char *)
492 *			- separator that ended the token returned
493 *			- NOTE: this is a pointer to a "char", e.g.:
494 *				- char a;
495 *				- pkgstrGetToken(&a, ...)
496 *		a_string - [RO, *RO] - (char *)
497 *			- pointer to string to extract token from
498 *		a_index - [RO, *RO] - (int)
499 *			- Index of token to return; '0' is first matching
500 *			  token, '1' is second matching token, etc.
501 *		a_separators - [RO, *RO] - (char *)
502 *			- String containing one or more characters that
503 *			  can separate one "token" from another
504 *		a_buf - [RO, *RW] - (char *)
505 *			- Pointer to buffer used as storage space for the
506 *			  returned token - the returned token is always
507 *			  null terminated
508 *			  a_buf[0] == '\0' - no token meeting criteria found
509 *			  a_buf[0] != '\0' - token meeting criteria returned
510 *		a_bufLen - [RO, *RO] - (int)
511 *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
512 *			  bytes will be placed in 'a_buf' - the returned
513 *			  token is always null terminated
514 * Returns:	void
515 */
516
517void
518pkgstrGetToken_r(char *r_sep, char *a_string, int a_index,
519	char *a_separators, char *a_buf, int a_bufLen)
520{
521	char	*p;
522	char	*q;
523	char	*lasts;
524
525	/* entry assertions */
526
527	assert(a_string != (char *)NULL);
528	assert(a_index >= 0);
529	assert(a_separators != (char *)NULL);
530	assert(*a_separators != '\0');
531	assert(a_buf != (char *)NULL);
532	assert(a_bufLen > 0);
533
534	/* reset returned separator */
535
536	if (r_sep != (char *)NULL) {
537		*r_sep = '\0';
538	}
539
540	/* zero out contents of return buffer */
541
542	bzero(a_buf, a_bufLen);
543
544	/* duplicate original string before breaking down into tokens */
545
546	p = strdup(a_string);
547	assert(p != (char *)NULL);
548	if (p == (char *)NULL) {
549		return;
550	}
551	lasts = p;
552
553	/* scan for separators and return 'index'th token found */
554
555	while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
556		/* retrieve separator if requested */
557
558		if (r_sep != (char *)NULL) {
559			char	*x;
560			x = strpbrk(a_string, a_separators);
561			if (x) {
562				*r_sep = *x;
563			}
564		}
565
566		/* if this is the 'index'th token requested return it */
567
568		if (a_index-- == 0) {
569			/* copy as many characters as possible to return buf */
570
571			(void) strncpy(a_buf, q, a_bufLen-1);
572			break;
573		}
574	}
575
576	/* free up copy of original input string */
577
578	free(p);
579}
580
581/*
582 * Name:	pkgstrAddToken
583 * Synopsis:	Add a token to a string
584 * Description:	Append a token (sequence of one or more characters) to a
585 *		string that is in allocated space - create new string if
586 *		no string to append to exists
587 * Arguments:	a_old - [RO, *RW] - (char **)
588 *			- Pointer to handle to string to append token to
589 *			  == (char *)NULL - new string is created
590 *		a_new - [RO, *RO] - (char *)
591 *			- Pointer to string representing token to append
592 *			  to the end of the "a_old" string
593 *			  == (char *)NULL - no action is performed
594 *			  a_new[0] == '\0' - no action is performed
595 *		a_separator - [RO, *RO] - (char)
596 *			- One character placed between the old (existing)
597 *			  string and the new token to be added IF the old
598 *			  string exists and is not empty (zero length)
599 * Returns:	void
600 * CAUTION:	The old (existing) string must be allocated space (via lu_mem*
601 *		or pkgstr* methods) - it must not be a static or inline
602 *		character string
603 * NOTE:	The old (existing) string may be freed with 'free'
604 *		if a token is appended to it
605 * NOTE:    	Any string returned in 'a_old' is placed in new storage for the
606 *		calling method. The caller must use 'free' to dispose
607 *		of the storage once the token string is no longer needed.
608 */
609
610void
611pkgstrAddToken(char **a_old, char *a_new, char a_separator)
612{
613	/* entry assertions */
614
615	assert(a_old != (char **)NULL);
616	assert(a_separator != '\0');
617
618	/* if token to add is null, just return */
619
620	if (a_new == (char *)NULL) {
621		return;
622	}
623
624	/* if token to add is empty (zero length), just return */
625
626	if (*a_new == '\0') {
627		return;
628	}
629
630	/* make sure that new token does not contain the separator */
631
632	assert(strchr(a_new, (int)a_separator) == (char *)NULL);
633
634	/* if old string is empty (zero length), deallocate */
635
636	if ((*a_old != (char *)NULL) && ((*a_old)[0] == '\0')) {
637		/* *a_old is set to NULL by free */
638		free(*a_old);
639		*a_old = (char *)NULL;
640	}
641
642	/* if old string is exists, append separator and token */
643
644	if (*a_old != (char *)NULL) {
645		char *p;
646		p = pkgstrPrintf("%s%c%s", *a_old, a_separator, a_new);
647		free(*a_old);
648		*a_old = p;
649		return;
650	}
651
652	/* old string does not exist - return duplicate of token */
653
654	assert(*a_old == (char *)NULL);
655	*a_old = strdup(a_new);
656	assert(*a_old != (char *)NULL);
657}
658
659/*
660 * Name:	pkgstrContainsToken
661 * Synopsis:	Does a given string contain a specified substring
662 * Description:	Determine if a given substring exists in a larger string
663 * Arguments:	a_string - [RO, *RO] - (char *)
664 *			Pointer to string to look for substring in
665 *		a_token - [RO, *RO] - (char *)
666 *			Pointer to substring to look for in larger string
667 * Results:	boolean_t
668 *			B_TRUE - substring exists in larger string
669 *			B_FALSE - substring does NOT exist in larger string
670 * NOTE:	The substring must match on a "token" basis; that is, the
671 *		substring must exist in the larger string delineated with
672 *		either spaces or tabs to match.
673 */
674
675boolean_t
676pkgstrContainsToken(char *a_string, char *a_token, char *a_separators)
677{
678	char	*lasts;
679	char	*current;
680	char	*p;
681
682	/* entry assertions */
683
684	assert(a_separators != (char *)NULL);
685	assert(*a_separators != '\0');
686
687	/* if token is not supplied, return false */
688
689	if (a_token == (char *)NULL) {
690		return (B_FALSE);
691	}
692
693	/* if no string provided, return false */
694
695	if (a_string == (char *)NULL) {
696		return (B_FALSE);
697	}
698
699	/* if string empty (zero length), return false */
700
701	if (*a_string == '\0') {
702		return (B_FALSE);
703	}
704
705	/* duplicate larger string because strtok_r changes it */
706
707	p = strdup(a_string);
708	assert(p != (char *)NULL);
709	if (p == (char *)NULL) {
710		return (B_FALSE);
711	}
712
713	lasts = p;
714
715	/* scan each token looking for a match */
716
717	while ((current = strtok_r((char *)NULL, a_separators, &lasts)) !=
718			(char *)NULL) {
719		if (streq(current, a_token)) {
720			free(p);
721			return (B_TRUE);
722		}
723	}
724
725	/* free up temporary storage */
726
727	free(p);
728
729	/* not found */
730
731	return (B_FALSE);
732}
733
734/*
735 * Name:	pkgstrRemoveToken
736 * Synopsis:	Remove a token from a string
737 * Description:	Remove a token (sequence of one or more characters) from a
738 *		string that is in allocated space
739 * Arguments:	r_string - [RO, *RW] - (char **)
740 *			- Pointer to handle to string to remove token from
741 *		a_token - [RO, *RO] - (char *)
742 *			Pointer to token (substring) to look for and remove
743 *			from r_string provided
744 *		a_separators - [RO, *RO] - (char *)
745 *			- String containing one or more characters that
746 *			  separate one "token" from another in r_string
747 *		a_index - [RO, *RO] - (int)
748 *			- Index of token to remove; '0' is first matching
749 *			  token, '1' is second matching token, etc.
750 * Returns:	void
751 * CAUTION:	The input string must be allocated space (via lu_mem* or
752 *		pkgstr* methods) - it must not be a static or inline
753 *		character string
754 * NOTE:	The input string r_string will be freed with 'free'
755 *		if the token to be removed is found
756 * NOTE:    	Any token string returned is placed in new storage for the
757 *		calling method. The caller must use 'free' to dispose
758 *		of the storage once the token string is no longer needed.
759 * Errors:	If the new token string cannot be created, the process exits
760 */
761
762void
763pkgstrRemoveToken(char **r_string, char *a_token, char *a_separators,
764	int a_index)
765{
766	char	*a_string;
767	char	*copyString;
768	char	sep = 0;
769	int	copyLength;
770	int	i;
771
772	/* entry assertions */
773
774	assert(r_string != (char **)NULL);
775	assert(a_token != (char *)NULL);
776	assert(*a_token != '\0');
777	assert(a_separators != (char *)NULL);
778	assert(*a_separators != '\0');
779
780	/* simple case: input string is null; return empty string */
781
782	a_string = *r_string;
783	if (*a_string == '\0') {
784		return;
785	}
786
787	/* simple case: token == input string; return empty string */
788
789	if (streq(a_string, a_token)) {
790		/* deallocate input string; free sets *r_string to NULL */
791
792		free(*r_string);
793		*r_string = (char *)NULL;
794		return;
795	}
796
797	/* simple case: token not in input string: return */
798
799	if (!pkgstrContainsToken(a_string, a_token, a_separators)) {
800		return;
801	}
802
803	/*
804	 * Pick apart the old string building the new one as we go along
805	 * removing the first occurance of the token provided
806	 */
807
808	copyLength = (strlen(a_string)-strlen(a_token))+2;
809	copyString = calloc(1, copyLength);
810	assert(copyString != (char *)NULL);
811	if (copyString == (char *)NULL) {
812		return;
813	}
814
815	for (i = 0; ; i++) {
816		char	*p;
817
818		p = pkgstrGetToken(&sep, a_string, i, a_separators);
819		if (p == (char *)NULL) {
820			break;
821		}
822
823		if (streq(p, a_token) && (a_index-- == 0)) {
824			continue;
825		}
826
827		if (*copyString) {
828			assert(sep != '\0');
829			(void) strncat(copyString, &sep, 1);
830		}
831
832		(void) strcat(copyString, p);
833	}
834
835	free(*r_string);
836	assert(*copyString);
837	*r_string = copyString;
838}
839
840/*
841 * Name:	pkgstrScaleNumericString
842 * Synopsis:	Convert unsigned long long to human readable form
843 * Description:	Convert a string containing an unsigned long long representation
844 *		and convert it into a human readable numeric string. The number
845 *		is scaled down until it is small enough to be in a good human
846 *		readable format i.e. in the range 0 thru scale-1.
847 * Arguments:	a_buf - [RO, *RW] - (char *)
848 *			Pointer to buffer containing string representation
849 *			of unsigned long long to convert
850 *		scale - [RO, *RO] - (unsigned long long)
851 *			Value to scale the number into
852 * Returns:	a_buf - contains human readable scaled representation of
853 *			original value contained in the buffer
854 * Note:	The value "(unsigned long long)-1" is a special case and
855 *		is always converted to "-1".
856 * Errors:	If the string cannot be created, the process exits
857 */
858
859void
860pkgstrScaleNumericString(char *a_buf, unsigned long long scale)
861{
862static char		*M = " KMGTPE"; /* Measurement: */
863					/* kilo, mega, giga, tera, peta, exa */
864
865	unsigned long long number = 0;	/* convert this number */
866	unsigned long long save = 0;
867	char	*uom = M;    /* unit of measurement, initially ' ' (=M[0]) */
868
869	/* entry assertions */
870
871	assert(scale > (unsigned long long)0);
872	assert(scale <=  (unsigned long long)1048576);
873
874	/*
875	 * Get the number - if no number of empty number, just return
876	 */
877
878	if (a_buf == (char *)NULL) {
879		return;
880	}
881
882	if (*a_buf == '\0') {
883		(void) strcpy(a_buf, "0");
884		return;
885	}
886
887	/* convert out the number from the input buffer */
888
889	number = strtoull(a_buf, (char **)NULL, 10);
890
891	/* if conversion error, return "-1" */
892
893	if ((long long)number == (long long)-1) {
894		(void) strcpy(a_buf, "-1");
895		return;
896	}
897
898	/*
899	 * Now have number as a count of scale units.
900	 * Stop scaling when we reached exa-bytes, then something is
901	 * probably wrong with our number (it is improbably large)
902	 */
903
904	while ((number >= scale) && (*uom != 'E')) {
905		uom++; /* next unit of measurement */
906		save = number;
907		number = (number + (scale / 2)) / scale;
908	}
909
910	/* check if we should output a decimal place after the point */
911
912	if (save && ((save / scale) < 10)) {
913		/* sprintf() will round for us */
914		float fnum = (float)save / scale;
915		(void) sprintf(a_buf, "%4.1f%c", fnum, *uom);
916	} else {
917		(void) sprintf(a_buf, "%4llu%c", number, *uom);
918	}
919}
920
921/*
922 * Name:	pkgstrLocatePathBasename
923 * Synopsis:	Locate position of base name in path string
924 * Description:	Locate the base name (last path item) in a path and
925 *		return a pointer to the first byte of the base name
926 *		within the given path
927 * Arguments:	a_path - [RO, *RO] - (char *)
928 *			- Pointer to string representing path to scan
929 * Returns:	char *
930 *			- Pointer into string of first byte of path base name
931 *			- == (char *)NULL - input path is (char *)NULL
932 */
933
934char *
935pkgstrLocatePathBasename(char *a_path)
936{
937	char	*p;
938
939	/* if path is NULL, return NULL */
940
941	if (!a_path) {
942		return (a_path);
943	}
944
945	/* locate last occurance of '/' in path */
946
947	p = strrchr(a_path, '/');
948	if (p != (char *)NULL) {
949		/* base name located - return -> first byte */
950		return (p+1);
951	}
952
953	/* no occurance of '/' - entry path must be basename */
954
955	return (a_path);
956}
957
958/*
959 * Name:	pkgstrConvertPathToBasename
960 * Synopsis:	Return copy of base name in path string
961 * Description:	Locate the base name (last path item) in a path and
962 *		return a copy of the base name in allocated storage
963 * Arguments:	a_path - [RO, *RO] - (char *)
964 *			- Pointer to string representing path to scan
965 * Returns:	char *
966 *			- String containing path base name
967 *			- == (char *)NULL - input path is (char *)NULL
968 * NOTE:    	Any string returned is placed in new storage for the
969 *		calling method. The caller must use 'free' to dispose
970 *		of the storage once the string is no longer needed.
971 * Errors:	If the string cannot be created, the process exits
972 */
973
974char *
975pkgstrConvertPathToBasename(char *a_path)
976{
977	char	*p;
978
979	/* if path is NULL, return NULL */
980
981	if (a_path == (char *)NULL) {
982		return ((char *)NULL);
983	}
984
985	/* if path is empty (zero length), return NULL */
986
987	if (*a_path == '\0') {
988		return ((char *)NULL);
989	}
990
991	/* locate last occurance of '/' in path */
992
993	p = strrchr(a_path, '/');
994	if (p == (char *)NULL) {
995		/* no occurance of '/' - entry path must be basename */
996
997		return (strdup(a_path));
998	}
999
1000	/* base name located - return string from -> first byte */
1001
1002	return (strdup(p+1));
1003}
1004
1005/*
1006 * Name:	pkgstrConvertPathToDirname
1007 * Synopsis:	Return copy of directory in path string
1008 * Description:	Locate the directory name (everything but last path item) in a
1009 *		path and return a copy of the dir name in allocated storage
1010 * Arguments:	a_path - [RO, *RO] - (char *)
1011 *			- Pointer to string representing path to scan
1012 * Returns:	char *
1013 *			- String containing path directory name
1014 *			- == (char *)NULL - input path is (char *)NULL,
1015 *			  or a_path is empty (*a_path == '\0'), or the
1016 *			  a_path has no directory name in it.
1017 * NOTE:    	Any string returned is placed in new storage for the
1018 *		calling method. The caller must use 'free' to dispose
1019 *		of the storage once the string is no longer needed.
1020 * Errors:	If the string cannot be created, the process exits
1021 */
1022
1023char *
1024pkgstrConvertPathToDirname(char *a_path)
1025{
1026	char	*p;
1027	char	*retPath;
1028
1029	/* if path is NULL, return NULL */
1030
1031	if (a_path == (char *)NULL) {
1032		return ((char *)NULL);
1033	}
1034
1035	/* if path is empty (zero length), return NULL */
1036
1037	if (*a_path == '\0') {
1038		return ((char *)NULL);
1039	}
1040
1041	/* locate last occurance of '/' in path */
1042
1043	p = strrchr(a_path, '/');
1044	if (p == (char *)NULL) {
1045		/* no occurance of '/' - entire path must be basename */
1046
1047		return ((char *)NULL);
1048	}
1049
1050	/* duplicate original path */
1051
1052	retPath = strdup(a_path);
1053	assert(retPath != (char *)NULL);
1054	if (retPath == (char *)NULL) {
1055		return ((char *)NULL);
1056	}
1057
1058	/* remove all trailing '/'s from copy of path */
1059
1060	for (p = strrchr(retPath, '/');	(p > retPath) && (*p == '/'); p--) {
1061		*p = '\0';
1062	}
1063
1064	/* if entire path was '/'s, return null string - no directory present */
1065
1066	if (*retPath == '\0') {
1067		free(retPath);
1068		return ((char *)NULL);
1069	}
1070
1071	/* path has at least one non-'/' in it - return -> directory portion */
1072
1073	return (retPath);
1074}
1075
1076/*
1077 * Name:	pkgstrConvertUllToTimeString_r
1078 * Synopsis:	Convert an unsigned long long into a "time string"
1079 * Description:	Given an unsigned long long, return a "time string" which is a
1080 *		conversion of the unsigned long long interpreted as a number of
1081 *		nanoseconds into a "hour:minute:second.ns" ascii string
1082 * Arguments:	a_time - [RO, *RO] - (unsigned long long)n
1083 *			- value to convert
1084 *		a_buf - [RO, *RW] - (char *)
1085 *			- Pointer to buffer used as storage space for the
1086 *			  returned string
1087 *		a_bufLen - [RO, *RO] - (int)
1088 *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
1089 *			  bytes will be placed in 'a_buf'
1090 * Returns:	char *
1091 *			- String containing converted value
1092 * NOTE:    	Any string returned is placed in new storage for the
1093 *		calling method. The caller must use 'free' to dispose
1094 *		of the storage once the string is no longer needed.
1095 * Errors:	If the string cannot be created, the process exits
1096 */
1097
1098void
1099pkgstrConvertUllToTimeString_r(unsigned long long a_time,
1100	char *a_buf, int a_bufLen)
1101{
1102	unsigned long long	seconds;
1103	unsigned long long	minutes;
1104	unsigned long long	hours;
1105	unsigned long long	ns;
1106
1107	/* entry assertions */
1108
1109	assert(a_buf != (char *)NULL);
1110	assert(a_bufLen > 0);
1111
1112	/* if time is 0, return immediate result */
1113
1114	if (a_time == 0) {
1115		pkgstrPrintf_r(a_buf, a_bufLen, "%s", "0:00:00.000000000");
1116		return;
1117	}
1118
1119	/* break out individual time components */
1120
1121	ns = a_time % 1000000000ll;	/* nanoseconds left over from seconds */
1122	seconds = a_time / 1000000000ll;	/* total seconds */
1123	minutes = seconds / 60ll;	/* total minutes */
1124	seconds = seconds % 60ll;	/* seconds left over from minutes */
1125	hours = minutes / 60ll;		/* total hours */
1126	minutes = minutes % 60ll;	/* minutes left over from hours */
1127
1128	/* return a converted string */
1129
1130	pkgstrPrintf_r(a_buf, a_bufLen, "%llu:%02llu:%02llu.%09llu",
1131						hours, minutes, seconds, ns);
1132}
1133