/* Formatted output to strings, using POSIX/XSI format strings with positions.
  Copyright (C) 2003, 2006 Free Software Foundation, Inc.
  Written by Bruno Haible <[email protected]>, 2003.

  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU Library General Public License as published
  by the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  USA.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef __GNUC__
# define alloca __builtin_alloca
# define HAVE_ALLOCA 1
#else
# ifdef _MSC_VER
#  include <malloc.h>
#  define alloca _alloca
# else
#  if defined HAVE_ALLOCA_H || defined _LIBC
#   include <alloca.h>
#  else
#   ifdef _AIX
#pragma alloca
#   else
#    ifndef alloca
char *alloca ();
#    endif
#   endif
#  endif
# endif
#endif

#include <stdio.h>

#if !HAVE_POSIX_PRINTF

#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

/* Some systems, like OSF/1 4.0 and Woe32, don't have EOVERFLOW.  */
#ifndef EOVERFLOW
# define EOVERFLOW E2BIG
#endif

/* When building a DLL, we must export some functions.  Note that because
  the functions are only defined for binary backward compatibility, we
  don't need to use __declspec(dllimport) in any case.  */
#if defined _MSC_VER && BUILDING_DLL
# define DLL_EXPORTED __declspec(dllexport)
#else
# define DLL_EXPORTED
#endif

#define STATIC static

/* This needs to be consistent with libgnuintl.h.in.  */
#if defined __NetBSD__ || defined __CYGWIN__ || defined __MINGW32__
/* Don't break __attribute__((format(printf,M,N))).
  This redefinition is only possible because the libc in NetBSD, Cygwin,
  mingw does not have a function __printf__.  */
# define libintl_printf __printf__
#endif

/* Define auxiliary functions declared in "printf-args.h".  */
#include "printf-args.c"

/* Define auxiliary functions declared in "printf-parse.h".  */
#include "printf-parse.c"

/* Define functions declared in "vasnprintf.h".  */
#define vasnprintf libintl_vasnprintf
#include "vasnprintf.c"
#if 0 /* not needed */
#define asnprintf libintl_asnprintf
#include "asnprintf.c"
#endif

DLL_EXPORTED
int
libintl_vfprintf (FILE *stream, const char *format, va_list args)
{
 if (strchr (format, '$') == NULL)
   return vfprintf (stream, format, args);
 else
   {
     size_t length;
     char *result = libintl_vasnprintf (NULL, &length, format, args);
     int retval = -1;
     if (result != NULL)
       {
         size_t written = fwrite (result, 1, length, stream);
         free (result);
         if (written == length)
           {
             if (length > INT_MAX)
               errno = EOVERFLOW;
             else
               retval = length;
           }
       }
     return retval;
   }
}

DLL_EXPORTED
int
libintl_fprintf (FILE *stream, const char *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vfprintf (stream, format, args);
 va_end (args);
 return retval;
}

DLL_EXPORTED
int
libintl_vprintf (const char *format, va_list args)
{
 return libintl_vfprintf (stdout, format, args);
}

DLL_EXPORTED
int
libintl_printf (const char *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vprintf (format, args);
 va_end (args);
 return retval;
}

DLL_EXPORTED
int
libintl_vsprintf (char *resultbuf, const char *format, va_list args)
{
 if (strchr (format, '$') == NULL)
   return vsprintf (resultbuf, format, args);
 else
   {
     size_t length = (size_t) ~0 / (4 * sizeof (char));
     char *result = libintl_vasnprintf (resultbuf, &length, format, args);
     if (result != resultbuf)
       {
         free (result);
         return -1;
       }
     if (length > INT_MAX)
       {
         errno = EOVERFLOW;
         return -1;
       }
     else
       return length;
   }
}

DLL_EXPORTED
int
libintl_sprintf (char *resultbuf, const char *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vsprintf (resultbuf, format, args);
 va_end (args);
 return retval;
}

#if HAVE_SNPRINTF

# if HAVE_DECL__SNPRINTF
  /* Windows.  */
#  define system_vsnprintf _vsnprintf
# else
  /* Unix.  */
#  define system_vsnprintf vsnprintf
# endif

