xref: /third_party/libinput/src/util-strings.c (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#include "config.h"
26
27#include "util-strings.h"
28
29/**
30 * Return the next word in a string pointed to by state before the first
31 * separator character. Call repeatedly to tokenize a whole string.
32 *
33 * @param state Current state
34 * @param len String length of the word returned
35 * @param separators List of separator characters
36 *
37 * @return The first word in *state, NOT null-terminated
38 */
39static const char *
40next_word(const char **state, size_t *len, const char *separators)
41{
42	assert(state != NULL);
43
44	const char *next = *state;
45	size_t l;
46
47	if (!*next)
48		return NULL;
49
50	next += strspn(next, separators);
51	if (!*next) {
52		*state = next;
53		return NULL;
54	}
55
56	l = strcspn(next, separators);
57	*state = next + l;
58	*len = l;
59
60	return next;
61}
62
63/**
64 * Return a null-terminated string array with the contents of argv
65 * duplicated.
66 *
67 * Use strv_free() to free the array.
68 *
69 * @return A null-terminated string array or NULL on errors
70 */
71char**
72strv_from_argv(int argc, char **argv)
73{
74	char **strv = NULL;
75
76	assert(argc >= 0);
77	assert(argv != NULL);
78
79	if (argc == 0)
80		return NULL;
81
82	strv = zalloc((argc + 1) * sizeof *strv);
83	for (int i = 0; i < argc; i++) {
84		char *copy = safe_strdup(argv[i]);
85		if (!copy) {
86			strv_free(strv);
87			return NULL;
88		}
89		strv[i] = copy;
90	}
91	return strv;
92}
93
94/**
95 * Return a null-terminated string array with the tokens in the input
96 * string, e.g. "one two\tthree" with a separator list of " \t" will return
97 * an array [ "one", "two", "three", NULL ] and num elements 3.
98 *
99 * Use strv_free() to free the array.
100 *
101 * Another example:
102 *   result = strv_from_string("+1-2++3--4++-+5-+-", "+-", &nelem)
103 *   result == [ "1", "2", "3", "4", "5", NULL ] and nelem == 5
104 *
105 * @param in Input string
106 * @param separators List of separator characters
107 * @param num_elements Number of elements found in the input string
108 *
109 * @return A null-terminated string array or NULL on errors
110 */
111char **
112strv_from_string(const char *in, const char *separators, size_t *num_elements)
113{
114	assert(in != NULL);
115	assert(separators != NULL);
116	assert(num_elements != NULL);
117
118	const char *s = in;
119	size_t l, nelems = 0;
120	while (next_word(&s, &l, separators) != NULL)
121		nelems++;
122
123	if (nelems == 0) {
124		*num_elements = 0;
125		return NULL;
126	}
127
128	size_t strv_len = nelems + 1; /* NULL-terminated */
129	char **strv = zalloc(strv_len * sizeof *strv);
130
131	size_t idx = 0;
132	const char *word;
133	s = in;
134	while ((word = next_word(&s, &l, separators)) != NULL) {
135		char *copy = strndup(word, l);
136		if (!copy) {
137			strv_free(strv);
138			*num_elements = 0;
139			return NULL;
140		}
141
142		strv[idx++] = copy;
143	}
144
145	*num_elements = nelems;
146
147	return strv;
148}
149
150/**
151 * Return a newly allocated string with all elements joined by the
152 * joiner, same as Python's string.join() basically.
153 * A strv of ["one", "two", "three", NULL] with a joiner of ", " results
154 * in "one, two, three".
155 *
156 * An empty strv ([NULL]) returns NULL, same for passing NULL as either
157 * argument.
158 *
159 * @param strv Input string array
160 * @param joiner Joiner between the elements in the final string
161 *
162 * @return A null-terminated string joining all elements
163 */
164char *
165strv_join(char **strv, const char *joiner)
166{
167	assert(strv != NULL);
168
169	char **s;
170	char *str;
171	size_t slen = 0;
172	size_t count = 0;
173
174	if (!strv || !joiner)
175		return NULL;
176
177	if (strv[0] == NULL)
178		return NULL;
179
180	for (s = strv, count = 0; *s; s++, count++) {
181		slen += strlen(*s);
182	}
183
184	assert(slen < 1000);
185	assert(strlen(joiner) < 1000);
186	assert(count > 0);
187	assert(count < 100);
188
189	slen += (count - 1) * strlen(joiner);
190
191	str = zalloc(slen + 1); /* trailing \0 */
192	for (s = strv; *s; s++) {
193		strcat(str, *s);
194		--count;
195		if (count > 0)
196			strcat(str, joiner);
197	}
198
199	return str;
200}
201
202/**
203 * Return a pointer to the basename within filename.
204 * If the filename the empty string or a directory (i.e. the last char of
205 * filename is '/') NULL is returned.
206 */
207const char *
208safe_basename(const char *filename)
209{
210	assert(filename != NULL);
211
212	const char *basename;
213
214	if (*filename == '\0')
215		return NULL;
216
217	basename = strrchr(filename, '/');
218	if (basename == NULL)
219		return filename;
220
221	if (*(basename + 1) == '\0')
222		return NULL;
223
224	return basename + 1;
225}
226
227/**
228 * Similar to basename() but returns the trunk only without the (last)
229 * trailing suffix, so that:
230 *
231 * - foo.c returns foo
232 * - foo.a.b returns foo.a
233 * - foo returns foo
234 * - foo/ returns ""
235 *
236 * @return an allocated string representing the trunk name of the file
237 */
238char *
239trunkname(const char *filename)
240{
241	assert(filename != NULL);
242
243	const char *base = safe_basename(filename);
244	char *suffix;
245
246	if (base == NULL)
247		return safe_strdup("");
248
249	suffix = rindex(base, '.');
250	if (suffix == NULL)
251		return safe_strdup(base);
252	else
253		return strndup(base, suffix-base);
254}
255