113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci#include "dynbuf.h"
2713498266Sopenharmony_ci#include "curl_printf.h"
2813498266Sopenharmony_ci#ifdef BUILDING_LIBCURL
2913498266Sopenharmony_ci#include "curl_memory.h"
3013498266Sopenharmony_ci#endif
3113498266Sopenharmony_ci#include "memdebug.h"
3213498266Sopenharmony_ci
3313498266Sopenharmony_ci#define MIN_FIRST_ALLOC 32
3413498266Sopenharmony_ci
3513498266Sopenharmony_ci#define DYNINIT 0xbee51da /* random pattern */
3613498266Sopenharmony_ci
3713498266Sopenharmony_ci/*
3813498266Sopenharmony_ci * Init a dynbuf struct.
3913498266Sopenharmony_ci */
4013498266Sopenharmony_civoid Curl_dyn_init(struct dynbuf *s, size_t toobig)
4113498266Sopenharmony_ci{
4213498266Sopenharmony_ci  DEBUGASSERT(s);
4313498266Sopenharmony_ci  DEBUGASSERT(toobig);
4413498266Sopenharmony_ci  s->bufr = NULL;
4513498266Sopenharmony_ci  s->leng = 0;
4613498266Sopenharmony_ci  s->allc = 0;
4713498266Sopenharmony_ci  s->toobig = toobig;
4813498266Sopenharmony_ci#ifdef DEBUGBUILD
4913498266Sopenharmony_ci  s->init = DYNINIT;
5013498266Sopenharmony_ci#endif
5113498266Sopenharmony_ci}
5213498266Sopenharmony_ci
5313498266Sopenharmony_ci/*
5413498266Sopenharmony_ci * free the buffer and re-init the necessary fields. It doesn't touch the
5513498266Sopenharmony_ci * 'init' field and thus this buffer can be reused to add data to again.
5613498266Sopenharmony_ci */
5713498266Sopenharmony_civoid Curl_dyn_free(struct dynbuf *s)
5813498266Sopenharmony_ci{
5913498266Sopenharmony_ci  DEBUGASSERT(s);
6013498266Sopenharmony_ci  Curl_safefree(s->bufr);
6113498266Sopenharmony_ci  s->leng = s->allc = 0;
6213498266Sopenharmony_ci}
6313498266Sopenharmony_ci
6413498266Sopenharmony_ci/*
6513498266Sopenharmony_ci * Store/append an chunk of memory to the dynbuf.
6613498266Sopenharmony_ci */
6713498266Sopenharmony_cistatic CURLcode dyn_nappend(struct dynbuf *s,
6813498266Sopenharmony_ci                            const unsigned char *mem, size_t len)
6913498266Sopenharmony_ci{
7013498266Sopenharmony_ci  size_t indx = s->leng;
7113498266Sopenharmony_ci  size_t a = s->allc;
7213498266Sopenharmony_ci  size_t fit = len + indx + 1; /* new string + old string + zero byte */
7313498266Sopenharmony_ci
7413498266Sopenharmony_ci  /* try to detect if there's rubbish in the struct */
7513498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
7613498266Sopenharmony_ci  DEBUGASSERT(s->toobig);
7713498266Sopenharmony_ci  DEBUGASSERT(indx < s->toobig);
7813498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
7913498266Sopenharmony_ci  DEBUGASSERT(a <= s->toobig);
8013498266Sopenharmony_ci  DEBUGASSERT(!len || mem);
8113498266Sopenharmony_ci
8213498266Sopenharmony_ci  if(fit > s->toobig) {
8313498266Sopenharmony_ci    Curl_dyn_free(s);
8413498266Sopenharmony_ci    return CURLE_TOO_LARGE;
8513498266Sopenharmony_ci  }
8613498266Sopenharmony_ci  else if(!a) {
8713498266Sopenharmony_ci    DEBUGASSERT(!indx);
8813498266Sopenharmony_ci    /* first invoke */
8913498266Sopenharmony_ci    if(MIN_FIRST_ALLOC > s->toobig)
9013498266Sopenharmony_ci      a = s->toobig;
9113498266Sopenharmony_ci    else if(fit < MIN_FIRST_ALLOC)
9213498266Sopenharmony_ci      a = MIN_FIRST_ALLOC;
9313498266Sopenharmony_ci    else
9413498266Sopenharmony_ci      a = fit;
9513498266Sopenharmony_ci  }
9613498266Sopenharmony_ci  else {
9713498266Sopenharmony_ci    while(a < fit)
9813498266Sopenharmony_ci      a *= 2;
9913498266Sopenharmony_ci    if(a > s->toobig)
10013498266Sopenharmony_ci      /* no point in allocating a larger buffer than this is allowed to use */
10113498266Sopenharmony_ci      a = s->toobig;
10213498266Sopenharmony_ci  }
10313498266Sopenharmony_ci
10413498266Sopenharmony_ci  if(a != s->allc) {
10513498266Sopenharmony_ci    /* this logic is not using Curl_saferealloc() to make the tool not have to
10613498266Sopenharmony_ci       include that as well when it uses this code */
10713498266Sopenharmony_ci    void *p = realloc(s->bufr, a);
10813498266Sopenharmony_ci    if(!p) {
10913498266Sopenharmony_ci      Curl_dyn_free(s);
11013498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
11113498266Sopenharmony_ci    }
11213498266Sopenharmony_ci    s->bufr = p;
11313498266Sopenharmony_ci    s->allc = a;
11413498266Sopenharmony_ci  }
11513498266Sopenharmony_ci
11613498266Sopenharmony_ci  if(len)
11713498266Sopenharmony_ci    memcpy(&s->bufr[indx], mem, len);
11813498266Sopenharmony_ci  s->leng = indx + len;
11913498266Sopenharmony_ci  s->bufr[s->leng] = 0;
12013498266Sopenharmony_ci  return CURLE_OK;
12113498266Sopenharmony_ci}
12213498266Sopenharmony_ci
12313498266Sopenharmony_ci/*
12413498266Sopenharmony_ci * Clears the string, keeps the allocation. This can also be called on a
12513498266Sopenharmony_ci * buffer that already was freed.
12613498266Sopenharmony_ci */
12713498266Sopenharmony_civoid Curl_dyn_reset(struct dynbuf *s)
12813498266Sopenharmony_ci{
12913498266Sopenharmony_ci  DEBUGASSERT(s);
13013498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
13113498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
13213498266Sopenharmony_ci  if(s->leng)
13313498266Sopenharmony_ci    s->bufr[0] = 0;
13413498266Sopenharmony_ci  s->leng = 0;
13513498266Sopenharmony_ci}
13613498266Sopenharmony_ci
13713498266Sopenharmony_ci/*
13813498266Sopenharmony_ci * Specify the size of the tail to keep (number of bytes from the end of the
13913498266Sopenharmony_ci * buffer). The rest will be dropped.
14013498266Sopenharmony_ci */
14113498266Sopenharmony_ciCURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
14213498266Sopenharmony_ci{
14313498266Sopenharmony_ci  DEBUGASSERT(s);
14413498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
14513498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
14613498266Sopenharmony_ci  if(trail > s->leng)
14713498266Sopenharmony_ci    return CURLE_BAD_FUNCTION_ARGUMENT;
14813498266Sopenharmony_ci  else if(trail == s->leng)
14913498266Sopenharmony_ci    return CURLE_OK;
15013498266Sopenharmony_ci  else if(!trail) {
15113498266Sopenharmony_ci    Curl_dyn_reset(s);
15213498266Sopenharmony_ci  }
15313498266Sopenharmony_ci  else {
15413498266Sopenharmony_ci    memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
15513498266Sopenharmony_ci    s->leng = trail;
15613498266Sopenharmony_ci    s->bufr[s->leng] = 0;
15713498266Sopenharmony_ci  }
15813498266Sopenharmony_ci  return CURLE_OK;
15913498266Sopenharmony_ci
16013498266Sopenharmony_ci}
16113498266Sopenharmony_ci
16213498266Sopenharmony_ci/*
16313498266Sopenharmony_ci * Appends a buffer with length.
16413498266Sopenharmony_ci */
16513498266Sopenharmony_ciCURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
16613498266Sopenharmony_ci{
16713498266Sopenharmony_ci  DEBUGASSERT(s);
16813498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
16913498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
17013498266Sopenharmony_ci  return dyn_nappend(s, mem, len);
17113498266Sopenharmony_ci}
17213498266Sopenharmony_ci
17313498266Sopenharmony_ci/*
17413498266Sopenharmony_ci * Append a null-terminated string at the end.
17513498266Sopenharmony_ci */
17613498266Sopenharmony_ciCURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
17713498266Sopenharmony_ci{
17813498266Sopenharmony_ci  size_t n;
17913498266Sopenharmony_ci  DEBUGASSERT(str);
18013498266Sopenharmony_ci  DEBUGASSERT(s);
18113498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
18213498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
18313498266Sopenharmony_ci  n = strlen(str);
18413498266Sopenharmony_ci  return dyn_nappend(s, (unsigned char *)str, n);
18513498266Sopenharmony_ci}
18613498266Sopenharmony_ci
18713498266Sopenharmony_ci/*
18813498266Sopenharmony_ci * Append a string vprintf()-style
18913498266Sopenharmony_ci */
19013498266Sopenharmony_ciCURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
19113498266Sopenharmony_ci{
19213498266Sopenharmony_ci#ifdef BUILDING_LIBCURL
19313498266Sopenharmony_ci  int rc;
19413498266Sopenharmony_ci  DEBUGASSERT(s);
19513498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
19613498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
19713498266Sopenharmony_ci  DEBUGASSERT(fmt);
19813498266Sopenharmony_ci  rc = Curl_dyn_vprintf(s, fmt, ap);
19913498266Sopenharmony_ci
20013498266Sopenharmony_ci  if(!rc)
20113498266Sopenharmony_ci    return CURLE_OK;
20213498266Sopenharmony_ci  else if(rc == MERR_TOO_LARGE)
20313498266Sopenharmony_ci    return CURLE_TOO_LARGE;
20413498266Sopenharmony_ci  return CURLE_OUT_OF_MEMORY;
20513498266Sopenharmony_ci#else
20613498266Sopenharmony_ci  char *str;
20713498266Sopenharmony_ci  str = vaprintf(fmt, ap); /* this allocs a new string to append */
20813498266Sopenharmony_ci
20913498266Sopenharmony_ci  if(str) {
21013498266Sopenharmony_ci    CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
21113498266Sopenharmony_ci    free(str);
21213498266Sopenharmony_ci    return result;
21313498266Sopenharmony_ci  }
21413498266Sopenharmony_ci  /* If we failed, we cleanup the whole buffer and return error */
21513498266Sopenharmony_ci  Curl_dyn_free(s);
21613498266Sopenharmony_ci  return CURLE_OK;
21713498266Sopenharmony_ci#endif
21813498266Sopenharmony_ci}
21913498266Sopenharmony_ci
22013498266Sopenharmony_ci/*
22113498266Sopenharmony_ci * Append a string printf()-style
22213498266Sopenharmony_ci */
22313498266Sopenharmony_ciCURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
22413498266Sopenharmony_ci{
22513498266Sopenharmony_ci  CURLcode result;
22613498266Sopenharmony_ci  va_list ap;
22713498266Sopenharmony_ci  DEBUGASSERT(s);
22813498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
22913498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
23013498266Sopenharmony_ci  va_start(ap, fmt);
23113498266Sopenharmony_ci  result = Curl_dyn_vaddf(s, fmt, ap);
23213498266Sopenharmony_ci  va_end(ap);
23313498266Sopenharmony_ci  return result;
23413498266Sopenharmony_ci}
23513498266Sopenharmony_ci
23613498266Sopenharmony_ci/*
23713498266Sopenharmony_ci * Returns a pointer to the buffer.
23813498266Sopenharmony_ci */
23913498266Sopenharmony_cichar *Curl_dyn_ptr(const struct dynbuf *s)
24013498266Sopenharmony_ci{
24113498266Sopenharmony_ci  DEBUGASSERT(s);
24213498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
24313498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
24413498266Sopenharmony_ci  return s->bufr;
24513498266Sopenharmony_ci}
24613498266Sopenharmony_ci
24713498266Sopenharmony_ci/*
24813498266Sopenharmony_ci * Returns an unsigned pointer to the buffer.
24913498266Sopenharmony_ci */
25013498266Sopenharmony_ciunsigned char *Curl_dyn_uptr(const struct dynbuf *s)
25113498266Sopenharmony_ci{
25213498266Sopenharmony_ci  DEBUGASSERT(s);
25313498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
25413498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
25513498266Sopenharmony_ci  return (unsigned char *)s->bufr;
25613498266Sopenharmony_ci}
25713498266Sopenharmony_ci
25813498266Sopenharmony_ci/*
25913498266Sopenharmony_ci * Returns the length of the buffer.
26013498266Sopenharmony_ci */
26113498266Sopenharmony_cisize_t Curl_dyn_len(const struct dynbuf *s)
26213498266Sopenharmony_ci{
26313498266Sopenharmony_ci  DEBUGASSERT(s);
26413498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
26513498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
26613498266Sopenharmony_ci  return s->leng;
26713498266Sopenharmony_ci}
26813498266Sopenharmony_ci
26913498266Sopenharmony_ci/*
27013498266Sopenharmony_ci * Set a new (smaller) length.
27113498266Sopenharmony_ci */
27213498266Sopenharmony_ciCURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set)
27313498266Sopenharmony_ci{
27413498266Sopenharmony_ci  DEBUGASSERT(s);
27513498266Sopenharmony_ci  DEBUGASSERT(s->init == DYNINIT);
27613498266Sopenharmony_ci  DEBUGASSERT(!s->leng || s->bufr);
27713498266Sopenharmony_ci  if(set > s->leng)
27813498266Sopenharmony_ci    return CURLE_BAD_FUNCTION_ARGUMENT;
27913498266Sopenharmony_ci  s->leng = set;
28013498266Sopenharmony_ci  s->bufr[s->leng] = 0;
28113498266Sopenharmony_ci  return CURLE_OK;
28213498266Sopenharmony_ci}
283