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