xref: /spdk/lib/util/string.c (revision 94a84ae98590bea46939eb1dcd7a9876bd393b54)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "spdk/string.h"
37 
38 char *
39 spdk_vsprintf_append_realloc(char *buffer, const char *format, va_list args)
40 {
41 	va_list args_copy;
42 	char *new_buffer;
43 	int orig_size = 0, new_size;
44 
45 	/* Original buffer size */
46 	if (buffer) {
47 		orig_size = strlen(buffer);
48 	}
49 
50 	/* Necessary buffer size */
51 	va_copy(args_copy, args);
52 	new_size = vsnprintf(NULL, 0, format, args_copy);
53 	va_end(args_copy);
54 
55 	if (new_size < 0) {
56 		return NULL;
57 	}
58 	new_size += orig_size + 1;
59 
60 	new_buffer = realloc(buffer, new_size);
61 	if (new_buffer == NULL) {
62 		return NULL;
63 	}
64 
65 	vsnprintf(new_buffer + orig_size, new_size - orig_size, format, args);
66 
67 	return new_buffer;
68 }
69 
70 char *
71 spdk_sprintf_append_realloc(char *buffer, const char *format, ...)
72 {
73 	va_list args;
74 	char *ret;
75 
76 	va_start(args, format);
77 	ret = spdk_vsprintf_append_realloc(buffer, format, args);
78 	va_end(args);
79 
80 	return ret;
81 }
82 
83 char *
84 spdk_vsprintf_alloc(const char *format, va_list args)
85 {
86 	return spdk_vsprintf_append_realloc(NULL, format, args);
87 }
88 
89 char *
90 spdk_sprintf_alloc(const char *format, ...)
91 {
92 	va_list args;
93 	char *ret;
94 
95 	va_start(args, format);
96 	ret = spdk_vsprintf_alloc(format, args);
97 	va_end(args);
98 
99 	return ret;
100 }
101 
102 char *
103 spdk_strlwr(char *s)
104 {
105 	char *p;
106 
107 	if (s == NULL) {
108 		return NULL;
109 	}
110 
111 	p = s;
112 	while (*p != '\0') {
113 		*p = tolower(*p);
114 		p++;
115 	}
116 
117 	return s;
118 }
119 
120 char *
121 spdk_strsepq(char **stringp, const char *delim)
122 {
123 	char *p, *q, *r;
124 	int quoted = 0, bslash = 0;
125 
126 	p = *stringp;
127 	if (p == NULL) {
128 		return NULL;
129 	}
130 
131 	r = q = p;
132 	while (*q != '\0' && *q != '\n') {
133 		/* eat quoted characters */
134 		if (bslash) {
135 			bslash = 0;
136 			*r++ = *q++;
137 			continue;
138 		} else if (quoted) {
139 			if (quoted == '"' && *q == '\\') {
140 				bslash = 1;
141 				q++;
142 				continue;
143 			} else if (*q == quoted) {
144 				quoted = 0;
145 				q++;
146 				continue;
147 			}
148 			*r++ = *q++;
149 			continue;
150 		} else if (*q == '\\') {
151 			bslash = 1;
152 			q++;
153 			continue;
154 		} else if (*q == '"' || *q == '\'') {
155 			quoted = *q;
156 			q++;
157 			continue;
158 		}
159 
160 		/* separator? */
161 		if (strchr(delim, *q) == NULL) {
162 			*r++ = *q++;
163 			continue;
164 		}
165 
166 		/* new string */
167 		q++;
168 		break;
169 	}
170 	*r = '\0';
171 
172 	/* skip tailer */
173 	while (*q != '\0' && strchr(delim, *q) != NULL) {
174 		q++;
175 	}
176 	if (*q != '\0') {
177 		*stringp = q;
178 	} else {
179 		*stringp = NULL;
180 	}
181 
182 	return p;
183 }
184 
185 char *
186 spdk_str_trim(char *s)
187 {
188 	char *p, *q;
189 
190 	if (s == NULL) {
191 		return NULL;
192 	}
193 
194 	/* remove header */
195 	p = s;
196 	while (*p != '\0' && isspace(*p)) {
197 		p++;
198 	}
199 
200 	/* remove tailer */
201 	q = p + strlen(p);
202 	while (q - 1 >= p && isspace(*(q - 1))) {
203 		q--;
204 		*q = '\0';
205 	}
206 
207 	/* if remove header, move */
208 	if (p != s) {
209 		q = s;
210 		while (*p != '\0') {
211 			*q++ = *p++;
212 		}
213 		*q = '\0';
214 	}
215 
216 	return s;
217 }
218 
219 void
220 spdk_strcpy_pad(void *dst, const char *src, size_t size, int pad)
221 {
222 	size_t len;
223 
224 	len = strlen(src);
225 	if (len < size) {
226 		memcpy(dst, src, len);
227 		memset((char *)dst + len, pad, size - len);
228 	} else {
229 		memcpy(dst, src, size);
230 	}
231 }
232 
233 size_t
234 spdk_strlen_pad(const void *str, size_t size, int pad)
235 {
236 	const uint8_t *start;
237 	const uint8_t *iter;
238 	uint8_t pad_byte;
239 
240 	pad_byte = (uint8_t)pad;
241 	start = (const uint8_t *)str;
242 
243 	if (size == 0) {
244 		return 0;
245 	}
246 
247 	iter = start + size - 1;
248 	while (1) {
249 		if (*iter != pad_byte) {
250 			return iter - start + 1;
251 		}
252 
253 		if (iter == start) {
254 			/* Hit the start of the string finding only pad_byte. */
255 			return 0;
256 		}
257 		iter--;
258 	}
259 }
260 
261 int
262 spdk_parse_ip_addr(char *ip, char **host, char **port)
263 {
264 	char *p;
265 
266 	if (ip == NULL) {
267 		return -EINVAL;
268 	}
269 
270 	*host = NULL;
271 	*port = NULL;
272 
273 	if (ip[0] == '[') {
274 		/* IPv6 */
275 		p = strchr(ip, ']');
276 		if (p == NULL) {
277 			return -EINVAL;
278 		}
279 		*host = &ip[1];
280 		*p = '\0';
281 
282 		p++;
283 		if (*p == '\0') {
284 			return 0;
285 		} else if (*p != ':') {
286 			return -EINVAL;
287 		}
288 
289 		p++;
290 		if (*p == '\0') {
291 			return 0;
292 		}
293 
294 		*port = p;
295 	} else {
296 		/* IPv4 */
297 		p = strchr(ip, ':');
298 		if (p == NULL) {
299 			*host = ip;
300 			return 0;
301 		}
302 
303 		*host = ip;
304 		*p = '\0';
305 
306 		p++;
307 		if (*p == '\0') {
308 			return 0;
309 		}
310 
311 		*port = p;
312 	}
313 
314 	return 0;
315 }
316 
317 size_t
318 spdk_str_chomp(char *s)
319 {
320 	size_t len = strlen(s);
321 	size_t removed = 0;
322 
323 	while (len > 0) {
324 		if (s[len - 1] != '\r' && s[len - 1] != '\n') {
325 			break;
326 		}
327 
328 		s[len - 1] = '\0';
329 		len--;
330 		removed++;
331 	}
332 
333 	return removed;
334 }
335 
336 void
337 spdk_strerror_r(int errnum, char *buf, size_t buflen)
338 {
339 	int rc;
340 
341 #if defined(__USE_GNU)
342 	char *new_buffer;
343 	new_buffer = strerror_r(errnum, buf, buflen);
344 	if (new_buffer == buf) {
345 		rc = 0;
346 	} else if (new_buffer != NULL) {
347 		snprintf(buf, buflen, "%s", new_buffer);
348 		rc = 0;
349 	} else {
350 		rc = 1;
351 	}
352 #else
353 	rc = strerror_r(errnum, buf, buflen);
354 #endif
355 
356 	if (rc != 0) {
357 		snprintf(buf, buflen, "Unknown error %d", errnum);
358 	}
359 }
360 
361 int
362 spdk_parse_capacity(const char *cap_str, uint64_t *cap, bool *has_prefix)
363 {
364 	int rc;
365 	char bin_prefix;
366 
367 	rc = sscanf(cap_str, "%"SCNu64"%c", cap, &bin_prefix);
368 	if (rc == 1) {
369 		*has_prefix = false;
370 		return 0;
371 	} else if (rc == 0) {
372 		if (errno == 0) {
373 			/* No scanf matches - the string does not start with a digit */
374 			return -EINVAL;
375 		} else {
376 			/* Parsing error */
377 			return -errno;
378 		}
379 	}
380 
381 	*has_prefix = true;
382 	switch (bin_prefix) {
383 	case 'k':
384 	case 'K':
385 		*cap *= 1024;
386 		break;
387 	case 'm':
388 	case 'M':
389 		*cap *= 1024 * 1024;
390 		break;
391 	case 'g':
392 	case 'G':
393 		*cap *= 1024 * 1024 * 1024;
394 		break;
395 	default:
396 		return -EINVAL;
397 	}
398 
399 	return 0;
400 }
401 
402 bool
403 spdk_mem_all_zero(const void *data, size_t size)
404 {
405 	const uint8_t *buf = data;
406 
407 	while (size--) {
408 		if (*buf++ != 0) {
409 			return false;
410 		}
411 	}
412 
413 	return true;
414 }
415 
416 long int
417 spdk_strtol(const char *nptr, int base)
418 {
419 	long val;
420 	char *endptr;
421 
422 	/* Since strtoll() can legitimately return 0, LONG_MAX, or LONG_MIN
423 	 * on both success and failure, the calling program should set errno
424 	 * to 0 before the call.
425 	 */
426 	errno = 0;
427 
428 	val = strtol(nptr, &endptr, base);
429 
430 	if (!errno && *endptr != '\0') {
431 		/* Non integer character was found. */
432 		return -EINVAL;
433 	} else if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) {
434 		/* Overflow occurred. */
435 		return -ERANGE;
436 	} else if (errno != 0 && val == 0) {
437 		/* Other error occurred. */
438 		return -errno;
439 	} else if (val < 0) {
440 		/* Input string was negative number. */
441 		return -ERANGE;
442 	}
443 
444 	return val;
445 }
446 
447 long long int
448 spdk_strtoll(const char *nptr, int base)
449 {
450 	long long val;
451 	char *endptr;
452 
453 	/* Since strtoll() can legitimately return 0, LLONG_MAX, or LLONG_MIN
454 	 * on both success and failure, the calling program should set errno
455 	 * to 0 before the call.
456 	 */
457 	errno = 0;
458 
459 	val = strtoll(nptr, &endptr, base);
460 
461 	if (!errno && *endptr != '\0') {
462 		/* Non integer character was found. */
463 		return -EINVAL;
464 	} else if (errno == ERANGE && (val == LLONG_MAX || val == LLONG_MIN)) {
465 		/* Overflow occurred. */
466 		return -ERANGE;
467 	} else if (errno != 0 && val == 0) {
468 		/* Other error occurred. */
469 		return -errno;
470 	} else if (val < 0) {
471 		/* Input string was negative number. */
472 		return -ERANGE;
473 	}
474 
475 	return val;
476 }
477