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