1 /*
2 * librdkafka - The Apache Kafka C/C++ library
3 *
4 * Copyright (c) 2016 Magnus Edenhill
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29
30 #include "rd.h"
31 #include "rdstring.h"
32 #include "rdunittest.h"
33
34 #include <ctype.h>
35
36
37 /**
38 * @brief Render string \p template using \p callback for key lookups.
39 *
40 * Keys in template follow the %{keyname} syntax.
41 *
42 * The \p callback must not write more than \p size bytes to \p buf, must
43 * should return the number of bytes it wanted to write (which will indicate
44 * a truncated write).
45 * If the key is not found -1 should be returned (which fails the rendering).
46 *
47 * @returns number of written bytes to \p dest,
48 * or -1 on failure (errstr is written)
49 */
rd_string_render(const char * template,char * errstr,size_t errstr_size,ssize_t (* callback)(const char * key,char * buf,size_t size,void * opaque),void * opaque)50 char *rd_string_render (const char *template,
51 char *errstr, size_t errstr_size,
52 ssize_t (*callback) (const char *key,
53 char *buf, size_t size,
54 void *opaque),
55 void *opaque) {
56 const char *s = template;
57 const char *tend = template + strlen(template);
58 size_t size = 256;
59 char *buf;
60 size_t of = 0;
61
62 buf = rd_malloc(size);
63
64 #define _remain() (size - of - 1)
65 #define _assure_space(SZ) do { \
66 if (of + (SZ) + 1 >= size) { \
67 size = (size + (SZ) + 1) * 2; \
68 buf = rd_realloc(buf, size); \
69 } \
70 } while (0)
71
72 #define _do_write(PTR,SZ) do { \
73 _assure_space(SZ); \
74 memcpy(buf+of, (PTR), (SZ)); \
75 of += (SZ); \
76 } while (0)
77
78
79
80 while (*s) {
81 const char *t;
82 size_t tof = (size_t)(s-template);
83
84 t = strstr(s, "%{");
85 if (t != s) {
86 /* Write "abc%{"
87 * ^^^ */
88 size_t len = (size_t)((t ? t : tend)-s);
89 if (len)
90 _do_write(s, len);
91 }
92
93 if (t) {
94 const char *te;
95 ssize_t r;
96 char *tmpkey;
97
98 /* Find "abc%{key}"
99 * ^ */
100 te = strchr(t+2, '}');
101 if (!te) {
102 rd_snprintf(errstr, errstr_size,
103 "Missing close-brace } for "
104 "%.*s at %"PRIusz,
105 15, t, tof);
106 rd_free(buf);
107 return NULL;
108 }
109
110 rd_strndupa(&tmpkey, t+2, (int)(te-t-2));
111
112 /* Query callback for length of key's value. */
113 r = callback(tmpkey, NULL, 0, opaque);
114 if (r == -1) {
115 rd_snprintf(errstr, errstr_size,
116 "Property not available: \"%s\"",
117 tmpkey);
118 rd_free(buf);
119 return NULL;
120 }
121
122 _assure_space(r);
123
124 /* Call again now providing a large enough buffer. */
125 r = callback(tmpkey, buf+of, _remain(), opaque);
126 if (r == -1) {
127 rd_snprintf(errstr, errstr_size,
128 "Property not available: "
129 "\"%s\"", tmpkey);
130 rd_free(buf);
131 return NULL;
132 }
133
134 assert(r < (ssize_t)_remain());
135 of += r;
136 s = te+1;
137
138 } else {
139 s = tend;
140 }
141 }
142
143 buf[of] = '\0';
144 return buf;
145 }
146
147
148
149
rd_strtup_destroy(rd_strtup_t * strtup)150 void rd_strtup_destroy (rd_strtup_t *strtup) {
151 rd_free(strtup);
152 }
153
rd_strtup_free(void * strtup)154 void rd_strtup_free (void *strtup) {
155 rd_strtup_destroy((rd_strtup_t *)strtup);
156 }
157
rd_strtup_new0(const char * name,ssize_t name_len,const char * value,ssize_t value_len)158 rd_strtup_t *rd_strtup_new0 (const char *name, ssize_t name_len,
159 const char *value, ssize_t value_len) {
160 rd_strtup_t *strtup;
161
162 /* Calculate lengths, if needed, and add space for \0 nul */
163
164 if (name_len == -1)
165 name_len = strlen(name);
166
167 if (!value)
168 value_len = 0;
169 else if (value_len == -1)
170 value_len = strlen(value);
171
172
173 strtup = rd_malloc(sizeof(*strtup) +
174 name_len + 1 + value_len + 1 - 1/*name[1]*/);
175 memcpy(strtup->name, name, name_len);
176 strtup->name[name_len] = '\0';
177 if (value) {
178 strtup->value = &strtup->name[name_len+1];
179 memcpy(strtup->value, value, value_len);
180 strtup->value[value_len] = '\0';
181 } else {
182 strtup->value = NULL;
183 }
184
185 return strtup;
186 }
187
rd_strtup_new(const char * name,const char * value)188 rd_strtup_t *rd_strtup_new (const char *name, const char *value) {
189 return rd_strtup_new0(name, -1, value, -1);
190 }
191
192
193 /**
194 * @returns a new copy of \p src
195 */
rd_strtup_dup(const rd_strtup_t * src)196 rd_strtup_t *rd_strtup_dup (const rd_strtup_t *src) {
197 return rd_strtup_new(src->name, src->value);
198 }
199
200 /**
201 * @brief Wrapper for rd_strtup_dup() suitable rd_list_copy*() use
202 */
rd_strtup_list_copy(const void * elem,void * opaque)203 void *rd_strtup_list_copy (const void *elem, void *opaque) {
204 const rd_strtup_t *src = elem;
205 return (void *)rd_strtup_dup(src);
206 }
207
208
209
210 /**
211 * @brief Convert bit-flags in \p flags to human-readable CSV string
212 * use the bit-description strings in \p desc.
213 *
214 * \p desc array element N corresponds to bit (1<<N).
215 * \p desc MUST be terminated by a NULL array element.
216 * Empty descriptions are ignored even if the bit is set.
217 *
218 * @returns a null-terminated \p dst
219 */
rd_flags2str(char * dst,size_t size,const char ** desc,int flags)220 char *rd_flags2str (char *dst, size_t size,
221 const char **desc, int flags) {
222 int bit = 0;
223 size_t of = 0;
224
225 for ( ; *desc ; desc++, bit++) {
226 int r;
227
228 if (!(flags & (1 << bit)) || !*desc)
229 continue;
230
231 if (of >= size) {
232 /* Dest buffer too small, indicate truncation */
233 if (size > 3)
234 rd_snprintf(dst+(size-3), 3, "..");
235 break;
236 }
237
238 r = rd_snprintf(dst+of, size-of, "%s%s",
239 !of ? "" : ",", *desc);
240
241 of += r;
242 }
243
244 if (of == 0 && size > 0)
245 *dst = '\0';
246
247 return dst;
248 }
249
250
251
252 /**
253 * @returns a djb2 hash of \p str.
254 *
255 * @param len If -1 the \p str will be hashed until nul is encountered,
256 * else up to the \p len.
257 */
rd_string_hash(const char * str,ssize_t len)258 unsigned int rd_string_hash (const char *str, ssize_t len) {
259 unsigned int hash = 5381;
260 ssize_t i;
261
262 if (len == -1) {
263 for (i = 0 ; str[i] != '\0' ; i++)
264 hash = ((hash << 5) + hash) + str[i];
265 } else {
266 for (i = 0 ; i < len ; i++)
267 hash = ((hash << 5) + hash) + str[i];
268 }
269
270 return hash;
271 }
272
273
274 /**
275 * @brief Same as strcmp() but handles NULL values.
276 */
rd_strcmp(const char * a,const char * b)277 int rd_strcmp (const char *a, const char *b) {
278 if (a == b)
279 return 0;
280 else if (!a && b)
281 return -1;
282 else if (!b)
283 return 1;
284 else
285 return strcmp(a, b);
286 }
287
288
289
290 /**
291 * @brief Case-insensitive strstr() for platforms where strcasestr()
292 * is not available.
293 */
_rd_strcasestr(const char * haystack,const char * needle)294 char *_rd_strcasestr (const char *haystack, const char *needle) {
295 const char *h_rem, *n_last;
296 size_t h_len = strlen(haystack);
297 size_t n_len = strlen(needle);
298
299
300 if (n_len == 0 || n_len > h_len)
301 return NULL;
302 else if (n_len == h_len)
303 return !rd_strcasecmp(haystack, needle) ?
304 (char *)haystack : NULL;
305
306 /*
307 * Scan inspired by Boyer-Moore:
308 *
309 * haystack = "this is a haystack"
310 * needle = "hays"
311 *
312 * "this is a haystack"
313 * ^ ^- h_last
314 * `-h (haystack + strlen(needle) - 1)
315 * `-h_rem
316 *
317 * "hays"
318 * ^-n
319 * ^-n_last
320 */
321 n_last = needle + n_len - 1;
322 h_rem = haystack + n_len - 1;
323
324 while (*h_rem) {
325 const char *h, *n = n_last;
326
327 /* Find first occurrence of last character in the needle
328 in the remaining haystack. */
329 for (h = h_rem ;
330 *h && tolower((int)*h) != tolower((int)*n) ;
331 h++)
332 ;
333
334 if (!*h)
335 return NULL; /* No match */
336
337 /* Backtrack both needle and haystack as long as each character
338 * matches, if the start of the needle is found we have
339 * a full match, else start over from the remaining part of the
340 * haystack. */
341 do {
342 if (n == needle)
343 return (char *)h; /* Full match */
344
345 /* Rewind both n and h */
346 n--;
347 h--;
348
349 } while (tolower((int)*n) == tolower((int)*h));
350
351 /* Mismatch, start over at the next haystack position */
352 h_rem++;
353 }
354
355 return NULL;
356 }
357
358
359
360 /**
361 * @brief Unittests for rd_strcasestr()
362 */
ut_strcasestr(void)363 static int ut_strcasestr (void) {
364 static const struct {
365 const char *haystack;
366 const char *needle;
367 ssize_t exp;
368 } strs[] = {
369 { "this is a haystack", "hays", 10 },
370 { "abc", "a", 0 },
371 { "abc", "b", 1 },
372 { "abc", "c", 2 },
373 { "AbcaBcabC", "ABC", 0 },
374 { "abcabcaBC", "BcA", 1 },
375 { "abcabcABc", "cAB", 2 },
376 { "need to estart stART the tart ReStArT!", "REsTaRt", 30 },
377 { "need to estart stART the tart ReStArT!", "?sTaRt", -1 },
378 { "aaaabaaAb", "ab", 3 },
379 { "0A!", "a", 1 },
380 { "a", "A", 0 },
381 { ".z", "Z", 1 },
382 { "", "", -1 },
383 { "", "a", -1 },
384 { "a", "", -1 },
385 { "peRfeCt", "peRfeCt", 0 },
386 { "perfect", "perfect", 0 },
387 { "PERFECT", "perfect", 0 },
388 { NULL },
389 };
390 int i;
391
392 RD_UT_BEGIN();
393
394 for (i = 0 ; strs[i].haystack ; i++) {
395 const char *ret;
396 ssize_t of = -1;
397
398 ret = _rd_strcasestr(strs[i].haystack, strs[i].needle);
399 if (ret)
400 of = ret - strs[i].haystack;
401 RD_UT_ASSERT(of == strs[i].exp,
402 "#%d: '%s' in '%s': expected offset %"PRIdsz
403 ", not %"PRIdsz" (%s)",
404 i, strs[i].needle, strs[i].haystack,
405 strs[i].exp, of, ret ? ret : "(NULL)");
406 }
407
408 return 0;
409 }
410
411
412 /**
413 * @brief Unittests for strings
414 */
unittest_string(void)415 int unittest_string (void) {
416 int fails = 0;
417
418 fails += ut_strcasestr();
419
420 return fails;
421 }
422