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