/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * SPDX-License-Identifier: curl * ***************************************************************************/ #include "curl_setup.h" #include "dynbuf.h" #include "curl_printf.h" #ifdef BUILDING_LIBCURL #include "curl_memory.h" #endif #include "memdebug.h" #define MIN_FIRST_ALLOC 32 #define DYNINIT 0xbee51da /* random pattern */ /* * Init a dynbuf struct. */ void Curl_dyn_init(struct dynbuf *s, size_t toobig) { DEBUGASSERT(s); DEBUGASSERT(toobig); s->bufr = NULL; s->leng = 0; s->allc = 0; s->toobig = toobig; #ifdef DEBUGBUILD s->init = DYNINIT; #endif } /* * free the buffer and re-init the necessary fields. It does not touch the * 'init' field and thus this buffer can be reused to add data to again. */ void Curl_dyn_free(struct dynbuf *s) { DEBUGASSERT(s); Curl_safefree(s->bufr); s->leng = s->allc = 0; } /* * Store/append an chunk of memory to the dynbuf. */ static CURLcode dyn_nappend(struct dynbuf *s, const unsigned char *mem, size_t len) { size_t indx = s->leng; size_t a = s->allc; size_t fit = len + indx + 1; /* new string + old string + zero byte */ /* try to detect if there is rubbish in the struct */ DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(s->toobig); DEBUGASSERT(indx < s->toobig); DEBUGASSERT(!s->leng || s->bufr); DEBUGASSERT(a <= s->toobig); DEBUGASSERT(!len || mem); if(fit > s->toobig) { Curl_dyn_free(s); return CURLE_TOO_LARGE; } else if(!a) { DEBUGASSERT(!indx); /* first invoke */ if(MIN_FIRST_ALLOC > s->toobig) a = s->toobig; else if(fit < MIN_FIRST_ALLOC) a = MIN_FIRST_ALLOC; else a = fit; } else { while(a < fit) a *= 2; if(a > s->toobig) /* no point in allocating a larger buffer than this is allowed to use */ a = s->toobig; } if(a != s->allc) { /* this logic is not using Curl_saferealloc() to make the tool not have to include that as well when it uses this code */ void *p = realloc(s->bufr, a); if(!p) { Curl_dyn_free(s); return CURLE_OUT_OF_MEMORY; } s->bufr = p; s->allc = a; } if(len) memcpy(&s->bufr[indx], mem, len); s->leng = indx + len; s->bufr[s->leng] = 0; return CURLE_OK; } /* * Clears the string, keeps the allocation. This can also be called on a * buffer that already was freed. */ void Curl_dyn_reset(struct dynbuf *s) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); if(s->leng) s->bufr[0] = 0; s->leng = 0; } /* * Specify the size of the tail to keep (number of bytes from the end of the * buffer). The rest will be dropped. */ CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); if(trail > s->leng) return CURLE_BAD_FUNCTION_ARGUMENT; else if(trail == s->leng) return CURLE_OK; else if(!trail) { Curl_dyn_reset(s); } else { memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail); s->leng = trail; s->bufr[s->leng] = 0; } return CURLE_OK; } /* * Appends a buffer with length. */ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); return dyn_nappend(s, mem, len); } /* * Append a null-terminated string at the end. */ CURLcode Curl_dyn_add(struct dynbuf *s, const char *str) { size_t n; DEBUGASSERT(str); DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); n = strlen(str); return dyn_nappend(s, (unsigned char *)str, n); } /* * Append a string vprintf()-style */ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) { #ifdef BUILDING_LIBCURL int rc; DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); DEBUGASSERT(fmt); rc = Curl_dyn_vprintf(s, fmt, ap); if(!rc) return CURLE_OK; else if(rc == MERR_TOO_LARGE) return CURLE_TOO_LARGE; return CURLE_OUT_OF_MEMORY; #else char *str; str = vaprintf(fmt, ap); /* this allocs a new string to append */ if(str) { CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str)); free(str); return result; } /* If we failed, we cleanup the whole buffer and return error */ Curl_dyn_free(s); return CURLE_OUT_OF_MEMORY; #endif } /* * Append a string printf()-style */ CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...) { CURLcode result; va_list ap; DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); va_start(ap, fmt); result = Curl_dyn_vaddf(s, fmt, ap); va_end(ap); return result; } /* * Returns a pointer to the buffer. */ char *Curl_dyn_ptr(const struct dynbuf *s) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); return s->bufr; } char *Curl_dyn_take(struct dynbuf *s, size_t *plen) { char *ptr = s->bufr; DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); *plen = s->leng; s->bufr = NULL; s->leng = 0; s->allc = 0; return ptr; } /* * Returns an unsigned pointer to the buffer. */ unsigned char *Curl_dyn_uptr(const struct dynbuf *s) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); return (unsigned char *)s->bufr; } /* * Returns the length of the buffer. */ size_t Curl_dyn_len(const struct dynbuf *s) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); return s->leng; } /* * Set a new (smaller) length. */ CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); DEBUGASSERT(!s->leng || s->bufr); if(set > s->leng) return CURLE_BAD_FUNCTION_ARGUMENT; s->leng = set; s->bufr[s->leng] = 0; return CURLE_OK; }