vasprintf.c revision 186690
1186690Sobrien/* 2186690Sobrien * Copyright (c) Ian F. Darwin 1986-1995. 3186690Sobrien * Software written by Ian F. Darwin and others; 4186690Sobrien * maintained 1995-present by Christos Zoulas and others. 5186690Sobrien * 6186690Sobrien * Redistribution and use in source and binary forms, with or without 7186690Sobrien * modification, are permitted provided that the following conditions 8186690Sobrien * are met: 9186690Sobrien * 1. Redistributions of source code must retain the above copyright 10186690Sobrien * notice immediately at the beginning of the file, without modification, 11186690Sobrien * this list of conditions, and the following disclaimer. 12186690Sobrien * 2. Redistributions in binary form must reproduce the above copyright 13186690Sobrien * notice, this list of conditions and the following disclaimer in the 14186690Sobrien * documentation and/or other materials provided with the distribution. 15186690Sobrien * 16186690Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17186690Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18186690Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19186690Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20186690Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21186690Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22186690Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23186690Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24186690Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25186690Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26186690Sobrien * SUCH DAMAGE. 27186690Sobrien */ 28186690Sobrien/*########################################################################### 29186690Sobrien # # 30186690Sobrien # vasprintf # 31186690Sobrien # # 32186690Sobrien # Copyright (c) 2002-2005 David TAILLANDIER # 33186690Sobrien # # 34186690Sobrien ###########################################################################*/ 35186690Sobrien 36186690Sobrien/* 37186690Sobrien 38186690SobrienThis software is distributed under the "modified BSD licence". 39186690Sobrien 40186690SobrienThis software is also released with GNU license (GPL) in another file (same 41186690Sobriensource-code, only license differ). 42186690Sobrien 43186690Sobrien 44186690Sobrien 45186690SobrienRedistribution and use in source and binary forms, with or without 46186690Sobrienmodification, are permitted provided that the following conditions are met: 47186690Sobrien 48186690SobrienRedistributions of source code must retain the above copyright notice, this 49186690Sobrienlist of conditions and the following disclaimer. Redistributions in binary 50186690Sobrienform must reproduce the above copyright notice, this list of conditions and 51186690Sobrienthe following disclaimer in the documentation and/or other materials 52186690Sobrienprovided with the distribution. The name of the author may not be used to 53186690Sobrienendorse or promote products derived from this software without specific 54186690Sobrienprior written permission. 55186690Sobrien 56186690SobrienTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 57186690SobrienWARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 58186690SobrienMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 59186690SobrienEVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 60186690SobrienSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 61186690SobrienPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 62186690SobrienOR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 63186690SobrienWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 64186690SobrienOTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 65186690SobrienADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 66186690Sobrien 67186690Sobrien==================== 68186690Sobrien 69186690SobrienHacked from xnprintf version of 26th February 2005 to provide only 70186690Sobrienvasprintf by Reuben Thomas <rrt@sc3d.org>. 71186690Sobrien 72186690Sobrien==================== 73186690Sobrien 74186690Sobrien 75186690Sobrien'printf' function family use the following format string: 76186690Sobrien 77186690Sobrien%[flag][width][.prec][modifier]type 78186690Sobrien 79186690Sobrien%% is the escape sequence to print a '%' 80186690Sobrien% followed by an unknown format will print the characters without 81186690Sobrientrying to do any interpretation 82186690Sobrien 83186690Sobrienflag: none + - # (blank) 84186690Sobrienwidth: n 0n * 85186690Sobrienprec: none .0 .n .* 86186690Sobrienmodifier: F N L h l ll ('F' and 'N' are ms-dos/16-bit specific) 87186690Sobrientype: d i o u x X f e g E G c s p n 88186690Sobrien 89186690Sobrien 90186690SobrienThe function needs to allocate memory to store the full text before to 91186690Sobrienactually writting it. i.e if you want to fnprintf() 1000 characters, the 92186690Sobrienfunctions will allocate 1000 bytes. 93186690SobrienThis behaviour can be modified: you have to customise the code to flush the 94186690Sobrieninternal buffer (writing to screen or file) when it reach a given size. Then 95186690Sobrienthe buffer can have a shorter length. But what? If you really need to write 96186690SobrienHUGE string, don't use printf! 97186690SobrienDuring the process, some other memory is allocated (1024 bytes minimum) 98186690Sobriento handle the output of partial sprintf() calls. If you have only 10000 bytes 99186690Sobrienfree in memory, you *may* not be able to nprintf() a 8000 bytes-long text. 100186690Sobrien 101186690Sobriennote: if a buffer overflow occurs, exit() is called. This situation should 102186690Sobriennever appear ... but if you want to be *really* sure, you have to modify the 103186690Sobriencode to handle those situations (only one place to modify). 104186690SobrienA buffer overflow can only occur if your sprintf() do strange things or when 105186690Sobrienyou use strange formats. 106186690Sobrien 107186690Sobrien*/ 108186690Sobrien#ifdef HAVE_CONFIG_H 109186690Sobrien#include "config.h" 110186690Sobrien#endif 111186690Sobrien 112186690Sobrien#include <assert.h> 113186690Sobrien#include <stdio.h> 114186690Sobrien#include <string.h> 115186690Sobrien#include <stdlib.h> 116186690Sobrien#include <stdarg.h> 117186690Sobrien#include <ctype.h> 118186690Sobrien#ifdef HAVE_LIMITS_H 119186690Sobrien#include <limits.h> 120186690Sobrien#endif 121186690Sobrien 122186690Sobrien#define ALLOC_CHUNK 2048 123186690Sobrien#define ALLOC_SECURITY_MARGIN 1024 /* big value because some platforms have very big 'G' exponent */ 124186690Sobrien#if ALLOC_CHUNK < ALLOC_SECURITY_MARGIN 125186690Sobrien# error !!! ALLOC_CHUNK < ALLOC_SECURITY_MARGIN !!! 126186690Sobrien#endif 127186690Sobrien/* note: to have some interest, ALLOC_CHUNK should be much greater than ALLOC_SECURITY_MARGIN */ 128186690Sobrien 129186690Sobrien/* 130186690Sobrien * To save a lot of push/pop, every variable are stored into this 131186690Sobrien * structure, which is passed among nearly every sub-functions. 132186690Sobrien */ 133186690Sobrientypedef struct { 134186690Sobrien const char * src_string; /* current position into intput string */ 135186690Sobrien char * buffer_base; /* output buffer */ 136186690Sobrien char * dest_string; /* current position into output string */ 137186690Sobrien size_t buffer_len; /* length of output buffer */ 138186690Sobrien size_t real_len; /* real current length of output text */ 139186690Sobrien size_t pseudo_len; /* total length of output text if it were not limited in size */ 140186690Sobrien size_t maxlen; 141186690Sobrien va_list vargs; /* pointer to current position into vargs */ 142186690Sobrien char * sprintf_string; 143186690Sobrien FILE * fprintf_file; 144186690Sobrien} xprintf_struct; 145186690Sobrien 146186690Sobrien/* 147186690Sobrien * Realloc buffer if needed 148186690Sobrien * Return value: 0 = ok 149186690Sobrien * EOF = not enought memory 150186690Sobrien */ 151186690Sobrienstatic int realloc_buff(xprintf_struct *s, size_t len) 152186690Sobrien{ 153186690Sobrien char * ptr; 154186690Sobrien 155186690Sobrien if (len + ALLOC_SECURITY_MARGIN + s->real_len > s->buffer_len) { 156186690Sobrien len += s->real_len + ALLOC_CHUNK; 157186690Sobrien ptr = (char *)realloc((void *)(s->buffer_base), len); 158186690Sobrien if (ptr == NULL) { 159186690Sobrien s->buffer_base = NULL; 160186690Sobrien return EOF; 161186690Sobrien } 162186690Sobrien 163186690Sobrien s->dest_string = ptr + (size_t)(s->dest_string - s->buffer_base); 164186690Sobrien s->buffer_base = ptr; 165186690Sobrien s->buffer_len = len; 166186690Sobrien 167186690Sobrien (s->buffer_base)[s->buffer_len - 1] = 1; /* overflow marker */ 168186690Sobrien } 169186690Sobrien 170186690Sobrien return 0; 171186690Sobrien} 172186690Sobrien 173186690Sobrien/* 174186690Sobrien * Prints 'usual' characters up to next '%' 175186690Sobrien * or up to end of text 176186690Sobrien */ 177186690Sobrienstatic int usual_char(xprintf_struct * s) 178186690Sobrien{ 179186690Sobrien size_t len; 180186690Sobrien 181186690Sobrien len = strcspn(s->src_string, "%"); /* reachs the next '%' or end of input string */ 182186690Sobrien /* note: 'len' is never 0 because the presence of '%' */ 183186690Sobrien /* or end-of-line is checked in the calling function */ 184186690Sobrien 185186690Sobrien if (realloc_buff(s,len) == EOF) 186186690Sobrien return EOF; 187186690Sobrien 188186690Sobrien memcpy(s->dest_string, s->src_string, len); 189186690Sobrien s->src_string += len; 190186690Sobrien s->dest_string += len; 191186690Sobrien s->real_len += len; 192186690Sobrien s->pseudo_len += len; 193186690Sobrien 194186690Sobrien return 0; 195186690Sobrien} 196186690Sobrien 197186690Sobrien/* 198186690Sobrien * Return value: 0 = ok 199186690Sobrien * EOF = error 200186690Sobrien */ 201186690Sobrienstatic int print_it(xprintf_struct *s, size_t approx_len, 202186690Sobrien const char *format_string, ...) 203186690Sobrien{ 204186690Sobrien va_list varg; 205186690Sobrien int vsprintf_len; 206186690Sobrien size_t len; 207186690Sobrien 208186690Sobrien if (realloc_buff(s,approx_len) == EOF) 209186690Sobrien return EOF; 210186690Sobrien 211186690Sobrien va_start(varg, format_string); 212186690Sobrien vsprintf_len = vsprintf(s->dest_string, format_string, varg); 213186690Sobrien va_end(varg); 214186690Sobrien 215186690Sobrien /* Check for overflow */ 216186690Sobrien assert((s->buffer_base)[s->buffer_len - 1] == 1); 217186690Sobrien 218186690Sobrien if (vsprintf_len == EOF) /* must be done *after* overflow-check */ 219186690Sobrien return EOF; 220186690Sobrien 221186690Sobrien s->pseudo_len += vsprintf_len; 222186690Sobrien len = strlen(s->dest_string); 223186690Sobrien s->real_len += len; 224186690Sobrien s->dest_string += len; 225186690Sobrien 226186690Sobrien return 0; 227186690Sobrien} 228186690Sobrien 229186690Sobrien/* 230186690Sobrien * Prints a string (%s) 231186690Sobrien * We need special handling because: 232186690Sobrien * a: the length of the string is unknown 233186690Sobrien * b: when .prec is used, we must not access any extra byte of the 234186690Sobrien * string (of course, if the original sprintf() does... what the 235186690Sobrien * hell, not my problem) 236186690Sobrien * 237186690Sobrien * Return value: 0 = ok 238186690Sobrien * EOF = error 239186690Sobrien */ 240186690Sobrienstatic int type_s(xprintf_struct *s, int width, int prec, 241186690Sobrien const char *format_string, const char *arg_string) 242186690Sobrien{ 243186690Sobrien size_t string_len; 244186690Sobrien 245186690Sobrien if (arg_string == NULL) 246186690Sobrien return print_it(s, (size_t)6, "(null)", 0); 247186690Sobrien 248186690Sobrien /* hand-made strlen() whitch stops when 'prec' is reached. */ 249186690Sobrien /* if 'prec' is -1 then it is never reached. */ 250186690Sobrien string_len = 0; 251186690Sobrien while (arg_string[string_len] != 0 && (size_t)prec != string_len) 252186690Sobrien string_len++; 253186690Sobrien 254186690Sobrien if (width != -1 && string_len < (size_t)width) 255186690Sobrien string_len = (size_t)width; 256186690Sobrien 257186690Sobrien return print_it(s, string_len, format_string, arg_string); 258186690Sobrien} 259186690Sobrien 260186690Sobrien/* 261186690Sobrien * Read a serie of digits. Stop when non-digit is found. 262186690Sobrien * Return value: the value read (between 0 and 32767). 263186690Sobrien * Note: no checks are made against overflow. If the string contain a big 264186690Sobrien * number, then the return value won't be what we want (but, in this case, 265186690Sobrien * the programmer don't know whatr he wants, then no problem). 266186690Sobrien */ 267186690Sobrienstatic int getint(const char **string) 268186690Sobrien{ 269186690Sobrien int i = 0; 270186690Sobrien 271186690Sobrien while (isdigit((unsigned char)**string) != 0) { 272186690Sobrien i = i * 10 + (**string - '0'); 273186690Sobrien (*string)++; 274186690Sobrien } 275186690Sobrien 276186690Sobrien if (i < 0 || i > 32767) 277186690Sobrien i = 32767; /* if we have i==-10 this is not because the number is */ 278186690Sobrien /* negative; this is because the number is big */ 279186690Sobrien return i; 280186690Sobrien} 281186690Sobrien 282186690Sobrien/* 283186690Sobrien * Read a part of the format string. A part is 'usual characters' (ie "blabla") 284186690Sobrien * or '%%' escape sequence (to print a single '%') or any combination of 285186690Sobrien * format specifier (ie "%i" or "%10.2d"). 286186690Sobrien * After the current part is managed, the function returns to caller with 287186690Sobrien * everything ready to manage the following part. 288186690Sobrien * The caller must ensure than the string is not empty, i.e. the first byte 289186690Sobrien * is not zero. 290186690Sobrien * 291186690Sobrien * Return value: 0 = ok 292186690Sobrien * EOF = error 293186690Sobrien */ 294186690Sobrienstatic int dispatch(xprintf_struct *s) 295186690Sobrien{ 296186690Sobrien const char *initial_ptr; 297186690Sobrien char format_string[24]; /* max length may be something like "% +-#032768.32768Ld" */ 298186690Sobrien char *format_ptr; 299186690Sobrien int flag_plus, flag_minus, flag_space, flag_sharp, flag_zero; 300186690Sobrien int width, prec, modifier, approx_width; 301186690Sobrien char type; 302186690Sobrien /* most of those variables are here to rewrite the format string */ 303186690Sobrien 304186690Sobrien#define SRCTXT (s->src_string) 305186690Sobrien#define DESTTXT (s->dest_string) 306186690Sobrien 307186690Sobrien /* incoherent format string. Characters after the '%' will be printed with the next call */ 308186690Sobrien#define INCOHERENT() do {SRCTXT=initial_ptr; return 0;} while (0) /* do/while to avoid */ 309186690Sobrien#define INCOHERENT_TEST() do {if(*SRCTXT==0) INCOHERENT();} while (0) /* a null statement */ 310186690Sobrien 311186690Sobrien /* 'normal' text */ 312186690Sobrien if (*SRCTXT != '%') 313186690Sobrien return usual_char(s); 314186690Sobrien 315186690Sobrien /* we then have a '%' */ 316186690Sobrien SRCTXT++; 317186690Sobrien /* don't check for end-of-string ; this is done later */ 318186690Sobrien 319186690Sobrien /* '%%' escape sequence */ 320186690Sobrien if (*SRCTXT == '%') { 321186690Sobrien if (realloc_buff(s, (size_t)1) == EOF) /* because we can have "%%%%%%%%..." */ 322186690Sobrien return EOF; 323186690Sobrien *DESTTXT = '%'; 324186690Sobrien DESTTXT++; 325186690Sobrien SRCTXT++; 326186690Sobrien (s->real_len)++; 327186690Sobrien (s->pseudo_len)++; 328186690Sobrien return 0; 329186690Sobrien } 330186690Sobrien 331186690Sobrien /* '%' managing */ 332186690Sobrien initial_ptr = SRCTXT; /* save current pointer in case of incorrect */ 333186690Sobrien /* 'decoding'. Points just after the '%' so the '%' */ 334186690Sobrien /* won't be printed in any case, as required. */ 335186690Sobrien 336186690Sobrien /* flag */ 337186690Sobrien flag_plus = flag_minus = flag_space = flag_sharp = flag_zero = 0; 338186690Sobrien 339186690Sobrien for (;; SRCTXT++) { 340186690Sobrien if (*SRCTXT == ' ') 341186690Sobrien flag_space = 1; 342186690Sobrien else if (*SRCTXT == '+') 343186690Sobrien flag_plus = 1; 344186690Sobrien else if (*SRCTXT == '-') 345186690Sobrien flag_minus = 1; 346186690Sobrien else if (*SRCTXT == '#') 347186690Sobrien flag_sharp = 1; 348186690Sobrien else if (*SRCTXT == '0') 349186690Sobrien flag_zero = 1; 350186690Sobrien else 351186690Sobrien break; 352186690Sobrien } 353186690Sobrien 354186690Sobrien INCOHERENT_TEST(); /* here is the first test for end of string */ 355186690Sobrien 356186690Sobrien /* width */ 357186690Sobrien if (*SRCTXT == '*') { /* width given by next argument */ 358186690Sobrien SRCTXT++; 359186690Sobrien width = va_arg(s->vargs, int); 360186690Sobrien if ((size_t)width > 0x3fffU) /* 'size_t' to check against negative values too */ 361186690Sobrien width = 0x3fff; 362186690Sobrien } else if (isdigit((unsigned char)*SRCTXT)) /* width given as ASCII number */ 363186690Sobrien width = getint(&SRCTXT); 364186690Sobrien else 365186690Sobrien width = -1; /* no width specified */ 366186690Sobrien 367186690Sobrien INCOHERENT_TEST(); 368186690Sobrien 369186690Sobrien /* .prec */ 370186690Sobrien if (*SRCTXT == '.') { 371186690Sobrien SRCTXT++; 372186690Sobrien if (*SRCTXT == '*') { /* .prec given by next argument */ 373186690Sobrien SRCTXT++; 374186690Sobrien prec = va_arg(s->vargs, int); 375186690Sobrien if ((size_t)prec >= 0x3fffU) /* 'size_t' to check against negative values too */ 376186690Sobrien prec = 0x3fff; 377186690Sobrien } else { /* .prec given as ASCII number */ 378186690Sobrien if (isdigit((unsigned char)*SRCTXT) == 0) 379186690Sobrien INCOHERENT(); 380186690Sobrien prec = getint(&SRCTXT); 381186690Sobrien } 382186690Sobrien INCOHERENT_TEST(); 383186690Sobrien } else 384186690Sobrien prec = -1; /* no .prec specified */ 385186690Sobrien 386186690Sobrien /* modifier */ 387186690Sobrien if (*SRCTXT == 'L' || *SRCTXT == 'h' || *SRCTXT == 'l') { 388186690Sobrien modifier = *SRCTXT; 389186690Sobrien SRCTXT++; 390186690Sobrien if (modifier=='l' && *SRCTXT=='l') { 391186690Sobrien SRCTXT++; 392186690Sobrien modifier = 'L'; /* 'll' == 'L' long long == long double */ 393186690Sobrien } /* only for compatibility ; not portable */ 394186690Sobrien INCOHERENT_TEST(); 395186690Sobrien } else 396186690Sobrien modifier = -1; /* no modifier specified */ 397186690Sobrien 398186690Sobrien /* type */ 399186690Sobrien type = *SRCTXT; 400186690Sobrien if (strchr("diouxXfegEGcspn",type) == NULL) 401186690Sobrien INCOHERENT(); /* unknown type */ 402186690Sobrien SRCTXT++; 403186690Sobrien 404186690Sobrien /* rewrite format-string */ 405186690Sobrien format_string[0] = '%'; 406186690Sobrien format_ptr = &(format_string[1]); 407186690Sobrien 408186690Sobrien if (flag_plus) { 409186690Sobrien *format_ptr = '+'; 410186690Sobrien format_ptr++; 411186690Sobrien } 412186690Sobrien if (flag_minus) { 413186690Sobrien *format_ptr = '-'; 414186690Sobrien format_ptr++; 415186690Sobrien } 416186690Sobrien if (flag_space) { 417186690Sobrien *format_ptr = ' '; 418186690Sobrien format_ptr++; 419186690Sobrien } 420186690Sobrien if (flag_sharp) { 421186690Sobrien *format_ptr = '#'; 422186690Sobrien format_ptr++; 423186690Sobrien } 424186690Sobrien if (flag_zero) { 425186690Sobrien *format_ptr = '0'; 426186690Sobrien format_ptr++; 427186690Sobrien } /* '0' *must* be the last one */ 428186690Sobrien 429186690Sobrien if (width != -1) { 430186690Sobrien sprintf(format_ptr, "%i", width); 431186690Sobrien format_ptr += strlen(format_ptr); 432186690Sobrien } 433186690Sobrien 434186690Sobrien if (prec != -1) { 435186690Sobrien *format_ptr = '.'; 436186690Sobrien format_ptr++; 437186690Sobrien sprintf(format_ptr, "%i", prec); 438186690Sobrien format_ptr += strlen(format_ptr); 439186690Sobrien } 440186690Sobrien 441186690Sobrien if (modifier != -1) { 442186690Sobrien if (modifier == 'L' && strchr("diouxX",type) != NULL) { 443186690Sobrien *format_ptr = 'l'; 444186690Sobrien format_ptr++; 445186690Sobrien *format_ptr = 'l'; 446186690Sobrien format_ptr++; 447186690Sobrien } else { 448186690Sobrien *format_ptr = modifier; 449186690Sobrien format_ptr++; 450186690Sobrien } 451186690Sobrien } 452186690Sobrien 453186690Sobrien *format_ptr = type; 454186690Sobrien format_ptr++; 455186690Sobrien *format_ptr = 0; 456186690Sobrien 457186690Sobrien /* vague approximation of minimal length if width or prec are specified */ 458186690Sobrien approx_width = width + prec; 459186690Sobrien if (approx_width < 0) /* because width == -1 and/or prec == -1 */ 460186690Sobrien approx_width = 0; 461186690Sobrien 462186690Sobrien switch (type) { 463186690Sobrien /* int */ 464186690Sobrien case 'd': 465186690Sobrien case 'i': 466186690Sobrien case 'o': 467186690Sobrien case 'u': 468186690Sobrien case 'x': 469186690Sobrien case 'X': 470186690Sobrien switch (modifier) { 471186690Sobrien case -1 : 472186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); 473186690Sobrien case 'L': 474186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long long int)); 475186690Sobrien case 'l': 476186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long int)); 477186690Sobrien case 'h': 478186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); 479186690Sobrien /* 'int' instead of 'short int' because default promotion is 'int' */ 480186690Sobrien default: 481186690Sobrien INCOHERENT(); 482186690Sobrien } 483186690Sobrien 484186690Sobrien /* char */ 485186690Sobrien case 'c': 486186690Sobrien if (modifier != -1) 487186690Sobrien INCOHERENT(); 488186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); 489186690Sobrien /* 'int' instead of 'char' because default promotion is 'int' */ 490186690Sobrien 491186690Sobrien /* math */ 492186690Sobrien case 'e': 493186690Sobrien case 'f': 494186690Sobrien case 'g': 495186690Sobrien case 'E': 496186690Sobrien case 'G': 497186690Sobrien switch (modifier) { 498186690Sobrien case -1 : /* because of default promotion, no modifier means 'l' */ 499186690Sobrien case 'l': 500186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, double)); 501186690Sobrien case 'L': 502186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long double)); 503186690Sobrien default: 504186690Sobrien INCOHERENT(); 505186690Sobrien } 506186690Sobrien 507186690Sobrien /* string */ 508186690Sobrien case 's': 509186690Sobrien return type_s(s, width, prec, format_string, va_arg(s->vargs, const char*)); 510186690Sobrien 511186690Sobrien /* pointer */ 512186690Sobrien case 'p': 513186690Sobrien if (modifier == -1) 514186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, void *)); 515186690Sobrien INCOHERENT(); 516186690Sobrien 517186690Sobrien /* store */ 518186690Sobrien case 'n': 519186690Sobrien if (modifier == -1) { 520186690Sobrien int * p; 521186690Sobrien p = va_arg(s->vargs, int *); 522186690Sobrien if (p != NULL) { 523186690Sobrien *p = s->pseudo_len; 524186690Sobrien return 0; 525186690Sobrien } 526186690Sobrien return EOF; 527186690Sobrien } 528186690Sobrien INCOHERENT(); 529186690Sobrien 530186690Sobrien } /* switch */ 531186690Sobrien 532186690Sobrien INCOHERENT(); /* unknown type */ 533186690Sobrien 534186690Sobrien#undef INCOHERENT 535186690Sobrien#undef INCOHERENT_TEST 536186690Sobrien#undef SRCTXT 537186690Sobrien#undef DESTTXT 538186690Sobrien} 539186690Sobrien 540186690Sobrien/* 541186690Sobrien * Return value: number of *virtually* written characters 542186690Sobrien * EOF = error 543186690Sobrien */ 544186690Sobrienstatic int core(xprintf_struct *s) 545186690Sobrien{ 546186690Sobrien size_t len, save_len; 547186690Sobrien char *dummy_base; 548186690Sobrien 549186690Sobrien /* basic checks */ 550186690Sobrien if ((int)(s->maxlen) <= 0) /* 'int' to check against some conversion */ 551186690Sobrien return EOF; /* error for example if value is (int)-10 */ 552186690Sobrien s->maxlen--; /* because initial maxlen counts final 0 */ 553186690Sobrien /* note: now 'maxlen' _can_ be zero */ 554186690Sobrien 555186690Sobrien if (s->src_string == NULL) 556186690Sobrien s->src_string = "(null)"; 557186690Sobrien 558186690Sobrien /* struct init and memory allocation */ 559186690Sobrien s->buffer_base = NULL; 560186690Sobrien s->buffer_len = 0; 561186690Sobrien s->real_len = 0; 562186690Sobrien s->pseudo_len = 0; 563186690Sobrien if (realloc_buff(s, (size_t)0) == EOF) 564186690Sobrien return EOF; 565186690Sobrien s->dest_string = s->buffer_base; 566186690Sobrien 567186690Sobrien /* process source string */ 568186690Sobrien for (;;) { 569186690Sobrien /* up to end of source string */ 570186690Sobrien if (*(s->src_string) == 0) { 571186690Sobrien *(s->dest_string) = 0; /* final 0 */ 572186690Sobrien len = s->real_len + 1; 573186690Sobrien break; 574186690Sobrien } 575186690Sobrien 576186690Sobrien if (dispatch(s) == EOF) 577186690Sobrien goto free_EOF; 578186690Sobrien 579186690Sobrien /* up to end of dest string */ 580186690Sobrien if (s->real_len >= s->maxlen) { 581186690Sobrien (s->buffer_base)[s->maxlen] = 0; /* final 0 */ 582186690Sobrien len = s->maxlen + 1; 583186690Sobrien break; 584186690Sobrien } 585186690Sobrien } 586186690Sobrien 587186690Sobrien /* for (v)asnprintf */ 588186690Sobrien dummy_base = s->buffer_base; 589186690Sobrien save_len = 0; /* just to avoid a compiler warning */ 590186690Sobrien 591186690Sobrien dummy_base = s->buffer_base + s->real_len; 592186690Sobrien save_len = s->real_len; 593186690Sobrien 594186690Sobrien /* process the remaining of source string to compute 'pseudo_len'. We 595186690Sobrien * overwrite again and again, starting at 'dummy_base' because we don't 596186690Sobrien * need the text, only char count. */ 597186690Sobrien while(*(s->src_string) != 0) { /* up to end of source string */ 598186690Sobrien s->real_len = 0; 599186690Sobrien s->dest_string = dummy_base; 600186690Sobrien if (dispatch(s) == EOF) 601186690Sobrien goto free_EOF; 602186690Sobrien } 603186690Sobrien 604186690Sobrien s->buffer_base = (char *)realloc((void *)(s->buffer_base), save_len + 1); 605186690Sobrien if (s->buffer_base == NULL) 606186690Sobrien return EOF; /* should rarely happen because we shrink the buffer */ 607186690Sobrien return s->pseudo_len; 608186690Sobrien 609186690Sobrien free_EOF: 610186690Sobrien if (s->buffer_base != NULL) 611186690Sobrien free(s->buffer_base); 612186690Sobrien return EOF; 613186690Sobrien} 614186690Sobrien 615186690Sobrienint vasprintf(char **ptr, const char *format_string, va_list vargs) 616186690Sobrien{ 617186690Sobrien xprintf_struct s; 618186690Sobrien int retval; 619186690Sobrien 620186690Sobrien s.src_string = format_string; 621186690Sobrien#ifdef va_copy 622186690Sobrien va_copy (s.vargs, vargs); 623186690Sobrien#else 624186690Sobrien#ifdef __va_copy 625186690Sobrien __va_copy (s.vargs, vargs); 626186690Sobrien#else 627186690Sobrien memcpy (&s.vargs, vargs, sizeof (va_list)); 628186690Sobrien#endif /* __va_copy */ 629186690Sobrien#endif /* va_copy */ 630186690Sobrien s.maxlen = (size_t)INT_MAX; 631186690Sobrien 632186690Sobrien retval = core(&s); 633186690Sobrien va_end(s.vargs); 634186690Sobrien if (retval == EOF) { 635186690Sobrien *ptr = NULL; 636186690Sobrien return EOF; 637186690Sobrien } 638186690Sobrien 639186690Sobrien *ptr = s.buffer_base; 640186690Sobrien return retval; 641186690Sobrien} 642