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