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