vasprintf.c revision 234250
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*/ 108191736Sobrien#include "file.h" 109186690Sobrien 110191736Sobrien#ifndef lint 111234250SobrienFILE_RCSID("@(#)$File: vasprintf.c,v 1.8 2011/12/08 12:38:24 rrt Exp $") 112191736Sobrien#endif /* lint */ 113191736Sobrien 114186690Sobrien#include <assert.h> 115186690Sobrien#include <string.h> 116186690Sobrien#include <stdlib.h> 117186690Sobrien#include <stdarg.h> 118186690Sobrien#include <ctype.h> 119186690Sobrien#ifdef HAVE_LIMITS_H 120186690Sobrien#include <limits.h> 121186690Sobrien#endif 122186690Sobrien 123186690Sobrien#define ALLOC_CHUNK 2048 124186690Sobrien#define ALLOC_SECURITY_MARGIN 1024 /* big value because some platforms have very big 'G' exponent */ 125186690Sobrien#if ALLOC_CHUNK < ALLOC_SECURITY_MARGIN 126186690Sobrien# error !!! ALLOC_CHUNK < ALLOC_SECURITY_MARGIN !!! 127186690Sobrien#endif 128186690Sobrien/* note: to have some interest, ALLOC_CHUNK should be much greater than ALLOC_SECURITY_MARGIN */ 129186690Sobrien 130186690Sobrien/* 131186690Sobrien * To save a lot of push/pop, every variable are stored into this 132186690Sobrien * structure, which is passed among nearly every sub-functions. 133186690Sobrien */ 134186690Sobrientypedef struct { 135186690Sobrien const char * src_string; /* current position into intput string */ 136186690Sobrien char * buffer_base; /* output buffer */ 137186690Sobrien char * dest_string; /* current position into output string */ 138186690Sobrien size_t buffer_len; /* length of output buffer */ 139186690Sobrien size_t real_len; /* real current length of output text */ 140186690Sobrien size_t pseudo_len; /* total length of output text if it were not limited in size */ 141186690Sobrien size_t maxlen; 142186690Sobrien va_list vargs; /* pointer to current position into vargs */ 143186690Sobrien char * sprintf_string; 144186690Sobrien FILE * fprintf_file; 145186690Sobrien} xprintf_struct; 146186690Sobrien 147186690Sobrien/* 148186690Sobrien * Realloc buffer if needed 149186690Sobrien * Return value: 0 = ok 150186690Sobrien * EOF = not enought memory 151186690Sobrien */ 152186690Sobrienstatic int realloc_buff(xprintf_struct *s, size_t len) 153186690Sobrien{ 154186690Sobrien char * ptr; 155186690Sobrien 156186690Sobrien if (len + ALLOC_SECURITY_MARGIN + s->real_len > s->buffer_len) { 157186690Sobrien len += s->real_len + ALLOC_CHUNK; 158186690Sobrien ptr = (char *)realloc((void *)(s->buffer_base), len); 159186690Sobrien if (ptr == NULL) { 160186690Sobrien s->buffer_base = NULL; 161186690Sobrien return EOF; 162186690Sobrien } 163186690Sobrien 164186690Sobrien s->dest_string = ptr + (size_t)(s->dest_string - s->buffer_base); 165186690Sobrien s->buffer_base = ptr; 166186690Sobrien s->buffer_len = len; 167186690Sobrien 168186690Sobrien (s->buffer_base)[s->buffer_len - 1] = 1; /* overflow marker */ 169186690Sobrien } 170186690Sobrien 171186690Sobrien return 0; 172186690Sobrien} 173186690Sobrien 174186690Sobrien/* 175186690Sobrien * Prints 'usual' characters up to next '%' 176186690Sobrien * or up to end of text 177186690Sobrien */ 178186690Sobrienstatic int usual_char(xprintf_struct * s) 179186690Sobrien{ 180186690Sobrien size_t len; 181186690Sobrien 182186690Sobrien len = strcspn(s->src_string, "%"); /* reachs the next '%' or end of input string */ 183186690Sobrien /* note: 'len' is never 0 because the presence of '%' */ 184186690Sobrien /* or end-of-line is checked in the calling function */ 185186690Sobrien 186186690Sobrien if (realloc_buff(s,len) == EOF) 187186690Sobrien return EOF; 188186690Sobrien 189186690Sobrien memcpy(s->dest_string, s->src_string, len); 190186690Sobrien s->src_string += len; 191186690Sobrien s->dest_string += len; 192186690Sobrien s->real_len += len; 193186690Sobrien s->pseudo_len += len; 194186690Sobrien 195186690Sobrien return 0; 196186690Sobrien} 197186690Sobrien 198186690Sobrien/* 199186690Sobrien * Return value: 0 = ok 200186690Sobrien * EOF = error 201186690Sobrien */ 202186690Sobrienstatic int print_it(xprintf_struct *s, size_t approx_len, 203186690Sobrien const char *format_string, ...) 204186690Sobrien{ 205186690Sobrien va_list varg; 206186690Sobrien int vsprintf_len; 207186690Sobrien size_t len; 208186690Sobrien 209186690Sobrien if (realloc_buff(s,approx_len) == EOF) 210186690Sobrien return EOF; 211186690Sobrien 212186690Sobrien va_start(varg, format_string); 213186690Sobrien vsprintf_len = vsprintf(s->dest_string, format_string, varg); 214186690Sobrien va_end(varg); 215186690Sobrien 216186690Sobrien /* Check for overflow */ 217186690Sobrien assert((s->buffer_base)[s->buffer_len - 1] == 1); 218186690Sobrien 219186690Sobrien if (vsprintf_len == EOF) /* must be done *after* overflow-check */ 220186690Sobrien return EOF; 221186690Sobrien 222186690Sobrien s->pseudo_len += vsprintf_len; 223186690Sobrien len = strlen(s->dest_string); 224186690Sobrien s->real_len += len; 225186690Sobrien s->dest_string += len; 226186690Sobrien 227186690Sobrien return 0; 228186690Sobrien} 229186690Sobrien 230186690Sobrien/* 231186690Sobrien * Prints a string (%s) 232186690Sobrien * We need special handling because: 233186690Sobrien * a: the length of the string is unknown 234186690Sobrien * b: when .prec is used, we must not access any extra byte of the 235186690Sobrien * string (of course, if the original sprintf() does... what the 236186690Sobrien * hell, not my problem) 237186690Sobrien * 238186690Sobrien * Return value: 0 = ok 239186690Sobrien * EOF = error 240186690Sobrien */ 241186690Sobrienstatic int type_s(xprintf_struct *s, int width, int prec, 242186690Sobrien const char *format_string, const char *arg_string) 243186690Sobrien{ 244186690Sobrien size_t string_len; 245186690Sobrien 246186690Sobrien if (arg_string == NULL) 247186690Sobrien return print_it(s, (size_t)6, "(null)", 0); 248186690Sobrien 249186690Sobrien /* hand-made strlen() whitch stops when 'prec' is reached. */ 250186690Sobrien /* if 'prec' is -1 then it is never reached. */ 251186690Sobrien string_len = 0; 252186690Sobrien while (arg_string[string_len] != 0 && (size_t)prec != string_len) 253186690Sobrien string_len++; 254186690Sobrien 255186690Sobrien if (width != -1 && string_len < (size_t)width) 256186690Sobrien string_len = (size_t)width; 257186690Sobrien 258186690Sobrien return print_it(s, string_len, format_string, arg_string); 259186690Sobrien} 260186690Sobrien 261186690Sobrien/* 262186690Sobrien * Read a serie of digits. Stop when non-digit is found. 263186690Sobrien * Return value: the value read (between 0 and 32767). 264186690Sobrien * Note: no checks are made against overflow. If the string contain a big 265186690Sobrien * number, then the return value won't be what we want (but, in this case, 266186690Sobrien * the programmer don't know whatr he wants, then no problem). 267186690Sobrien */ 268186690Sobrienstatic int getint(const char **string) 269186690Sobrien{ 270186690Sobrien int i = 0; 271186690Sobrien 272186690Sobrien while (isdigit((unsigned char)**string) != 0) { 273186690Sobrien i = i * 10 + (**string - '0'); 274186690Sobrien (*string)++; 275186690Sobrien } 276186690Sobrien 277186690Sobrien if (i < 0 || i > 32767) 278186690Sobrien i = 32767; /* if we have i==-10 this is not because the number is */ 279186690Sobrien /* negative; this is because the number is big */ 280186690Sobrien return i; 281186690Sobrien} 282186690Sobrien 283186690Sobrien/* 284186690Sobrien * Read a part of the format string. A part is 'usual characters' (ie "blabla") 285186690Sobrien * or '%%' escape sequence (to print a single '%') or any combination of 286186690Sobrien * format specifier (ie "%i" or "%10.2d"). 287186690Sobrien * After the current part is managed, the function returns to caller with 288186690Sobrien * everything ready to manage the following part. 289186690Sobrien * The caller must ensure than the string is not empty, i.e. the first byte 290186690Sobrien * is not zero. 291186690Sobrien * 292186690Sobrien * Return value: 0 = ok 293186690Sobrien * EOF = error 294186690Sobrien */ 295186690Sobrienstatic int dispatch(xprintf_struct *s) 296186690Sobrien{ 297186690Sobrien const char *initial_ptr; 298186690Sobrien char format_string[24]; /* max length may be something like "% +-#032768.32768Ld" */ 299186690Sobrien char *format_ptr; 300186690Sobrien int flag_plus, flag_minus, flag_space, flag_sharp, flag_zero; 301186690Sobrien int width, prec, modifier, approx_width; 302186690Sobrien char type; 303186690Sobrien /* most of those variables are here to rewrite the format string */ 304186690Sobrien 305186690Sobrien#define SRCTXT (s->src_string) 306186690Sobrien#define DESTTXT (s->dest_string) 307186690Sobrien 308186690Sobrien /* incoherent format string. Characters after the '%' will be printed with the next call */ 309186690Sobrien#define INCOHERENT() do {SRCTXT=initial_ptr; return 0;} while (0) /* do/while to avoid */ 310186690Sobrien#define INCOHERENT_TEST() do {if(*SRCTXT==0) INCOHERENT();} while (0) /* a null statement */ 311186690Sobrien 312186690Sobrien /* 'normal' text */ 313186690Sobrien if (*SRCTXT != '%') 314186690Sobrien return usual_char(s); 315186690Sobrien 316186690Sobrien /* we then have a '%' */ 317186690Sobrien SRCTXT++; 318186690Sobrien /* don't check for end-of-string ; this is done later */ 319186690Sobrien 320186690Sobrien /* '%%' escape sequence */ 321186690Sobrien if (*SRCTXT == '%') { 322186690Sobrien if (realloc_buff(s, (size_t)1) == EOF) /* because we can have "%%%%%%%%..." */ 323186690Sobrien return EOF; 324186690Sobrien *DESTTXT = '%'; 325186690Sobrien DESTTXT++; 326186690Sobrien SRCTXT++; 327186690Sobrien (s->real_len)++; 328186690Sobrien (s->pseudo_len)++; 329186690Sobrien return 0; 330186690Sobrien } 331186690Sobrien 332186690Sobrien /* '%' managing */ 333186690Sobrien initial_ptr = SRCTXT; /* save current pointer in case of incorrect */ 334186690Sobrien /* 'decoding'. Points just after the '%' so the '%' */ 335186690Sobrien /* won't be printed in any case, as required. */ 336186690Sobrien 337186690Sobrien /* flag */ 338186690Sobrien flag_plus = flag_minus = flag_space = flag_sharp = flag_zero = 0; 339186690Sobrien 340186690Sobrien for (;; SRCTXT++) { 341186690Sobrien if (*SRCTXT == ' ') 342186690Sobrien flag_space = 1; 343186690Sobrien else if (*SRCTXT == '+') 344186690Sobrien flag_plus = 1; 345186690Sobrien else if (*SRCTXT == '-') 346186690Sobrien flag_minus = 1; 347186690Sobrien else if (*SRCTXT == '#') 348186690Sobrien flag_sharp = 1; 349186690Sobrien else if (*SRCTXT == '0') 350186690Sobrien flag_zero = 1; 351186690Sobrien else 352186690Sobrien break; 353186690Sobrien } 354186690Sobrien 355186690Sobrien INCOHERENT_TEST(); /* here is the first test for end of string */ 356186690Sobrien 357186690Sobrien /* width */ 358186690Sobrien if (*SRCTXT == '*') { /* width given by next argument */ 359186690Sobrien SRCTXT++; 360186690Sobrien width = va_arg(s->vargs, int); 361186690Sobrien if ((size_t)width > 0x3fffU) /* 'size_t' to check against negative values too */ 362186690Sobrien width = 0x3fff; 363186690Sobrien } else if (isdigit((unsigned char)*SRCTXT)) /* width given as ASCII number */ 364186690Sobrien width = getint(&SRCTXT); 365186690Sobrien else 366186690Sobrien width = -1; /* no width specified */ 367186690Sobrien 368186690Sobrien INCOHERENT_TEST(); 369186690Sobrien 370186690Sobrien /* .prec */ 371186690Sobrien if (*SRCTXT == '.') { 372186690Sobrien SRCTXT++; 373186690Sobrien if (*SRCTXT == '*') { /* .prec given by next argument */ 374186690Sobrien SRCTXT++; 375186690Sobrien prec = va_arg(s->vargs, int); 376186690Sobrien if ((size_t)prec >= 0x3fffU) /* 'size_t' to check against negative values too */ 377186690Sobrien prec = 0x3fff; 378186690Sobrien } else { /* .prec given as ASCII number */ 379186690Sobrien if (isdigit((unsigned char)*SRCTXT) == 0) 380186690Sobrien INCOHERENT(); 381186690Sobrien prec = getint(&SRCTXT); 382186690Sobrien } 383186690Sobrien INCOHERENT_TEST(); 384186690Sobrien } else 385186690Sobrien prec = -1; /* no .prec specified */ 386186690Sobrien 387186690Sobrien /* modifier */ 388186690Sobrien if (*SRCTXT == 'L' || *SRCTXT == 'h' || *SRCTXT == 'l') { 389186690Sobrien modifier = *SRCTXT; 390186690Sobrien SRCTXT++; 391186690Sobrien if (modifier=='l' && *SRCTXT=='l') { 392186690Sobrien SRCTXT++; 393186690Sobrien modifier = 'L'; /* 'll' == 'L' long long == long double */ 394186690Sobrien } /* only for compatibility ; not portable */ 395186690Sobrien INCOHERENT_TEST(); 396186690Sobrien } else 397186690Sobrien modifier = -1; /* no modifier specified */ 398186690Sobrien 399186690Sobrien /* type */ 400186690Sobrien type = *SRCTXT; 401186690Sobrien if (strchr("diouxXfegEGcspn",type) == NULL) 402186690Sobrien INCOHERENT(); /* unknown type */ 403186690Sobrien SRCTXT++; 404186690Sobrien 405186690Sobrien /* rewrite format-string */ 406186690Sobrien format_string[0] = '%'; 407186690Sobrien format_ptr = &(format_string[1]); 408186690Sobrien 409186690Sobrien if (flag_plus) { 410186690Sobrien *format_ptr = '+'; 411186690Sobrien format_ptr++; 412186690Sobrien } 413186690Sobrien if (flag_minus) { 414186690Sobrien *format_ptr = '-'; 415186690Sobrien format_ptr++; 416186690Sobrien } 417186690Sobrien if (flag_space) { 418186690Sobrien *format_ptr = ' '; 419186690Sobrien format_ptr++; 420186690Sobrien } 421186690Sobrien if (flag_sharp) { 422186690Sobrien *format_ptr = '#'; 423186690Sobrien format_ptr++; 424186690Sobrien } 425186690Sobrien if (flag_zero) { 426186690Sobrien *format_ptr = '0'; 427186690Sobrien format_ptr++; 428186690Sobrien } /* '0' *must* be the last one */ 429186690Sobrien 430186690Sobrien if (width != -1) { 431186690Sobrien sprintf(format_ptr, "%i", width); 432186690Sobrien format_ptr += strlen(format_ptr); 433186690Sobrien } 434186690Sobrien 435186690Sobrien if (prec != -1) { 436186690Sobrien *format_ptr = '.'; 437186690Sobrien format_ptr++; 438186690Sobrien sprintf(format_ptr, "%i", prec); 439186690Sobrien format_ptr += strlen(format_ptr); 440186690Sobrien } 441186690Sobrien 442186690Sobrien if (modifier != -1) { 443186690Sobrien if (modifier == 'L' && strchr("diouxX",type) != NULL) { 444186690Sobrien *format_ptr = 'l'; 445186690Sobrien format_ptr++; 446186690Sobrien *format_ptr = 'l'; 447186690Sobrien format_ptr++; 448186690Sobrien } else { 449186690Sobrien *format_ptr = modifier; 450186690Sobrien format_ptr++; 451186690Sobrien } 452186690Sobrien } 453186690Sobrien 454186690Sobrien *format_ptr = type; 455186690Sobrien format_ptr++; 456186690Sobrien *format_ptr = 0; 457186690Sobrien 458186690Sobrien /* vague approximation of minimal length if width or prec are specified */ 459186690Sobrien approx_width = width + prec; 460186690Sobrien if (approx_width < 0) /* because width == -1 and/or prec == -1 */ 461186690Sobrien approx_width = 0; 462186690Sobrien 463186690Sobrien switch (type) { 464186690Sobrien /* int */ 465186690Sobrien case 'd': 466186690Sobrien case 'i': 467186690Sobrien case 'o': 468186690Sobrien case 'u': 469186690Sobrien case 'x': 470186690Sobrien case 'X': 471186690Sobrien switch (modifier) { 472186690Sobrien case -1 : 473186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); 474186690Sobrien case 'L': 475186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long long int)); 476186690Sobrien case 'l': 477186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long int)); 478186690Sobrien case 'h': 479186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); 480186690Sobrien /* 'int' instead of 'short int' because default promotion is 'int' */ 481186690Sobrien default: 482186690Sobrien INCOHERENT(); 483186690Sobrien } 484186690Sobrien 485186690Sobrien /* char */ 486186690Sobrien case 'c': 487186690Sobrien if (modifier != -1) 488186690Sobrien INCOHERENT(); 489186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, int)); 490186690Sobrien /* 'int' instead of 'char' because default promotion is 'int' */ 491186690Sobrien 492186690Sobrien /* math */ 493186690Sobrien case 'e': 494186690Sobrien case 'f': 495186690Sobrien case 'g': 496186690Sobrien case 'E': 497186690Sobrien case 'G': 498186690Sobrien switch (modifier) { 499186690Sobrien case -1 : /* because of default promotion, no modifier means 'l' */ 500186690Sobrien case 'l': 501186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, double)); 502186690Sobrien case 'L': 503186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, long double)); 504186690Sobrien default: 505186690Sobrien INCOHERENT(); 506186690Sobrien } 507186690Sobrien 508186690Sobrien /* string */ 509186690Sobrien case 's': 510186690Sobrien return type_s(s, width, prec, format_string, va_arg(s->vargs, const char*)); 511186690Sobrien 512186690Sobrien /* pointer */ 513186690Sobrien case 'p': 514186690Sobrien if (modifier == -1) 515186690Sobrien return print_it(s, (size_t)approx_width, format_string, va_arg(s->vargs, void *)); 516186690Sobrien INCOHERENT(); 517186690Sobrien 518186690Sobrien /* store */ 519186690Sobrien case 'n': 520186690Sobrien if (modifier == -1) { 521186690Sobrien int * p; 522186690Sobrien p = va_arg(s->vargs, int *); 523186690Sobrien if (p != NULL) { 524186690Sobrien *p = s->pseudo_len; 525186690Sobrien return 0; 526186690Sobrien } 527186690Sobrien return EOF; 528186690Sobrien } 529186690Sobrien INCOHERENT(); 530186690Sobrien 531186690Sobrien } /* switch */ 532186690Sobrien 533186690Sobrien INCOHERENT(); /* unknown type */ 534186690Sobrien 535186690Sobrien#undef INCOHERENT 536186690Sobrien#undef INCOHERENT_TEST 537186690Sobrien#undef SRCTXT 538186690Sobrien#undef DESTTXT 539186690Sobrien} 540186690Sobrien 541186690Sobrien/* 542186690Sobrien * Return value: number of *virtually* written characters 543186690Sobrien * EOF = error 544186690Sobrien */ 545186690Sobrienstatic int core(xprintf_struct *s) 546186690Sobrien{ 547186690Sobrien size_t len, save_len; 548186690Sobrien char *dummy_base; 549186690Sobrien 550186690Sobrien /* basic checks */ 551186690Sobrien if ((int)(s->maxlen) <= 0) /* 'int' to check against some conversion */ 552186690Sobrien return EOF; /* error for example if value is (int)-10 */ 553186690Sobrien s->maxlen--; /* because initial maxlen counts final 0 */ 554186690Sobrien /* note: now 'maxlen' _can_ be zero */ 555186690Sobrien 556186690Sobrien if (s->src_string == NULL) 557186690Sobrien s->src_string = "(null)"; 558186690Sobrien 559186690Sobrien /* struct init and memory allocation */ 560186690Sobrien s->buffer_base = NULL; 561186690Sobrien s->buffer_len = 0; 562186690Sobrien s->real_len = 0; 563186690Sobrien s->pseudo_len = 0; 564186690Sobrien if (realloc_buff(s, (size_t)0) == EOF) 565186690Sobrien return EOF; 566186690Sobrien s->dest_string = s->buffer_base; 567186690Sobrien 568186690Sobrien /* process source string */ 569186690Sobrien for (;;) { 570186690Sobrien /* up to end of source string */ 571186690Sobrien if (*(s->src_string) == 0) { 572186690Sobrien *(s->dest_string) = 0; /* final 0 */ 573186690Sobrien len = s->real_len + 1; 574186690Sobrien break; 575186690Sobrien } 576186690Sobrien 577186690Sobrien if (dispatch(s) == EOF) 578186690Sobrien goto free_EOF; 579186690Sobrien 580186690Sobrien /* up to end of dest string */ 581186690Sobrien if (s->real_len >= s->maxlen) { 582186690Sobrien (s->buffer_base)[s->maxlen] = 0; /* final 0 */ 583186690Sobrien len = s->maxlen + 1; 584186690Sobrien break; 585186690Sobrien } 586186690Sobrien } 587186690Sobrien 588186690Sobrien /* for (v)asnprintf */ 589186690Sobrien dummy_base = s->buffer_base; 590186690Sobrien save_len = 0; /* just to avoid a compiler warning */ 591186690Sobrien 592186690Sobrien dummy_base = s->buffer_base + s->real_len; 593186690Sobrien save_len = s->real_len; 594186690Sobrien 595186690Sobrien /* process the remaining of source string to compute 'pseudo_len'. We 596186690Sobrien * overwrite again and again, starting at 'dummy_base' because we don't 597186690Sobrien * need the text, only char count. */ 598186690Sobrien while(*(s->src_string) != 0) { /* up to end of source string */ 599186690Sobrien s->real_len = 0; 600186690Sobrien s->dest_string = dummy_base; 601186690Sobrien if (dispatch(s) == EOF) 602186690Sobrien goto free_EOF; 603186690Sobrien } 604186690Sobrien 605186690Sobrien s->buffer_base = (char *)realloc((void *)(s->buffer_base), save_len + 1); 606186690Sobrien if (s->buffer_base == NULL) 607186690Sobrien return EOF; /* should rarely happen because we shrink the buffer */ 608186690Sobrien return s->pseudo_len; 609186690Sobrien 610186690Sobrien free_EOF: 611234250Sobrien 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