xref: /spdk/lib/util/string.c (revision 856388d8738ae265b142b395d01e616a69257602)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2015 Intel Corporation.
3  *   Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES.
4  *   All rights reserved.
5  */
6 
7 #include "spdk/stdinc.h"
8 
9 #include "spdk/string.h"
10 
11 char *
spdk_vsprintf_append_realloc(char * buffer,const char * format,va_list args)12 spdk_vsprintf_append_realloc(char *buffer, const char *format, va_list args)
13 {
14 	va_list args_copy;
15 	char *new_buffer;
16 	int orig_size = 0, new_size;
17 
18 	/* Original buffer size */
19 	if (buffer) {
20 		orig_size = strlen(buffer);
21 	}
22 
23 	/* Necessary buffer size */
24 	va_copy(args_copy, args);
25 	new_size = vsnprintf(NULL, 0, format, args_copy);
26 	va_end(args_copy);
27 
28 	if (new_size < 0) {
29 		return NULL;
30 	}
31 	new_size += orig_size + 1;
32 
33 	new_buffer = realloc(buffer, new_size);
34 	if (new_buffer == NULL) {
35 		return NULL;
36 	}
37 
38 	vsnprintf(new_buffer + orig_size, new_size - orig_size, format, args);
39 
40 	return new_buffer;
41 }
42 
43 char *
spdk_sprintf_append_realloc(char * buffer,const char * format,...)44 spdk_sprintf_append_realloc(char *buffer, const char *format, ...)
45 {
46 	va_list args;
47 	char *ret;
48 
49 	va_start(args, format);
50 	ret = spdk_vsprintf_append_realloc(buffer, format, args);
51 	va_end(args);
52 
53 	return ret;
54 }
55 
56 char *
spdk_vsprintf_alloc(const char * format,va_list args)57 spdk_vsprintf_alloc(const char *format, va_list args)
58 {
59 	return spdk_vsprintf_append_realloc(NULL, format, args);
60 }
61 
62 char *
spdk_sprintf_alloc(const char * format,...)63 spdk_sprintf_alloc(const char *format, ...)
64 {
65 	va_list args;
66 	char *ret;
67 
68 	va_start(args, format);
69 	ret = spdk_vsprintf_alloc(format, args);
70 	va_end(args);
71 
72 	return ret;
73 }
74 
75 char *
spdk_strlwr(char * s)76 spdk_strlwr(char *s)
77 {
78 	char *p;
79 
80 	if (s == NULL) {
81 		return NULL;
82 	}
83 
84 	p = s;
85 	while (*p != '\0') {
86 		*p = tolower(*p);
87 		p++;
88 	}
89 
90 	return s;
91 }
92 
93 char *
spdk_strsepq(char ** stringp,const char * delim)94 spdk_strsepq(char **stringp, const char *delim)
95 {
96 	char *p, *q, *r;
97 	int quoted = 0, bslash = 0;
98 
99 	p = *stringp;
100 	if (p == NULL) {
101 		return NULL;
102 	}
103 
104 	r = q = p;
105 	while (*q != '\0' && *q != '\n') {
106 		/* eat quoted characters */
107 		if (bslash) {
108 			bslash = 0;
109 			*r++ = *q++;
110 			continue;
111 		} else if (quoted) {
112 			if (quoted == '"' && *q == '\\') {
113 				bslash = 1;
114 				q++;
115 				continue;
116 			} else if (*q == quoted) {
117 				quoted = 0;
118 				q++;
119 				continue;
120 			}
121 			*r++ = *q++;
122 			continue;
123 		} else if (*q == '\\') {
124 			bslash = 1;
125 			q++;
126 			continue;
127 		} else if (*q == '"' || *q == '\'') {
128 			quoted = *q;
129 			q++;
130 			continue;
131 		}
132 
133 		/* separator? */
134 		if (strchr(delim, *q) == NULL) {
135 			*r++ = *q++;
136 			continue;
137 		}
138 
139 		/* new string */
140 		q++;
141 		break;
142 	}
143 	*r = '\0';
144 
145 	/* skip tailer */
146 	while (*q != '\0' && strchr(delim, *q) != NULL) {
147 		q++;
148 	}
149 	if (*q != '\0') {
150 		*stringp = q;
151 	} else {
152 		*stringp = NULL;
153 	}
154 
155 	return p;
156 }
157 
158 char *
spdk_str_trim(char * s)159 spdk_str_trim(char *s)
160 {
161 	char *p, *q;
162 
163 	if (s == NULL) {
164 		return NULL;
165 	}
166 
167 	/* remove header */
168 	p = s;
169 	while (*p != '\0' && isspace(*p)) {
170 		p++;
171 	}
172 
173 	/* remove tailer */
174 	q = p + strlen(p);
175 	while (q - 1 >= p && isspace(*(q - 1))) {
176 		q--;
177 		*q = '\0';
178 	}
179 
180 	/* if remove header, move */
181 	if (p != s) {
182 		q = s;
183 		while (*p != '\0') {
184 			*q++ = *p++;
185 		}
186 		*q = '\0';
187 	}
188 
189 	return s;
190 }
191 
192 void
spdk_strcpy_pad(void * dst,const char * src,size_t size,int pad)193 spdk_strcpy_pad(void *dst, const char *src, size_t size, int pad)
194 {
195 	size_t len;
196 
197 	len = strlen(src);
198 	if (len < size) {
199 		memcpy(dst, src, len);
200 		memset((char *)dst + len, pad, size - len);
201 	} else {
202 		memcpy(dst, src, size);
203 	}
204 }
205 
206 size_t
spdk_strlen_pad(const void * str,size_t size,int pad)207 spdk_strlen_pad(const void *str, size_t size, int pad)
208 {
209 	const uint8_t *start;
210 	const uint8_t *iter;
211 	uint8_t pad_byte;
212 
213 	pad_byte = (uint8_t)pad;
214 	start = (const uint8_t *)str;
215 
216 	if (size == 0) {
217 		return 0;
218 	}
219 
220 	iter = start + size - 1;
221 	while (1) {
222 		if (*iter != pad_byte) {
223 			return iter - start + 1;
224 		}
225 
226 		if (iter == start) {
227 			/* Hit the start of the string finding only pad_byte. */
228 			return 0;
229 		}
230 		iter--;
231 	}
232 }
233 
234 int
spdk_parse_ip_addr(char * ip,char ** host,char ** port)235 spdk_parse_ip_addr(char *ip, char **host, char **port)
236 {
237 	char *p;
238 
239 	if (ip == NULL) {
240 		return -EINVAL;
241 	}
242 
243 	*host = NULL;
244 	*port = NULL;
245 
246 	if (ip[0] == '[') {
247 		/* IPv6 */
248 		p = strchr(ip, ']');
249 		if (p == NULL) {
250 			return -EINVAL;
251 		}
252 		*host = &ip[1];
253 		*p = '\0';
254 
255 		p++;
256 		if (*p == '\0') {
257 			return 0;
258 		} else if (*p != ':') {
259 			return -EINVAL;
260 		}
261 
262 		p++;
263 		if (*p == '\0') {
264 			return 0;
265 		}
266 
267 		*port = p;
268 	} else {
269 		/* IPv4 */
270 		p = strchr(ip, ':');
271 		if (p == NULL) {
272 			*host = ip;
273 			return 0;
274 		}
275 
276 		*host = ip;
277 		*p = '\0';
278 
279 		p++;
280 		if (*p == '\0') {
281 			return 0;
282 		}
283 
284 		*port = p;
285 	}
286 
287 	return 0;
288 }
289 
290 size_t
spdk_str_chomp(char * s)291 spdk_str_chomp(char *s)
292 {
293 	size_t len = strlen(s);
294 	size_t removed = 0;
295 
296 	while (len > 0) {
297 		if (s[len - 1] != '\r' && s[len - 1] != '\n') {
298 			break;
299 		}
300 
301 		s[len - 1] = '\0';
302 		len--;
303 		removed++;
304 	}
305 
306 	return removed;
307 }
308 
309 void
spdk_strerror_r(int errnum,char * buf,size_t buflen)310 spdk_strerror_r(int errnum, char *buf, size_t buflen)
311 {
312 	int rc;
313 
314 #if defined(__USE_GNU)
315 	char *new_buffer;
316 	new_buffer = strerror_r(errnum, buf, buflen);
317 	if (new_buffer == buf) {
318 		rc = 0;
319 	} else if (new_buffer != NULL) {
320 		snprintf(buf, buflen, "%s", new_buffer);
321 		rc = 0;
322 	} else {
323 		rc = 1;
324 	}
325 #else
326 	rc = strerror_r(errnum, buf, buflen);
327 #endif
328 
329 	if (rc != 0) {
330 		snprintf(buf, buflen, "Unknown error %d", errnum);
331 	}
332 }
333 
334 int
spdk_parse_capacity(const char * cap_str,uint64_t * cap,bool * has_prefix)335 spdk_parse_capacity(const char *cap_str, uint64_t *cap, bool *has_prefix)
336 {
337 	int rc;
338 	char bin_prefix;
339 
340 	rc = sscanf(cap_str, "%"SCNu64"%c", cap, &bin_prefix);
341 	if (rc == 1) {
342 		if (has_prefix != NULL) {
343 			*has_prefix = false;
344 		}
345 		return 0;
346 	} else if (rc == 0) {
347 		if (errno == 0) {
348 			/* No scanf matches - the string does not start with a digit */
349 			return -EINVAL;
350 		} else {
351 			/* Parsing error */
352 			return -errno;
353 		}
354 	}
355 
356 	if (has_prefix != NULL) {
357 		*has_prefix = true;
358 	}
359 
360 	switch (bin_prefix) {
361 	case 'k':
362 	case 'K':
363 		*cap *= 1024;
364 		break;
365 	case 'm':
366 	case 'M':
367 		*cap *= 1024 * 1024;
368 		break;
369 	case 'g':
370 	case 'G':
371 		*cap *= 1024 * 1024 * 1024;
372 		break;
373 	default:
374 		return -EINVAL;
375 	}
376 
377 	return 0;
378 }
379 
380 bool
spdk_mem_all_zero(const void * data,size_t size)381 spdk_mem_all_zero(const void *data, size_t size)
382 {
383 	const uint8_t *buf = data;
384 
385 	while (size--) {
386 		if (*buf++ != 0) {
387 			return false;
388 		}
389 	}
390 
391 	return true;
392 }
393 
394 long int
spdk_strtol(const char * nptr,int base)395 spdk_strtol(const char *nptr, int base)
396 {
397 	long val;
398 	char *endptr;
399 
400 	/* Since strtoll() can legitimately return 0, LONG_MAX, or LONG_MIN
401 	 * on both success and failure, the calling program should set errno
402 	 * to 0 before the call.
403 	 */
404 	errno = 0;
405 
406 	val = strtol(nptr, &endptr, base);
407 
408 	if (!errno && *endptr != '\0') {
409 		/* Non integer character was found. */
410 		return -EINVAL;
411 	} else if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) {
412 		/* Overflow occurred. */
413 		return -ERANGE;
414 	} else if (errno != 0 && val == 0) {
415 		/* Other error occurred. */
416 		return -errno;
417 	} else if (val < 0) {
418 		/* Input string was negative number. */
419 		return -ERANGE;
420 	}
421 
422 	return val;
423 }
424 
425 long long int
spdk_strtoll(const char * nptr,int base)426 spdk_strtoll(const char *nptr, int base)
427 {
428 	long long val;
429 	char *endptr;
430 
431 	/* Since strtoll() can legitimately return 0, LLONG_MAX, or LLONG_MIN
432 	 * on both success and failure, the calling program should set errno
433 	 * to 0 before the call.
434 	 */
435 	errno = 0;
436 
437 	val = strtoll(nptr, &endptr, base);
438 
439 	if (!errno && *endptr != '\0') {
440 		/* Non integer character was found. */
441 		return -EINVAL;
442 	} else if (errno == ERANGE && (val == LLONG_MAX || val == LLONG_MIN)) {
443 		/* Overflow occurred. */
444 		return -ERANGE;
445 	} else if (errno != 0 && val == 0) {
446 		/* Other error occurred. */
447 		return -errno;
448 	} else if (val < 0) {
449 		/* Input string was negative number. */
450 		return -ERANGE;
451 	}
452 
453 	return val;
454 }
455 
456 void
spdk_strarray_free(char ** strarray)457 spdk_strarray_free(char **strarray)
458 {
459 	size_t i;
460 
461 	if (strarray == NULL) {
462 		return;
463 	}
464 
465 	for (i = 0; strarray[i] != NULL; i++) {
466 		free(strarray[i]);
467 	}
468 	free(strarray);
469 }
470 
471 char **
spdk_strarray_from_string(const char * str,const char * delim)472 spdk_strarray_from_string(const char *str, const char *delim)
473 {
474 	const char *c = str;
475 	size_t count = 0;
476 	char **result;
477 	size_t i;
478 
479 	assert(str != NULL);
480 	assert(delim != NULL);
481 
482 	/* Count number of entries. */
483 	for (;;) {
484 		const char *next = strpbrk(c, delim);
485 
486 		count++;
487 
488 		if (next == NULL) {
489 			break;
490 		}
491 
492 		c = next + 1;
493 	}
494 
495 	/* Account for the terminating NULL entry. */
496 	result = calloc(count + 1, sizeof(char *));
497 	if (result == NULL) {
498 		return NULL;
499 	}
500 
501 	c = str;
502 
503 	for (i = 0; i < count; i++) {
504 		const char *next = strpbrk(c, delim);
505 
506 		if (next == NULL) {
507 			result[i] = strdup(c);
508 		} else {
509 			result[i] = strndup(c, next - c);
510 		}
511 
512 		if (result[i] == NULL) {
513 			spdk_strarray_free(result);
514 			return NULL;
515 		}
516 
517 		if (next != NULL) {
518 			c = next + 1;
519 		}
520 	}
521 
522 	return result;
523 }
524 
525 char **
spdk_strarray_dup(const char ** strarray)526 spdk_strarray_dup(const char **strarray)
527 {
528 	size_t count, i;
529 	char **result;
530 
531 	assert(strarray != NULL);
532 
533 	for (count = 0; strarray[count] != NULL; count++)
534 		;
535 
536 	result = calloc(count + 1, sizeof(char *));
537 	if (result == NULL) {
538 		return NULL;
539 	}
540 
541 	for (i = 0; i < count; i++) {
542 		result[i] = strdup(strarray[i]);
543 		if (result[i] == NULL) {
544 			spdk_strarray_free(result);
545 			return NULL;
546 		}
547 	}
548 
549 	return result;
550 }
551 
552 int
spdk_strcpy_replace(char * dst,size_t size,const char * src,const char * search,const char * replace)553 spdk_strcpy_replace(char *dst, size_t size, const char *src, const char *search,
554 		    const char *replace)
555 {
556 	const char *p, *q;
557 	char *r;
558 	size_t c, search_size, replace_size, dst_size;
559 
560 	if (dst == NULL || src == NULL || search == NULL || replace == NULL) {
561 		return -EINVAL;
562 	}
563 
564 	search_size = strlen(search);
565 	replace_size = strlen(replace);
566 
567 	c = 0;
568 	for (p = strstr(src, search); p != NULL; p = strstr(p + search_size, search)) {
569 		c++;
570 	}
571 
572 	dst_size = strlen(src) + (replace_size - search_size) * c;
573 	if (dst_size >= size) {
574 		return -EINVAL;
575 	}
576 
577 	q = src;
578 	r = dst;
579 
580 	for (p = strstr(src, search); p != NULL; p = strstr(p + search_size, search)) {
581 		memcpy(r, q, p - q);
582 		r += p - q;
583 
584 		memcpy(r, replace, replace_size);
585 		r += replace_size;
586 
587 		q = p + search_size;
588 	}
589 
590 	memcpy(r, q, strlen(q));
591 	r += strlen(q);
592 
593 	*r = '\0';
594 
595 	return 0;
596 }
597