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