• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
39 static const char *
next_word(const char ** state,size_t * len,const char * separators)40 next_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  */
71 char**
strv_from_argv(int argc,char ** argv)72 strv_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  */
111 char **
strv_from_string(const char * in,const char * separators,size_t * num_elements)112 strv_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  */
164 char *
strv_join(char ** strv,const char * joiner)165 strv_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  */
207 const char *
safe_basename(const char * filename)208 safe_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  */
238 char *
trunkname(const char * filename)239 trunkname(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