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