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
33/**
34 * @brief Render string \p template using \p callback for key lookups.
35 *
36 * Keys in template follow the %{keyname} syntax.
37 *
38 * The \p callback must not write more than \p size bytes to \p buf, must
39 * should return the number of bytes it wanted to write (which will indicate
40 * a truncated write).
41 * If the key is not found -1 should be returned (which fails the rendering).
42 *
43 * @returns number of written bytes to \p dest,
44 * or -1 on failure (errstr is written)
45 */
46char *rd_string_render (const char *template,
47 char *errstr, size_t errstr_size,
48 ssize_t (*callback) (const char *key,
49 char *buf, size_t size,
50 void *opaque),
51 void *opaque) {
52 const char *s = template;
53 const char *tend = template + strlen(template);
54 size_t size = 256;
55 char *buf;
56 size_t of = 0;
57
58 buf = rd_malloc(size);
59
60#define _remain() (size - of - 1)
61#define _assure_space(SZ) do { \
62 if (of + (SZ) + 1 >= size) { \
63 size = (size + (SZ) + 1) * 2; \
64 buf = realloc(buf, size); \
65 } \
66 } while (0)
67
68#define _do_write(PTR,SZ) do { \
69 _assure_space(SZ); \
70 memcpy(buf+of, (PTR), (SZ)); \
71 of += (SZ); \
72 } while (0)
73
74
75
76 while (*s) {
77 const char *t;
78 size_t tof = (size_t)(s-template);
79
80 t = strstr(s, "%{");
81 if (t != s) {
82 /* Write "abc%{"
83 * ^^^ */
84 size_t len = (size_t)((t ? t : tend)-s);
85 if (len)
86 _do_write(s, len);
87 }
88
89 if (t) {
90 const char *te;
91 ssize_t r;
92 char *tmpkey;
93
94 /* Find "abc%{key}"
95 * ^ */
96 te = strchr(t+2, '}');
97 if (!te) {
98 rd_snprintf(errstr, errstr_size,
99 "Missing close-brace } for "
100 "%.*s at %"PRIusz,
101 15, t, tof);
102 rd_free(buf);
103 return NULL;
104 }
105
106 rd_strndupa(&tmpkey, t+2, (int)(te-t-2));
107
108 /* Query callback for length of key's value. */
109 r = callback(tmpkey, NULL, 0, opaque);
110 if (r == -1) {
111 rd_snprintf(errstr, errstr_size,
112 "Property not available: \"%s\"",
113 tmpkey);
114 rd_free(buf);
115 return NULL;
116 }
117
118 _assure_space(r);
119
120 /* Call again now providing a large enough buffer. */
121 r = callback(tmpkey, buf+of, _remain(), opaque);
122 if (r == -1) {
123 rd_snprintf(errstr, errstr_size,
124 "Property not available: "
125 "\"%s\"", tmpkey);
126 rd_free(buf);
127 return NULL;
128 }
129
130 assert(r < (ssize_t)_remain());
131 of += r;
132 s = te+1;
133
134 } else {
135 s = tend;
136 }
137 }
138
139 buf[of] = '\0';
140 return buf;
141}
142
143
144
145
146void rd_strtup_destroy (rd_strtup_t *strtup) {
147 rd_free(strtup);
148}
149
150void rd_strtup_free (void *strtup) {
151 rd_strtup_destroy((rd_strtup_t *)strtup);
152}
153
154rd_strtup_t *rd_strtup_new0 (const char *name, ssize_t name_len,
155 const char *value, ssize_t value_len) {
156 rd_strtup_t *strtup;
157
158 /* Calculate lengths, if needed, and add space for \0 nul */
159
160 if (name_len == -1)
161 name_len = strlen(name);
162
163 if (!value)
164 value_len = 0;
165 else if (value_len == -1)
166 value_len = strlen(value);
167
168
169 strtup = rd_malloc(sizeof(*strtup) +
170 name_len + 1 + value_len + 1 - 1/*name[1]*/);
171 memcpy(strtup->name, name, name_len);
172 strtup->name[name_len] = '\0';
173 if (value) {
174 strtup->value = &strtup->name[name_len+1];
175 memcpy(strtup->value, value, value_len);
176 strtup->value[value_len] = '\0';
177 } else {
178 strtup->value = NULL;
179 }
180
181 return strtup;
182}
183
184rd_strtup_t *rd_strtup_new (const char *name, const char *value) {
185 return rd_strtup_new0(name, -1, value, -1);
186}
187
188
189/**
190 * @returns a new copy of \p src
191 */
192rd_strtup_t *rd_strtup_dup (const rd_strtup_t *src) {
193 return rd_strtup_new(src->name, src->value);
194}
195
196/**
197 * @brief Wrapper for rd_strtup_dup() suitable rd_list_copy*() use
198 */
199void *rd_strtup_list_copy (const void *elem, void *opaque) {
200 const rd_strtup_t *src = elem;
201 return (void *)rd_strtup_dup(src);
202}
203
204
205
206/**
207 * @brief Convert bit-flags in \p flags to human-readable CSV string
208 * use the bit-description strings in \p desc.
209 *
210 * \p desc array element N corresponds to bit (1<<N).
211 * \p desc MUST be terminated by a NULL array element.
212 * Empty descriptions are ignored even if the bit is set.
213 *
214 * @returns a null-terminated \p dst
215 */
216char *rd_flags2str (char *dst, size_t size,
217 const char **desc, int flags) {
218 int bit = 0;
219 size_t of = 0;
220
221 for ( ; *desc ; desc++, bit++) {
222 int r;
223
224 if (!(flags & (1 << bit)) || !*desc)
225 continue;
226
227 if (of >= size) {
228 /* Dest buffer too small, indicate truncation */
229 if (size > 3)
230 rd_snprintf(dst+(size-3), 3, "..");
231 break;
232 }
233
234 r = rd_snprintf(dst+of, size-of, "%s%s",
235 !of ? "" : ",", *desc);
236
237 of += r;
238 }
239
240 if (of == 0 && size > 0)
241 *dst = '\0';
242
243 return dst;
244}
245