Home | History | Annotate | Line # | Download | only in libgettextpo
vasprintf.c revision 1.1
      1 /* Like vsprintf but provides a pointer to malloc'd storage, which must
      2    be freed by the caller.
      3    Copyright (C) 1994, 1998, 1999, 2000-2003, 2006 Free Software Foundation, Inc.
      4 
      5 This program is free software; you can redistribute it and/or modify
      6 it under the terms of the GNU General Public License as published by
      7 the Free Software Foundation; either version 2, or (at your option)
      8 any later version.
      9 
     10 This program is distributed in the hope that it will be useful,
     11 but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software Foundation,
     17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     18 
     19 #include <config.h>
     20 
     21 /* Specification.  */
     22 #include "vasprintf.h"
     23 
     24 #include <stdio.h>
     25 #include <string.h>
     26 #include <stdlib.h>
     27 #include <stdarg.h>
     28 #include <math.h>
     29 
     30 #ifdef TEST
     31 size_t global_total_width;
     32 #endif
     33 
     34 static int
     35 int_vasprintf (char **result, const char *format, va_list *args)
     36 {
     37   const char *p = format;
     38   /* Add one to make sure that it is never zero, which might cause malloc
     39      to return NULL.  */
     40   size_t total_width = strlen (format) + 1;
     41   va_list ap = *args;
     42 
     43   while (*p != '\0')
     44     {
     45       if (*p++ == '%')
     46 	{
     47 	  while (strchr ("-+ #0", *p))
     48 	    ++p;
     49 	  if (*p == '*')
     50 	    {
     51 	      ++p;
     52 	      total_width += abs (va_arg (ap, int));
     53 	    }
     54 	  else
     55 	    total_width += strtoul (p, (char **) &p, 10);
     56 	  if (*p == '.')
     57 	    {
     58 	      ++p;
     59 	      if (*p == '*')
     60 		{
     61 		  ++p;
     62 		  total_width += abs (va_arg (ap, int));
     63 		}
     64 	      else
     65 		total_width += strtoul (p, (char **) &p, 10);
     66 	    }
     67 	  while (strchr ("hlLjtz", *p))
     68 	    ++p;
     69 	  /* Should be big enough for any format specifier except %s
     70 	     and floats.  */
     71 	  total_width += 30;
     72 	  switch (*p)
     73 	    {
     74 	    case 'd':
     75 	    case 'i':
     76 	    case 'o':
     77 	    case 'u':
     78 	    case 'x':
     79 	    case 'X':
     80 	    case 'c':
     81 	      (void) va_arg (ap, int);
     82 	      break;
     83 	    case 'f':
     84 	    case 'F':
     85 	      {
     86 		double arg = va_arg (ap, double);
     87 		if (arg >= 1.0 || arg <= -1.0)
     88 		  /* Since an ieee double can have an exponent of 307, we'll
     89 		     make the buffer wide enough to cover the gross case. */
     90 		  total_width += 307;
     91 	      }
     92 	      break;
     93 	    case 'e':
     94 	    case 'E':
     95 	    case 'g':
     96 	    case 'G':
     97 	      (void) va_arg (ap, double);
     98 	      break;
     99 	    case 's':
    100 	      total_width += strlen (va_arg (ap, char *));
    101 	      break;
    102 	    case 'p':
    103 	    case 'n':
    104 	      (void) va_arg (ap, char *);
    105 	      break;
    106 	    }
    107 	  p++;
    108 	}
    109     }
    110 #ifdef TEST
    111   global_total_width = total_width;
    112 #endif
    113   *result = malloc (total_width);
    114   if (*result != NULL)
    115     return vsprintf (*result, format, *args);
    116   else
    117     return -1;
    118 }
    119 
    120 int
    121 vasprintf (char **result, const char *format, va_list args)
    122 {
    123   return int_vasprintf (result, format, &args);
    124 }
    125 
    126 int
    127 asprintf (char **result, const char *format, ...)
    128 {
    129   va_list args;
    130   int done;
    131 
    132   va_start (args, format);
    133   done = vasprintf (result, format, args);
    134   va_end (args);
    135 
    136   return done;
    137 }
    138 
    139 /* ========================= test program ========================= */
    140 
    141 #ifdef TEST
    142 
    143 #include <float.h>
    144 
    145 void
    146 checkit (const char* format, ...)
    147 {
    148   va_list args;
    149   char *result;
    150 
    151   va_start (args, format);
    152   vasprintf (&result, format, args);
    153   if (strlen (result) < global_total_width)
    154     printf ("PASS: ");
    155   else
    156     printf ("FAIL: ");
    157   printf ("%lu %s\n", (unsigned long) global_total_width, result);
    158 }
    159 
    160 int
    161 main ()
    162 {
    163   checkit ("%d", 0x12345678);
    164   checkit ("%200d", 5);
    165   checkit ("%.300d", 6);
    166   checkit ("%100.150d", 7);
    167   checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
    168 777777777777777777333333333333366666666666622222222222777777777777733333");
    169   checkit ("%f%s%d%s", 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx");
    170   checkit ("%e", DBL_MIN);
    171   checkit ("%e", DBL_MAX);
    172   checkit ("%.400f", DBL_MIN);
    173   checkit ("%f", DBL_MAX);
    174   checkit ("%g", DBL_MIN);
    175   checkit ("%g", DBL_MAX);
    176   return 0;
    177 }
    178 
    179 #endif /* TEST */
    180