xref: /illumos-gate/usr/src/lib/libpkg/common/pkgstr.c (revision 5c51f124)
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 
122 void
pkgstrRemoveLeadingWhitespace(char ** a_str)123 pkgstrRemoveLeadingWhitespace(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 
178 unsigned long
pkgstrNumTokens(char * a_string,char * a_separators)179 pkgstrNumTokens(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*/
222 void
pkgstrPrintf_r(char * a_buf,int a_bufLen,char * a_format,...)223 pkgstrPrintf_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*/
265 char *
pkgstrPrintf(char * a_format,...)266 pkgstrPrintf(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 
331 void
pkgstrExpandTokens(char ** a_old,char * a_string,char a_separator,char * a_separators)332 pkgstrExpandTokens(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 
405 char *
pkgstrGetToken(char * r_sep,char * a_string,int a_index,char * a_separators)406 pkgstrGetToken(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 
517 void
pkgstrGetToken_r(char * r_sep,char * a_string,int a_index,char * a_separators,char * a_buf,int a_bufLen)518 pkgstrGetToken_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 
610 void
pkgstrAddToken(char ** a_old,char * a_new,char a_separator)611 pkgstrAddToken(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 
675 boolean_t
pkgstrContainsToken(char * a_string,char * a_token,char * a_separators)676 pkgstrContainsToken(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 
762 void
pkgstrRemoveToken(char ** r_string,char * a_token,char * a_separators,int a_index)763 pkgstrRemoveToken(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 
859 void
pkgstrScaleNumericString(char * a_buf,unsigned long long scale)860 pkgstrScaleNumericString(char *a_buf, unsigned long long scale)
861 {
862 static 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 
934 char *
pkgstrLocatePathBasename(char * a_path)935 pkgstrLocatePathBasename(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 
974 char *
pkgstrConvertPathToBasename(char * a_path)975 pkgstrConvertPathToBasename(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 
1023 char *
pkgstrConvertPathToDirname(char * a_path)1024 pkgstrConvertPathToDirname(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 
1098 void
pkgstrConvertUllToTimeString_r(unsigned long long a_time,char * a_buf,int a_bufLen)1099 pkgstrConvertUllToTimeString_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