/*1* Copyright (c) Ian F. Darwin 1986-1995.2* Software written by Ian F. Darwin and others;3* maintained 1995-present by Christos Zoulas and others.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice immediately at the beginning of the file, without modification,10* this list of conditions, and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR19* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/27/*###########################################################################28# #29# vasprintf #30# #31# Copyright (c) 2002-2005 David TAILLANDIER #32# #33###########################################################################*/3435/*3637This software is distributed under the "modified BSD licence".3839This software is also released with GNU license (GPL) in another file (same40source-code, only license differ).41424344Redistribution and use in source and binary forms, with or without45modification, are permitted provided that the following conditions are met:4647Redistributions of source code must retain the above copyright notice, this48list of conditions and the following disclaimer. Redistributions in binary49form must reproduce the above copyright notice, this list of conditions and50the following disclaimer in the documentation and/or other materials51provided with the distribution. The name of the author may not be used to52endorse or promote products derived from this software without specific53prior written permission.5455THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED56WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF57MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO58EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,59SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,60PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;61OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,62WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR63OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF64ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.6566====================6768Hacked from xnprintf version of 26th February 2005 to provide only69vasprintf by Reuben Thomas <[email protected]>.7071====================727374'printf' function family use the following format string:7576%[flag][width][.prec][modifier]type7778%% is the escape sequence to print a '%'79% followed by an unknown format will print the characters without80trying to do any interpretation8182flag: none + - # (blank)83width: n 0n *84prec: none .0 .n .*85modifier: F N L h l ll z t ('F' and 'N' are ms-dos/16-bit specific)86type: d i o u x X f e g E G c s p n878889The function needs to allocate memory to store the full text before to90actually writing it. i.e if you want to fnprintf() 1000 characters, the91functions will allocate 1000 bytes.92This behaviour can be modified: you have to customise the code to flush the93internal buffer (writing to screen or file) when it reach a given size. Then94the buffer can have a shorter length. But what? If you really need to write95HUGE string, don't use printf!96During the process, some other memory is allocated (1024 bytes minimum)97to handle the output of partial sprintf() calls. If you have only 10000 bytes98free in memory, you *may* not be able to nprintf() an 8000 bytes-long text.99100note: if a buffer overflow occurs, exit() is called. This situation should101never appear ... but if you want to be *really* sure, you have to modify the102code to handle those situations (only one place to modify).103A buffer overflow can only occur if your sprintf() do strange things or when104you use strange formats.105106*/107#include "file.h"108109#ifndef lint110FILE_RCSID("@(#)$File: vasprintf.c,v 1.23 2022/09/24 20:30:13 christos Exp $")111#endif /* lint */112113#include <assert.h>114#include <string.h>115#include <stdlib.h>116#include <stdarg.h>117#include <ctype.h>118#include <limits.h>119#include <stddef.h>120121#define ALLOC_CHUNK 2048122#define ALLOC_SECURITY_MARGIN 1024 /* big value because some platforms have very big 'G' exponent */123#if ALLOC_CHUNK < ALLOC_SECURITY_MARGIN124# error !!! ALLOC_CHUNK < ALLOC_SECURITY_MARGIN !!!125#endif126/* note: to have some interest, ALLOC_CHUNK should be much greater than ALLOC_SECURITY_MARGIN */127128/*129* To save a lot of push/pop, every variable are stored into this130* structure, which is passed among nearly every sub-functions.131*/132typedef struct {133const char * src_string; /* current position into input string */134char * buffer_base; /* output buffer */135char * dest_string; /* current position into output string */136size_t buffer_len; /* length of output buffer */137size_t real_len; /* real current length of output text */138size_t pseudo_len; /* total length of output text if it were not limited in size */139size_t maxlen;140va_list vargs; /* pointer to current position into vargs */141} xprintf_struct;142143/*144* Realloc buffer if needed145* Return value: 0 = ok146* EOF = not enough memory147*/148static int realloc_buff(xprintf_struct *s, size_t len)149{150char * ptr;151152if (len + ALLOC_SECURITY_MARGIN + s->real_len > s->buffer_len) {153len += s->real_len + ALLOC_CHUNK;154ptr = (char *)realloc((void *)(s->buffer_base), len);155if (ptr == NULL) {156s->buffer_base = NULL;157return EOF;158}159160s->dest_string = ptr + (size_t)(s->dest_string - s->buffer_base);161s->buffer_base = ptr;162s->buffer_len = len;163164(s->buffer_base)[s->buffer_len - 1] = 1; /* overflow marker */165}166167return 0;168}169170/*171* Prints 'usual' characters up to next '%'172* or up to end of text173*/174static int usual_char(xprintf_struct * s)175{176size_t len;177178len = strcspn(s->src_string, "%"); /* reaches the next '%' or end of input string */179/* note: 'len' is never 0 because the presence of '%' */180/* or end-of-line is checked in the calling function */181182if (realloc_buff(s,len) == EOF)183return EOF;184185memcpy(s->dest_string, s->src_string, len);186s->src_string += len;187s->dest_string += len;188s->real_len += len;189s->pseudo_len += len;190191return 0;192}193194/*195* Return value: 0 = ok196* EOF = error197*/198static int print_it(xprintf_struct *s, size_t approx_len,199const char *format_string, ...)200{201va_list varg;202int vsprintf_len;203size_t len;204205if (realloc_buff(s,approx_len) == EOF)206return EOF;207208va_start(varg, format_string);209vsprintf_len = vsprintf(s->dest_string, format_string, varg);210va_end(varg);211212/* Check for overflow */213assert((s->buffer_base)[s->buffer_len - 1] == 1);214215if (vsprintf_len == EOF) /* must be done *after* overflow-check */216return EOF;217218s->pseudo_len += vsprintf_len;219len = strlen(s->dest_string);220s->real_len += len;221s->dest_string += len;222223return 0;224}225226/*227* Prints a string (%s)228* We need special handling because:229* a: the length of the string is unknown230* b: when .prec is used, we must not access any extra byte of the231* string (of course, if the original sprintf() does... what the232* hell, not my problem)233*234* Return value: 0 = ok235* EOF = error236*/237static int type_s(xprintf_struct *s, int width, int prec,238const char *format_string, const char *arg_string)239{240size_t string_len;241242if (arg_string == NULL)243return print_it(s, (size_t)6, "(null)", 0);244245/* hand-made strlen() which stops when 'prec' is reached. */246/* if 'prec' is -1 then it is never reached. */247string_len = 0;248while (arg_string[string_len] != 0 && (size_t)prec != string_len)249string_len++;250251if (width != -1 && string_len < (size_t)width)252string_len = (size_t)width;253254return print_it(s, string_len, format_string, arg_string);255}256257/*258* Read a series of digits. Stop when non-digit is found.259* Return value: the value read (between 0 and 32767).260* Note: no checks are made against overflow. If the string contain a big261* number, then the return value won't be what we want (but, in this case,262* the programmer don't know whatr he wants, then no problem).263*/264static int getint(const char **string)265{266int i = 0;267268while (isdigit((unsigned char)**string) != 0) {269i = i * 10 + (**string - '0');270(*string)++;271}272273if (i < 0 || i > 32767)274i = 32767; /* if we have i==-10 this is not because the number is */275/* negative; this is because the number is big */276return i;277}278279/*280* Read a part of the format string. A part is 'usual characters' (ie "blabla")281* or '%%' escape sequence (to print a single '%') or any combination of282* format specifier (ie "%i" or "%10.2d").283* After the current part is managed, the function returns to caller with284* everything ready to manage the following part.285* The caller must ensure than the string is not empty, i.e. the first byte286* is not zero.287*288* Return value: 0 = ok289* EOF = error290*/291static int dispatch(xprintf_struct *s)292{293const char *initial_ptr;294char format_string[24]; /* max length may be something like "% +-#032768.32768Ld" */295char *format_ptr;296int flag_plus, flag_minus, flag_space, flag_sharp, flag_zero;297int width, prec, modifier, approx_width;298char type;299/* most of those variables are here to rewrite the format string */300301#define SRCTXT (s->src_string)302#define DESTTXT (s->dest_string)303304/* incoherent format string. Characters after the '%' will be printed with the next call */305#define INCOHERENT() do {SRCTXT=initial_ptr; return 0;} while (0) /* do/while to avoid */306#define INCOHERENT_TEST() do {if(*SRCTXT==0) INCOHERENT();} while (0) /* a null statement */307308/* 'normal' text */309if (*SRCTXT != '%')310return usual_char(s);311312/* we then have a '%' */313SRCTXT++;314/* don't check for end-of-string ; this is done later */315316/* '%%' escape sequence */317if (*SRCTXT == '%') {318if (realloc_buff(s, (size_t)1) == EOF) /* because we can have "%%%%%%%%..." */319return EOF;320*DESTTXT = '%';321DESTTXT++;322SRCTXT++;323(s->real_len)++;324(s->pseudo_len)++;325return 0;326}327328/* '%' managing */329initial_ptr = SRCTXT; /* save current pointer in case of incorrect */330/* 'decoding'. Points just after the '%' so the '%' */331/* won't be printed in any case, as required. */332333/* flag */334flag_plus = flag_minus = flag_space = flag_sharp = flag_zero = 0;335336for (;; SRCTXT++) {337if (*SRCTXT == ' ')338flag_space = 1;339else if (*SRCTXT == '+')340flag_plus = 1;341else if (*SRCTXT == '-')342flag_minus = 1;343else if (*SRCTXT == '#')344flag_sharp = 1;345else if (*SRCTXT == '0')346flag_zero = 1;347else348break;349}350351INCOHERENT_TEST(); /* here is the first test for end of string */352353/* width */354if (*SRCTXT == '*') { /* width given by next argument */355SRCTXT++;356width = va_arg(s->vargs, int);357if ((size_t)width > 0x3fffU) /* 'size_t' to check against negative values too */358width = 0x3fff;359} else if (isdigit((unsigned char)*SRCTXT)) /* width given as ASCII number */360width = getint(&SRCTXT);361else362width = -1; /* no width specified */363364INCOHERENT_TEST();365366/* .prec */367if (*SRCTXT == '.') {368SRCTXT++;369if (*SRCTXT == '*') { /* .prec given by next argument */370SRCTXT++;371prec = va_arg(s->vargs, int);372if ((size_t)prec >= 0x3fffU) /* 'size_t' to check against negative values too */373prec = 0x3fff;374} else { /* .prec given as ASCII number */375if (isdigit((unsigned char)*SRCTXT) == 0)376INCOHERENT();377prec = getint(&SRCTXT);378}379INCOHERENT_TEST();380} else381prec = -1; /* no .prec specified */382383/* modifier */384switch (*SRCTXT) {385case 'L':386case 'h':387case 'l':388case 'z':389case 't':390modifier = *SRCTXT;391SRCTXT++;392if (modifier=='l' && *SRCTXT=='l') {393SRCTXT++;394modifier = 'L'; /* 'll' == 'L' long long == long double */395} /* only for compatibility ; not portable */396INCOHERENT_TEST();397break;398default:399modifier = -1; /* no modifier specified */400break;401}402403/* type */404type = *SRCTXT;405if (strchr("diouxXfegEGcspn",type) == NULL)406INCOHERENT(); /* unknown type */407SRCTXT++;408409/* rewrite format-string */410format_string[0] = '%';411format_ptr = &(format_string[1]);412413if (flag_plus) {414*format_ptr = '+';415format_ptr++;416}417if (flag_minus) {418*format_ptr = '-';419format_ptr++;420}421if (flag_space) {422*format_ptr = ' ';423format_ptr++;424}425if (flag_sharp) {426*format_ptr = '#';427format_ptr++;428}429if (flag_zero) {430*format_ptr = '0';431format_ptr++;432} /* '0' *must* be the last one */433434if (width != -1) {435sprintf(format_ptr, "%i", width);436format_ptr += strlen(format_ptr);437}438439if (prec != -1) {440*format_ptr = '.';441format_ptr++;442sprintf(format_ptr, "%i", prec);443format_ptr += strlen(format_ptr);444}445446if (modifier != -1) {447if (modifier == 'L' && strchr("diouxX",type) != NULL) {448*format_ptr = 'l';449format_ptr++;450*format_ptr = 'l';451format_ptr++;452} else {453*format_ptr = modifier;454format_ptr++;455}456}457458*format_ptr = type;459format_ptr++;460*format_ptr = 0;461462/* vague approximation of minimal length if width or prec are specified */463approx_width = width + prec;464if (approx_width < 0) /* because width == -1 and/or prec == -1 */465approx_width = 0;466467switch (type) {468/* int */469case 'd':470case 'i':471case 'o':472case 'u':473case 'x':474case 'X':475switch (modifier) {476case -1 :477return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));478case 'L':479return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long long int));480case 'l':481return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long int));482case 'h':483return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));484case 'z':485return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, size_t));486case 't':487return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, ptrdiff_t));488/* 'int' instead of 'short int' because default promotion is 'int' */489default:490INCOHERENT();491}492493/* char */494case 'c':495if (modifier != -1)496INCOHERENT();497return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int));498/* 'int' instead of 'char' because default promotion is 'int' */499500/* math */501case 'e':502case 'f':503case 'g':504case 'E':505case 'G':506switch (modifier) {507case -1 : /* because of default promotion, no modifier means 'l' */508case 'l':509return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, double));510case 'L':511return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long double));512default:513INCOHERENT();514}515516/* string */517case 's':518return type_s(s, width, prec, format_string, va_arg(s->vargs, const char*));519520/* pointer */521case 'p':522if (modifier == -1)523return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, void *));524INCOHERENT();525526/* store */527case 'n':528if (modifier == -1) {529int * p;530p = va_arg(s->vargs, int *);531if (p != NULL) {532*p = s->pseudo_len;533return 0;534}535return EOF;536}537INCOHERENT();538539} /* switch */540541INCOHERENT(); /* unknown type */542543#undef INCOHERENT544#undef INCOHERENT_TEST545#undef SRCTXT546#undef DESTTXT547}548549/*550* Return value: number of *virtually* written characters551* EOF = error552*/553static int core(xprintf_struct *s)554{555size_t save_len;556char *dummy_base;557558/* basic checks */559if ((int)(s->maxlen) <= 0) /* 'int' to check against some conversion */560return EOF; /* error for example if value is (int)-10 */561s->maxlen--; /* because initial maxlen counts final 0 */562/* note: now 'maxlen' _can_ be zero */563564if (s->src_string == NULL)565s->src_string = "(null)";566567/* struct init and memory allocation */568s->buffer_base = NULL;569s->buffer_len = 0;570s->real_len = 0;571s->pseudo_len = 0;572if (realloc_buff(s, (size_t)0) == EOF)573return EOF;574s->dest_string = s->buffer_base;575576/* process source string */577for (;;) {578/* up to end of source string */579if (*(s->src_string) == 0) {580*(s->dest_string) = '\0'; /* final NUL */581break;582}583584if (dispatch(s) == EOF)585goto free_EOF;586587/* up to end of dest string */588if (s->real_len >= s->maxlen) {589(s->buffer_base)[s->maxlen] = '\0'; /* final NUL */590break;591}592}593594/* for (v)asnprintf */595dummy_base = s->buffer_base + s->real_len;596save_len = s->real_len;597598/* process the remaining of source string to compute 'pseudo_len'. We599* overwrite again and again, starting at 'dummy_base' because we don't600* need the text, only char count. */601while(*(s->src_string) != 0) { /* up to end of source string */602s->real_len = 0;603s->dest_string = dummy_base;604if (dispatch(s) == EOF)605goto free_EOF;606}607608s->buffer_base = (char *)realloc((void *)(s->buffer_base), save_len + 1);609if (s->buffer_base == NULL)610return EOF; /* should rarely happen because we shrink the buffer */611return s->pseudo_len;612613free_EOF:614free(s->buffer_base);615return EOF;616}617618int vasprintf(char **ptr, const char *format_string, va_list vargs)619{620xprintf_struct s;621int retval;622623memset(&s, 0, sizeof(s));624s.src_string = format_string;625#ifdef va_copy626va_copy (s.vargs, vargs);627#else628# ifdef __va_copy629__va_copy (s.vargs, vargs);630# else631# ifdef WIN32632s.vargs = vargs;633# else634memcpy (&s.vargs, &vargs, sizeof (s.vargs));635# endif /* WIN32 */636# endif /* __va_copy */637#endif /* va_copy */638s.maxlen = (size_t)INT_MAX;639640retval = core(&s);641va_end(s.vargs);642if (retval == EOF) {643*ptr = NULL;644return EOF;645}646647*ptr = s.buffer_base;648return retval;649}650651652