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