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