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