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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 
28 /*
29  * Module:	zones_str.c
30  * Group:	libinstzones
31  * Description:	Private functions used by zones library functions to manipulate
32  *		strings
33  *
34  * Public Methods:
35  *
36  * _z_strAddToken - Add a token to a string
37  * _z_strContainsToken - Does a given string contain a specified substring
38  * _z_strGetToken - Get a separator delimited token from a string
39  * _z_strGetToken_r - Get separator delimited token from string to fixed buffer
40  * _z_strPrintf - Create string from printf style format and arguments
41  * _z_strPrintf_r - Create string from printf style format and arguments
42  * _z_strRemoveLeadingWhitespace - Remove leading whitespace from string
43  * _z_strRemoveToken - Remove a token from a string
44  */
45 
46 /*
47  * System includes
48  */
49 
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <ctype.h>
54 #include <sys/types.h>
55 #include <sys/param.h>
56 #include <string.h>
57 #include <strings.h>
58 #include <stdarg.h>
59 #include <limits.h>
60 #include <stropts.h>
61 #include <libintl.h>
62 #include <locale.h>
63 #include <assert.h>
64 
65 /*
66  * local includes
67  */
68 
69 #include "instzones_lib.h"
70 #include "zones_strings.h"
71 
72 /*
73  * Private structures
74  */
75 
76 /*
77  * Library Function Prototypes
78  */
79 
80 /*
81  * Local Function Prototypes
82  */
83 
84 /*
85  * Global internal (private) declarations
86  */
87 
88 /*
89  * *****************************************************************************
90  * global external (public) functions
91  * *****************************************************************************
92  */
93 
94 /*
95  * Name:	_z_strAddToken
96  * Synopsis:	Add a token to a string
97  * Description:	Append a token (sequence of one or more characters) to a
98  *		string that is in allocated space - create new string if
99  *		no string to append to exists
100  * Arguments:	a_old - [RO, *RW] - (char **)
101  *			- Pointer to handle to string to append token to
102  *			  == NULL - new string is created
103  *		a_new - [RO, *RO] - (char *)
104  *			- Pointer to string representing token to append
105  *			  to the end of the "a_old" string
106  *			  == NULL - no action is performed
107  *			  a_new[0] == '\0' - no action is performed
108  *		a_separator - [RO, *RO] - (char)
109  *			- One character placed between the old (existing)
110  *			  string and the new token to be added IF the old
111  *			  string exists and is not empty (zero length)
112  * Returns:	void
113  * CAUTION:	The old (existing) string must be allocated space (via lu_mem*
114  *		or _z_str* methods) - it must not be a static or inline
115  *		character string
116  * NOTE:	The old (existing) string may be freed with 'free'
117  *		if a token is appended to it
118  * NOTE:    	Any string returned in 'a_old' is placed in new storage for the
119  *		calling method. The caller must use 'free' to dispose
120  *		of the storage once the token string is no longer needed.
121  */
122 
123 void
_z_strAddToken(char ** a_old,char * a_new,char a_separator)124 _z_strAddToken(char **a_old, char *a_new, char a_separator)
125 {
126 	/* entry assertions */
127 
128 	assert(a_old != NULL);
129 	assert(a_separator != '\0');
130 
131 	/* if token to add is null or token is zero length, just return */
132 
133 	if (a_new == NULL || *a_new == '\0') {
134 		return;
135 	}
136 
137 	/* make sure that new token does not contain the separator */
138 
139 	assert(strchr(a_new, (int)a_separator) == NULL);
140 
141 	/* if old string is empty (zero length), deallocate */
142 
143 	if ((*a_old != NULL) && ((*a_old)[0] == '\0')) {
144 		/* *a_old is set to NULL by free */
145 		free(*a_old);
146 		*a_old = NULL;
147 	}
148 
149 	/* if old string exists, append separator and token */
150 
151 	if (*a_old != NULL) {
152 		char *p;
153 		p = _z_strPrintf("%s%c%s", *a_old, a_separator, a_new);
154 		free(*a_old);
155 		*a_old = p;
156 		return;
157 	}
158 
159 	/* old string does not exist - return duplicate of token */
160 
161 	assert(*a_old == NULL);
162 	*a_old = _z_strdup(a_new);
163 }
164 
165 /*
166  * Name:	_z_strContainsToken
167  * Synopsis:	Does a given string contain a specified substring
168  * Description:	Determine if a given substring exists in a larger string
169  * Arguments:	a_string - [RO, *RO] - (char *)
170  *			Pointer to string to look for substring in
171  *		a_token - [RO, *RO] - (char *)
172  *			Pointer to substring to look for in larger string
173  * Results:	boolean_t
174  *			B_TRUE - substring exists in larger string
175  *			B_FALSE - substring does NOT exist in larger string
176  * NOTE:	The substring must match on a "token" basis; that is, the
177  *		substring must exist in the larger string delineated with
178  *		either spaces or tabs to match.
179  */
180 
181 boolean_t
_z_strContainsToken(char * a_string,char * a_token,char * a_separators)182 _z_strContainsToken(char *a_string, char *a_token, char *a_separators)
183 {
184 	char	*lasts;
185 	char	*current;
186 	char	*p;
187 
188 	/* entry assertions */
189 
190 	assert(a_separators != NULL);
191 	assert(*a_separators != '\0');
192 
193 	/*
194 	 * if token is not supplied, no string provided,
195 	 * or the string is an empty string, return false
196 	 */
197 
198 	if (a_token == NULL || a_string == NULL || *a_string == '\0') {
199 		return (B_FALSE);
200 	}
201 
202 	/* if no string provided, return false */
203 
204 	/* if string empty (zero length), return false */
205 
206 	/* duplicate larger string because strtok_r changes it */
207 
208 	p = _z_strdup(a_string);
209 
210 	lasts = p;
211 
212 	/* scan each token looking for a match */
213 
214 	while ((current = strtok_r(NULL, a_separators, &lasts)) !=
215 	    NULL) {
216 		if (strcmp(current, a_token) == 0) {
217 			free(p);
218 			return (B_TRUE);
219 		}
220 	}
221 
222 	/* free up temporary storage */
223 
224 	free(p);
225 
226 	/* not found */
227 
228 	return (B_FALSE);
229 }
230 
231 /*
232  * Name:	_z_strGetToken
233  * Synopsis:	Get a separator delimited token from a string
234  * Description:	Given a string and a list of one or more separators,
235  *		return the position specified token (sequence of one or
236  *		more characters that do not include any of the separators)
237  * Arguments:	r_sep - [*RW] - (char *)
238  *			- separator that ended the token returned
239  *			- NOTE: this is a pointer to a "char", e.g.:
240  *				- char a;
241  *				- _z_strGetToken(&a, ...)
242  *		a_string - [RO, *RO] - (char *)
243  *			- pointer to string to extract token from
244  *		a_index - [RO, *RO] - (int)
245  *			- Index of token to return; '0' is first matching
246  *			  token, '1' is second matching token, etc.
247  *		a_separators - [RO, *RO] - (char *)
248  *			- String containing one or more characters that
249  *			  can separate one "token" from another
250  * Returns:	char *
251  *			== NULL - no token matching criteria found
252  *			!= NULL - token matching criteria
253  * NOTE:    	Any token string returned is placed in new storage for the
254  *		calling method. The caller must use 'free' to dispose
255  *		of the storage once the token string is no longer needed.
256  */
257 
258 char *
_z_strGetToken(char * r_sep,char * a_string,int a_index,char * a_separators)259 _z_strGetToken(char *r_sep, char *a_string, int a_index, char *a_separators)
260 {
261 	char	*p;
262 	char	*q;
263 	char	*lasts;
264 
265 	/* entry assertions */
266 
267 	assert(a_string != NULL);
268 	assert(a_index >= 0);
269 	assert(a_separators != NULL);
270 	assert(*a_separators != '\0');
271 
272 	/* if returned separator requested, reset to null until token found */
273 
274 	if (r_sep != NULL) {
275 		*r_sep = '\0';
276 	}
277 
278 	/* duplicate original string before breaking down into tokens */
279 
280 	p = _z_strdup(a_string);
281 
282 	lasts = p;
283 
284 	/* scan for separators and return 'index'th token found */
285 
286 	while (q = strtok_r(NULL, a_separators, &lasts)) {
287 		/* retrieve separator if requested */
288 
289 		if (r_sep != NULL) {
290 			char	*x;
291 
292 			x = strpbrk(a_string, a_separators);
293 			if (x != NULL) {
294 				*r_sep = *x;
295 			}
296 		}
297 
298 		/* if this is the 'index'th token requested return it */
299 
300 		if (a_index-- == 0) {
301 			char	*tmp;
302 
303 			/* duplicate token into its own storage */
304 
305 			tmp = _z_strdup(q);
306 
307 			/* free up copy of original input string */
308 
309 			free(p);
310 
311 			/* return token found */
312 
313 			return (tmp);
314 		}
315 	}
316 
317 	/*
318 	 * token not found
319 	 */
320 
321 	/* free up copy of original input string */
322 
323 	free(p);
324 
325 	/* return NULL pointer (token not found) */
326 
327 	return (NULL);
328 }
329 
330 /*
331  * Name:	_z_strGetToken_r
332  * Synopsis:	Get separator delimited token from a string into a fixed buffer
333  * Description:	Given a string and a list of one or more separators,
334  *		return the position specified token (sequence of one or
335  *		more characters that do not include any of the separators)
336  *		into a specified buffer of a fixed maximum size
337  * Arguments:	r_sep - [*RW] - (char *)
338  *			- separator that ended the token returned
339  *			- NOTE: this is a pointer to a "char", e.g.:
340  *				- char a;
341  *				- _z_strGetToken(&a, ...)
342  *		a_string - [RO, *RO] - (char *)
343  *			- pointer to string to extract token from
344  *		a_index - [RO, *RO] - (int)
345  *			- Index of token to return; '0' is first matching
346  *			  token, '1' is second matching token, etc.
347  *		a_separators - [RO, *RO] - (char *)
348  *			- String containing one or more characters that
349  *			  can separate one "token" from another
350  *		a_buf - [RO, *RW] - (char *)
351  *			- Pointer to buffer used as storage space for the
352  *			  returned token - the returned token is always
353  *			  null terminated
354  *			  a_buf[0] == '\0' - no token meeting criteria found
355  *			  a_buf[0] != '\0' - token meeting criteria returned
356  *		a_bufLen - [RO, *RO] - (int)
357  *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
358  *			  bytes will be placed in 'a_buf' - the returned
359  *			  token is always null terminated
360  * Returns:	void
361  */
362 
363 void
_z_strGetToken_r(char * r_sep,char * a_string,int a_index,char * a_separators,char * a_buf,int a_bufLen)364 _z_strGetToken_r(char *r_sep, char *a_string, int a_index,
365     char *a_separators, char *a_buf, int a_bufLen)
366 {
367 	char	*p;
368 	char	*q;
369 	char	*lasts;
370 
371 	/* entry assertions */
372 
373 	assert(a_string != NULL);
374 	assert(a_index >= 0);
375 	assert(a_separators != NULL);
376 	assert(*a_separators != '\0');
377 	assert(a_buf != NULL);
378 	assert(a_bufLen > 0);
379 
380 	/* reset returned separator */
381 
382 	if (r_sep != NULL) {
383 		*r_sep = '\0';
384 	}
385 
386 	/* zero out contents of return buffer */
387 
388 	bzero(a_buf, a_bufLen);
389 
390 	/* duplicate original string before breaking down into tokens */
391 
392 	p = _z_strdup(a_string);
393 
394 	lasts = p;
395 
396 	/* scan for separators and return 'index'th token found */
397 
398 	while (q = strtok_r(NULL, a_separators, &lasts)) {
399 		/* retrieve separator if requested */
400 
401 		if (r_sep != NULL) {
402 			char	*x;
403 			x = strpbrk(a_string, a_separators);
404 			if (x != NULL) {
405 				*r_sep = *x;
406 			}
407 		}
408 
409 		/* if this is the 'index'th token requested return it */
410 
411 		if (a_index-- == 0) {
412 			/* copy as many characters as possible to return buf */
413 
414 			(void) strncpy(a_buf, q, a_bufLen-1);
415 			break;
416 		}
417 	}
418 
419 	/* free up copy of original input string */
420 
421 	free(p);
422 }
423 
424 /*
425  * Name:	_z_strPrintf
426  * Synopsis:	Create string from printf style format and arguments
427  * Description:	Call to convert a printf style format and arguments into a
428  *		string of characters placed in allocated storage
429  * Arguments:	format - [RO, RO*] (char *)
430  *			printf-style format for string to be formatted
431  *		VARG_LIST - [RO] (?)
432  *			arguments as appropriate to 'format' specified
433  * Returns:	char *
434  *			A string representing the printf conversion results
435  * NOTE:    	Any string returned is placed in new storage for the
436  *		calling method. The caller must use 'free' to dispose
437  *		of the storage once the string is no longer needed.
438  * Errors:	If the string cannot be created, the process exits
439  */
440 
441 /*PRINTFLIKE1*/
442 char *
_z_strPrintf(char * a_format,...)443 _z_strPrintf(char *a_format, ...)
444 {
445 	va_list		ap;
446 	size_t		vres = 0;
447 	char		bfr[1];
448 	char		*rstr = NULL;
449 
450 	/* entry assertions */
451 
452 	assert(a_format != NULL);
453 	assert(*a_format != '\0');
454 
455 	/* determine size of the message in bytes */
456 
457 	va_start(ap, a_format);
458 	vres = vsnprintf(bfr, 1, a_format, ap);
459 	va_end(ap);
460 
461 	assert(vres > 0);
462 	assert(vres < LINE_MAX);
463 
464 	/* allocate storage to hold the message */
465 
466 	rstr = (char *)_z_calloc(vres+2);
467 
468 	/* generate the results of the printf conversion */
469 
470 	va_start(ap, a_format);
471 	vres = vsnprintf(rstr, vres+1, a_format, ap);
472 	va_end(ap);
473 
474 	assert(vres > 0);
475 	assert(vres < LINE_MAX);
476 	assert(*rstr != '\0');
477 
478 	/* return the results */
479 
480 	return (rstr);
481 }
482 
483 /*
484  * Name:	_z_strPrintf_r
485  * Synopsis:	Create string from printf style format and arguments
486  * Description:	Call to convert a printf style format and arguments into a
487  *		string of characters placed in allocated storage
488  * Arguments:	a_buf - [RO, *RW] - (char *)
489  *			- Pointer to buffer used as storage space for the
490  *			  returned string created
491  *		a_bufLen - [RO, *RO] - (int)
492  *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
493  *			  bytes will be placed in 'a_buf' - the returned
494  *			  string is always null terminated
495  *		a_format - [RO, RO*] (char *)
496  *			printf-style format for string to be formatted
497  *		VARG_LIST - [RO] (?)
498  *			arguments as appropriate to 'format' specified
499  * Returns:	void
500  */
501 
502 /*PRINTFLIKE3*/
503 void
_z_strPrintf_r(char * a_buf,int a_bufLen,char * a_format,...)504 _z_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
505 {
506 	va_list		ap;
507 	size_t		vres = 0;
508 
509 	/* entry assertions */
510 
511 	assert(a_format != NULL);
512 	assert(*a_format != '\0');
513 	assert(a_buf != NULL);
514 	assert(a_bufLen > 1);
515 
516 	/* generate the results of the printf conversion */
517 
518 	va_start(ap, a_format);
519 	vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
520 	va_end(ap);
521 
522 	assert(vres > 0);
523 	assert(vres < a_bufLen);
524 
525 	a_buf[a_bufLen-1] = '\0';
526 }
527 
528 /*
529  * Name:	_z_strRemoveLeadingWhitespace
530  * Synopsis:	Remove leading whitespace from string
531  * Description:	Remove all leading whitespace characters from a string
532  * Arguments:	a_str - [RO, *RW] - (char **)
533  *			Pointer to handle to string (in allocated storage) to
534  *			remove all leading whitespace from
535  * Returns:	void
536  *			The input string is modified as follows:
537  *			== NULL:
538  *				- input string was NULL
539  *				- input string is all whitespace
540  *			!= NULL:
541  *				- copy of input string with leading
542  *				  whitespace removed
543  * CAUTION:	The input string must be allocated space (via mem* or
544  *		_z_str* methods) - it must not be a static or inline
545  *		character string
546  * NOTE:	The input string a_str will be freed with 'free'
547  *		if it is all whitespace, or if it contains any leading
548  *		whitespace characters
549  * NOTE:    	Any string returned is placed in new storage for the
550  *		calling method. The caller must use 'free' to dispose
551  *		of the storage once the string is no longer needed.
552  * Errors:	If the string cannot be created, the process exits
553  */
554 
555 void
_z_strRemoveLeadingWhitespace(char ** a_str)556 _z_strRemoveLeadingWhitespace(char **a_str)
557 {
558 	char	*o_str;
559 
560 	/* entry assertions */
561 
562 	assert(a_str != NULL);
563 
564 	/* if string is null, just return */
565 
566 	if (*a_str == NULL) {
567 		return;
568 	}
569 	o_str = *a_str;
570 
571 	/* if string is empty, deallocate and return NULL */
572 
573 	if (*o_str == '\0') {
574 		/* free string - handle is not reset to NULL by free */
575 		free(*a_str);
576 		*a_str = NULL;
577 		return;
578 	}
579 
580 	/* if first character is not a space, just return */
581 
582 	if (!isspace(*o_str)) {
583 		return;
584 	}
585 
586 	/* advance past all space characters */
587 
588 	while ((*o_str != '\0') && (isspace(*o_str))) {
589 		o_str++;
590 	}
591 
592 	/* if string was all space characters, deallocate and return NULL */
593 
594 	if (*o_str == '\0') {
595 		/* free string - *a_str is not reset to NULL by free */
596 		free(*a_str);
597 		*a_str = NULL;
598 		return;
599 	}
600 
601 	/* have non-space/null byte, return dup, deallocate original */
602 
603 	free(*a_str);
604 	*a_str = _z_strdup(o_str);
605 }
606 
607 /*
608  * Name:	_z_strRemoveToken
609  * Synopsis:	Remove a token from a string
610  * Description:	Remove a token (sequence of one or more characters) from a
611  *		string that is in allocated space
612  * Arguments:	r_string - [RO, *RW] - (char **)
613  *			- Pointer to handle to string to remove token from
614  *		a_token - [RO, *RO] - (char *)
615  *			Pointer to token (substring) to look for and remove
616  *			from r_string provided
617  *		a_separators - [RO, *RO] - (char *)
618  *			- String containing one or more characters that
619  *			  separate one "token" from another in r_string
620  *		a_index - [RO, *RO] - (int)
621  *			- Index of token to remove; '0' is first matching
622  *			  token, '1' is second matching token, etc.
623  * Returns:	void
624  * CAUTION:	The input string must be allocated space (via lu_mem* or
625  *		_z_str* methods) - it must not be a static or inline
626  *		character string
627  * NOTE:	The input string r_string will be freed with 'free'
628  *		if the token to be removed is found
629  * NOTE:    	Any token string returned is placed in new storage for the
630  *		calling method. The caller must use 'free' to dispose
631  *		of the storage once the token string is no longer needed.
632  * Errors:	If the new token string cannot be created, the process exits
633  */
634 
635 void
_z_strRemoveToken(char ** r_string,char * a_token,char * a_separators,int a_index)636 _z_strRemoveToken(char **r_string, char *a_token, char *a_separators,
637 	int a_index)
638 {
639 	char	*a_string;
640 	char	*copyString;
641 	char	sep = 0;
642 	int	copyLength;
643 	int	i;
644 
645 	/* entry assertions */
646 
647 	assert(r_string != NULL);
648 	assert(a_token != NULL);
649 	assert(*a_token != '\0');
650 	assert(a_separators != NULL);
651 	assert(*a_separators != '\0');
652 
653 	/* simple case: input string is null; return empty string */
654 
655 	a_string = *r_string;
656 	if (*a_string == '\0') {
657 		return;
658 	}
659 
660 	/* simple case: token == input string; return empty string */
661 
662 	if (strcmp(a_string, a_token) == 0) {
663 		/*
664 		 * deallocate input string; free doesn't
665 		 * set *r_string to NULL
666 		 */
667 		free(*r_string);
668 		*r_string = NULL;
669 		return;
670 	}
671 
672 	/* simple case: token not in input string: return */
673 
674 	if (!_z_strContainsToken(a_string, a_token, a_separators)) {
675 		return;
676 	}
677 
678 	/*
679 	 * Pick apart the old string building the new one as we go along
680 	 * removing the first occurance of the token provided
681 	 */
682 
683 	copyLength = (strlen(a_string)-strlen(a_token))+2;
684 	copyString = (char *)_z_calloc(copyLength);
685 
686 	for (i = 0; ; i++) {
687 		char	*p;
688 
689 		p = _z_strGetToken(&sep, a_string, i, a_separators);
690 		if (p == NULL) {
691 			break;
692 		}
693 
694 		if ((strcmp(p, a_token) == 0) && (a_index-- == 0)) {
695 			free(p);
696 			continue;
697 		}
698 
699 		if (*copyString) {
700 			assert(sep != '\0');
701 			(void) strncat(copyString, &sep, 1);
702 		}
703 
704 		(void) strcat(copyString, p);
705 		free(p);
706 	}
707 
708 	free(*r_string);
709 	assert(*copyString);
710 	*r_string = copyString;
711 }
712