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