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