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