/src/freeradius-server/src/lib/util/sbuff.c
Line | Count | Source |
1 | | /* |
2 | | * This library is free software; you can redistribute it and/or |
3 | | * modify it under the terms of the GNU Lesser General Public |
4 | | * License as published by the Free Software Foundation; either |
5 | | * version 2.1 of the License, or (at your option) any later version. |
6 | | * |
7 | | * This library is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
10 | | * Lesser General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU Lesser General Public |
13 | | * License along with this library; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** A generic string buffer structure for string printing and parsing |
18 | | * |
19 | | * @file src/lib/util/sbuff.c |
20 | | * |
21 | | * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org> |
22 | | */ |
23 | | RCSID("$Id: 44331d5943621b7463c5e9a0ddb1e5f705f11c11 $") |
24 | | |
25 | | #include <freeradius-devel/util/misc.h> |
26 | | #include <freeradius-devel/util/syserror.h> |
27 | | #include <freeradius-devel/util/atexit.h> |
28 | | |
29 | | |
30 | | static _Thread_local char *sbuff_scratch; |
31 | | |
32 | | /** When true, prevent use of the scratch space |
33 | | * |
34 | | * This prevents us from initialising a pool after the thread local destructors have run. |
35 | | * |
36 | | * The destructors may be called manually before thread exit, and we don't want to re-initialise the pool |
37 | | */ |
38 | | static _Thread_local bool sbuff_scratch_freed; |
39 | | |
40 | | static_assert(sizeof(long long) >= sizeof(int64_t), "long long must be as wide or wider than an int64_t"); |
41 | | static_assert(sizeof(unsigned long long) >= sizeof(uint64_t), "long long must be as wide or wider than an uint64_t"); |
42 | | |
43 | | fr_table_num_ordered_t const sbuff_parse_error_table[] = { |
44 | | { L("ok"), FR_SBUFF_PARSE_OK }, |
45 | | { L("token not found"), FR_SBUFF_PARSE_ERROR_NOT_FOUND }, |
46 | | { L("trailing data"), FR_SBUFF_PARSE_ERROR_TRAILING }, |
47 | | { L("token format invalid"), FR_SBUFF_PARSE_ERROR_FORMAT }, |
48 | | { L("out of space"), FR_SBUFF_PARSE_ERROR_OUT_OF_SPACE }, |
49 | | { L("integer overflow"), FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW }, |
50 | | { L("integer underflow"), FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW }, |
51 | | { L("empty input is invalid"), FR_SBUFF_PARSE_ERROR_INPUT_EMPTY }, |
52 | | }; |
53 | | size_t sbuff_parse_error_table_len = NUM_ELEMENTS(sbuff_parse_error_table); |
54 | | |
55 | | #if defined(STATIC_ANALYZER) || !defined(NDEBUG) |
56 | 82.3M | # define CHECK_SBUFF_INIT(_sbuff) do { if (!(_sbuff)->extend && (unlikely(!(_sbuff)->buff) || unlikely(!(_sbuff)->start) || unlikely(!(_sbuff)->end) || unlikely(!(_sbuff)->p))) return 0; } while (0) |
57 | 82.1M | # define CHECK_SBUFF_WRITEABLE(_sbuff) do { CHECK_SBUFF_INIT(_sbuff); if (unlikely((_sbuff)->is_const)) return 0; } while (0) |
58 | | |
59 | | #else |
60 | | # define CHECK_SBUFF_INIT(_sbuff) |
61 | | # define CHECK_SBUFF_WRITEABLE(_sbuff) |
62 | | #endif |
63 | | |
64 | | bool const sbuff_char_class_uint[SBUFF_CHAR_CLASS] = { |
65 | | SBUFF_CHAR_CLASS_NUM, |
66 | | ['+'] = true |
67 | | }; |
68 | | |
69 | | bool const sbuff_char_class_int[SBUFF_CHAR_CLASS] = { |
70 | | SBUFF_CHAR_CLASS_NUM, |
71 | | ['+'] = true, ['-'] = true |
72 | | }; |
73 | | |
74 | | bool const sbuff_char_class_float[SBUFF_CHAR_CLASS] = { |
75 | | SBUFF_CHAR_CLASS_NUM, |
76 | | ['-'] = true, ['+'] = true, ['e'] = true, ['E'] = true, ['.'] = true, |
77 | | }; |
78 | | |
79 | | bool const sbuff_char_class_zero[SBUFF_CHAR_CLASS] = { |
80 | | ['0'] = true |
81 | | }; |
82 | | |
83 | | /* |
84 | | * Anything which vaguely resembles an IP address, prefix, or host name. |
85 | | */ |
86 | | bool const sbuff_char_class_hostname[SBUFF_CHAR_CLASS] = { |
87 | | SBUFF_CHAR_CLASS_ALPHA_NUM, |
88 | | ['.'] = true, /* only for IPv4 and host names */ |
89 | | [':'] = true, /* only for IPv6 numerical addresses */ |
90 | | ['-'] = true, /* only for host names */ |
91 | | ['/'] = true, /* only for prefixes */ |
92 | | ['['] = true, /* only for IPv6 numerical addresses */ |
93 | | [']'] = true, /* only for IPv6 numerical addresses */ |
94 | | ['_'] = true, /* only for certain host name labels */ |
95 | | ['*'] = true, /* really only for ipv4 addresses */ |
96 | | }; |
97 | | |
98 | | bool const sbuff_char_class_hex[SBUFF_CHAR_CLASS] = { SBUFF_CHAR_CLASS_HEX }; |
99 | | bool const sbuff_char_alpha_num[SBUFF_CHAR_CLASS] = { SBUFF_CHAR_CLASS_ALPHA_NUM }; |
100 | | bool const sbuff_char_word[SBUFF_CHAR_CLASS] = { |
101 | | SBUFF_CHAR_CLASS_ALPHA_NUM, |
102 | | ['-'] = true, ['_'] = true, |
103 | | }; |
104 | | bool const sbuff_char_whitespace[SBUFF_CHAR_CLASS] = { |
105 | | ['\t'] = true, ['\n'] = true, ['\r'] = true, ['\f'] = true, ['\v'] = true, [' '] = true, |
106 | | }; |
107 | | |
108 | | bool const sbuff_char_line_endings[SBUFF_CHAR_CLASS] = { |
109 | | ['\n'] = true, ['\r'] = true |
110 | | }; |
111 | | |
112 | | bool const sbuff_char_blank[SBUFF_CHAR_CLASS] = { |
113 | | ['\t'] = true, [' '] = true, |
114 | | }; |
115 | | |
116 | | /** Copy function that allows overlapping memory ranges to be copied |
117 | | * |
118 | | * @param[out] o_start start of output buffer. |
119 | | * @param[in] o_end end of the output buffer. |
120 | | * @param[in] i_start start of the input buffer. |
121 | | * @param[in] i_end end of data to copy. |
122 | | * @return |
123 | | * - >0 the number of bytes copied. |
124 | | * - 0 invalid args. |
125 | | * - <0 the number of bytes we'd need to complete the copy. |
126 | | */ |
127 | | static inline CC_HINT(always_inline) ssize_t safecpy(char *o_start, char *o_end, |
128 | | char const *i_start, char const *i_end) |
129 | 44.6M | { |
130 | 44.6M | ssize_t diff; |
131 | 44.6M | size_t i_len = i_end - i_start; |
132 | | |
133 | 44.6M | if (unlikely((o_end < o_start) || (i_end < i_start))) return 0; /* sanity check */ |
134 | | |
135 | 44.6M | diff = (o_end - o_start) - (i_len); |
136 | 44.6M | if (diff < 0) return diff; |
137 | | |
138 | 44.6M | if ((i_start > o_end) || (i_end < o_start)) { /* no-overlap */ |
139 | 44.6M | memcpy(o_start, i_start, i_len); |
140 | 44.6M | } else { /* overlap */ |
141 | 0 | memmove(o_start, i_start, i_len); |
142 | 0 | } |
143 | | |
144 | 44.6M | return (i_len); |
145 | 44.6M | } |
146 | | |
147 | | static inline CC_HINT(always_inline) size_t min(size_t x, size_t y) |
148 | 235k | { |
149 | 235k | return x < y ? x : y; |
150 | 235k | } |
151 | | |
152 | | /** Update all markers and pointers in the set of sbuffs to point to new_buff |
153 | | * |
154 | | * This function should be used if the underlying buffer is realloced. |
155 | | * |
156 | | * @param[in] sbuff to update. |
157 | | * @param[in] new_buff to assign to to sbuff. |
158 | | * @param[in] new_len Length of the new buffer. |
159 | | */ |
160 | | void fr_sbuff_update(fr_sbuff_t *sbuff, char *new_buff, size_t new_len) |
161 | 43.0k | { |
162 | 43.0k | fr_sbuff_t *sbuff_i; |
163 | 43.0k | char *old_buff; /* Current buff */ |
164 | | |
165 | 43.0k | old_buff = sbuff->buff; |
166 | | |
167 | | /* |
168 | | * Update pointers to point to positions |
169 | | * in new buffer based on their relative |
170 | | * offsets in the old buffer... but not |
171 | | * past the end of the new buffer. |
172 | | */ |
173 | 160k | for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) { |
174 | 117k | fr_sbuff_marker_t *m_i; |
175 | | |
176 | 117k | sbuff_i->buff = new_buff; |
177 | 117k | sbuff_i->start = new_buff + min(new_len, sbuff_i->start - old_buff); |
178 | 117k | sbuff_i->end = sbuff_i->buff + new_len; |
179 | 117k | *(sbuff_i->end) = '\0'; /* Re-terminate */ |
180 | | |
181 | 117k | sbuff_i->p = new_buff + min(new_len, sbuff_i->p - old_buff); |
182 | | |
183 | 117k | for (m_i = sbuff_i->m; m_i; m_i = m_i->next) m_i->p = new_buff + min(new_len, m_i->p - old_buff); |
184 | 117k | } |
185 | 43.0k | } |
186 | | |
187 | | /** Shift the contents of the sbuff, returning the number of bytes we managed to shift |
188 | | * |
189 | | * @param[in] sbuff to shift. |
190 | | * @param[in] shift the contents of the buffer this many bytes |
191 | | * towards the start of the buffer. |
192 | | * @param[in] move_end If the buffer is used for reading, then this should be true |
193 | | * so we cannot read passed the end of valid data. |
194 | | * @return |
195 | | * - 0 the shift failed due to constraining pointers. |
196 | | * - >0 the number of bytes we managed to shift pointers |
197 | | * in the sbuff. memmove should be used to move the |
198 | | * existing contents of the buffer, and fill the free |
199 | | * space at the end of the buffer with additional data. |
200 | | */ |
201 | | size_t fr_sbuff_shift(fr_sbuff_t *sbuff, size_t shift, bool move_end) |
202 | 0 | { |
203 | 0 | fr_sbuff_t *sbuff_i; |
204 | 0 | char *buff, *end; /* Current start */ |
205 | 0 | size_t max_shift = shift; |
206 | 0 | bool reterminate = false; |
207 | |
|
208 | 0 | CHECK_SBUFF_INIT(sbuff); |
209 | | |
210 | 0 | buff = sbuff->buff; |
211 | 0 | end = sbuff->end; |
212 | | |
213 | | /* |
214 | | * If the sbuff is already \0 terminated |
215 | | * and we're not working on a const buffer |
216 | | * then assume we need to re-terminate |
217 | | * later. |
218 | | */ |
219 | 0 | reterminate = (sbuff->p < sbuff->end) && (*sbuff->p == '\0') && !sbuff->is_const; |
220 | | |
221 | | /* |
222 | | * First pass: find the maximum shift, which is the minimum |
223 | | * of the distances from buff to any of the current pointers |
224 | | * or current pointers of markers of dbuff and its ancestors. |
225 | | * (We're also constrained by the requested shift count.) |
226 | | */ |
227 | 0 | for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) { |
228 | 0 | fr_sbuff_marker_t *m_i; |
229 | |
|
230 | 0 | max_shift = min(max_shift, sbuff_i->p - buff); |
231 | 0 | if (!max_shift) return 0; |
232 | | |
233 | 0 | for (m_i = sbuff_i->m; m_i; m_i = m_i->next) { |
234 | 0 | max_shift = min(max_shift, m_i->p - buff); |
235 | 0 | if (!max_shift) return 0; |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | | /* |
240 | | * Second pass: adjust pointers. |
241 | | * The first pass means we need only subtract shift from |
242 | | * current pointers. Start pointers can't constrain shift, |
243 | | * or we'd never free any space, so they require the added |
244 | | * check. |
245 | | */ |
246 | 0 | for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) { |
247 | 0 | fr_sbuff_marker_t *m_i; |
248 | 0 | char *start = sbuff_i->start; |
249 | |
|
250 | 0 | sbuff_i->start -= min(max_shift, sbuff_i->start - buff); |
251 | 0 | sbuff_i->p -= max_shift; |
252 | 0 | if (move_end) sbuff_i->end -= max_shift; |
253 | 0 | sbuff_i->shifted += (max_shift - (start - sbuff_i->start)); |
254 | 0 | for (m_i = sbuff_i->m; m_i; m_i = m_i->next) m_i->p -= max_shift; |
255 | 0 | } |
256 | | |
257 | | /* |
258 | | * Only memmove if the shift wasn't the |
259 | | * entire contents of the buffer. |
260 | | */ |
261 | 0 | if ((buff + max_shift) < end) memmove(buff, buff + max_shift, end - (buff + max_shift)); |
262 | |
|
263 | 0 | if (reterminate) *sbuff->p = '\0'; |
264 | |
|
265 | 0 | return max_shift; |
266 | 0 | } |
267 | | |
268 | | /** Refresh the buffer with more data from the file |
269 | | * |
270 | | */ |
271 | | size_t fr_sbuff_extend_file(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension) |
272 | 0 | { |
273 | 0 | fr_sbuff_t *sbuff_i; |
274 | 0 | size_t read, available, total_read, shift; |
275 | 0 | fr_sbuff_uctx_file_t *fctx; |
276 | |
|
277 | 0 | CHECK_SBUFF_INIT(sbuff); |
278 | | |
279 | 0 | fctx = sbuff->uctx; |
280 | 0 | if (fctx->eof) return 0; |
281 | | |
282 | 0 | if (extension == SIZE_MAX) extension = 0; |
283 | |
|
284 | 0 | total_read = fctx->shifted + (sbuff->end - sbuff->buff); |
285 | 0 | if (total_read >= fctx->max) { |
286 | 0 | fr_strerror_const("Can't satisfy extension request, max bytes read"); |
287 | 0 | return 0; /* There's no way we could satisfy the extension request */ |
288 | 0 | } |
289 | | |
290 | | /* |
291 | | * Shift out the maximum number of bytes we can |
292 | | * irrespective of the amount that was requested |
293 | | * as the extension. It's more efficient to do |
294 | | * this than lots of small shifts, and just |
295 | | * looking and the number of bytes used in the |
296 | | * deepest sbuff, and using that as the shift |
297 | | * amount, might mean we don't shift anything at |
298 | | * all! |
299 | | * |
300 | | * fr_sbuff_shift will cap the max shift amount, |
301 | | * so markers and positions will remain valid for |
302 | | * all sbuffs in the chain. |
303 | | */ |
304 | 0 | shift = fr_sbuff_current(sbuff) - fr_sbuff_buff(sbuff); |
305 | 0 | if (shift) { |
306 | | /* |
307 | | * Try and shift as much as we can out |
308 | | * of the buffer to make space. |
309 | | * |
310 | | * Note: p and markers are constraints here. |
311 | | */ |
312 | 0 | fctx->shifted += fr_sbuff_shift(sbuff, shift, true); |
313 | 0 | } |
314 | |
|
315 | 0 | available = fctx->buff_end - sbuff->end; |
316 | 0 | if (available > (fctx->max - total_read)) available = fctx->max - total_read; |
317 | 0 | if (available < extension) { |
318 | 0 | fr_strerror_printf("Can't satisfy extension request for %zu bytes", extension); |
319 | 0 | return 0; /* There's no way we could satisfy the extension request */ |
320 | 0 | } |
321 | | |
322 | 0 | read = fread(sbuff->end, 1, available, fctx->file); |
323 | 0 | for (sbuff_i = sbuff; sbuff_i; sbuff_i = sbuff_i->parent) { |
324 | 0 | sbuff_i->end += read; /* Advance end, which increases fr_sbuff_remaining() */ |
325 | 0 | } |
326 | | |
327 | | /** Check for errors |
328 | | */ |
329 | 0 | if (read < available) { |
330 | 0 | if (!feof(fctx->file)) { |
331 | | /* |
332 | | * It's an error, but ferror() returns a ??? error number, |
333 | | * and not errno. |
334 | | * |
335 | | * Posix says "The ferror() function shall not change the setting of errno if |
336 | | * stream is valid". And the return value is defined to be non-zero, but with no |
337 | | * meaning associated with any non-zero values. |
338 | | */ |
339 | 0 | fr_strerror_printf("Error extending buffer: %d", ferror(fctx->file)); |
340 | 0 | *status |= FR_SBUFF_FLAG_EXTEND_ERROR; |
341 | 0 | return 0; |
342 | 0 | } |
343 | | |
344 | 0 | fctx->eof = true; |
345 | 0 | } |
346 | | |
347 | 0 | return read; |
348 | 0 | } |
349 | | |
350 | | /** Accessor function for the EOF state of the file extendor |
351 | | * |
352 | | */ |
353 | | bool fr_sbuff_eof_file(fr_sbuff_t *sbuff) |
354 | 0 | { |
355 | 0 | fr_sbuff_uctx_file_t *fctx = sbuff->uctx; |
356 | 0 | return fctx->eof; |
357 | 0 | } |
358 | | |
359 | | /** Reallocate the current buffer |
360 | | * |
361 | | * @param[in] status Extend status. |
362 | | * @param[in] sbuff to be extended. |
363 | | * @param[in] extension How many additional bytes should be allocated |
364 | | * in the buffer. |
365 | | * @return |
366 | | * - 0 the extension operation failed. |
367 | | * - >0 the number of bytes the buffer was extended by. |
368 | | */ |
369 | | size_t fr_sbuff_extend_talloc(fr_sbuff_extend_status_t *status, fr_sbuff_t *sbuff, size_t extension) |
370 | 37.5k | { |
371 | 37.5k | fr_sbuff_uctx_talloc_t *tctx = sbuff->uctx; |
372 | 37.5k | size_t clen, nlen, elen = extension; |
373 | 37.5k | char *new_buff; |
374 | | |
375 | 37.5k | CHECK_SBUFF_INIT(sbuff); |
376 | | |
377 | 37.5k | clen = sbuff->buff ? talloc_array_length(sbuff->buff) : 0; |
378 | | /* |
379 | | * If the current buffer size + the extension |
380 | | * is less than init, extend the buffer to init. |
381 | | * |
382 | | * This can happen if the buffer has been |
383 | | * trimmed, and then additional data is added. |
384 | | */ |
385 | 37.5k | if ((clen + elen) < tctx->init) { |
386 | 0 | elen = (tctx->init - clen) + 1; /* add \0 */ |
387 | | /* |
388 | | * Double the buffer size if it's more than the |
389 | | * requested amount. |
390 | | */ |
391 | 37.5k | } else if (elen < clen) { |
392 | 28.6k | elen = clen - 1; /* Don't double alloc \0 */ |
393 | 28.6k | } |
394 | | |
395 | | /* |
396 | | * Check we don't exceed the maximum buffer |
397 | | * length, including the NUL byte. |
398 | | */ |
399 | 37.5k | if (tctx->max && ((clen + elen + 1) > tctx->max)) { |
400 | 0 | elen = tctx->max - clen; |
401 | 0 | if (elen == 0) { |
402 | 0 | fr_strerror_printf("Failed extending buffer by %zu bytes to " |
403 | 0 | "%zu bytes, max is %zu bytes", |
404 | 0 | extension, clen + extension, tctx->max); |
405 | 0 | return 0; |
406 | 0 | } |
407 | 0 | elen += 1; /* add \0 */ |
408 | 0 | } |
409 | 37.5k | nlen = clen + elen; |
410 | | |
411 | 37.5k | new_buff = talloc_realloc(tctx->ctx, sbuff->buff, char, nlen); |
412 | 37.5k | if (unlikely(!new_buff)) { |
413 | 0 | fr_strerror_printf("Failed extending buffer by %zu bytes to %zu bytes", elen, nlen); |
414 | 0 | *status |= FR_SBUFF_FLAG_EXTEND_ERROR; |
415 | 0 | return 0; |
416 | 0 | } |
417 | | |
418 | 37.5k | (void)fr_sbuff_update(sbuff, new_buff, nlen - 1); /* Shouldn't fail as we're extending */ |
419 | | |
420 | 37.5k | return elen; |
421 | 37.5k | } |
422 | | |
423 | | /** Trim a talloced sbuff to the minimum length required to represent the contained string |
424 | | * |
425 | | * @param[in] sbuff to trim. |
426 | | * @param[in] len Length to trim to. Passing SIZE_MAX will |
427 | | * result in the buffer being trimmed to the |
428 | | * length of the content. |
429 | | * @return |
430 | | * - 0 on success. |
431 | | * - -1 on failure - markers present pointing past the end of string data. |
432 | | */ |
433 | | int fr_sbuff_trim_talloc(fr_sbuff_t *sbuff, size_t len) |
434 | 7.55k | { |
435 | 7.55k | size_t clen = 0, nlen = 1; |
436 | 7.55k | char *new_buff; |
437 | 7.55k | fr_sbuff_uctx_talloc_t *tctx = sbuff->uctx; |
438 | | |
439 | 7.55k | CHECK_SBUFF_INIT(sbuff); |
440 | | |
441 | 7.55k | if (sbuff->buff) clen = talloc_array_length(sbuff->buff); |
442 | | |
443 | 7.55k | if (len != SIZE_MAX) { |
444 | 0 | nlen += len; |
445 | 7.55k | } else if (sbuff->buff){ |
446 | 7.55k | nlen += (sbuff->p - sbuff->start); |
447 | 7.55k | } |
448 | | |
449 | 7.55k | if (nlen != clen) { |
450 | 5.46k | new_buff = talloc_realloc(tctx->ctx, sbuff->buff, char, nlen); |
451 | 5.46k | if (unlikely(!new_buff)) { |
452 | 0 | fr_strerror_printf("Failed trimming buffer from %zu to %zu", clen, nlen); |
453 | 0 | return -1; |
454 | 0 | } |
455 | 5.46k | fr_sbuff_update(sbuff, new_buff, nlen - 1); |
456 | 5.46k | } |
457 | | |
458 | 7.55k | return 0; |
459 | 7.55k | } |
460 | | |
461 | | /** Reset a talloced buffer to its initial length, clearing any data stored |
462 | | * |
463 | | * @param[in] sbuff to reset. |
464 | | * @return |
465 | | * - 0 on success. |
466 | | * - -1 on failure - markers present pointing past the end of string data. |
467 | | */ |
468 | | int fr_sbuff_reset_talloc(fr_sbuff_t *sbuff) |
469 | 18 | { |
470 | 18 | fr_sbuff_uctx_talloc_t *tctx = sbuff->uctx; |
471 | | |
472 | 18 | CHECK_SBUFF_INIT(sbuff); |
473 | | |
474 | 18 | fr_sbuff_set_to_start(sbuff); /* Clear data */ |
475 | 18 | sbuff->m = NULL; /* Remove any maker references */ |
476 | | |
477 | 18 | if (fr_sbuff_used(sbuff) != tctx->init) { |
478 | 18 | char *new_buff; |
479 | | |
480 | 18 | new_buff = talloc_realloc(tctx->ctx, sbuff->buff, char, tctx->init); |
481 | 18 | if (!new_buff) { |
482 | 0 | fr_strerror_printf("Failed reallocing from %zu to %zu", |
483 | 0 | talloc_array_length(sbuff->buff), tctx->init); |
484 | 0 | return -1; |
485 | 0 | } |
486 | 18 | sbuff->buff = new_buff; |
487 | 18 | fr_sbuff_update(sbuff, new_buff, tctx->init - 1); |
488 | 18 | } |
489 | | |
490 | 18 | return 0; |
491 | 18 | } |
492 | | |
493 | | /** Fill as much of the output buffer we can and break on partial copy |
494 | | * |
495 | | * @param[in] _out sbuff to write to. |
496 | | * @param[in] _in sbuff to copy from. |
497 | | * @param[in] _len maximum amount to copy. |
498 | | */ |
499 | 48.9k | #define FILL_OR_GOTO_DONE(_out, _in, _len) if (fr_sbuff_move(_out, _in, _len) < (size_t)(_len)) goto done |
500 | | |
501 | | /** Constrain end pointer to prevent advancing more than the amount the caller specified |
502 | | * |
503 | | * @param[in] _sbuff to constrain. |
504 | | * @param[in] _max maximum amount to advance. |
505 | | * @param[in] _used how much we've advanced so far. |
506 | | * @return a temporary end pointer. |
507 | | */ |
508 | | #define CONSTRAINED_END(_sbuff, _max, _used) \ |
509 | 63.4k | (((_max) - (_used)) > fr_sbuff_remaining(_sbuff) ? (_sbuff)->end : (_sbuff)->p + ((_max) - (_used))) |
510 | | |
511 | | |
512 | | /** Populate a terminal index |
513 | | * |
514 | | * @param[out] needle_len the longest needle. Will not be set |
515 | | * if the terminal array is empty. |
516 | | * @param[out] idx to populate. |
517 | | * @param[in] term Terminals to populate the index with. |
518 | | */ |
519 | | static inline CC_HINT(always_inline) void fr_sbuff_terminal_idx_init(size_t *needle_len, |
520 | | uint8_t idx[static SBUFF_CHAR_CLASS], |
521 | | fr_sbuff_term_t const *term) |
522 | 929 | { |
523 | 929 | size_t i, len, max = 0; |
524 | | |
525 | 929 | if (!term) return; |
526 | | |
527 | 4 | memset(idx, 0, SBUFF_CHAR_CLASS); |
528 | | |
529 | 12 | for (i = 0; i < term->len; i++) { |
530 | 8 | len = term->elem[i].len; |
531 | 8 | if (len > max) max = len; |
532 | | |
533 | 8 | idx[(uint8_t)term->elem[i].str[0]] = i + 1; |
534 | 8 | } |
535 | | |
536 | 4 | if (i > 0) *needle_len = max; |
537 | 4 | } |
538 | | |
539 | | /** Efficient terminal string search |
540 | | * |
541 | | * Caller should ensure that a buffer extension of needle_len bytes has been requested |
542 | | * before calling this function. |
543 | | * |
544 | | * @param[in] in Sbuff to search in. |
545 | | * @param[in] p Current position (may be ahead of in->p). |
546 | | * @param[in] idx Fastpath index, populated by |
547 | | * fr_sbuff_terminal_idx_init. |
548 | | * @param[in] term terminals to search in. |
549 | | * @param[in] needle_len Length of the longest needle. |
550 | | * @return |
551 | | * - true if found. |
552 | | * - false if not. |
553 | | */ |
554 | | static inline bool fr_sbuff_terminal_search(fr_sbuff_t *in, char const *p, |
555 | | uint8_t idx[static SBUFF_CHAR_CLASS], |
556 | | fr_sbuff_term_t const *term, size_t needle_len) |
557 | 20.6M | { |
558 | 20.6M | uint8_t term_idx; |
559 | | |
560 | 20.6M | ssize_t start = 0; |
561 | 20.6M | ssize_t end; |
562 | 20.6M | ssize_t mid; |
563 | | |
564 | 20.6M | size_t remaining; |
565 | | |
566 | 20.6M | if (!term) return false; /* If there's no terminals, we don't need to search */ |
567 | | |
568 | 24 | end = term->len - 1; |
569 | | |
570 | 24 | term_idx = idx[(uint8_t)*p]; /* Fast path */ |
571 | 24 | if (!term_idx) return false; |
572 | | |
573 | | /* |
574 | | * Special case for EOFlike states |
575 | | */ |
576 | 0 | remaining = fr_sbuff_remaining(in); |
577 | 0 | if ((remaining == 0) && !fr_sbuff_is_extendable(in)) { |
578 | 0 | if (idx['\0'] != 0) return true; |
579 | 0 | return false; |
580 | 0 | } |
581 | | |
582 | 0 | if (remaining < needle_len) { |
583 | 0 | fr_assert_msg(!fr_sbuff_is_extendable(in), |
584 | 0 | "Caller failed to extend buffer by %zu bytes before calling fr_sbuff_terminal_search", |
585 | 0 | needle_len); |
586 | | /* |
587 | | * We can't search for the needle if we don't have |
588 | | * enough data to match it. |
589 | | */ |
590 | 0 | return false; |
591 | 0 | } |
592 | | |
593 | 0 | mid = term_idx - 1; /* Inform the mid point from the index */ |
594 | |
|
595 | 0 | while (start <= end) { |
596 | 0 | fr_sbuff_term_elem_t const *elem; |
597 | 0 | size_t tlen; |
598 | 0 | int ret; |
599 | |
|
600 | 0 | elem = &term->elem[mid]; |
601 | 0 | tlen = elem->len; |
602 | |
|
603 | 0 | ret = memcmp(p, elem->str, tlen < (size_t)remaining ? tlen : (size_t)remaining); |
604 | 0 | if (ret == 0) { |
605 | | /* |
606 | | * If we have more text than the table element, that's fine |
607 | | */ |
608 | 0 | if (remaining >= tlen) return true; |
609 | | |
610 | | /* |
611 | | * If input was shorter than the table element we need to |
612 | | * keep searching. |
613 | | */ |
614 | 0 | ret = -1; |
615 | 0 | } |
616 | | |
617 | 0 | if (ret < 0) { |
618 | 0 | end = mid - 1; |
619 | 0 | } else { |
620 | 0 | start = mid + 1; |
621 | 0 | } |
622 | |
|
623 | 0 | mid = start + ((end - start) / 2); /* Avoid overflow */ |
624 | 0 | } |
625 | | |
626 | 0 | return false; |
627 | 0 | } |
628 | | |
629 | | /** Compare two terminal elements for ordering purposes |
630 | | * |
631 | | * @param[in] a first terminal to compare. |
632 | | * @param[in] b second terminal to compare. |
633 | | * @return CMP(a,b) |
634 | | */ |
635 | | static inline int8_t terminal_cmp(fr_sbuff_term_elem_t const *a, fr_sbuff_term_elem_t const *b) |
636 | 0 | { |
637 | 0 | return MEMCMP_FIELDS(a, b, str, len); |
638 | 0 | } |
639 | | |
640 | | #if 0 |
641 | | static void fr_sbuff_terminal_debug_tmp(fr_sbuff_term_elem_t const *elem[], size_t len) |
642 | | { |
643 | | size_t i; |
644 | | |
645 | | FR_FAULT_LOG("Terminal count %zu", len); |
646 | | |
647 | | for (i = 0; i < len; i++) FR_FAULT_LOG("\t\"%s\" (%zu)", elem[i] ? elem[i]->str : "NULL", elem[i] ? elem[i]->len : 0); |
648 | | } |
649 | | #endif |
650 | | |
651 | | /** Merge two sets of terminal strings |
652 | | * |
653 | | * @param[in] ctx to allocate the new terminal array in. |
654 | | * @param[in] a first set of terminals to merge. |
655 | | * @param[in] b second set of terminals to merge. |
656 | | * @return A new set of de-duplicated and sorted terminals. |
657 | | */ |
658 | | fr_sbuff_term_t *fr_sbuff_terminals_amerge(TALLOC_CTX *ctx, fr_sbuff_term_t const *a, fr_sbuff_term_t const *b) |
659 | 0 | { |
660 | 0 | size_t i, j, num; |
661 | 0 | fr_sbuff_term_t *out; |
662 | 0 | fr_sbuff_term_elem_t const *tmp[SBUFF_CHAR_CLASS]; |
663 | | |
664 | | /* |
665 | | * Check all inputs are pre-sorted. It doesn't break this |
666 | | * function, but it's useful in case the terminal arrays |
667 | | * are defined elsewhere without merging. |
668 | | */ |
669 | 0 | #if !defined(NDEBUG) && defined(WITH_VERIFY_PTR) |
670 | 0 | if (a->len) for (i = 0; i < a->len - 1; i++) fr_assert(terminal_cmp(&a->elem[i], &a->elem[i + 1]) < 0); |
671 | 0 | if (b->len) for (i = 0; i < b->len - 1; i++) fr_assert(terminal_cmp(&b->elem[i], &b->elem[i + 1]) < 0); |
672 | 0 | #endif |
673 | | |
674 | | /* |
675 | | * Since the inputs are sorted, we can just do an O(n+m) |
676 | | * walk through the arrays, comparing entries across the |
677 | | * two arrays. |
678 | | * |
679 | | * If there are duplicates, we prefer "a", for no particular reason. |
680 | | */ |
681 | 0 | num = i = j = 0; |
682 | 0 | while ((i < a->len) && (j < b->len)) { |
683 | 0 | int8_t cmp; |
684 | |
|
685 | 0 | cmp = terminal_cmp(&a->elem[i], &b->elem[j]); |
686 | 0 | if (cmp == 0) { |
687 | 0 | j++; |
688 | 0 | tmp[num++] = &a->elem[i++]; |
689 | |
|
690 | 0 | } else if (cmp < 0) { |
691 | 0 | tmp[num++] = &a->elem[i++]; |
692 | |
|
693 | 0 | } else if (cmp > 0) { |
694 | 0 | tmp[num++] = &b->elem[j++]; |
695 | 0 | } |
696 | |
|
697 | 0 | fr_assert(num < SBUFF_CHAR_CLASS); |
698 | 0 | } |
699 | | |
700 | | /* |
701 | | * Only one of these will be hit, and it's simpler than nested "if" statements. |
702 | | */ |
703 | 0 | while (i < a->len) tmp[num++] = &a->elem[i++]; |
704 | 0 | while (j < b->len) tmp[num++] = &b->elem[j++]; |
705 | |
|
706 | 0 | out = talloc_pooled_object(ctx, fr_sbuff_term_t, num, num * sizeof(fr_sbuff_term_elem_t)); |
707 | 0 | if (unlikely(!out)) return NULL; |
708 | | |
709 | 0 | out->elem = talloc_array(out, fr_sbuff_term_elem_t, num); |
710 | 0 | if (unlikely(!out->elem)) { |
711 | 0 | talloc_free(out); |
712 | 0 | return NULL; |
713 | 0 | } |
714 | 0 | out->len = num; |
715 | |
|
716 | 0 | for (i = 0; i < num; i++) out->elem[i] = *tmp[i]; /* copy merged results back */ |
717 | |
|
718 | 0 | #if !defined(NDEBUG) && defined(WITH_VERIFY_PTR) |
719 | 0 | for (i = 0; i < num - 1; i++) fr_assert(terminal_cmp(&out->elem[i], &out->elem[i + 1]) < 0); |
720 | 0 | #endif |
721 | |
|
722 | 0 | return out; |
723 | 0 | } |
724 | | |
725 | | /** Copy as many bytes as possible from a sbuff to a sbuff |
726 | | * |
727 | | * Copy size is limited by available data in sbuff and space in output sbuff. |
728 | | * |
729 | | * @param[out] out Where to copy to. |
730 | | * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. |
731 | | * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. |
732 | | * @return |
733 | | * - 0 no bytes copied. |
734 | | * - >0 the number of bytes copied. |
735 | | */ |
736 | | size_t fr_sbuff_out_bstrncpy(fr_sbuff_t *out, fr_sbuff_t *in, size_t len) |
737 | 40.0k | { |
738 | 40.0k | fr_sbuff_t our_in = FR_SBUFF_BIND_CURRENT(in); |
739 | 40.0k | size_t remaining; |
740 | | |
741 | 40.0k | CHECK_SBUFF_INIT(in); |
742 | | |
743 | 79.9k | while (fr_sbuff_used_total(&our_in) < len) { |
744 | 73.0k | size_t chunk_len; |
745 | | |
746 | 73.0k | remaining = (len - fr_sbuff_used_total(&our_in)); |
747 | | |
748 | 73.0k | if (!fr_sbuff_extend(&our_in)) break; |
749 | | |
750 | 39.9k | chunk_len = fr_sbuff_remaining(&our_in); |
751 | 39.9k | if (chunk_len > remaining) chunk_len = remaining; |
752 | | |
753 | 39.9k | FILL_OR_GOTO_DONE(out, &our_in, chunk_len); |
754 | 39.9k | } |
755 | | |
756 | 40.0k | done: |
757 | 40.0k | *out->p = '\0'; |
758 | 40.0k | return fr_sbuff_used_total(&our_in); |
759 | 40.0k | } |
760 | | |
761 | | /** Copy exactly len bytes from a sbuff to a sbuff or fail |
762 | | * |
763 | | * Copy size is limited by available data in sbuff, space in output sbuff, and length. |
764 | | * |
765 | | * @param[out] out Where to copy to. |
766 | | * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. |
767 | | * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. |
768 | | * @return |
769 | | * - 0 no bytes copied, no token found of sufficient length in input buffer. |
770 | | * - >0 the number of bytes copied. |
771 | | * - <0 the number of additional output bytes we would have needed to |
772 | | * complete the copy. |
773 | | */ |
774 | | ssize_t fr_sbuff_out_bstrncpy_exact(fr_sbuff_t *out, fr_sbuff_t *in, size_t len) |
775 | 0 | { |
776 | 0 | fr_sbuff_t our_in = FR_SBUFF(in); |
777 | 0 | size_t remaining; |
778 | 0 | fr_sbuff_marker_t m; |
779 | |
|
780 | 0 | CHECK_SBUFF_INIT(in); |
781 | | |
782 | 0 | fr_sbuff_marker(&m, out); |
783 | |
|
784 | 0 | do { |
785 | 0 | size_t chunk_len; |
786 | 0 | ssize_t copied; |
787 | |
|
788 | 0 | remaining = (len - fr_sbuff_used_total(&our_in)); |
789 | 0 | if (remaining && !fr_sbuff_extend(&our_in)) { |
790 | 0 | fr_sbuff_marker_release(&m); |
791 | 0 | return 0; |
792 | 0 | } |
793 | | |
794 | 0 | chunk_len = fr_sbuff_remaining(&our_in); |
795 | 0 | if (chunk_len > remaining) chunk_len = remaining; |
796 | |
|
797 | 0 | copied = fr_sbuff_in_bstrncpy(out, our_in.p, chunk_len); |
798 | 0 | if (copied < 0) { |
799 | 0 | fr_sbuff_set(out, &m); /* Reset out */ |
800 | 0 | *m.p = '\0'; /* Re-terminate */ |
801 | | |
802 | | /* Amount remaining in input buffer minus the amount we could have copied */ |
803 | 0 | if (len == SIZE_MAX) { |
804 | 0 | fr_sbuff_marker_release(&m); |
805 | 0 | return -(fr_sbuff_remaining(in) - (chunk_len + copied)); |
806 | 0 | } |
807 | | /* Amount remaining to copy minus the amount we could have copied */ |
808 | 0 | fr_sbuff_marker_release(&m); |
809 | 0 | return -(remaining - (chunk_len + copied)); |
810 | 0 | } |
811 | 0 | fr_sbuff_advance(&our_in, copied); |
812 | 0 | } while (fr_sbuff_used_total(&our_in) < len); |
813 | | |
814 | 0 | fr_sbuff_marker_release(&m); |
815 | |
|
816 | 0 | FR_SBUFF_SET_RETURN(in, &our_in); /* in was pinned, so this works */ |
817 | 0 | } |
818 | | |
819 | | /** Copy as many allowed characters as possible from a sbuff to a sbuff |
820 | | * |
821 | | * Copy size is limited by available data in sbuff and output buffer length. |
822 | | * |
823 | | * As soon as a disallowed character is found the copy is stopped. |
824 | | * The input sbuff will be left pointing at the first disallowed character. |
825 | | * |
826 | | * @param[out] out Where to copy to. |
827 | | * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. |
828 | | * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. |
829 | | * @param[in] allowed Characters to include the copy. |
830 | | * @return |
831 | | * - 0 no bytes copied. |
832 | | * - >0 the number of bytes copied. |
833 | | */ |
834 | | size_t fr_sbuff_out_bstrncpy_allowed(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, |
835 | | bool const allowed[static SBUFF_CHAR_CLASS]) |
836 | 8.82k | { |
837 | 8.82k | fr_sbuff_t our_in = FR_SBUFF_BIND_CURRENT(in); |
838 | | |
839 | 8.82k | CHECK_SBUFF_INIT(in); |
840 | | |
841 | 16.1k | while (fr_sbuff_used_total(&our_in) < len) { |
842 | 16.1k | char *p; |
843 | 16.1k | char *end; |
844 | | |
845 | 16.1k | if (!fr_sbuff_extend(&our_in)) break; |
846 | | |
847 | 8.83k | p = fr_sbuff_current(&our_in); |
848 | 8.83k | end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in)); |
849 | | |
850 | 3.84M | while ((p < end) && allowed[(uint8_t)*p]) p++; |
851 | | |
852 | 8.83k | FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p); |
853 | | |
854 | 8.77k | if (p != end) break; /* stopped early, break */ |
855 | 8.77k | } |
856 | | |
857 | 8.82k | done: |
858 | 8.82k | *out->p = '\0'; |
859 | 8.82k | return fr_sbuff_used_total(&our_in); |
860 | 8.82k | } |
861 | | |
862 | | /** Copy as many allowed characters as possible from a sbuff to a sbuff |
863 | | * |
864 | | * Copy size is limited by available data in sbuff and output buffer length. |
865 | | * |
866 | | * As soon as a disallowed character is found the copy is stopped. |
867 | | * The input sbuff will be left pointing at the first disallowed character. |
868 | | * |
869 | | * @param[out] out Where to copy to. |
870 | | * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. |
871 | | * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. |
872 | | * @param[in] tt Token terminals in the encompassing grammar. |
873 | | * @param[in] u_rules If not NULL, ignore characters in the until set when |
874 | | * prefixed with u_rules->chr. FIXME - Should actually evaluate |
875 | | * u_rules fully. |
876 | | * @return |
877 | | * - 0 no bytes copied. |
878 | | * - >0 the number of bytes copied. |
879 | | */ |
880 | | size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, |
881 | | fr_sbuff_term_t const *tt, |
882 | | fr_sbuff_unescape_rules_t const *u_rules) |
883 | 182 | { |
884 | 182 | fr_sbuff_t our_in = FR_SBUFF_BIND_CURRENT(in); |
885 | 182 | bool do_escape = false; /* Track state across extensions */ |
886 | | |
887 | 182 | uint8_t idx[SBUFF_CHAR_CLASS]; /* Fast path index */ |
888 | 182 | size_t needle_len = 1; |
889 | 182 | char escape_chr = u_rules ? u_rules->chr : '\0'; |
890 | | |
891 | 182 | CHECK_SBUFF_INIT(in); |
892 | | |
893 | | /* |
894 | | * Initialise the fastpath index and |
895 | | * figure out the longest needle. |
896 | | */ |
897 | 182 | fr_sbuff_terminal_idx_init(&needle_len, idx, tt); |
898 | | |
899 | 363 | while (fr_sbuff_used_total(&our_in) < len) { |
900 | 363 | char *p; |
901 | 363 | char *end; |
902 | | |
903 | 363 | if (fr_sbuff_extend_lowat(NULL, &our_in, needle_len) == 0) break; |
904 | | |
905 | 181 | p = fr_sbuff_current(&our_in); |
906 | 181 | end = CONSTRAINED_END(&our_in, len, fr_sbuff_used_total(&our_in)); |
907 | | |
908 | 181 | if (p == end) break; |
909 | | |
910 | 181 | if (escape_chr == '\0') { |
911 | 20.6M | while ((p < end) && !fr_sbuff_terminal_search(in, p, idx, tt, needle_len)) p++; |
912 | 181 | } else { |
913 | 0 | while (p < end) { |
914 | 0 | if (do_escape) { |
915 | 0 | do_escape = false; |
916 | 0 | } else if (*p == escape_chr) { |
917 | 0 | do_escape = true; |
918 | 0 | } else if (fr_sbuff_terminal_search(in, p, idx, tt, needle_len)) { |
919 | 0 | break; |
920 | 0 | } |
921 | 0 | p++; |
922 | 0 | } |
923 | 0 | } |
924 | | |
925 | 181 | FILL_OR_GOTO_DONE(out, &our_in, p - our_in.p); |
926 | | |
927 | 181 | if (p != end) break; /* stopped early, break */ |
928 | 181 | } |
929 | | |
930 | 182 | done: |
931 | 182 | *out->p = '\0'; |
932 | 182 | return fr_sbuff_used_total(&our_in); |
933 | 182 | } |
934 | | |
935 | | /** Copy as many allowed characters as possible from a sbuff to a sbuff |
936 | | * |
937 | | * Copy size is limited by available data in sbuff and output buffer length. |
938 | | * |
939 | | * As soon as a disallowed character is found the copy is stopped. |
940 | | * The input sbuff will be left pointing at the first disallowed character. |
941 | | * |
942 | | * This de-escapes characters as they're copied out of the sbuff. |
943 | | * |
944 | | * @param[out] out Where to copy to. |
945 | | * @param[in] in Where to copy from. Will copy len bytes from current position in buffer. |
946 | | * @param[in] len How many bytes to copy. If SIZE_MAX the entire buffer will be copied. |
947 | | * @param[in] tt Token terminal strings in the encompassing grammar. |
948 | | * @param[in] u_rules for processing unescape sequences. |
949 | | * @return |
950 | | * - 0 no bytes copied. |
951 | | * - >0 the number of bytes written to out. |
952 | | */ |
953 | | size_t fr_sbuff_out_unescape_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, |
954 | | fr_sbuff_term_t const *tt, |
955 | | fr_sbuff_unescape_rules_t const *u_rules) |
956 | 178 | { |
957 | 178 | fr_sbuff_t our_in; |
958 | 178 | bool do_escape = false; /* Track state across extensions */ |
959 | 178 | fr_sbuff_marker_t o_s; |
960 | 178 | fr_sbuff_marker_t c_s; |
961 | 178 | fr_sbuff_marker_t end; |
962 | | |
963 | 178 | uint8_t idx[SBUFF_CHAR_CLASS]; /* Fast path index */ |
964 | 178 | size_t needle_len = 1; |
965 | 178 | fr_sbuff_extend_status_t status = 0; |
966 | | |
967 | | /* |
968 | | * If we don't need to do unescaping |
969 | | * call a more suitable function. |
970 | | */ |
971 | 178 | if (!u_rules || (u_rules->chr == '\0')) return fr_sbuff_out_bstrncpy_until(out, in, len, tt, u_rules); |
972 | | |
973 | 0 | CHECK_SBUFF_INIT(in); |
974 | | |
975 | 0 | our_in = FR_SBUFF(in); |
976 | | |
977 | | /* |
978 | | * Chunk tracking... |
979 | | */ |
980 | 0 | fr_sbuff_marker(&c_s, &our_in); |
981 | 0 | fr_sbuff_marker(&end, &our_in); |
982 | 0 | fr_sbuff_marker_update_end(&end, len); |
983 | |
|
984 | 0 | fr_sbuff_marker(&o_s, out); |
985 | | |
986 | | /* |
987 | | * Initialise the fastpath index and |
988 | | * figure out the longest needle. |
989 | | */ |
990 | 0 | fr_sbuff_terminal_idx_init(&needle_len, idx, tt); |
991 | | |
992 | | /* |
993 | | * ...while we have remaining data |
994 | | */ |
995 | 0 | while (fr_sbuff_extend_lowat(&status, &our_in, needle_len) > 0) { |
996 | 0 | if (fr_sbuff_was_extended(status)) fr_sbuff_marker_update_end(&end, len); |
997 | 0 | if (!fr_sbuff_diff(&our_in, &end)) break; /* Reached the end */ |
998 | | |
999 | 0 | if (do_escape) { |
1000 | 0 | do_escape = false; |
1001 | | |
1002 | | /* |
1003 | | * Check for \x<hex><hex> |
1004 | | */ |
1005 | 0 | if (u_rules->do_hex && fr_sbuff_is_char(&our_in, 'x')) { |
1006 | 0 | uint8_t escape; |
1007 | 0 | fr_sbuff_marker_t m; |
1008 | |
|
1009 | 0 | fr_sbuff_marker(&m, &our_in); /* allow for backtrack */ |
1010 | 0 | fr_sbuff_advance(&our_in, 1); /* skip over the 'x' */ |
1011 | |
|
1012 | 0 | if (fr_sbuff_out_uint8_hex(NULL, &escape, &our_in, false) != 2) { |
1013 | 0 | fr_sbuff_set(&our_in, &m); /* backtrack */ |
1014 | 0 | fr_sbuff_marker_release(&m); |
1015 | 0 | goto check_subs; /* allow sub for \x */ |
1016 | 0 | } |
1017 | | |
1018 | 0 | if (fr_sbuff_in_char(out, escape) <= 0) { |
1019 | 0 | fr_sbuff_set(&our_in, &m); /* backtrack */ |
1020 | 0 | fr_sbuff_marker_release(&m); |
1021 | 0 | break; |
1022 | 0 | } |
1023 | 0 | fr_sbuff_marker_release(&m); |
1024 | 0 | fr_sbuff_set(&c_s, &our_in); |
1025 | 0 | continue; |
1026 | 0 | } |
1027 | | |
1028 | | /* |
1029 | | * Check for \<oct><oct><oct> |
1030 | | */ |
1031 | 0 | if (u_rules->do_oct && fr_sbuff_is_digit(&our_in)) { |
1032 | 0 | uint8_t escape; |
1033 | 0 | fr_sbuff_marker_t m; |
1034 | |
|
1035 | 0 | fr_sbuff_marker(&m, &our_in); /* allow for backtrack */ |
1036 | |
|
1037 | 0 | if (fr_sbuff_out_uint8_oct(NULL, &escape, &our_in, false) != 3) { |
1038 | 0 | fr_sbuff_set(&our_in, &m); /* backtrack */ |
1039 | 0 | fr_sbuff_marker_release(&m); |
1040 | 0 | goto check_subs; /* allow sub for \<oct> */ |
1041 | 0 | } |
1042 | | |
1043 | 0 | if (fr_sbuff_in_char(out, escape) <= 0) { |
1044 | 0 | fr_sbuff_set(&our_in, &m); /* backtrack */ |
1045 | 0 | fr_sbuff_marker_release(&m); |
1046 | 0 | break; |
1047 | 0 | } |
1048 | 0 | fr_sbuff_marker_release(&m); |
1049 | 0 | fr_sbuff_set(&c_s, &our_in); |
1050 | 0 | continue; |
1051 | 0 | } |
1052 | | |
1053 | 0 | check_subs: |
1054 | | /* |
1055 | | * Not a recognised hex or octal escape sequence |
1056 | | * may be a substitution or a sequence that |
1057 | | * should be copied to the output buffer. |
1058 | | */ |
1059 | 0 | { |
1060 | 0 | uint8_t c = *fr_sbuff_current(&our_in); |
1061 | |
|
1062 | 0 | if (u_rules->subs[c] == '\0') { |
1063 | 0 | if (u_rules->skip[c] == true) goto next; |
1064 | 0 | goto next_esc; |
1065 | 0 | } |
1066 | | |
1067 | | /* |
1068 | | * We already copied everything up |
1069 | | * to this point, so we can now |
1070 | | * write the substituted char to |
1071 | | * the output buffer. |
1072 | | */ |
1073 | 0 | if (fr_sbuff_in_char(out, u_rules->subs[c]) <= 0) break; |
1074 | | |
1075 | | /* |
1076 | | * ...and advance past the entire |
1077 | | * escape seq in the input buffer. |
1078 | | */ |
1079 | 0 | fr_sbuff_advance(&our_in, 1); |
1080 | 0 | fr_sbuff_set(&c_s, &our_in); |
1081 | 0 | continue; |
1082 | 0 | } |
1083 | 0 | } |
1084 | | |
1085 | 0 | next_esc: |
1086 | 0 | if (*fr_sbuff_current(&our_in) == u_rules->chr) { |
1087 | | /* |
1088 | | * Copy out any data we got before |
1089 | | * we hit the escape char. |
1090 | | * |
1091 | | * We need to do this before we |
1092 | | * can write the escape char to |
1093 | | * the output sbuff. |
1094 | | */ |
1095 | 0 | FILL_OR_GOTO_DONE(out, &c_s, fr_sbuff_behind(&c_s)); |
1096 | | |
1097 | 0 | do_escape = true; |
1098 | 0 | fr_sbuff_advance(&our_in, 1); |
1099 | 0 | continue; |
1100 | 0 | } |
1101 | | |
1102 | 0 | next: |
1103 | 0 | if (tt && fr_sbuff_terminal_search(&our_in, fr_sbuff_current(&our_in), idx, tt, needle_len)) break; |
1104 | 0 | fr_sbuff_advance(&our_in, 1); |
1105 | 0 | } |
1106 | | |
1107 | | /* |
1108 | | * Copy any remaining data over |
1109 | | */ |
1110 | 0 | FILL_OR_GOTO_DONE(out, &c_s, fr_sbuff_behind(&c_s)); |
1111 | | |
1112 | 0 | done: |
1113 | 0 | fr_sbuff_set(in, &c_s); /* Only advance by as much as we copied */ |
1114 | 0 | *out->p = '\0'; |
1115 | |
|
1116 | 0 | return fr_sbuff_marker_release_behind(&o_s); |
1117 | 0 | } |
1118 | | |
1119 | | /** See if the string contains a truth value |
1120 | | * |
1121 | | * @param[out] out Where to write boolean value. |
1122 | | * @param[in] in Where to search for a truth value. |
1123 | | * @return |
1124 | | * - >0 the number of bytes consumed. |
1125 | | * - -1 no bytes copied, was not a truth value. |
1126 | | */ |
1127 | | fr_slen_t fr_sbuff_out_bool(bool *out, fr_sbuff_t *in) |
1128 | 311 | { |
1129 | 311 | fr_sbuff_t our_in = FR_SBUFF(in); |
1130 | | |
1131 | 311 | static bool const bool_prefix[SBUFF_CHAR_CLASS] = { |
1132 | 311 | ['t'] = true, ['T'] = true, /* true */ |
1133 | 311 | ['f'] = true, ['F'] = true, /* false */ |
1134 | 311 | ['y'] = true, ['Y'] = true, /* yes */ |
1135 | 311 | ['n'] = true, ['N'] = true, /* no */ |
1136 | 311 | }; |
1137 | | |
1138 | 311 | if (fr_sbuff_is_in_charset(&our_in, bool_prefix)) { |
1139 | 21 | switch (tolower(fr_sbuff_char(&our_in, '\0'))) { |
1140 | 0 | default: |
1141 | 0 | break; |
1142 | | |
1143 | 3 | case 't': |
1144 | 3 | if (fr_sbuff_adv_past_strcase_literal(&our_in, "true")) { |
1145 | 1 | *out = true; |
1146 | 1 | FR_SBUFF_SET_RETURN(in, &our_in); |
1147 | 1 | } |
1148 | 2 | break; |
1149 | | |
1150 | 9 | case 'f': |
1151 | 9 | if (fr_sbuff_adv_past_strcase_literal(&our_in, "false")) { |
1152 | 5 | *out = false; |
1153 | 5 | FR_SBUFF_SET_RETURN(in, &our_in); |
1154 | 5 | } |
1155 | 4 | break; |
1156 | | |
1157 | 5 | case 'y': |
1158 | 5 | if (fr_sbuff_adv_past_strcase_literal(&our_in, "yes")) { |
1159 | 2 | *out = true; |
1160 | 2 | FR_SBUFF_SET_RETURN(in, &our_in); |
1161 | 2 | } |
1162 | 3 | break; |
1163 | | |
1164 | 4 | case 'n': |
1165 | 4 | if (fr_sbuff_adv_past_strcase_literal(&our_in, "no")) { |
1166 | 1 | *out = false; |
1167 | 1 | FR_SBUFF_SET_RETURN(in, &our_in); |
1168 | 1 | } |
1169 | 3 | break; |
1170 | 21 | } |
1171 | 21 | } |
1172 | | |
1173 | 302 | *out = false; /* Always initialise out */ |
1174 | | |
1175 | 302 | fr_strerror_const("Not a valid boolean value. Accepted values are 'yes', 'no', 'true', 'false'"); |
1176 | | |
1177 | 302 | return -1; |
1178 | 311 | } |
1179 | | |
1180 | | /** Used to define a number parsing functions for signed integers |
1181 | | * |
1182 | | * @param[in] _name Function suffix. |
1183 | | * @param[in] _type Output type. |
1184 | | * @param[in] _min value. |
1185 | | * @param[in] _max value. |
1186 | | * @param[in] _max_char Maximum digits that can be used to represent an integer. |
1187 | | * Can't use stringify because of width modifiers like 'u' |
1188 | | * used in <stdint.h>. |
1189 | | * @param[in] _base to use. |
1190 | | */ |
1191 | | #define SBUFF_PARSE_INT_DEF(_name, _type, _min, _max, _max_char, _base) \ |
1192 | 7.16k | fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \ |
1193 | 7.16k | { \ |
1194 | 7.16k | char buff[_max_char + 1]; \ |
1195 | 7.16k | char *end, *a_end; \ |
1196 | 7.16k | size_t len; \ |
1197 | 7.16k | long long num; \ |
1198 | 7.16k | _type cast_num; \ |
1199 | 7.16k | fr_sbuff_t our_in = FR_SBUFF(in); \ |
1200 | 7.16k | buff[0] = '\0'; /* clang scan */ \ |
1201 | 7.16k | len = fr_sbuff_out_bstrncpy(&FR_SBUFF_IN(buff, sizeof(buff)), &our_in, _max_char); \ |
1202 | 7.16k | if (len == 0) { \ |
1203 | 6 | if (err) *err = (fr_sbuff_remaining(in) == 0) ? FR_SBUFF_PARSE_ERROR_INPUT_EMPTY : FR_SBUFF_PARSE_ERROR_NOT_FOUND; \ |
1204 | 6 | return -1; \ |
1205 | 6 | } \ |
1206 | 7.16k | errno = 0; /* this is needed as strtoll doesn't reset errno */ \ |
1207 | 7.15k | num = strtoll(buff, &end, _base); \ |
1208 | 7.15k | cast_num = (_type)(num); \ |
1209 | 7.15k | if (end == buff) { \ |
1210 | 188 | if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \ |
1211 | 188 | return -1; \ |
1212 | 188 | } \ |
1213 | 7.15k | if (num > cast_num) { \ |
1214 | 37 | overflow: \ |
1215 | 37 | if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \ |
1216 | 37 | *out = (_type)(_max); \ |
1217 | 37 | return -1; \ |
1218 | 20 | } \ |
1219 | 6.96k | if (((errno == EINVAL) && (num == 0)) || ((errno == ERANGE) && (num == LLONG_MAX))) goto overflow; \ |
1220 | 6.94k | if (num < cast_num) { \ |
1221 | 47 | underflow: \ |
1222 | 47 | if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \ |
1223 | 47 | *out = (_type)(_min); \ |
1224 | 47 | return -1; \ |
1225 | 10 | } \ |
1226 | 6.92k | if ((errno == ERANGE) && (num == LLONG_MIN)) goto underflow; \ |
1227 | 6.91k | if (no_trailing && ((a_end = in->p + (end - buff)) < in->end)) { \ |
1228 | 6.53k | if (isdigit((uint8_t) *a_end) || (((_base > 10) || ((_base == 0) && (len > 2) && (buff[0] == '0') && (buff[1] == 'x'))) && \ |
1229 | 6.42k | ((tolower((uint8_t) *a_end) >= 'a') && (tolower((uint8_t) *a_end) <= 'f')))) { \ |
1230 | 133 | if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \ |
1231 | 133 | *out = (_type)(_max); \ |
1232 | 133 | FR_SBUFF_ERROR_RETURN(&our_in); \ |
1233 | 133 | } \ |
1234 | 6.53k | *out = cast_num; \ |
1235 | 6.40k | } else { \ |
1236 | 348 | if (err) *err = FR_SBUFF_PARSE_OK; \ |
1237 | 348 | *out = cast_num; \ |
1238 | 348 | } \ |
1239 | 6.88k | return fr_sbuff_advance(in, end - buff); /* Advance by the length strtoll gives us */ \ |
1240 | 6.88k | } |
1241 | | |
1242 | 103 | SBUFF_PARSE_INT_DEF(int8, int8_t, INT8_MIN, INT8_MAX, 4, 0) |
1243 | 100 | SBUFF_PARSE_INT_DEF(int16, int16_t, INT16_MIN, INT16_MAX, 6, 0) |
1244 | 83 | SBUFF_PARSE_INT_DEF(int32, int32_t, INT32_MIN, INT32_MAX, 11, 0) |
1245 | 6.87k | SBUFF_PARSE_INT_DEF(int64, int64_t, INT64_MIN, INT64_MAX, 20, 0) |
1246 | 0 | SBUFF_PARSE_INT_DEF(ssize, ssize_t, SSIZE_MIN, SSIZE_MAX, 20, 0) |
1247 | | |
1248 | | /** Used to define a number parsing functions for signed integers |
1249 | | * |
1250 | | * @param[in] _name Function suffix. |
1251 | | * @param[in] _type Output type. |
1252 | | * @param[in] _max value. |
1253 | | * @param[in] _max_char Maximum digits that can be used to represent an integer. |
1254 | | * Can't use stringify because of width modifiers like 'u' |
1255 | | * used in <stdint.h>. |
1256 | | * @param[in] _base of the number being parsed, 8, 10, 16 etc... |
1257 | | */ |
1258 | | #define SBUFF_PARSE_UINT_DEF(_name, _type, _max, _max_char, _base) \ |
1259 | 32.8k | fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \ |
1260 | 32.8k | { \ |
1261 | 32.8k | char buff[_max_char + 1]; \ |
1262 | 32.8k | char *end, *a_end; \ |
1263 | 32.8k | size_t len; \ |
1264 | 32.8k | unsigned long long num; \ |
1265 | 32.8k | _type cast_num; \ |
1266 | 32.8k | fr_sbuff_t our_in = FR_SBUFF(in); \ |
1267 | 32.8k | buff[0] = '\0'; /* clang scan */ \ |
1268 | 32.8k | len = fr_sbuff_out_bstrncpy(&FR_SBUFF_IN(buff, sizeof(buff)), &our_in, _max_char); \ |
1269 | 32.8k | if (len == 0) { \ |
1270 | 117 | if (err) *err = (fr_sbuff_remaining(in) == 0) ? FR_SBUFF_PARSE_ERROR_INPUT_EMPTY : FR_SBUFF_PARSE_ERROR_NOT_FOUND; \ |
1271 | 117 | return -1; \ |
1272 | 117 | } \ |
1273 | 32.8k | if (buff[0] == '-') { \ |
1274 | 716 | if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \ |
1275 | 716 | return -1; \ |
1276 | 716 | } \ |
1277 | 32.7k | errno = 0; /* this is needed as strtoull doesn't reset errno */ \ |
1278 | 32.0k | num = strtoull(buff, &end, _base); \ |
1279 | 32.0k | cast_num = (_type)(num); \ |
1280 | 32.0k | if (end == buff) { \ |
1281 | 1.83k | if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \ |
1282 | 1.83k | return -1; \ |
1283 | 1.83k | } \ |
1284 | 32.0k | if (num > cast_num) { \ |
1285 | 359 | overflow: \ |
1286 | 359 | if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \ |
1287 | 359 | *out = (_type)(_max); \ |
1288 | 359 | return -1; \ |
1289 | 321 | } \ |
1290 | 30.2k | if (((errno == EINVAL) && (num == 0)) || ((errno == ERANGE) && (num == ULLONG_MAX))) goto overflow; \ |
1291 | 29.8k | if (no_trailing && ((a_end = in->p + (end - buff)) < in->end)) { \ |
1292 | 7.33k | if (isdigit((uint8_t) *a_end) || (((_base > 10) || ((_base == 0) && (len > 2) && (buff[0] == '0') && (buff[1] == 'x'))) && \ |
1293 | 6.82k | ((tolower((uint8_t) *a_end) >= 'a') && (tolower((uint8_t) *a_end) <= 'f')))) { \ |
1294 | 705 | if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \ |
1295 | 705 | *out = (_type)(_max); \ |
1296 | 705 | FR_SBUFF_ERROR_RETURN(&our_in); \ |
1297 | 705 | } \ |
1298 | 7.33k | if (err) *err = FR_SBUFF_PARSE_OK; \ |
1299 | 6.63k | *out = cast_num; \ |
1300 | 22.5k | } else { \ |
1301 | 22.5k | if (err) *err = FR_SBUFF_PARSE_OK; \ |
1302 | 22.5k | *out = cast_num; \ |
1303 | 22.5k | } \ |
1304 | 29.8k | return fr_sbuff_advance(in, end - buff); /* Advance by the length strtoull gives us */ \ |
1305 | 29.8k | } |
1306 | | |
1307 | | /* max chars here is the octal string value with prefix */ |
1308 | 4.18k | SBUFF_PARSE_UINT_DEF(uint8, uint8_t, UINT8_MAX, 4, 0) |
1309 | 2.05k | SBUFF_PARSE_UINT_DEF(uint16, uint16_t, UINT16_MAX, 7, 0) |
1310 | 20.8k | SBUFF_PARSE_UINT_DEF(uint32, uint32_t, UINT32_MAX, 12, 0) |
1311 | 5.79k | SBUFF_PARSE_UINT_DEF(uint64, uint64_t, UINT64_MAX, 23, 0) |
1312 | 0 | SBUFF_PARSE_UINT_DEF(size, size_t, SIZE_MAX, 23, 0) |
1313 | | |
1314 | 0 | SBUFF_PARSE_UINT_DEF(uint8_dec, uint8_t, UINT8_MAX, 3, 0) |
1315 | 0 | SBUFF_PARSE_UINT_DEF(uint16_dec, uint16_t, UINT16_MAX, 4, 0) |
1316 | 0 | SBUFF_PARSE_UINT_DEF(uint32_dec, uint32_t, UINT32_MAX, 10, 0) |
1317 | 0 | SBUFF_PARSE_UINT_DEF(uint64_dec, uint64_t, UINT64_MAX, 19, 0) |
1318 | 0 | SBUFF_PARSE_UINT_DEF(size_dec, size_t, SIZE_MAX, 19, 0) |
1319 | | |
1320 | | |
1321 | 0 | SBUFF_PARSE_UINT_DEF(uint8_oct, uint8_t, UINT8_MAX, 3, 8) |
1322 | 0 | SBUFF_PARSE_UINT_DEF(uint16_oct, uint16_t, UINT16_MAX, 6, 8) |
1323 | 0 | SBUFF_PARSE_UINT_DEF(uint32_oct, uint32_t, UINT32_MAX, 11, 8) |
1324 | 0 | SBUFF_PARSE_UINT_DEF(uint64_oct, uint64_t, UINT64_MAX, 22, 8) |
1325 | 0 | SBUFF_PARSE_UINT_DEF(size_oct, size_t, SIZE_MAX, 22, 8) |
1326 | | |
1327 | 0 | SBUFF_PARSE_UINT_DEF(uint8_hex, uint8_t, UINT8_MAX, 2, 16) |
1328 | 0 | SBUFF_PARSE_UINT_DEF(uint16_hex, uint16_t, UINT16_MAX, 4, 16) |
1329 | 0 | SBUFF_PARSE_UINT_DEF(uint32_hex, uint32_t, UINT32_MAX, 8, 16) |
1330 | 0 | SBUFF_PARSE_UINT_DEF(uint64_hex, uint64_t, UINT64_MAX, 16, 16) |
1331 | 0 | SBUFF_PARSE_UINT_DEF(size_hex, size_t, SIZE_MAX, 22, 16) |
1332 | | |
1333 | | /** Used to define a number parsing functions for floats |
1334 | | * |
1335 | | * @param[in] _name Function suffix. |
1336 | | * @param[in] _type Output type. |
1337 | | * @param[in] _func Parsing function to use. |
1338 | | * @param[in] _max_char Maximum digits that can be used to represent an integer. |
1339 | | * Can't use stringify because of width modifiers like 'u' |
1340 | | * used in <stdint.h>. |
1341 | | */ |
1342 | | #define SBUFF_PARSE_FLOAT_DEF(_name, _type, _func, _max_char) \ |
1343 | 1.17k | fr_slen_t fr_sbuff_out_##_name(fr_sbuff_parse_error_t *err, _type *out, fr_sbuff_t *in, bool no_trailing) \ |
1344 | 1.17k | { \ |
1345 | 1.17k | char buff[_max_char + 1] = ""; \ |
1346 | 1.17k | char *end; \ |
1347 | 1.17k | fr_sbuff_t our_in = FR_SBUFF(in); \ |
1348 | 1.17k | size_t len; \ |
1349 | 1.17k | _type res; \ |
1350 | 1.17k | len = fr_sbuff_out_bstrncpy_allowed(&FR_SBUFF_OUT(buff, sizeof(buff)), &our_in, SIZE_MAX, sbuff_char_class_float); \ |
1351 | 1.17k | if (len == sizeof(buff)) { \ |
1352 | 0 | if (err) *err = FR_SBUFF_PARSE_ERROR_NOT_FOUND; \ |
1353 | 0 | return -1; \ |
1354 | 1.17k | } else if (len == 0) { \ |
1355 | 152 | if (err) *err = (fr_sbuff_remaining(in) == 0) ? FR_SBUFF_PARSE_ERROR_INPUT_EMPTY : FR_SBUFF_PARSE_ERROR_NOT_FOUND; \ |
1356 | 152 | return -1; \ |
1357 | 152 | } \ |
1358 | 1.17k | errno = 0; /* this is needed as parsing functions don't reset errno */ \ |
1359 | 1.01k | res = _func(buff, &end); \ |
1360 | 1.01k | if (errno == ERANGE) { \ |
1361 | 124 | if (res > 0) { \ |
1362 | 110 | if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_OVERFLOW; \ |
1363 | 110 | } else { \ |
1364 | 14 | if (err) *err = FR_SBUFF_PARSE_ERROR_NUM_UNDERFLOW; \ |
1365 | 14 | } \ |
1366 | 124 | return -1; \ |
1367 | 124 | } \ |
1368 | 1.01k | if (no_trailing && (*end != '\0')) { \ |
1369 | 194 | if (err) *err = FR_SBUFF_PARSE_ERROR_TRAILING; \ |
1370 | 194 | FR_SBUFF_ERROR_RETURN(&our_in); \ |
1371 | 194 | } \ |
1372 | 894 | *out = res; \ |
1373 | 700 | return fr_sbuff_advance(in, end - buff); \ |
1374 | 894 | } |
1375 | | |
1376 | 82 | SBUFF_PARSE_FLOAT_DEF(float32, float, strtof, 100) |
1377 | 1.08k | SBUFF_PARSE_FLOAT_DEF(float64, double, strtod, 100) |
1378 | | |
1379 | | /** Move data from one sbuff to another |
1380 | | * |
1381 | | * @note Do not call this function directly use #fr_sbuff_move |
1382 | | * |
1383 | | * Both in and out will be advanced by len, with len set to the shortest |
1384 | | * value between the user specified value, the number of bytes remaining |
1385 | | * in the input buffer (after extension), and the number of bytes remaining |
1386 | | * in the output buffer (after extension). |
1387 | | * |
1388 | | * @param[in] out sbuff to copy data to. |
1389 | | * @param[in] in sbuff to copy data from. |
1390 | | * @param[in] len Maximum length of string to copy. |
1391 | | * @return The amount of data copied. |
1392 | | */ |
1393 | | size_t _fr_sbuff_move_sbuff_to_sbuff(fr_sbuff_t *out, fr_sbuff_t *in, size_t len) |
1394 | 48.9k | { |
1395 | 48.9k | size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len); |
1396 | 48.9k | size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len); |
1397 | 48.9k | size_t to_copy = len; |
1398 | 48.9k | if (to_copy > o_remaining) to_copy = o_remaining; |
1399 | 48.9k | if (to_copy > i_remaining) to_copy = i_remaining; |
1400 | 48.9k | safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy); |
1401 | 48.9k | return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy)); |
1402 | 48.9k | } |
1403 | | |
1404 | | /** Move data from a marker to an sbuff |
1405 | | * |
1406 | | * @note Do not call this function directly use #fr_sbuff_move |
1407 | | * |
1408 | | * @param[in] out sbuff to copy data to. |
1409 | | * @param[in] in marker to copy data from. |
1410 | | * @param[in] len Maximum length of string to copy. |
1411 | | * @return The amount of data copied. |
1412 | | */ |
1413 | | size_t _fr_sbuff_move_marker_to_sbuff(fr_sbuff_t *out, fr_sbuff_marker_t *in, size_t len) |
1414 | 0 | { |
1415 | 0 | size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len); |
1416 | 0 | size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len); |
1417 | 0 | size_t to_copy = len; |
1418 | 0 | if (to_copy > o_remaining) to_copy = o_remaining; |
1419 | 0 | if (to_copy > i_remaining) to_copy = i_remaining; |
1420 | 0 | safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy); |
1421 | 0 | return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy)); |
1422 | 0 | } |
1423 | | |
1424 | | /** Move data from one marker to another |
1425 | | * |
1426 | | * @note Do not call this function directly use #fr_sbuff_move |
1427 | | * |
1428 | | * @param[in] out marker to copy data to. |
1429 | | * @param[in] in marker to copy data from. |
1430 | | * @param[in] len Maximum length of string to copy. |
1431 | | * @return The amount of data copied. |
1432 | | */ |
1433 | | size_t _fr_sbuff_move_marker_to_marker(fr_sbuff_marker_t *out, fr_sbuff_marker_t *in, size_t len) |
1434 | 0 | { |
1435 | 0 | size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len); |
1436 | 0 | size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len); |
1437 | 0 | size_t to_copy = len; |
1438 | 0 | if (to_copy > o_remaining) to_copy = o_remaining; |
1439 | 0 | if (to_copy > i_remaining) to_copy = i_remaining; |
1440 | 0 | safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy); |
1441 | 0 | return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy)); |
1442 | 0 | } |
1443 | | |
1444 | | /** Move data from an sbuff to a marker |
1445 | | * |
1446 | | * @note Do not call this function directly use #fr_sbuff_move |
1447 | | * |
1448 | | * @param[in] out marker to copy data to. |
1449 | | * @param[in] in sbuff to copy data from. |
1450 | | * @param[in] len Maximum length of string to copy. |
1451 | | * @return The amount of data copied. |
1452 | | */ |
1453 | | size_t _fr_sbuff_move_sbuff_to_marker(fr_sbuff_marker_t *out, fr_sbuff_t *in, size_t len) |
1454 | 0 | { |
1455 | 0 | size_t o_remaining = fr_sbuff_extend_lowat(NULL, out, len); |
1456 | 0 | size_t i_remaining = fr_sbuff_extend_lowat(NULL, in, len); |
1457 | 0 | size_t to_copy = len; |
1458 | 0 | if (to_copy > o_remaining) to_copy = o_remaining; |
1459 | 0 | if (to_copy > i_remaining) to_copy = i_remaining; |
1460 | 0 | safecpy(fr_sbuff_current(out), fr_sbuff_end(out), fr_sbuff_current(in), fr_sbuff_current(in) + to_copy); |
1461 | 0 | return fr_sbuff_advance(out, fr_sbuff_advance(in, to_copy)); |
1462 | 0 | } |
1463 | | |
1464 | | /** Copy bytes into the sbuff up to the first \0 |
1465 | | * |
1466 | | * @param[in] sbuff to copy into. |
1467 | | * @param[in] str to copy into buffer. |
1468 | | * @return |
1469 | | * - >= 0 the number of bytes copied into the sbuff. |
1470 | | * - <0 the number of bytes required to complete the copy operation. |
1471 | | */ |
1472 | | ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str) |
1473 | 547 | { |
1474 | 547 | size_t len; |
1475 | | |
1476 | 547 | CHECK_SBUFF_WRITEABLE(sbuff); |
1477 | | |
1478 | 547 | len = strlen(str); |
1479 | 547 | FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len); |
1480 | | |
1481 | 547 | safecpy(sbuff->p, sbuff->end, str, str + len); |
1482 | 547 | sbuff->p[len] = '\0'; |
1483 | | |
1484 | 547 | return fr_sbuff_advance(sbuff, len); |
1485 | 547 | } |
1486 | | |
1487 | | /** Copy bytes into the sbuff up to the first \0 |
1488 | | * |
1489 | | * @param[in] sbuff to copy into. |
1490 | | * @param[in] str to copy into buffer. |
1491 | | * @param[in] len number of bytes to copy. |
1492 | | * @return |
1493 | | * - >= 0 the number of bytes copied into the sbuff. |
1494 | | * - <0 the number of bytes required to complete the copy operation. |
1495 | | */ |
1496 | | ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len) |
1497 | 25.8M | { |
1498 | 25.8M | CHECK_SBUFF_WRITEABLE(sbuff); |
1499 | | |
1500 | 25.8M | FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len); |
1501 | | |
1502 | 25.8M | safecpy(sbuff->p, sbuff->end, str, str + len); |
1503 | 25.8M | sbuff->p[len] = '\0'; |
1504 | | |
1505 | 25.8M | return fr_sbuff_advance(sbuff, len); |
1506 | 25.8M | } |
1507 | | |
1508 | | /** Copy bytes into the sbuff up to the first \0 |
1509 | | * |
1510 | | * @param[in] sbuff to copy into. |
1511 | | * @param[in] str talloced buffer to copy into sbuff. |
1512 | | * @return |
1513 | | * - >= 0 the number of bytes copied into the sbuff. |
1514 | | * - <0 the number of bytes required to complete the copy operation. |
1515 | | */ |
1516 | | ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str) |
1517 | 18.7M | { |
1518 | 18.7M | size_t len; |
1519 | | |
1520 | 18.7M | CHECK_SBUFF_WRITEABLE(sbuff); |
1521 | | |
1522 | 18.7M | len = talloc_strlen(str); |
1523 | | |
1524 | 18.7M | FR_SBUFF_EXTEND_LOWAT_OR_RETURN(sbuff, len); |
1525 | | |
1526 | 18.7M | safecpy(sbuff->p, sbuff->end, str, str + len); |
1527 | 18.7M | sbuff->p[len] = '\0'; |
1528 | | |
1529 | 18.7M | return fr_sbuff_advance(sbuff, len); |
1530 | 18.7M | } |
1531 | | |
1532 | | /** Free the scratch buffer used for printf |
1533 | | * |
1534 | | */ |
1535 | | static int _sbuff_scratch_free(void *arg) |
1536 | 20 | { |
1537 | 20 | sbuff_scratch_freed = true; |
1538 | 20 | return talloc_free(arg); |
1539 | 20 | } |
1540 | | |
1541 | | static inline CC_HINT(always_inline) int sbuff_scratch_init(TALLOC_CTX **out) |
1542 | 18.7M | { |
1543 | 18.7M | TALLOC_CTX *scratch; |
1544 | | |
1545 | 18.7M | if (sbuff_scratch_freed) { |
1546 | 0 | *out = NULL; |
1547 | 0 | return 0; |
1548 | 0 | } |
1549 | | |
1550 | 18.7M | scratch = sbuff_scratch; |
1551 | 18.7M | if (!scratch) { |
1552 | 20 | scratch = talloc_pool(NULL, 4096); |
1553 | 20 | if (unlikely(!scratch)) { |
1554 | 0 | fr_strerror_const("Out of Memory"); |
1555 | 0 | return -1; |
1556 | 0 | } |
1557 | 20 | fr_atexit_thread_local(sbuff_scratch, _sbuff_scratch_free, scratch); |
1558 | 20 | } |
1559 | | |
1560 | 18.7M | *out = scratch; |
1561 | | |
1562 | 18.7M | return 0; |
1563 | 18.7M | } |
1564 | | |
1565 | | /** Print using a fmt string to an sbuff |
1566 | | * |
1567 | | * @param[in] sbuff to print into. |
1568 | | * @param[in] fmt string. |
1569 | | * @param[in] ap arguments for format string. |
1570 | | < * @return |
1571 | | * - >= 0 the number of bytes printed into the sbuff. |
1572 | | * - <0 the number of bytes required to complete the print operation. |
1573 | | */ |
1574 | | ssize_t fr_sbuff_in_vsprintf(fr_sbuff_t *sbuff, char const *fmt, va_list ap) |
1575 | 18.7M | { |
1576 | 18.7M | TALLOC_CTX *scratch; |
1577 | 18.7M | va_list ap_p; |
1578 | 18.7M | char *tmp; |
1579 | 18.7M | ssize_t slen; |
1580 | | |
1581 | 18.7M | CHECK_SBUFF_WRITEABLE(sbuff); |
1582 | | |
1583 | 18.7M | if (sbuff_scratch_init(&scratch) < 0) return 0; |
1584 | | |
1585 | 18.7M | va_copy(ap_p, ap); |
1586 | 18.7M | tmp = fr_vasprintf(scratch, fmt, ap_p); |
1587 | 18.7M | va_end(ap_p); |
1588 | 18.7M | if (!tmp) return 0; |
1589 | | |
1590 | 18.7M | slen = fr_sbuff_in_bstrcpy_buffer(sbuff, tmp); |
1591 | 18.7M | talloc_free(tmp); /* Free the temporary buffer */ |
1592 | | |
1593 | 18.7M | return slen; |
1594 | 18.7M | } |
1595 | | |
1596 | | /** Print using a fmt string to an sbuff |
1597 | | * |
1598 | | * @param[in] sbuff to print into. |
1599 | | * @param[in] fmt string. |
1600 | | * @param[in] ... arguments for format string. |
1601 | | * @return |
1602 | | * - >= 0 the number of bytes printed into the sbuff. |
1603 | | * - <0 the number of bytes required to complete the print operation. |
1604 | | */ |
1605 | | ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt, ...) |
1606 | 18.7M | { |
1607 | 18.7M | va_list ap; |
1608 | 18.7M | ssize_t slen; |
1609 | | |
1610 | 18.7M | CHECK_SBUFF_WRITEABLE(sbuff); |
1611 | | |
1612 | 18.7M | va_start(ap, fmt); |
1613 | 18.7M | slen = fr_sbuff_in_vsprintf(sbuff, fmt, ap); |
1614 | 18.7M | va_end(ap); |
1615 | | |
1616 | 18.7M | return slen; |
1617 | 18.7M | } |
1618 | | |
1619 | | /** Print an escaped string to an sbuff |
1620 | | * |
1621 | | * @param[in] sbuff to print into. |
1622 | | * @param[in] in to escape. |
1623 | | * @param[in] inlen of string to escape. |
1624 | | * @param[in] e_rules Escaping rules. Used to escape special characters |
1625 | | * as data is written to the sbuff. May be NULL. |
1626 | | * @return |
1627 | | * - >= 0 the number of bytes printed into the sbuff. |
1628 | | * - <0 the number of bytes required to complete the print operation. |
1629 | | */ |
1630 | | ssize_t fr_sbuff_in_escape(fr_sbuff_t *sbuff, char const *in, size_t inlen, fr_sbuff_escape_rules_t const *e_rules) |
1631 | 7.37k | { |
1632 | 7.37k | char const *end = in + inlen; |
1633 | 7.37k | char const *p = in; |
1634 | 7.37k | fr_sbuff_t our_sbuff; |
1635 | | |
1636 | | /* Significantly quicker if there are no rules */ |
1637 | 7.37k | if (!e_rules || (e_rules->chr == '\0')) return fr_sbuff_in_bstrncpy(sbuff, in, inlen); |
1638 | | |
1639 | 7.37k | CHECK_SBUFF_WRITEABLE(sbuff); |
1640 | | |
1641 | 7.37k | our_sbuff = FR_SBUFF(sbuff); |
1642 | 42.5M | while (p < end) { |
1643 | 42.5M | size_t clen; |
1644 | 42.5M | uint8_t c = (uint8_t)*p; |
1645 | 42.5M | char sub; |
1646 | | |
1647 | | /* |
1648 | | * We don't support escaping UTF8 sequences |
1649 | | * as they're not used anywhere in our |
1650 | | * grammar. |
1651 | | */ |
1652 | 42.5M | if (e_rules->do_utf8 && ((clen = fr_utf8_char((uint8_t const *)p, end - p)) > 1)) { |
1653 | 182k | FR_SBUFF_IN_BSTRNCPY_RETURN(&our_sbuff, p, clen); |
1654 | 182k | p += clen; |
1655 | 182k | continue; |
1656 | 182k | } |
1657 | | |
1658 | | /* |
1659 | | * Check if there's a special substitution |
1660 | | * like 0x0a -> \n. |
1661 | | */ |
1662 | 42.4M | sub = e_rules->subs[c]; |
1663 | 42.4M | if (sub != '\0') { |
1664 | 320k | FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, e_rules->chr, sub); |
1665 | 320k | p++; |
1666 | 320k | continue; |
1667 | 320k | } |
1668 | | |
1669 | | /* |
1670 | | * Check if the character is in the range |
1671 | | * we escape. |
1672 | | */ |
1673 | 42.0M | if (e_rules->esc[c]) { |
1674 | | /* |
1675 | | * For legacy reasons we prefer |
1676 | | * octal escape sequences. |
1677 | | */ |
1678 | 16.7M | if (e_rules->do_oct) { |
1679 | 16.7M | FR_SBUFF_IN_SPRINTF_RETURN(&our_sbuff, "%c%03o", e_rules->chr, (uint8_t)*p++); |
1680 | 16.7M | continue; |
1681 | 16.7M | } else if (e_rules->do_hex) { |
1682 | 0 | FR_SBUFF_IN_SPRINTF_RETURN(&our_sbuff, "%cx%02x", e_rules->chr, (uint8_t)*p++); |
1683 | 0 | continue; |
1684 | 0 | } |
1685 | 16.7M | } |
1686 | | |
1687 | 25.3M | FR_SBUFF_IN_CHAR_RETURN(&our_sbuff, *p++); |
1688 | 25.3M | } |
1689 | | |
1690 | 7.37k | FR_SBUFF_SET_RETURN(sbuff, &our_sbuff); |
1691 | 7.37k | } |
1692 | | |
1693 | | /** Print an escaped string to an sbuff taking a talloced buffer as input |
1694 | | * |
1695 | | * @param[in] sbuff to print into. |
1696 | | * @param[in] in to escape. |
1697 | | * @param[in] e_rules Escaping rules. Used to escape special characters |
1698 | | * as data is written to the sbuff. May be NULL. |
1699 | | * @return |
1700 | | * - >= 0 the number of bytes printed into the sbuff. |
1701 | | * - <0 the number of bytes required to complete the print operation. |
1702 | | */ |
1703 | | ssize_t fr_sbuff_in_escape_buffer(fr_sbuff_t *sbuff, char const *in, fr_sbuff_escape_rules_t const *e_rules) |
1704 | 0 | { |
1705 | 0 | if (unlikely(!in)) return 0; |
1706 | | |
1707 | 0 | CHECK_SBUFF_WRITEABLE(sbuff); |
1708 | | |
1709 | 0 | return fr_sbuff_in_escape(sbuff, in, talloc_strlen(in), e_rules); |
1710 | 0 | } |
1711 | | |
1712 | | /** Concat an array of strings (NULL terminated), with a string separator |
1713 | | * |
1714 | | * @param[out] out Where to write the resulting string. |
1715 | | * @param[in] array of strings to concat. |
1716 | | * @param[in] sep to insert between elements. May be NULL. |
1717 | | * @return |
1718 | | * - >= 0 on success - length of the string created. |
1719 | | * - <0 on failure. How many bytes we would need. |
1720 | | */ |
1721 | | fr_slen_t fr_sbuff_in_array(fr_sbuff_t *out, char const * const *array, char const *sep) |
1722 | 0 | { |
1723 | 0 | fr_sbuff_t our_out = FR_SBUFF(out); |
1724 | 0 | char const * const * p; |
1725 | 0 | fr_sbuff_escape_rules_t e_rules = { |
1726 | 0 | .name = __FUNCTION__, |
1727 | 0 | .chr = '\\' |
1728 | 0 | }; |
1729 | |
|
1730 | 0 | if (sep) e_rules.subs[(uint8_t)*sep] = *sep; |
1731 | |
|
1732 | 0 | CHECK_SBUFF_WRITEABLE(out); |
1733 | | |
1734 | 0 | for (p = array; *p; p++) { |
1735 | 0 | if (*p) FR_SBUFF_RETURN(fr_sbuff_in_escape, &our_out, *p, strlen(*p), &e_rules); |
1736 | | |
1737 | 0 | if (sep && p[1]) { |
1738 | 0 | FR_SBUFF_RETURN(fr_sbuff_in_strcpy, &our_out, sep); |
1739 | 0 | } |
1740 | 0 | } |
1741 | | |
1742 | 0 | FR_SBUFF_SET_RETURN(out, &our_out); |
1743 | 0 | } |
1744 | | |
1745 | | /** Return true and advance past the end of the needle if needle occurs next in the sbuff |
1746 | | * |
1747 | | * @param[in] sbuff to search in. |
1748 | | * @param[in] needle to search for. |
1749 | | * @param[in] needle_len of needle. If SIZE_MAX strlen is used |
1750 | | * to determine length of the needle. |
1751 | | * @return how many bytes we advanced |
1752 | | */ |
1753 | | size_t fr_sbuff_adv_past_str(fr_sbuff_t *sbuff, char const *needle, size_t needle_len) |
1754 | 2.18k | { |
1755 | 2.18k | char const *found; |
1756 | | |
1757 | 2.18k | CHECK_SBUFF_INIT(sbuff); |
1758 | | |
1759 | 2.18k | if (needle_len == SIZE_MAX) needle_len = strlen(needle); |
1760 | | |
1761 | | /* |
1762 | | * If there's insufficient bytes in the |
1763 | | * buffer currently, try to extend it, |
1764 | | * returning if we can't. |
1765 | | */ |
1766 | 2.18k | if (fr_sbuff_extend_lowat(NULL, sbuff, needle_len) < needle_len) return 0; |
1767 | | |
1768 | 2.08k | found = memmem(sbuff->p, needle_len, needle, needle_len); /* sbuff needle_len and needle needle_len ensures match must be next */ |
1769 | 2.08k | if (!found) return 0; |
1770 | | |
1771 | 101 | return fr_sbuff_advance(sbuff, needle_len); |
1772 | 2.08k | } |
1773 | | |
1774 | | /** Return true and advance past the end of the needle if needle occurs next in the sbuff |
1775 | | * |
1776 | | * This function is similar to fr_sbuff_adv_past_str but is case insensitive. |
1777 | | * |
1778 | | * @param[in] sbuff to search in. |
1779 | | * @param[in] needle to search for. |
1780 | | * @param[in] needle_len of needle. If SIZE_MAX strlen is used |
1781 | | * to determine length of the needle. |
1782 | | * @return how many bytes we advanced |
1783 | | */ |
1784 | | size_t fr_sbuff_adv_past_strcase(fr_sbuff_t *sbuff, char const *needle, size_t needle_len) |
1785 | 196 | { |
1786 | 196 | char const *p, *n_p; |
1787 | 196 | char const *end; |
1788 | | |
1789 | 196 | CHECK_SBUFF_INIT(sbuff); |
1790 | | |
1791 | 196 | if (needle_len == SIZE_MAX) needle_len = strlen(needle); |
1792 | | |
1793 | | /* |
1794 | | * If there's insufficient bytes in the |
1795 | | * buffer currently, try to extend it, |
1796 | | * returning if we can't. |
1797 | | */ |
1798 | 196 | if (fr_sbuff_extend_lowat(NULL, sbuff, needle_len) < needle_len) return 0; |
1799 | | |
1800 | 190 | p = sbuff->p; |
1801 | 190 | end = p + needle_len; |
1802 | | |
1803 | 410 | for (p = sbuff->p, n_p = needle; p < end; p++, n_p++) { |
1804 | 320 | if (tolower((uint8_t) *p) != tolower((uint8_t) *n_p)) return 0; |
1805 | 320 | } |
1806 | | |
1807 | 90 | return fr_sbuff_advance(sbuff, needle_len); |
1808 | 190 | } |
1809 | | |
1810 | | /** Wind position past characters in the allowed set |
1811 | | * |
1812 | | * @param[in] sbuff sbuff to search in. |
1813 | | * @param[in] len Maximum amount to advance by. Unconstrained if SIZE_MAX. |
1814 | | * @param[in] allowed character set. |
1815 | | * @param[in] tt If not NULL, stop if we find a terminal sequence. |
1816 | | * @return how many bytes we advanced. |
1817 | | */ |
1818 | | size_t fr_sbuff_adv_past_allowed(fr_sbuff_t *sbuff, size_t len, bool |
1819 | | const allowed[static SBUFF_CHAR_CLASS], fr_sbuff_term_t const *tt) |
1820 | 53.6k | { |
1821 | 53.6k | size_t total = 0; |
1822 | 53.6k | char const *p; |
1823 | 53.6k | uint8_t idx[SBUFF_CHAR_CLASS]; /* Fast path index */ |
1824 | 53.6k | size_t needle_len = 0; |
1825 | | |
1826 | 53.6k | CHECK_SBUFF_INIT(sbuff); |
1827 | | |
1828 | 53.6k | if (tt) fr_sbuff_terminal_idx_init(&needle_len, idx, tt); |
1829 | | |
1830 | 55.8k | while (total < len) { |
1831 | 53.7k | char *end; |
1832 | | |
1833 | 53.7k | if (!fr_sbuff_extend(sbuff)) break; |
1834 | | |
1835 | 53.6k | end = CONSTRAINED_END(sbuff, len, total); |
1836 | 53.6k | p = sbuff->p; |
1837 | 24.9M | while ((p < end) && allowed[(uint8_t)*p]) { |
1838 | 24.9M | if (needle_len == 0) { |
1839 | 24.9M | p++; |
1840 | 24.9M | continue; |
1841 | 24.9M | } |
1842 | | |
1843 | | /* |
1844 | | * If this character is allowed, BUT is also listed as a one-character terminal, |
1845 | | * then we still allow it. This decision implements "greedy" parsing. |
1846 | | */ |
1847 | 0 | if (fr_sbuff_terminal_search(sbuff, p, idx, tt, 1)) { |
1848 | 0 | p++; |
1849 | 0 | continue; |
1850 | 0 | } |
1851 | | |
1852 | | /* |
1853 | | * Otherwise if the next *set* of characters) is not in the terminals, then |
1854 | | * allow the current character. |
1855 | | */ |
1856 | 0 | if (!fr_sbuff_terminal_search(sbuff, p, idx, tt, needle_len)) { |
1857 | 0 | p++; |
1858 | 0 | continue; |
1859 | 0 | } |
1860 | | |
1861 | | /* |
1862 | | * The character is allowed, and is NOT listed as a terminal character by itself. |
1863 | | * However, it is part of a multi-character terminal sequence. We therefore |
1864 | | * stop. |
1865 | | * |
1866 | | * This decision allows us to parse things like "Framed-User", where we might |
1867 | | * normally stop at the "-". However, we will still stop at "Framed-=User", as |
1868 | | * "-=" may be a terminal sequence. |
1869 | | * |
1870 | | * There is no perfect solution here, other than to fix the input grammar so that |
1871 | | * it has no ambiguity. Since we can't do that, we choose to err on the side of |
1872 | | * allowing the existing grammar, where it makes sense |
1873 | | */ |
1874 | 0 | break; |
1875 | 0 | } |
1876 | | |
1877 | 53.6k | total += fr_sbuff_set(sbuff, p); |
1878 | 53.6k | if (p != end) break; /* stopped early, break */ |
1879 | 53.6k | } |
1880 | | |
1881 | 53.6k | return total; |
1882 | 53.6k | } |
1883 | | |
1884 | | /** Wind position until we hit a character in the terminal set |
1885 | | * |
1886 | | * @param[in] sbuff sbuff to search in. |
1887 | | * @param[in] len Maximum amount to advance by. Unconstrained if SIZE_MAX. |
1888 | | * @param[in] tt Token terminals in the encompassing grammar. |
1889 | | * @param[in] escape_chr If not '\0', ignore characters in the tt set when |
1890 | | * prefixed with this escape character. |
1891 | | * @return how many bytes we advanced. |
1892 | | */ |
1893 | | size_t fr_sbuff_adv_until(fr_sbuff_t *sbuff, size_t len, fr_sbuff_term_t const *tt, char escape_chr) |
1894 | 747 | { |
1895 | 747 | size_t total = 0; |
1896 | 747 | char const *p; |
1897 | 747 | bool do_escape = false; /* Track state across extensions */ |
1898 | | |
1899 | 747 | uint8_t idx[SBUFF_CHAR_CLASS]; /* Fast path index */ |
1900 | 747 | size_t needle_len = 1; |
1901 | | |
1902 | 747 | CHECK_SBUFF_INIT(sbuff); |
1903 | | |
1904 | | /* |
1905 | | * Initialise the fastpath index and |
1906 | | * figure out the longest needle. |
1907 | | */ |
1908 | 747 | fr_sbuff_terminal_idx_init(&needle_len, idx, tt); |
1909 | | |
1910 | 1.49k | while (total < len) { |
1911 | 1.49k | char *end; |
1912 | | |
1913 | 1.49k | if (fr_sbuff_extend_lowat(NULL, sbuff, needle_len) == 0) break; |
1914 | | |
1915 | 747 | end = CONSTRAINED_END(sbuff, len, total); |
1916 | 747 | p = sbuff->p; |
1917 | | |
1918 | 747 | if (escape_chr == '\0') { |
1919 | 34.6k | while ((p < end) && !fr_sbuff_terminal_search(sbuff, p, idx, tt, needle_len)) p++; |
1920 | 747 | } else { |
1921 | 0 | while (p < end) { |
1922 | 0 | if (do_escape) { |
1923 | 0 | do_escape = false; |
1924 | 0 | } else if (*p == escape_chr) { |
1925 | 0 | do_escape = true; |
1926 | 0 | } else if (fr_sbuff_terminal_search(sbuff, p, idx, tt, needle_len)) { |
1927 | 0 | break; |
1928 | 0 | } |
1929 | 0 | p++; |
1930 | 0 | } |
1931 | 0 | } |
1932 | | |
1933 | 747 | total += fr_sbuff_set(sbuff, p); |
1934 | 747 | if (p != end) break; /* stopped early, break */ |
1935 | 747 | } |
1936 | | |
1937 | 747 | return total; |
1938 | 747 | } |
1939 | | |
1940 | | /** Wind position to first instance of specified multibyte utf8 char |
1941 | | * |
1942 | | * Only use this function if the search char could be multibyte, |
1943 | | * as there's a large performance penalty. |
1944 | | * |
1945 | | * @param[in,out] sbuff to search in. |
1946 | | * @param[in] len the maximum number of characters to search in sbuff. |
1947 | | * @param[in] chr to search for. |
1948 | | * @return |
1949 | | * - NULL, no instances found. |
1950 | | * - The position of the first character. |
1951 | | */ |
1952 | | char *fr_sbuff_adv_to_chr_utf8(fr_sbuff_t *sbuff, size_t len, char const *chr) |
1953 | 0 | { |
1954 | 0 | fr_sbuff_t our_sbuff = FR_SBUFF(sbuff); |
1955 | 0 | size_t total = 0; |
1956 | 0 | size_t clen = strlen(chr); |
1957 | |
|
1958 | 0 | CHECK_SBUFF_INIT(sbuff); |
1959 | | |
1960 | | /* |
1961 | | * Needle bigger than haystack |
1962 | | */ |
1963 | 0 | if (len < clen) return NULL; |
1964 | | |
1965 | 0 | while (total <= (len - clen)) { |
1966 | 0 | char const *found; |
1967 | 0 | char *end; |
1968 | | |
1969 | | /* |
1970 | | * Ensure we have enough chars to match |
1971 | | * the needle. |
1972 | | */ |
1973 | 0 | if (fr_sbuff_extend_lowat(NULL, &our_sbuff, clen) < clen) break; |
1974 | | |
1975 | 0 | end = CONSTRAINED_END(&our_sbuff, len, total); |
1976 | |
|
1977 | 0 | found = fr_utf8_strchr(NULL, our_sbuff.p, end - our_sbuff.p, chr); |
1978 | 0 | if (found) { |
1979 | 0 | (void)fr_sbuff_set(sbuff, found); |
1980 | 0 | return sbuff->p; |
1981 | 0 | } |
1982 | 0 | total += fr_sbuff_set(&our_sbuff, (end - clen) + 1); |
1983 | 0 | } |
1984 | | |
1985 | 0 | return NULL; |
1986 | 0 | } |
1987 | | |
1988 | | /** Wind position to first instance of specified char |
1989 | | * |
1990 | | * @param[in,out] sbuff to search in. |
1991 | | * @param[in] len Maximum amount to advance by. Unconstrained if SIZE_MAX. |
1992 | | * @param[in] c to search for. |
1993 | | * @return |
1994 | | * - NULL, no instances found. |
1995 | | * - The position of the first character. |
1996 | | */ |
1997 | | char *fr_sbuff_adv_to_chr(fr_sbuff_t *sbuff, size_t len, char c) |
1998 | 0 | { |
1999 | 0 | fr_sbuff_t our_sbuff = FR_SBUFF(sbuff); |
2000 | 0 | size_t total = 0; |
2001 | |
|
2002 | 0 | CHECK_SBUFF_INIT(sbuff); |
2003 | | |
2004 | 0 | while (total < len) { |
2005 | 0 | char const *found; |
2006 | 0 | char *end; |
2007 | |
|
2008 | 0 | if (!fr_sbuff_extend(&our_sbuff)) break; |
2009 | | |
2010 | 0 | end = CONSTRAINED_END(&our_sbuff, len, total); |
2011 | 0 | found = memchr(our_sbuff.p, c, end - our_sbuff.p); |
2012 | 0 | if (found) { |
2013 | 0 | (void)fr_sbuff_set(sbuff, found); |
2014 | 0 | return sbuff->p; |
2015 | 0 | } |
2016 | | |
2017 | 0 | total += fr_sbuff_set(&our_sbuff, end); |
2018 | 0 | } |
2019 | | |
2020 | 0 | return NULL; |
2021 | 0 | } |
2022 | | |
2023 | | /** Wind position to the first instance of the specified needle |
2024 | | * |
2025 | | * @param[in,out] sbuff sbuff to search in. |
2026 | | * @param[in] len Maximum amount to advance by. Unconstrained if SIZE_MAX. |
2027 | | * @param[in] needle to search for. |
2028 | | * @param[in] needle_len Length of the needle. SIZE_MAX to used strlen. |
2029 | | * @return |
2030 | | * - NULL, no instances found. |
2031 | | * - The position of the first character. |
2032 | | */ |
2033 | | char *fr_sbuff_adv_to_str(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len) |
2034 | 0 | { |
2035 | 0 | fr_sbuff_t our_sbuff = FR_SBUFF(sbuff); |
2036 | 0 | size_t total = 0; |
2037 | |
|
2038 | 0 | CHECK_SBUFF_INIT(sbuff); |
2039 | | |
2040 | 0 | if (needle_len == SIZE_MAX) needle_len = strlen(needle); |
2041 | 0 | if (!needle_len) return NULL; |
2042 | | |
2043 | | /* |
2044 | | * Needle bigger than haystack |
2045 | | */ |
2046 | 0 | if (len < needle_len) return NULL; |
2047 | | |
2048 | 0 | while (total <= (len - needle_len)) { |
2049 | 0 | char const *found; |
2050 | 0 | char *end; |
2051 | | |
2052 | | /* |
2053 | | * If the needle is longer than |
2054 | | * the remaining buffer, return. |
2055 | | */ |
2056 | 0 | if (fr_sbuff_extend_lowat(NULL, &our_sbuff, needle_len) < needle_len) break; |
2057 | | |
2058 | 0 | end = CONSTRAINED_END(&our_sbuff, len, total); |
2059 | 0 | found = memmem(our_sbuff.p, end - our_sbuff.p, needle, needle_len); |
2060 | 0 | if (found) { |
2061 | 0 | (void)fr_sbuff_set(sbuff, found); |
2062 | 0 | return sbuff->p; |
2063 | 0 | } |
2064 | | |
2065 | | /* |
2066 | | * Partial needle may be in |
2067 | | * the end of the buffer so |
2068 | | * don't advance too far. |
2069 | | */ |
2070 | 0 | total += fr_sbuff_set(&our_sbuff, (end - needle_len) + 1); |
2071 | 0 | } |
2072 | | |
2073 | 0 | return NULL; |
2074 | 0 | } |
2075 | | |
2076 | | /** Wind position to the first instance of the specified needle |
2077 | | * |
2078 | | * @param[in,out] sbuff sbuff to search in. |
2079 | | * @param[in] len Maximum amount to advance by. Unconstrained if SIZE_MAX. |
2080 | | * @param[in] needle to search for. |
2081 | | * @param[in] needle_len Length of the needle. SIZE_MAX to used strlen. |
2082 | | * @return |
2083 | | * - NULL, no instances found. |
2084 | | * - The position of the first character. |
2085 | | */ |
2086 | | char *fr_sbuff_adv_to_strcase(fr_sbuff_t *sbuff, size_t len, char const *needle, size_t needle_len) |
2087 | 0 | { |
2088 | 0 | fr_sbuff_t our_sbuff = FR_SBUFF(sbuff); |
2089 | 0 | size_t total = 0; |
2090 | |
|
2091 | 0 | CHECK_SBUFF_INIT(sbuff); |
2092 | | |
2093 | 0 | if (needle_len == SIZE_MAX) needle_len = strlen(needle); |
2094 | 0 | if (!needle_len) return NULL; |
2095 | | |
2096 | | /* |
2097 | | * Needle bigger than haystack |
2098 | | */ |
2099 | 0 | if (len < needle_len) return NULL; |
2100 | | |
2101 | 0 | while (total <= (len - needle_len)) { |
2102 | 0 | char *p, *end; |
2103 | 0 | char const *n_p; |
2104 | |
|
2105 | 0 | if (fr_sbuff_extend_lowat(NULL, &our_sbuff, needle_len) < needle_len) break; |
2106 | | |
2107 | 0 | for (p = our_sbuff.p, n_p = needle, end = our_sbuff.p + needle_len; |
2108 | 0 | (p < end) && (tolower((uint8_t) *p) == tolower((uint8_t) *n_p)); |
2109 | 0 | p++, n_p++); |
2110 | 0 | if (p == end) { |
2111 | 0 | (void)fr_sbuff_set(sbuff, our_sbuff.p); |
2112 | 0 | return sbuff->p; |
2113 | 0 | } |
2114 | | |
2115 | 0 | total += fr_sbuff_advance(&our_sbuff, 1); |
2116 | 0 | } |
2117 | | |
2118 | 0 | return NULL; |
2119 | 0 | } |
2120 | | |
2121 | | /** Return true if the current char matches, and if it does, advance |
2122 | | * |
2123 | | * @param[in] sbuff to search for char in. |
2124 | | * @param[in] c char to search for. |
2125 | | * @return |
2126 | | * - true and advance if the next character matches. |
2127 | | * - false and don't advance if the next character doesn't match. |
2128 | | */ |
2129 | | bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c) |
2130 | 24.6k | { |
2131 | 24.6k | CHECK_SBUFF_INIT(sbuff); |
2132 | | |
2133 | 24.6k | if (!fr_sbuff_extend(sbuff)) return false; |
2134 | | |
2135 | 16.8k | if (*sbuff->p != c) return false; |
2136 | | |
2137 | 6.28k | fr_sbuff_advance(sbuff, 1); |
2138 | | |
2139 | 6.28k | return true; |
2140 | 16.8k | } |
2141 | | |
2142 | | /** Return true and advance if the next char does not match |
2143 | | * |
2144 | | * @param[in] sbuff to search for char in. |
2145 | | * @param[in] c char to search for. |
2146 | | * @return |
2147 | | * - true and advance unless the character matches. |
2148 | | * - false and don't advance if the next character matches. |
2149 | | */ |
2150 | | bool fr_sbuff_next_unless_char(fr_sbuff_t *sbuff, char c) |
2151 | 0 | { |
2152 | 0 | CHECK_SBUFF_INIT(sbuff); |
2153 | | |
2154 | 0 | if (!fr_sbuff_extend(sbuff)) return false; |
2155 | | |
2156 | 0 | if (*sbuff->p == c) return false; |
2157 | | |
2158 | 0 | fr_sbuff_advance(sbuff, 1); |
2159 | |
|
2160 | 0 | return true; |
2161 | 0 | } |
2162 | | |
2163 | | /** Trim trailing characters from a string we're composing |
2164 | | * |
2165 | | * @param[in] sbuff to trim trailing characters from. |
2166 | | * @param[in] to_trim Charset to trim. |
2167 | | * @return how many chars we removed. |
2168 | | */ |
2169 | | size_t fr_sbuff_trim(fr_sbuff_t *sbuff, bool const to_trim[static SBUFF_CHAR_CLASS]) |
2170 | 0 | { |
2171 | 0 | char *p = sbuff->p - 1; |
2172 | 0 | ssize_t slen; |
2173 | |
|
2174 | 0 | while ((p >= sbuff->start) && to_trim[(uint8_t)*p]) p--; |
2175 | |
|
2176 | 0 | slen = fr_sbuff_set(sbuff, p + 1); |
2177 | 0 | if (slen != 0) fr_sbuff_terminate(sbuff); |
2178 | |
|
2179 | 0 | return slen; |
2180 | 0 | } |
2181 | | |
2182 | | /** Efficient terminal string search |
2183 | | * |
2184 | | * Caller should ensure that a buffer extension of needle_len bytes has been requested |
2185 | | * before calling this function. |
2186 | | * |
2187 | | * @param[in] in Sbuff to search in. |
2188 | | * @param[in] tt Token terminals in the encompassing grammar. |
2189 | | * @return |
2190 | | * - true if found. |
2191 | | * - false if not. |
2192 | | */ |
2193 | | bool fr_sbuff_is_terminal(fr_sbuff_t *in, fr_sbuff_term_t const *tt) |
2194 | 2.71k | { |
2195 | 2.71k | uint8_t idx[SBUFF_CHAR_CLASS]; /* Fast path index */ |
2196 | 2.71k | size_t needle_len = 1; |
2197 | | |
2198 | | /* |
2199 | | * No terminal, check for EOF. |
2200 | | */ |
2201 | 2.71k | if (!tt) { |
2202 | 2.71k | fr_sbuff_extend_status_t status = 0; |
2203 | | |
2204 | 2.71k | if ((fr_sbuff_extend_lowat(&status, in, 1) == 0) && |
2205 | 735 | (status & FR_SBUFF_FLAG_EXTEND_ERROR) == 0) { |
2206 | 735 | return true; |
2207 | 735 | } |
2208 | | |
2209 | 1.97k | return false; |
2210 | 2.71k | } |
2211 | | |
2212 | | /* |
2213 | | * Initialise the fastpath index and |
2214 | | * figure out the longest needle. |
2215 | | */ |
2216 | 0 | fr_sbuff_terminal_idx_init(&needle_len, idx, tt); |
2217 | |
|
2218 | 0 | fr_sbuff_extend_lowat(NULL, in, needle_len); |
2219 | |
|
2220 | 0 | return fr_sbuff_terminal_search(in, in->p, idx, tt, needle_len); |
2221 | 2.71k | } |
2222 | | |
2223 | | /** Print a char in a friendly format |
2224 | | * |
2225 | | */ |
2226 | | static char const *sbuff_print_char(char c) |
2227 | 0 | { |
2228 | 0 | static bool const unprintables[SBUFF_CHAR_CLASS] = { |
2229 | 0 | SBUFF_CHAR_UNPRINTABLES_LOW, |
2230 | 0 | SBUFF_CHAR_UNPRINTABLES_EXTENDED |
2231 | 0 | }; |
2232 | |
|
2233 | 0 | static _Thread_local char str[10][5]; |
2234 | 0 | static _Thread_local size_t i = 0; |
2235 | |
|
2236 | 0 | switch (c) { |
2237 | 0 | case '\a': |
2238 | 0 | return "\a"; |
2239 | | |
2240 | 0 | case '\b': |
2241 | 0 | return "\b"; |
2242 | | |
2243 | 0 | case '\n': |
2244 | 0 | return "\n"; |
2245 | | |
2246 | 0 | case '\r': |
2247 | 0 | return "\r"; |
2248 | | |
2249 | 0 | case '\t': |
2250 | 0 | return "\t"; |
2251 | | |
2252 | 0 | case '\f': |
2253 | 0 | return "\f"; |
2254 | | |
2255 | 0 | case '\v': |
2256 | 0 | return "\v"; |
2257 | | |
2258 | 0 | default: |
2259 | 0 | if (i >= NUM_ELEMENTS(str)) i = 0; |
2260 | |
|
2261 | 0 | if (unprintables[(uint8_t)c]) { |
2262 | 0 | snprintf(str[i], sizeof(str[i]), "\\x%02x", (uint8_t) c); |
2263 | 0 | return str[i++]; |
2264 | 0 | } |
2265 | | |
2266 | 0 | str[i][0] = c; |
2267 | 0 | str[i][1] = '\0'; |
2268 | 0 | return str[i++]; |
2269 | 0 | } |
2270 | 0 | } |
2271 | | |
2272 | | void fr_sbuff_unescape_debug(FILE *fp, fr_sbuff_unescape_rules_t const *escapes) |
2273 | 0 | { |
2274 | 0 | int i; |
2275 | |
|
2276 | 0 | fprintf(fp, "Escape rules %s (%p)\n", escapes->name, escapes); |
2277 | 0 | fprintf(fp, "chr : %c\n", escapes->chr ? escapes->chr : ' '); |
2278 | 0 | fprintf(fp, "do_hex : %s\n", escapes->do_hex ? "yes" : "no"); |
2279 | 0 | fprintf(fp, "do_oct : %s\n", escapes->do_oct ? "yes" : "no"); |
2280 | |
|
2281 | 0 | fprintf(fp, "substitutions:\n"); |
2282 | 0 | for (i = 0; i < SBUFF_CHAR_CLASS; i++) { |
2283 | 0 | if (escapes->subs[i]) FR_FAULT_LOG("\t%s -> %s\n", |
2284 | 0 | sbuff_print_char((char)i), |
2285 | 0 | sbuff_print_char((char)escapes->subs[i])); |
2286 | 0 | } |
2287 | 0 | fprintf(fp, "skips:\n"); |
2288 | 0 | for (i = 0; i < SBUFF_CHAR_CLASS; i++) { |
2289 | 0 | if (escapes->skip[i]) fprintf(fp, "\t%s\n", sbuff_print_char((char)i)); |
2290 | 0 | } |
2291 | 0 | } |
2292 | | |
2293 | | void fr_sbuff_terminal_debug(FILE *fp, fr_sbuff_term_t const *tt) |
2294 | 0 | { |
2295 | 0 | size_t i; |
2296 | |
|
2297 | 0 | fprintf(fp, "Terminal count %zu\n", tt->len); |
2298 | |
|
2299 | 0 | for (i = 0; i < tt->len; i++) fprintf(fp, "\t\"%s\" (%zu)\n", tt->elem[i].str, tt->elem[i].len); |
2300 | 0 | } |
2301 | | |
2302 | | void fr_sbuff_parse_rules_debug(FILE *fp, fr_sbuff_parse_rules_t const *p_rules) |
2303 | 0 | { |
2304 | 0 | fprintf(fp, "Parse rules %p\n", p_rules); |
2305 | |
|
2306 | 0 | FR_FAULT_LOG("Escapes - "); |
2307 | 0 | if (p_rules->escapes) { |
2308 | 0 | fr_sbuff_unescape_debug(fp, p_rules->escapes); |
2309 | 0 | } else { |
2310 | 0 | fprintf(fp, "<none>\n"); |
2311 | 0 | } |
2312 | |
|
2313 | 0 | FR_FAULT_LOG("Terminals - "); |
2314 | 0 | if (p_rules->terminals) { |
2315 | 0 | fr_sbuff_terminal_debug(fp, p_rules->terminals); |
2316 | 0 | } else { |
2317 | 0 | fprintf(fp, "<none>\n"); |
2318 | 0 | } |
2319 | 0 | } |
2320 | | |
2321 | | /** Concat an array of strings (not NULL terminated), with a string separator |
2322 | | * |
2323 | | * @param[out] out Where to write the resulting string. |
2324 | | * @param[in] array of strings to concat. |
2325 | | * @param[in] sep to insert between elements. May be NULL. |
2326 | | * @return |
2327 | | * - >= 0 on success - length of the string created. |
2328 | | * - <0 on failure. How many bytes we would need. |
2329 | | */ |
2330 | | fr_slen_t fr_sbuff_array_concat(fr_sbuff_t *out, char const * const *array, char const *sep) |
2331 | 0 | { |
2332 | 0 | fr_sbuff_t our_out = FR_SBUFF(out); |
2333 | 0 | size_t len = talloc_array_length(array); |
2334 | 0 | char const * const * p; |
2335 | 0 | char const * const * end; |
2336 | 0 | fr_sbuff_escape_rules_t e_rules = { |
2337 | 0 | .name = __FUNCTION__, |
2338 | 0 | .chr = '\\' |
2339 | 0 | }; |
2340 | |
|
2341 | 0 | if (sep) e_rules.subs[(uint8_t)*sep] = *sep; |
2342 | |
|
2343 | 0 | for (p = array, end = array + len; |
2344 | 0 | (p < end); |
2345 | 0 | p++) { |
2346 | 0 | if (*p) FR_SBUFF_RETURN(fr_sbuff_in_escape, &our_out, *p, strlen(*p), &e_rules); |
2347 | | |
2348 | 0 | if (sep && ((p + 1) < end)) { |
2349 | 0 | FR_SBUFF_RETURN(fr_sbuff_in_strcpy, &our_out, sep); |
2350 | 0 | } |
2351 | 0 | } |
2352 | | |
2353 | 0 | FR_SBUFF_SET_RETURN(out, &our_out); |
2354 | 0 | } |