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