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