strfuncs.c revision 1d9f94199ff5c49cf9df18fd0c9eb85c0fb77a6c
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/*
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen strfuncs.c : String manipulation functions (note: LGPL, because the )
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen Copyright (C) 2001-2002 Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen printf_string_upper_bound() code is taken from GLIB:
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen Modified by the GLib Team and others 1997-1999.
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen This library is free software; you can redistribute it and/or
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen modify it under the terms of the GNU Library General Public
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen License as published by the Free Software Foundation; either
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen version 2 of the License, or (at your option) any later version.
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen This library is distributed in the hope that it will be useful,
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen but WITHOUT ANY WARRANTY; without even the implied warranty of
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen Library General Public License for more details.
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen You should have received a copy of the GNU Library General Public
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen License along with this library; if not, write to the
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen Free Software Foundation, Inc., 59 Temple Place - Suite 330,
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen Boston, MA 02111-1307, USA.
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen*/
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen#include "lib.h"
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen#include "strfuncs.h"
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen#include <stdio.h>
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen#include <limits.h>
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen#include <ctype.h>
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen#define STRCONCAT_BUFSIZE 512
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainentypedef void *(*ALLOC_FUNC)(Pool, size_t);
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainenstatic void *tp_malloc(Pool pool __attr_unused__, size_t size)
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen{
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen return t_malloc(size);
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen}
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainentypedef union _GDoubleIEEE754 GDoubleIEEE754;
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen#define G_IEEE754_DOUBLE_BIAS (1023)
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen/* multiply with base2 exponent to get base10 exponent (nomal numbers) */
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen#define G_LOG_2_BASE_10 (0.30102999566398119521)
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen#if G_BYTE_ORDER == G_LITTLE_ENDIAN
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainenunion _GDoubleIEEE754
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen double v_double;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen struct {
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int mantissa_low : 32;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int mantissa_high : 20;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int biased_exponent : 11;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int sign : 1;
89b3a2a06a87e66798d21998c8ddf525c3946e7fTimo Sirainen } mpn;
89b3a2a06a87e66798d21998c8ddf525c3946e7fTimo Sirainen};
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#elif G_BYTE_ORDER == G_BIG_ENDIAN
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainenunion _GDoubleIEEE754
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen double v_double;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen struct {
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int sign : 1;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int biased_exponent : 11;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int mantissa_high : 20;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int mantissa_low : 32;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen } mpn;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen};
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#error unknown ENDIAN type
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainentypedef struct
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int min_width;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen unsigned int precision;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen int alternate_format, zero_padding, adjust_left, locale_grouping;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen int add_space, add_sign, possible_sign, seen_precision;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen int mod_long, mod_extra_long;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen} PrintfArgSpec;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#if (SIZEOF_LONG > 4) || (SIZEOF_VOID_P > 4)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen# define HONOUR_LONGS 1
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#else
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen# define HONOUR_LONGS 0
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen#endif
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainensize_t printf_string_upper_bound(const char *format, va_list args)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen{
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen size_t len = 1;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen if (!format)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen return len;
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen while (*format)
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen {
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen register char c = *format++;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen if (c != '%')
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen len += 1;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen else /* (c == '%') */
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen {
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen PrintfArgSpec spec;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen int seen_l = FALSE, conv_done = FALSE;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen unsigned int conv_len = 0;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen memset(&spec, 0, sizeof(spec));
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen do
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen {
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen c = *format++;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen switch (c)
a8319b1f667a808f335447e9fa10ff66b99705c5Timo Sirainen {
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch GDoubleIEEE754 u_double;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch unsigned int v_uint;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch int v_int;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch const char *v_string;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch /* beware of positional parameters
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch */
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '$':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch i_panic("unable to handle positional parameters (%%n$)");
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch /* parse flags
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch */
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '#':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.alternate_format = TRUE;
a8319b1f667a808f335447e9fa10ff66b99705c5Timo Sirainen break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '0':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.zero_padding = TRUE;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '-':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.adjust_left = TRUE;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case ' ':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.add_space = TRUE;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '+':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.add_sign = TRUE;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '\'':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.locale_grouping = TRUE;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch /* parse output size specifications
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch */
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '.':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.seen_precision = TRUE;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch break;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '1':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '2':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '3':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '4':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '5':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '6':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '7':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '8':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case '9':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch v_uint = c - '0';
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch c = *format;
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch while (c >= '0' && c <= '9')
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen {
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen format++;
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen v_uint = v_uint * 10 + (c - '0');
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen c = *format;
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen }
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen if (spec.seen_precision)
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen spec.precision = I_MAX (spec.precision, v_uint);
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen else
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen spec.min_width = I_MAX (spec.min_width, v_uint);
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen break;
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen case '*':
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen v_int = va_arg (args, int);
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen if (spec.seen_precision)
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen {
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen /* forget about negative precision */
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen if (v_int >= 0)
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen spec.precision = I_MAX ((int)spec.precision, v_int);
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen }
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen else
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen {
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen if (v_int < 0)
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen {
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen v_int = - v_int;
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen spec.adjust_left = TRUE;
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen }
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen spec.min_width = I_MAX ((int)spec.min_width, v_int);
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen }
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen break;
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen /* parse type modifiers
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen */
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen case 'h':
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen /* ignore */
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen break;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen case 'l':
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen if (!seen_l)
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen {
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen spec.mod_long = TRUE;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen seen_l = TRUE;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen break;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen }
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen /* else, fall through */
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen case 'L':
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen spec.mod_long = TRUE;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen spec.mod_extra_long = TRUE;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen break;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen /* parse output conversions
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen */
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen case '%':
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen conv_len += 1;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen break;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen case 'o':
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen conv_len += 2;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen /* fall through */
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen case 'd':
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen case 'i':
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen conv_len += 1; /* sign */
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen /* fall through */
a1fff894178eb4279a3acb44948c1ba98db104deTimo Sirainen case 'u':
86441ffc028f11857152c15fe7b0d24ff0874504Timo Sirainen conv_len += 4;
be1749e2421dea6465f9096fd35b28b2500d06e4Timo Sirainen /* fall through */
a8319b1f667a808f335447e9fa10ff66b99705c5Timo Sirainen case 'x':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch case 'X':
f43734e772a94c8d2b684b59c212fecf66122d15Stephan Bosch spec.possible_sign = TRUE;
1f35f54096589b0fdb04bfac4d079d06b43f2a74Timo Sirainen conv_len += 10;
c8d88cb784cc7f7edcdc97f8b24b243eb1bba7aaTimo Sirainen if (spec.mod_long && HONOUR_LONGS)
12ff27eadb44c6877298d3e214c7039342669669Timo Sirainen conv_len *= 2;
if (spec.mod_extra_long)
conv_len *= 2;
if (spec.mod_extra_long)
{
#if SIZEOF_LONG_LONG > 0
(void) va_arg (args, long long);
#else
(void) va_arg (args, long);
#endif
}
else if (spec.mod_long)
(void) va_arg (args, long);
else
(void) va_arg (args, int);
break;
case 'A':
case 'a':
/* 0x */
conv_len += 2;
/* fall through */
case 'g':
case 'G':
case 'e':
case 'E':
case 'f':
spec.possible_sign = TRUE;
/* n . dddddddddddddddddddddddd E +- eeee */
conv_len += 1 + 1 + I_MAX (24, spec.precision) + 1 + 1 + 4;
if (spec.mod_extra_long)
i_panic("unable to handle long double");
#ifdef HAVE_LONG_DOUBLE
#error need to implement special handling for long double
#endif
u_double.v_double = va_arg (args, double);
/* %f can expand up to all significant digits before '.' (308) */
if (c == 'f' &&
u_double.mpn.biased_exponent > 0 && u_double.mpn.biased_exponent < 2047)
{
int exp = u_double.mpn.biased_exponent;
exp -= G_IEEE754_DOUBLE_BIAS;
exp = exp * G_LOG_2_BASE_10 + 1;
conv_len += exp;
}
/* some printf() implementations require extra padding for rounding */
conv_len += 2;
/* we can't really handle locale specific grouping here */
if (spec.locale_grouping)
conv_len *= 2;
break;
case 'c':
conv_len += spec.mod_long ? MB_LEN_MAX : 1;
(void) va_arg (args, int);
break;
case 's':
v_string = va_arg (args, char*);
if (!v_string)
conv_len += 8; /* hold "(null)" */
else if (spec.seen_precision)
conv_len += spec.precision;
else
conv_len += strlen (v_string);
conv_done = TRUE;
if (spec.mod_long)
i_panic("unable to handle wide char strings");
break;
case 'p':
spec.alternate_format = TRUE;
conv_len += 10;
if (HONOUR_LONGS)
conv_len *= 2;
conv_done = TRUE;
(void) va_arg (args, void*);
break;
/* handle invalid cases
*/
case '\000':
/* no conversion specification, bad bad */
i_panic("Missing conversion specifier");
break;
default:
i_panic("unable to handle `%c' while parsing format", c);
break;
}
conv_done |= conv_len > 0;
}
while (!conv_done);
/* handle width specifications */
conv_len = I_MAX (conv_len, I_MAX (spec.precision, spec.min_width));
/* handle flags */
conv_len += spec.alternate_format ? 2 : 0;
conv_len += (spec.add_space || spec.add_sign || spec.possible_sign);
/* finally done */
len += conv_len;
} /* else (c == '%') */
} /* while (*format) */
return len;
}
static const char *fix_format_real(const char *fmt, const char *p)
{
const char *errstr;
char *buf;
size_t pos, alloc, errlen;
errstr = strerror(errno);
errlen = strlen(errstr);
pos = (size_t) (p-fmt);
i_assert(pos < SSIZE_T_MAX);
alloc = pos + errlen + 128;
buf = t_buffer_get(alloc);
memcpy(buf, fmt, pos);
while (*p != '\0') {
if (*p == '%' && p[1] == 'm') {
if (pos+errlen+1 > alloc) {
alloc += errlen+1 + 128;
buf = t_buffer_get(alloc);
}
memcpy(buf+pos, errstr, errlen);
pos += errlen;
p += 2;
} else {
/* p + \0 */
if (pos+2 > alloc) {
alloc += 128;
buf = t_buffer_get(alloc);
}
buf[pos++] = *p;
p++;
}
}
buf[pos++] = '\0';
t_buffer_alloc(pos);
return buf;
}
/* replace %m with strerror() */
static const char *fix_format(const char *fmt)
{
const char *p;
for (p = fmt; *p != '\0'; p++) {
if (*p == '%' && p[1] == 'm')
return fix_format_real(fmt, p);
}
return fmt;
}
int i_snprintf(char *str, size_t max_chars, const char *format, ...)
{
#ifdef HAVE_VSNPRINTF
va_list args;
int ret;
i_assert(str != NULL);
i_assert(max_chars < SSIZE_T_MAX);
i_assert(format != NULL);
t_push();
va_start(args, format);
ret = vsnprintf(str, max_chars, fix_format(format), args);
va_end(args);
t_pop();
if (ret < 0) {
str[max_chars-1] = '\0';
ret = strlen(str);
}
return ret;
#else
char *buf;
va_list args;
int len;
i_assert(str != NULL);
i_assert(max_chars < SSIZE_T_MAX);
i_assert(format != NULL);
t_push();
va_start(args, format);
format = fix_format(format);
buf = t_buffer_get(printf_string_upper_bound(format, args));
va_end(args);
len = vsprintf(buf, format, args);
if (len >= (int)max_chars)
len = max_chars-1;
memcpy(str, buf, len);
str[len] = '\0';
t_pop();
return len;
#endif
}
#define STRDUP_CORE(alloc_func, str) STMT_START { \
void *mem; \
size_t len; \
\
for (len = 0; (str)[len] != '\0'; ) \
len++; \
len++; \
mem = alloc_func; \
memcpy(mem, str, sizeof(str[0])*len); \
return mem; \
} STMT_END
char *p_strdup(Pool pool, const char *str)
{
if (str == NULL)
return NULL;
STRDUP_CORE(p_malloc(pool, len), str);
}
const char *t_strdup(const char *str)
{
if (str == NULL)
return NULL;
STRDUP_CORE(t_malloc(len), str);
}
char *t_strdup_noconst(const char *str)
{
if (str == NULL)
return NULL;
STRDUP_CORE(t_malloc(len), str);
}
int *p_intarrdup(Pool pool, const int *arr)
{
if (arr == NULL)
return NULL;
STRDUP_CORE(p_malloc(pool, sizeof(int) * len), arr);
}
const int *t_intarrdup(const int *arr)
{
if (arr == NULL)
return NULL;
STRDUP_CORE(t_malloc(sizeof(int) * len), arr);
}
#define STRDUP_EMPTY_CORE(alloc_func, str) STMT_START { \
if ((str) == NULL || (str)[0] == '\0') \
return NULL; \
\
STRDUP_CORE(alloc_func, str); \
} STMT_END
char *p_strdup_empty(Pool pool, const char *str)
{
STRDUP_EMPTY_CORE(p_malloc(pool, len), str);
}
const char *t_strdup_empty(const char *str)
{
STRDUP_EMPTY_CORE(t_malloc(len), str);
}
char *p_strdup_until(Pool pool, const char *start, const char *end)
{
size_t size;
char *mem;
i_assert(start <= end);
size = (size_t) (end-start);
i_assert(size < SSIZE_T_MAX);
mem = p_malloc(pool, size + 1);
memcpy(mem, start, size);
return mem;
}
const char *t_strdup_until(const char *start, const char *end)
{
size_t size;
char *mem;
i_assert(start <= end);
size = (size_t) (end-start);
i_assert(size < SSIZE_T_MAX);
mem = t_malloc(size + 1);
memcpy(mem, start, size);
mem[size] = '\0';
return mem;
}
static inline char *strndup_core(const char *str, size_t max_chars,
ALLOC_FUNC alloc, Pool pool)
{
char *mem;
size_t len;
i_assert(max_chars < SSIZE_T_MAX);
if (str == NULL)
return NULL;
len = 0;
while (str[len] != '\0' && len < max_chars)
len++;
mem = alloc(pool, len+1);
memcpy(mem, str, len);
mem[len] = '\0';
return mem;
}
char *p_strndup(Pool pool, const char *str, size_t max_chars)
{
return strndup_core(str, max_chars, pool->malloc, pool);
}
const char *t_strndup(const char *str, size_t max_chars)
{
return strndup_core(str, max_chars, tp_malloc, NULL);
}
char *p_strdup_printf(Pool pool, const char *format, ...)
{
va_list args;
char *ret;
va_start(args, format);
ret = p_strdup_vprintf(pool, format, args);
va_end(args);
return ret;
}
const char *t_strdup_printf(const char *format, ...)
{
va_list args;
const char *ret;
va_start(args, format);
ret = t_strdup_vprintf(format, args);
va_end(args);
return ret;
}
static inline char *
strdup_vprintf_core(const char *format, va_list args,
ALLOC_FUNC alloc_func, Pool pool)
{
va_list temp_args;
char *ret;
if (format == NULL)
return NULL;
format = fix_format(format);
VA_COPY(temp_args, args);
ret = alloc_func(pool, printf_string_upper_bound(format, args));
vsprintf(ret, format, args);
va_end(temp_args);
return ret;
}
char *p_strdup_vprintf(Pool pool, const char *format, va_list args)
{
char *ret;
t_push();
ret = strdup_vprintf_core(format, args, pool->malloc, pool);
t_pop();
return ret;
}
const char *t_strdup_vprintf(const char *format, va_list args)
{
return strdup_vprintf_core(format, args, tp_malloc, NULL);
}
void p_strdup_replace(Pool pool, char **dest, const char *str)
{
p_free(pool, *dest);
*dest = p_strdup(pool, str);
}
const char *temp_strconcat(const char *str1, va_list args,
size_t *ret_len)
{
const char *str;
char *temp;
size_t full_len, len, bufsize;
if (str1 == NULL)
return NULL;
/* put str1 to buffer */
len = strlen(str1);
bufsize = len <= STRCONCAT_BUFSIZE ? STRCONCAT_BUFSIZE :
nearest_power(len+1);
temp = t_buffer_get(bufsize);
memcpy(temp, str1, len);
full_len = len;
/* put rest of the strings to buffer */
while ((str = va_arg(args, char *)) != NULL) {
len = strlen(str);
if (len == 0)
continue;
if (bufsize < full_len+len+1) {
bufsize = nearest_power(bufsize+len+1);
temp = t_buffer_reget(temp, bufsize);
}
memcpy(temp+full_len, str, len);
full_len += len;
}
temp[full_len] = '\0';
*ret_len = full_len+1;
return temp;
}
char *p_strconcat(Pool pool, const char *str1, ...)
{
va_list args;
const char *temp;
char *ret;
size_t len;
va_start(args, str1);
temp = temp_strconcat(str1, args, &len);
if (temp == NULL)
ret = NULL;
else {
ret = p_malloc(pool, len);
memcpy(ret, temp, len);
}
va_end(args);
return ret;
}
const char *t_strconcat(const char *str1, ...)
{
va_list args;
const char *ret;
size_t len;
va_start(args, str1);
ret = temp_strconcat(str1, args, &len);
if (ret != NULL)
t_buffer_alloc(len);
va_end(args);
return ret;
}
const char *t_strcut(const char *str, char cutchar)
{
const char *p;
for (p = str; *p != '\0'; p++) {
if (*p == cutchar)
return t_strdup_until(str, p);
}
return str;
}
int is_numeric(const char *str, char end_char)
{
if (*str == '\0' || *str == end_char)
return FALSE;
while (*str != '\0' && *str != end_char) {
if (!i_isdigit(*str))
return FALSE;
str++;
}
return TRUE;
}
char *str_ucase(char *str)
{
char *p;
for (p = str; *p != '\0'; p++)
*p = i_toupper(*p);
return str;
}
char *str_lcase(char *str)
{
char *p;
for (p = str; *p != '\0'; p++)
*p = i_tolower(*p);
return str;
}
char *i_strtoken(char **str, char delim)
{
char *ret;
if (*str == NULL || **str == '\0')
return NULL;
ret = *str;
while (**str != '\0') {
if (**str == delim) {
**str = '\0';
(*str)++;
break;
}
(*str)++;
}
return ret;
}
void string_remove_escapes(char *str)
{
char *dest;
for (dest = str; *str != '\0'; str++) {
if (*str != '\\' || str[1] == '\0')
*dest++ = *str;
}
*dest = '\0';
}
int strarray_length(char *const array[])
{
int len;
len = 0;
while (*array) {
len++;
array++;
}
return len;
}
int strarray_find(char *const array[], const char *item)
{
int index;
i_assert(item != NULL);
for (index = 0; *array != NULL; index++, array++) {
if (strcasecmp(*array, item) == 0)
return index;
}
return -1;
}
char *const *t_strsplit(const char *data, const char *separators)
{
char **array;
char *str;
size_t alloc_len, len;
i_assert(*separators != '\0');
len = strlen(data)+1;
str = t_malloc(len);
memcpy(str, data, len);
alloc_len = 20;
array = t_buffer_get(sizeof(const char *) * alloc_len);
array[0] = str; len = 1;
while (*str != '\0') {
if (strchr(separators, *str) != NULL) {
/* separator found */
if (len+1 >= alloc_len) {
alloc_len *= 2;
array = t_buffer_reget(array,
sizeof(const char *) *
alloc_len);
}
*str = '\0';
array[len++] = str+1;
}
str++;
}
array[len] = NULL;
t_buffer_alloc(sizeof(const char *) * (len+1));
return (char *const *) array;
}
const char *t_strjoin_replace(char *const args[], char separator,
int replacearg, const char *replacedata)
{
const char *arg;
char *data;
size_t alloc_len, arg_len, full_len;
int i;
if (args[0] == NULL)
return NULL;
alloc_len = 512; full_len = 0;
data = t_buffer_get(alloc_len);
for (i = 0; args[i] != NULL; i++) {
arg = i == replacearg ? replacedata : args[i];
arg_len = strlen(arg);
if (full_len + arg_len+1 >= alloc_len) {
alloc_len = nearest_power(full_len + arg_len+1);
data = t_buffer_reget(data, alloc_len);
}
memcpy(data+full_len, arg, arg_len);
full_len += arg_len;
data[full_len++] = separator;
}
data[full_len-1] = '\0';
t_buffer_alloc(full_len);
return data;
}
static size_t dec2str_recurse(char *buffer, size_t pos, size_t size,
largest_t number)
{
if (number == 0)
return 0;
pos = dec2str_recurse(buffer, pos, size-1, number / 10);
if (pos < size)
buffer[pos] = '0' + (number % 10);
return pos + 1;
}
void dec2str(char *buffer, size_t size, largest_t number)
{
size_t pos;
if (size == 0)
return;
pos = dec2str_recurse(buffer, 0, size, number);
if (pos == 0 && size > 1) {
/* we wrote nothing, because number is 0 */
buffer[0] = '0';
pos++;
}
buffer[pos < size ? pos : size-1] = '\0';
}