xref: /spdk/lib/util/string.c (revision 1fc4165fe9bf8512483356ad8e6d27f793f2e3db)
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 != NULL) {
345 		snprintf(buf, buflen, "%s", new_buffer);
346 		rc = 0;
347 	} else {
348 		rc = 1;
349 	}
350 #else
351 	rc = strerror_r(errnum, buf, buflen);
352 #endif
353 
354 	if (rc != 0) {
355 		snprintf(buf, buflen, "Unknown error %d", errnum);
356 	}
357 }
358 
359 int
360 spdk_parse_capacity(const char *cap_str, uint64_t *cap, bool *has_prefix)
361 {
362 	int rc;
363 	char bin_prefix;
364 
365 	rc = sscanf(cap_str, "%"SCNu64"%c", cap, &bin_prefix);
366 	if (rc == 1) {
367 		*has_prefix = false;
368 		return 0;
369 	} else if (rc == 0) {
370 		if (errno == 0) {
371 			/* No scanf matches - the string does not start with a digit */
372 			return -EINVAL;
373 		} else {
374 			/* Parsing error */
375 			return -errno;
376 		}
377 	}
378 
379 	*has_prefix = true;
380 	switch (bin_prefix) {
381 	case 'k':
382 	case 'K':
383 		*cap *= 1024;
384 		break;
385 	case 'm':
386 	case 'M':
387 		*cap *= 1024 * 1024;
388 		break;
389 	case 'g':
390 	case 'G':
391 		*cap *= 1024 * 1024 * 1024;
392 		break;
393 	default:
394 		return -EINVAL;
395 	}
396 
397 	return 0;
398 }
399 
400 bool
401 spdk_mem_all_zero(const void *data, size_t size)
402 {
403 	const uint8_t *buf = data;
404 
405 	while (size--) {
406 		if (*buf++ != 0) {
407 			return false;
408 		}
409 	}
410 
411 	return true;
412 }
413 
414 long int
415 spdk_strtol(const char *nptr, int base)
416 {
417 	long val;
418 	char *endptr;
419 
420 	/* Since strtoll() can legitimately return 0, LONG_MAX, or LONG_MIN
421 	 * on both success and failure, the calling program should set errno
422 	 * to 0 before the call.
423 	 */
424 	errno = 0;
425 
426 	val = strtol(nptr, &endptr, base);
427 
428 	if (!errno && *endptr != '\0') {
429 		/* Non integer character was found. */
430 		return -EINVAL;
431 	} else if (errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) {
432 		/* Overflow occurred. */
433 		return -ERANGE;
434 	} else if (errno != 0 && val == 0) {
435 		/* Other error occurred. */
436 		return -errno;
437 	} else if (val < 0) {
438 		/* Input string was negative number. */
439 		return -ERANGE;
440 	}
441 
442 	return val;
443 }
444 
445 long long int
446 spdk_strtoll(const char *nptr, int base)
447 {
448 	long long val;
449 	char *endptr;
450 
451 	/* Since strtoll() can legitimately return 0, LLONG_MAX, or LLONG_MIN
452 	 * on both success and failure, the calling program should set errno
453 	 * to 0 before the call.
454 	 */
455 	errno = 0;
456 
457 	val = strtoll(nptr, &endptr, base);
458 
459 	if (!errno && *endptr != '\0') {
460 		/* Non integer character was found. */
461 		return -EINVAL;
462 	} else if (errno == ERANGE && (val == LLONG_MAX || val == LLONG_MIN)) {
463 		/* Overflow occurred. */
464 		return -ERANGE;
465 	} else if (errno != 0 && val == 0) {
466 		/* Other error occurred. */
467 		return -errno;
468 	} else if (val < 0) {
469 		/* Input string was negative number. */
470 		return -ERANGE;
471 	}
472 
473 	return val;
474 }
475