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