1a46c0ec8Sopenharmony_ci/*
2a46c0ec8Sopenharmony_ci * Copyright © 2008 Kristian Høgsberg
3a46c0ec8Sopenharmony_ci * Copyright © 2013-2015 Red Hat, Inc.
4a46c0ec8Sopenharmony_ci *
5a46c0ec8Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
6a46c0ec8Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
7a46c0ec8Sopenharmony_ci * to deal in the Software without restriction, including without limitation
8a46c0ec8Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9a46c0ec8Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
10a46c0ec8Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
11a46c0ec8Sopenharmony_ci *
12a46c0ec8Sopenharmony_ci * The above copyright notice and this permission notice (including the next
13a46c0ec8Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
14a46c0ec8Sopenharmony_ci * Software.
15a46c0ec8Sopenharmony_ci *
16a46c0ec8Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17a46c0ec8Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18a46c0ec8Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19a46c0ec8Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20a46c0ec8Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21a46c0ec8Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22a46c0ec8Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
23a46c0ec8Sopenharmony_ci */
24a46c0ec8Sopenharmony_ci
25a46c0ec8Sopenharmony_ci#pragma once
26a46c0ec8Sopenharmony_ci
27a46c0ec8Sopenharmony_ci#include "config.h"
28a46c0ec8Sopenharmony_ci
29a46c0ec8Sopenharmony_ci#include <assert.h>
30a46c0ec8Sopenharmony_ci#include <ctype.h>
31a46c0ec8Sopenharmony_ci#include <errno.h>
32a46c0ec8Sopenharmony_ci#include <limits.h>
33a46c0ec8Sopenharmony_ci#include <math.h>
34a46c0ec8Sopenharmony_ci#include <string.h>
35a46c0ec8Sopenharmony_ci#include <stdio.h>
36a46c0ec8Sopenharmony_ci#include <stdbool.h>
37a46c0ec8Sopenharmony_ci#include <stdlib.h>
38a46c0ec8Sopenharmony_ci#include <stdarg.h>
39a46c0ec8Sopenharmony_ci#ifdef HAVE_LOCALE_H
40a46c0ec8Sopenharmony_ci#include <locale.h>
41a46c0ec8Sopenharmony_ci#endif
42a46c0ec8Sopenharmony_ci#ifdef HAVE_XLOCALE_H
43a46c0ec8Sopenharmony_ci#include <xlocale.h>
44a46c0ec8Sopenharmony_ci#endif
45a46c0ec8Sopenharmony_ci
46a46c0ec8Sopenharmony_ci#include "util-macros.h"
47a46c0ec8Sopenharmony_ci
48a46c0ec8Sopenharmony_cistatic inline bool
49a46c0ec8Sopenharmony_cistreq(const char *str1, const char *str2)
50a46c0ec8Sopenharmony_ci{
51a46c0ec8Sopenharmony_ci	/* one NULL, one not NULL is always false */
52a46c0ec8Sopenharmony_ci	if (str1 && str2)
53a46c0ec8Sopenharmony_ci		return strcmp(str1, str2) == 0;
54a46c0ec8Sopenharmony_ci	return str1 == str2;
55a46c0ec8Sopenharmony_ci}
56a46c0ec8Sopenharmony_ci
57a46c0ec8Sopenharmony_cistatic inline bool
58a46c0ec8Sopenharmony_cistrneq(const char *str1, const char *str2, int n)
59a46c0ec8Sopenharmony_ci{
60a46c0ec8Sopenharmony_ci	/* one NULL, one not NULL is always false */
61a46c0ec8Sopenharmony_ci	if (str1 && str2)
62a46c0ec8Sopenharmony_ci		return strncmp(str1, str2, n) == 0;
63a46c0ec8Sopenharmony_ci	return str1 == str2;
64a46c0ec8Sopenharmony_ci}
65a46c0ec8Sopenharmony_ci
66a46c0ec8Sopenharmony_cistatic inline void *
67a46c0ec8Sopenharmony_cizalloc(size_t size)
68a46c0ec8Sopenharmony_ci{
69a46c0ec8Sopenharmony_ci	void *p;
70a46c0ec8Sopenharmony_ci
71a46c0ec8Sopenharmony_ci	/* We never need to alloc anything more than 1,5 MB so we can assume
72a46c0ec8Sopenharmony_ci	 * if we ever get above that something's going wrong */
73a46c0ec8Sopenharmony_ci	if (size > 1536 * 1024)
74a46c0ec8Sopenharmony_ci		assert(!"bug: internal malloc size limit exceeded");
75a46c0ec8Sopenharmony_ci
76a46c0ec8Sopenharmony_ci	p = calloc(1, size);
77a46c0ec8Sopenharmony_ci	if (!p)
78a46c0ec8Sopenharmony_ci		abort();
79a46c0ec8Sopenharmony_ci
80a46c0ec8Sopenharmony_ci	return p;
81a46c0ec8Sopenharmony_ci}
82a46c0ec8Sopenharmony_ci
83a46c0ec8Sopenharmony_ci/**
84a46c0ec8Sopenharmony_ci * strdup guaranteed to succeed. If the input string is NULL, the output
85a46c0ec8Sopenharmony_ci * string is NULL. If the input string is a string pointer, we strdup or
86a46c0ec8Sopenharmony_ci * abort on failure.
87a46c0ec8Sopenharmony_ci */
88a46c0ec8Sopenharmony_cistatic inline char*
89a46c0ec8Sopenharmony_cisafe_strdup(const char *str)
90a46c0ec8Sopenharmony_ci{
91a46c0ec8Sopenharmony_ci	char *s;
92a46c0ec8Sopenharmony_ci
93a46c0ec8Sopenharmony_ci	if (!str)
94a46c0ec8Sopenharmony_ci		return NULL;
95a46c0ec8Sopenharmony_ci
96a46c0ec8Sopenharmony_ci	s = strdup(str);
97a46c0ec8Sopenharmony_ci	if (!s)
98a46c0ec8Sopenharmony_ci		abort();
99a46c0ec8Sopenharmony_ci	return s;
100a46c0ec8Sopenharmony_ci}
101a46c0ec8Sopenharmony_ci
102a46c0ec8Sopenharmony_ci/**
103a46c0ec8Sopenharmony_ci * Simple wrapper for asprintf that ensures the passed in-pointer is set
104a46c0ec8Sopenharmony_ci * to NULL upon error.
105a46c0ec8Sopenharmony_ci * The standard asprintf() call does not guarantee the passed in pointer
106a46c0ec8Sopenharmony_ci * will be NULL'ed upon failure, whereas this wrapper does.
107a46c0ec8Sopenharmony_ci *
108a46c0ec8Sopenharmony_ci * @param strp pointer to set to newly allocated string.
109a46c0ec8Sopenharmony_ci * This pointer should be passed to free() to release when done.
110a46c0ec8Sopenharmony_ci * @param fmt the format string to use for printing.
111a46c0ec8Sopenharmony_ci * @return The number of bytes printed (excluding the null byte terminator)
112a46c0ec8Sopenharmony_ci * upon success or -1 upon failure. In the case of failure the pointer is set
113a46c0ec8Sopenharmony_ci * to NULL.
114a46c0ec8Sopenharmony_ci */
115a46c0ec8Sopenharmony_ci__attribute__ ((format (printf, 2, 3)))
116a46c0ec8Sopenharmony_cistatic inline int
117a46c0ec8Sopenharmony_cixasprintf(char **strp, const char *fmt, ...)
118a46c0ec8Sopenharmony_ci{
119a46c0ec8Sopenharmony_ci	int rc = 0;
120a46c0ec8Sopenharmony_ci	va_list args;
121a46c0ec8Sopenharmony_ci
122a46c0ec8Sopenharmony_ci	va_start(args, fmt);
123a46c0ec8Sopenharmony_ci	rc = vasprintf(strp, fmt, args);
124a46c0ec8Sopenharmony_ci	va_end(args);
125a46c0ec8Sopenharmony_ci	if ((rc == -1) && strp)
126a46c0ec8Sopenharmony_ci		*strp = NULL;
127a46c0ec8Sopenharmony_ci
128a46c0ec8Sopenharmony_ci	return rc;
129a46c0ec8Sopenharmony_ci}
130a46c0ec8Sopenharmony_ci
131a46c0ec8Sopenharmony_ci__attribute__ ((format (printf, 2, 0)))
132a46c0ec8Sopenharmony_cistatic inline int
133a46c0ec8Sopenharmony_cixvasprintf(char **strp, const char *fmt, va_list args)
134a46c0ec8Sopenharmony_ci{
135a46c0ec8Sopenharmony_ci	int rc = 0;
136a46c0ec8Sopenharmony_ci	rc = vasprintf(strp, fmt, args);
137a46c0ec8Sopenharmony_ci	if ((rc == -1) && strp)
138a46c0ec8Sopenharmony_ci		*strp = NULL;
139a46c0ec8Sopenharmony_ci
140a46c0ec8Sopenharmony_ci	return rc;
141a46c0ec8Sopenharmony_ci}
142a46c0ec8Sopenharmony_ci
143a46c0ec8Sopenharmony_cistatic inline bool
144a46c0ec8Sopenharmony_cisafe_atoi_base(const char *str, int *val, int base)
145a46c0ec8Sopenharmony_ci{
146a46c0ec8Sopenharmony_ci	assert(str != NULL);
147a46c0ec8Sopenharmony_ci
148a46c0ec8Sopenharmony_ci	char *endptr;
149a46c0ec8Sopenharmony_ci	long v;
150a46c0ec8Sopenharmony_ci
151a46c0ec8Sopenharmony_ci	assert(base == 10 || base == 16 || base == 8);
152a46c0ec8Sopenharmony_ci
153a46c0ec8Sopenharmony_ci	errno = 0;
154a46c0ec8Sopenharmony_ci	v = strtol(str, &endptr, base);
155a46c0ec8Sopenharmony_ci	if (errno > 0)
156a46c0ec8Sopenharmony_ci		return false;
157a46c0ec8Sopenharmony_ci	if (str == endptr)
158a46c0ec8Sopenharmony_ci		return false;
159a46c0ec8Sopenharmony_ci	if (*str != '\0' && *endptr != '\0')
160a46c0ec8Sopenharmony_ci		return false;
161a46c0ec8Sopenharmony_ci
162a46c0ec8Sopenharmony_ci	if (v > INT_MAX || v < INT_MIN)
163a46c0ec8Sopenharmony_ci		return false;
164a46c0ec8Sopenharmony_ci
165a46c0ec8Sopenharmony_ci	*val = v;
166a46c0ec8Sopenharmony_ci	return true;
167a46c0ec8Sopenharmony_ci}
168a46c0ec8Sopenharmony_ci
169a46c0ec8Sopenharmony_cistatic inline bool
170a46c0ec8Sopenharmony_cisafe_atoi(const char *str, int *val)
171a46c0ec8Sopenharmony_ci{
172a46c0ec8Sopenharmony_ci	assert(str != NULL);
173a46c0ec8Sopenharmony_ci	return safe_atoi_base(str, val, 10);
174a46c0ec8Sopenharmony_ci}
175a46c0ec8Sopenharmony_ci
176a46c0ec8Sopenharmony_cistatic inline bool
177a46c0ec8Sopenharmony_cisafe_atou_base(const char *str, unsigned int *val, int base)
178a46c0ec8Sopenharmony_ci{
179a46c0ec8Sopenharmony_ci	assert(str != NULL);
180a46c0ec8Sopenharmony_ci
181a46c0ec8Sopenharmony_ci	char *endptr;
182a46c0ec8Sopenharmony_ci	unsigned long v;
183a46c0ec8Sopenharmony_ci
184a46c0ec8Sopenharmony_ci	assert(base == 10 || base == 16 || base == 8);
185a46c0ec8Sopenharmony_ci
186a46c0ec8Sopenharmony_ci	errno = 0;
187a46c0ec8Sopenharmony_ci	v = strtoul(str, &endptr, base);
188a46c0ec8Sopenharmony_ci	if (errno > 0)
189a46c0ec8Sopenharmony_ci		return false;
190a46c0ec8Sopenharmony_ci	if (str == endptr)
191a46c0ec8Sopenharmony_ci		return false;
192a46c0ec8Sopenharmony_ci	if (*str != '\0' && *endptr != '\0')
193a46c0ec8Sopenharmony_ci		return false;
194a46c0ec8Sopenharmony_ci
195a46c0ec8Sopenharmony_ci	if ((long)v < 0)
196a46c0ec8Sopenharmony_ci		return false;
197a46c0ec8Sopenharmony_ci
198a46c0ec8Sopenharmony_ci	*val = v;
199a46c0ec8Sopenharmony_ci	return true;
200a46c0ec8Sopenharmony_ci}
201a46c0ec8Sopenharmony_ci
202a46c0ec8Sopenharmony_cistatic inline bool
203a46c0ec8Sopenharmony_cisafe_atou(const char *str, unsigned int *val)
204a46c0ec8Sopenharmony_ci{
205a46c0ec8Sopenharmony_ci	assert(str != NULL);
206a46c0ec8Sopenharmony_ci	return safe_atou_base(str, val, 10);
207a46c0ec8Sopenharmony_ci}
208a46c0ec8Sopenharmony_ci
209a46c0ec8Sopenharmony_cistatic inline bool
210a46c0ec8Sopenharmony_cisafe_atod(const char *str, double *val)
211a46c0ec8Sopenharmony_ci{
212a46c0ec8Sopenharmony_ci	assert(str != NULL);
213a46c0ec8Sopenharmony_ci
214a46c0ec8Sopenharmony_ci	char *endptr;
215a46c0ec8Sopenharmony_ci	double v;
216a46c0ec8Sopenharmony_ci#ifdef HAVE_LOCALE_H
217a46c0ec8Sopenharmony_ci	locale_t c_locale;
218a46c0ec8Sopenharmony_ci#endif
219a46c0ec8Sopenharmony_ci	size_t slen = strlen(str);
220a46c0ec8Sopenharmony_ci
221a46c0ec8Sopenharmony_ci	/* We don't have a use-case where we want to accept hex for a double
222a46c0ec8Sopenharmony_ci	 * or any of the other values strtod can parse */
223a46c0ec8Sopenharmony_ci	for (size_t i = 0; i < slen; i++) {
224a46c0ec8Sopenharmony_ci		char c = str[i];
225a46c0ec8Sopenharmony_ci
226a46c0ec8Sopenharmony_ci		if (isdigit(c))
227a46c0ec8Sopenharmony_ci		       continue;
228a46c0ec8Sopenharmony_ci		switch(c) {
229a46c0ec8Sopenharmony_ci		case '+':
230a46c0ec8Sopenharmony_ci		case '-':
231a46c0ec8Sopenharmony_ci		case '.':
232a46c0ec8Sopenharmony_ci			break;
233a46c0ec8Sopenharmony_ci		default:
234a46c0ec8Sopenharmony_ci			return false;
235a46c0ec8Sopenharmony_ci		}
236a46c0ec8Sopenharmony_ci	}
237a46c0ec8Sopenharmony_ci
238a46c0ec8Sopenharmony_ci#ifdef HAVE_LOCALE_H
239a46c0ec8Sopenharmony_ci	/* Create a "C" locale to force strtod to use '.' as separator */
240a46c0ec8Sopenharmony_ci	c_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
241a46c0ec8Sopenharmony_ci	if (c_locale == (locale_t)0)
242a46c0ec8Sopenharmony_ci		return false;
243a46c0ec8Sopenharmony_ci
244a46c0ec8Sopenharmony_ci	errno = 0;
245a46c0ec8Sopenharmony_ci	v = strtod_l(str, &endptr, c_locale);
246a46c0ec8Sopenharmony_ci	freelocale(c_locale);
247a46c0ec8Sopenharmony_ci#else
248a46c0ec8Sopenharmony_ci	/* No locale support in provided libc, assume it already uses '.' */
249a46c0ec8Sopenharmony_ci	errno = 0;
250a46c0ec8Sopenharmony_ci	v = strtod(str, &endptr);
251a46c0ec8Sopenharmony_ci#endif
252a46c0ec8Sopenharmony_ci	if (errno > 0)
253a46c0ec8Sopenharmony_ci		return false;
254a46c0ec8Sopenharmony_ci	if (str == endptr)
255a46c0ec8Sopenharmony_ci		return false;
256a46c0ec8Sopenharmony_ci	if (*str != '\0' && *endptr != '\0')
257a46c0ec8Sopenharmony_ci		return false;
258a46c0ec8Sopenharmony_ci	if (v != 0.0 && !isnormal(v))
259a46c0ec8Sopenharmony_ci		return false;
260a46c0ec8Sopenharmony_ci
261a46c0ec8Sopenharmony_ci	*val = v;
262a46c0ec8Sopenharmony_ci	return true;
263a46c0ec8Sopenharmony_ci}
264a46c0ec8Sopenharmony_ci
265a46c0ec8Sopenharmony_cichar **strv_from_argv(int argc, char **argv);
266a46c0ec8Sopenharmony_cichar **strv_from_string(const char *in, const char *separator, size_t *num_elements);
267a46c0ec8Sopenharmony_cichar *strv_join(char **strv, const char *joiner);
268a46c0ec8Sopenharmony_ci
269a46c0ec8Sopenharmony_cistatic inline void
270a46c0ec8Sopenharmony_cistrv_free(char **strv) {
271a46c0ec8Sopenharmony_ci	char **s = strv;
272a46c0ec8Sopenharmony_ci
273a46c0ec8Sopenharmony_ci	if (!strv)
274a46c0ec8Sopenharmony_ci		return;
275a46c0ec8Sopenharmony_ci
276a46c0ec8Sopenharmony_ci	while (*s != NULL) {
277a46c0ec8Sopenharmony_ci		free(*s);
278a46c0ec8Sopenharmony_ci		*s = (char*)0x1; /* detect use-after-free */
279a46c0ec8Sopenharmony_ci		s++;
280a46c0ec8Sopenharmony_ci	}
281a46c0ec8Sopenharmony_ci
282a46c0ec8Sopenharmony_ci	free (strv);
283a46c0ec8Sopenharmony_ci}
284a46c0ec8Sopenharmony_ci
285a46c0ec8Sopenharmony_ci/**
286a46c0ec8Sopenharmony_ci * parse a string containing a list of doubles into a double array.
287a46c0ec8Sopenharmony_ci *
288a46c0ec8Sopenharmony_ci * @param in string to parse
289a46c0ec8Sopenharmony_ci * @param separator string used to separate double in list e.g. ","
290a46c0ec8Sopenharmony_ci * @param result double array
291a46c0ec8Sopenharmony_ci * @param length length of double array
292a46c0ec8Sopenharmony_ci * @return true when parsed successfully otherwise false
293a46c0ec8Sopenharmony_ci */
294a46c0ec8Sopenharmony_cistatic inline double *
295a46c0ec8Sopenharmony_cidouble_array_from_string(const char *in,
296a46c0ec8Sopenharmony_ci			 const char *separator,
297a46c0ec8Sopenharmony_ci			 size_t *length)
298a46c0ec8Sopenharmony_ci{
299a46c0ec8Sopenharmony_ci	assert(in != NULL);
300a46c0ec8Sopenharmony_ci	assert(separator != NULL);
301a46c0ec8Sopenharmony_ci	assert(length != NULL);
302a46c0ec8Sopenharmony_ci
303a46c0ec8Sopenharmony_ci	double *result = NULL;
304a46c0ec8Sopenharmony_ci	*length = 0;
305a46c0ec8Sopenharmony_ci
306a46c0ec8Sopenharmony_ci	size_t nelem;
307a46c0ec8Sopenharmony_ci	char **strv = strv_from_string(in, separator, &nelem);
308a46c0ec8Sopenharmony_ci	if (!strv)
309a46c0ec8Sopenharmony_ci		return result;
310a46c0ec8Sopenharmony_ci
311a46c0ec8Sopenharmony_ci	double *numv = zalloc(sizeof(double) * nelem);
312a46c0ec8Sopenharmony_ci	for (size_t idx = 0; idx < nelem; idx++) {
313a46c0ec8Sopenharmony_ci		double val;
314a46c0ec8Sopenharmony_ci		if (!safe_atod(strv[idx], &val))
315a46c0ec8Sopenharmony_ci			goto out;
316a46c0ec8Sopenharmony_ci
317a46c0ec8Sopenharmony_ci		numv[idx] = val;
318a46c0ec8Sopenharmony_ci	}
319a46c0ec8Sopenharmony_ci
320a46c0ec8Sopenharmony_ci	result = numv;
321a46c0ec8Sopenharmony_ci	numv = NULL;
322a46c0ec8Sopenharmony_ci	*length = nelem;
323a46c0ec8Sopenharmony_ci
324a46c0ec8Sopenharmony_ciout:
325a46c0ec8Sopenharmony_ci	strv_free(strv);
326a46c0ec8Sopenharmony_ci	free(numv);
327a46c0ec8Sopenharmony_ci	return result;
328a46c0ec8Sopenharmony_ci}
329a46c0ec8Sopenharmony_ci
330a46c0ec8Sopenharmony_cistruct key_value_str{
331a46c0ec8Sopenharmony_ci	char *key;
332a46c0ec8Sopenharmony_ci	char *value;
333a46c0ec8Sopenharmony_ci};
334a46c0ec8Sopenharmony_ci
335a46c0ec8Sopenharmony_cistruct key_value_double {
336a46c0ec8Sopenharmony_ci	double key;
337a46c0ec8Sopenharmony_ci	double value;
338a46c0ec8Sopenharmony_ci};
339a46c0ec8Sopenharmony_ci
340a46c0ec8Sopenharmony_cistatic inline ssize_t
341a46c0ec8Sopenharmony_cikv_double_from_string(const char *string,
342a46c0ec8Sopenharmony_ci		      const char *pair_separator,
343a46c0ec8Sopenharmony_ci		      const char *kv_separator,
344a46c0ec8Sopenharmony_ci		      struct key_value_double **result_out)
345a46c0ec8Sopenharmony_ci
346a46c0ec8Sopenharmony_ci{
347a46c0ec8Sopenharmony_ci	struct key_value_double *result = NULL;
348a46c0ec8Sopenharmony_ci
349a46c0ec8Sopenharmony_ci	if (!pair_separator || pair_separator[0] == '\0' ||
350a46c0ec8Sopenharmony_ci	    !kv_separator || kv_separator[0] == '\0')
351a46c0ec8Sopenharmony_ci		return -1;
352a46c0ec8Sopenharmony_ci
353a46c0ec8Sopenharmony_ci	size_t npairs;
354a46c0ec8Sopenharmony_ci	char **pairs = strv_from_string(string, pair_separator, &npairs);
355a46c0ec8Sopenharmony_ci	if (!pairs || npairs == 0)
356a46c0ec8Sopenharmony_ci		goto error;
357a46c0ec8Sopenharmony_ci
358a46c0ec8Sopenharmony_ci	result = zalloc(npairs * sizeof *result);
359a46c0ec8Sopenharmony_ci
360a46c0ec8Sopenharmony_ci	for (size_t idx = 0; idx < npairs; idx++) {
361a46c0ec8Sopenharmony_ci		char *pair = pairs[idx];
362a46c0ec8Sopenharmony_ci		size_t nelem;
363a46c0ec8Sopenharmony_ci		char **kv = strv_from_string(pair, kv_separator, &nelem);
364a46c0ec8Sopenharmony_ci		double k, v;
365a46c0ec8Sopenharmony_ci
366a46c0ec8Sopenharmony_ci		if (!kv || nelem != 2 ||
367a46c0ec8Sopenharmony_ci		    !safe_atod(kv[0], &k) ||
368a46c0ec8Sopenharmony_ci		    !safe_atod(kv[1], &v)) {
369a46c0ec8Sopenharmony_ci			strv_free(kv);
370a46c0ec8Sopenharmony_ci			goto error;
371a46c0ec8Sopenharmony_ci		}
372a46c0ec8Sopenharmony_ci
373a46c0ec8Sopenharmony_ci		result[idx].key = k;
374a46c0ec8Sopenharmony_ci		result[idx].value = v;
375a46c0ec8Sopenharmony_ci
376a46c0ec8Sopenharmony_ci		strv_free(kv);
377a46c0ec8Sopenharmony_ci	}
378a46c0ec8Sopenharmony_ci
379a46c0ec8Sopenharmony_ci	strv_free(pairs);
380a46c0ec8Sopenharmony_ci
381a46c0ec8Sopenharmony_ci	*result_out = result;
382a46c0ec8Sopenharmony_ci
383a46c0ec8Sopenharmony_ci	return npairs;
384a46c0ec8Sopenharmony_ci
385a46c0ec8Sopenharmony_cierror:
386a46c0ec8Sopenharmony_ci	strv_free(pairs);
387a46c0ec8Sopenharmony_ci	free(result);
388a46c0ec8Sopenharmony_ci	return -1;
389a46c0ec8Sopenharmony_ci}
390a46c0ec8Sopenharmony_ci
391a46c0ec8Sopenharmony_ci/**
392a46c0ec8Sopenharmony_ci * Strip any of the characters in what from the beginning and end of the
393a46c0ec8Sopenharmony_ci * input string.
394a46c0ec8Sopenharmony_ci *
395a46c0ec8Sopenharmony_ci * @return a newly allocated string with none of "what" at the beginning or
396a46c0ec8Sopenharmony_ci * end of string
397a46c0ec8Sopenharmony_ci */
398a46c0ec8Sopenharmony_cistatic inline char *
399a46c0ec8Sopenharmony_cistrstrip(const char *input, const char *what)
400a46c0ec8Sopenharmony_ci{
401a46c0ec8Sopenharmony_ci	assert(input != NULL);
402a46c0ec8Sopenharmony_ci
403a46c0ec8Sopenharmony_ci	char *str, *last;
404a46c0ec8Sopenharmony_ci
405a46c0ec8Sopenharmony_ci	str = safe_strdup(&input[strspn(input, what)]);
406a46c0ec8Sopenharmony_ci
407a46c0ec8Sopenharmony_ci	last = str;
408a46c0ec8Sopenharmony_ci
409a46c0ec8Sopenharmony_ci	for (char *c = str; *c != '\0'; c++) {
410a46c0ec8Sopenharmony_ci		if (!strchr(what, *c))
411a46c0ec8Sopenharmony_ci			last = c + 1;
412a46c0ec8Sopenharmony_ci	}
413a46c0ec8Sopenharmony_ci
414a46c0ec8Sopenharmony_ci	*last = '\0';
415a46c0ec8Sopenharmony_ci
416a46c0ec8Sopenharmony_ci	return str;
417a46c0ec8Sopenharmony_ci}
418a46c0ec8Sopenharmony_ci
419a46c0ec8Sopenharmony_ci/**
420a46c0ec8Sopenharmony_ci * Return true if str ends in suffix, false otherwise. If the suffix is the
421a46c0ec8Sopenharmony_ci * empty string, strendswith() always returns false.
422a46c0ec8Sopenharmony_ci */
423a46c0ec8Sopenharmony_cistatic inline bool
424a46c0ec8Sopenharmony_cistrendswith(const char *str, const char *suffix)
425a46c0ec8Sopenharmony_ci{
426a46c0ec8Sopenharmony_ci	if (str == NULL)
427a46c0ec8Sopenharmony_ci		return false;
428a46c0ec8Sopenharmony_ci
429a46c0ec8Sopenharmony_ci	size_t slen = strlen(str);
430a46c0ec8Sopenharmony_ci	size_t suffixlen = strlen(suffix);
431a46c0ec8Sopenharmony_ci	size_t offset;
432a46c0ec8Sopenharmony_ci
433a46c0ec8Sopenharmony_ci	if (slen == 0 || suffixlen == 0 || suffixlen > slen)
434a46c0ec8Sopenharmony_ci		return false;
435a46c0ec8Sopenharmony_ci
436a46c0ec8Sopenharmony_ci	offset = slen - suffixlen;
437a46c0ec8Sopenharmony_ci	return strneq(&str[offset], suffix, suffixlen);
438a46c0ec8Sopenharmony_ci}
439a46c0ec8Sopenharmony_ci
440a46c0ec8Sopenharmony_cistatic inline bool
441a46c0ec8Sopenharmony_cistrstartswith(const char *str, const char *prefix)
442a46c0ec8Sopenharmony_ci{
443a46c0ec8Sopenharmony_ci	if (str == NULL)
444a46c0ec8Sopenharmony_ci		return false;
445a46c0ec8Sopenharmony_ci
446a46c0ec8Sopenharmony_ci	size_t prefixlen = strlen(prefix);
447a46c0ec8Sopenharmony_ci
448a46c0ec8Sopenharmony_ci	return prefixlen > 0 ? strneq(str, prefix, strlen(prefix)) : false;
449a46c0ec8Sopenharmony_ci}
450a46c0ec8Sopenharmony_ci
451a46c0ec8Sopenharmony_ciconst char *
452a46c0ec8Sopenharmony_cisafe_basename(const char *filename);
453a46c0ec8Sopenharmony_ci
454a46c0ec8Sopenharmony_cichar *
455a46c0ec8Sopenharmony_citrunkname(const char *filename);
456a46c0ec8Sopenharmony_ci
457a46c0ec8Sopenharmony_ci/**
458a46c0ec8Sopenharmony_ci * Return a copy of str with all % converted to %% to make the string
459a46c0ec8Sopenharmony_ci * acceptable as printf format.
460a46c0ec8Sopenharmony_ci */
461a46c0ec8Sopenharmony_cistatic inline char *
462a46c0ec8Sopenharmony_cistr_sanitize(const char *str)
463a46c0ec8Sopenharmony_ci{
464a46c0ec8Sopenharmony_ci	if (!str)
465a46c0ec8Sopenharmony_ci		return NULL;
466a46c0ec8Sopenharmony_ci
467a46c0ec8Sopenharmony_ci	if (!strchr(str, '%'))
468a46c0ec8Sopenharmony_ci		return strdup(str);
469a46c0ec8Sopenharmony_ci
470a46c0ec8Sopenharmony_ci	size_t slen = min(strlen(str), 512);
471a46c0ec8Sopenharmony_ci	char *sanitized = zalloc(2 * slen + 1);
472a46c0ec8Sopenharmony_ci	const char *src = str;
473a46c0ec8Sopenharmony_ci	char *dst = sanitized;
474a46c0ec8Sopenharmony_ci
475a46c0ec8Sopenharmony_ci	for (size_t i = 0; i < slen; i++) {
476a46c0ec8Sopenharmony_ci		if (*src == '%')
477a46c0ec8Sopenharmony_ci			*dst++ = '%';
478a46c0ec8Sopenharmony_ci		*dst++ = *src++;
479a46c0ec8Sopenharmony_ci	}
480a46c0ec8Sopenharmony_ci	*dst = '\0';
481a46c0ec8Sopenharmony_ci
482a46c0ec8Sopenharmony_ci	return sanitized;
483a46c0ec8Sopenharmony_ci}
484