DLL_EXPORTED
int
libintl_vsnprintf (char *resultbuf, size_t length, const char *format, va_list args)
{
 if (strchr (format, '$') == NULL)
   return system_vsnprintf (resultbuf, length, format, args);
 else
   {
     size_t maxlength = length;
     char *result = libintl_vasnprintf (resultbuf, &length, format, args);
     if (result != resultbuf)
       {
         if (maxlength > 0)
           {
             size_t pruned_length =
               (length < maxlength ? length : maxlength - 1);
             memcpy (resultbuf, result, pruned_length);
             resultbuf[pruned_length] = '\0';
           }
         free (result);
       }
     if (length > INT_MAX)
       {
         errno = EOVERFLOW;
         return -1;
       }
     else
       return length;
   }
}

DLL_EXPORTED
int
libintl_snprintf (char *resultbuf, size_t length, const char *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vsnprintf (resultbuf, length, format, args);
 va_end (args);
 return retval;
}

#endif

#if HAVE_ASPRINTF

DLL_EXPORTED
int
libintl_vasprintf (char **resultp, const char *format, va_list args)
{
 size_t length;
 char *result = libintl_vasnprintf (NULL, &length, format, args);
 if (result == NULL)
   return -1;
 if (length > INT_MAX)
   {
     free (result);
     errno = EOVERFLOW;
     return -1;
   }
 *resultp = result;
 return length;
}

DLL_EXPORTED
int
libintl_asprintf (char **resultp, const char *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vasprintf (resultp, format, args);
 va_end (args);
 return retval;
}

#endif

#if HAVE_FWPRINTF

#include <wchar.h>

#define WIDE_CHAR_VERSION 1

/* Define auxiliary functions declared in "wprintf-parse.h".  */
#include "printf-parse.c"

/* Define functions declared in "vasnprintf.h".  */
#define vasnwprintf libintl_vasnwprintf
#include "vasnprintf.c"
#if 0 /* not needed */
#define asnwprintf libintl_asnwprintf
#include "asnprintf.c"
#endif

# if HAVE_DECL__SNWPRINTF
  /* Windows.  */
#  define system_vswprintf _vsnwprintf
# else
  /* Unix.  */
#  define system_vswprintf vswprintf
# endif

DLL_EXPORTED
int
libintl_vfwprintf (FILE *stream, const wchar_t *format, va_list args)
{
 if (wcschr (format, '$') == NULL)
   return vfwprintf (stream, format, args);
 else
   {
     size_t length;
     wchar_t *result = libintl_vasnwprintf (NULL, &length, format, args);
     int retval = -1;
     if (result != NULL)
       {
         size_t i;
         for (i = 0; i < length; i++)
           if (fputwc (result[i], stream) == WEOF)
             break;
         free (result);
         if (i == length)
           {
             if (length > INT_MAX)
               errno = EOVERFLOW;
             else
               retval = length;
           }
       }
     return retval;
   }
}

DLL_EXPORTED
int
libintl_fwprintf (FILE *stream, const wchar_t *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vfwprintf (stream, format, args);
 va_end (args);
 return retval;
}

DLL_EXPORTED
int
libintl_vwprintf (const wchar_t *format, va_list args)
{
 return libintl_vfwprintf (stdout, format, args);
}

DLL_EXPORTED
int
libintl_wprintf (const wchar_t *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vwprintf (format, args);
 va_end (args);
 return retval;
}

DLL_EXPORTED
int
libintl_vswprintf (wchar_t *resultbuf, size_t length, const wchar_t *format, va_list args)
{
 if (wcschr (format, '$') == NULL)
   return system_vswprintf (resultbuf, length, format, args);
 else
   {
     size_t maxlength = length;
     wchar_t *result = libintl_vasnwprintf (resultbuf, &length, format, args);
     if (result != resultbuf)
       {
         if (maxlength > 0)
           {
             size_t pruned_length =
               (length < maxlength ? length : maxlength - 1);
             memcpy (resultbuf, result, pruned_length * sizeof (wchar_t));
             resultbuf[pruned_length] = 0;
           }
         free (result);
         /* Unlike vsnprintf, which has to return the number of character that
            would have been produced if the resultbuf had been sufficiently
            large, the vswprintf function has to return a negative value if
            the resultbuf was not sufficiently large.  */
         if (length >= maxlength)
           return -1;
       }
     if (length > INT_MAX)
       {
         errno = EOVERFLOW;
         return -1;
       }
     else
       return length;
   }
}

DLL_EXPORTED
int
libintl_swprintf (wchar_t *resultbuf, size_t length, const wchar_t *format, ...)
{
 va_list args;
 int retval;

 va_start (args, format);
 retval = libintl_vswprintf (resultbuf, length, format, args);
 va_end (args);
 return retval;
}

#endif

#endif