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