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