2003-04-14 11:36:19 +04:00
/*
* NOTE : If you change this file , please merge it into rsync , samba , etc .
*/
1998-10-31 07:35:03 +03:00
/*
* Copyright Patrick Powell 1995
* This code is based on code written by Patrick Powell ( papowell @ astart . com )
* It may be used for any purpose as long as this notice remains intact
* on all source code distributions
1998-10-27 02:40:33 +03:00
*/
/**************************************************************
* Original :
* Patrick Powell Tue Apr 11 09 : 48 : 21 PDT 1995
* A bombproof version of doprnt ( dopr ) included .
* Sigh . This sort of thing is always nasty do deal with . Note that
* the version here does not include floating point . . .
*
* snprintf ( ) is used instead of sprintf ( ) as it does limit checks
* for string length . This covers a nasty loophole .
*
* The other functions are there to prevent NULL pointers from
* causing nast effects .
*
* More Recently :
* Brandon Long < blong @ fiction . net > 9 / 15 / 96 for mutt 0.43
* This was ugly . It is still ugly . I opted out of floating point
* numbers , but the formatter understands just about everything
* from the normal C string format , at least as far as I can tell from
* the Solaris 2.5 printf ( 3 S ) man page .
*
* Brandon Long < blong @ fiction . net > 10 / 22 / 97 for mutt 0.87 .1
* Ok , added some minimal floating point support , which means this
* probably requires libm on most operating systems . Don ' t yet
* support the exponent ( e , E ) and sigfig ( g , G ) . Also , fmtint ( )
* was pretty badly broken , it just wasn ' t being exercised in ways
2017-02-17 22:54:58 +03:00
* which showed it , so that ' s been fixed . Also , formatted the code
1998-10-27 02:40:33 +03:00
* to mutt conventions , and removed dead code left over from the
* original . Also , there is now a builtin - test , just compile with :
* gcc - DTEST_SNPRINTF - o snprintf snprintf . c - lm
* and run snprintf for results .
*
* Thomas Roessler < roessler @ guug . de > 01 / 27 / 98 for mutt 0.89 i
* The PGP code was using unsigned hexadecimal formats .
* Unfortunately , unsigned formats simply didn ' t work .
*
* Michael Elkins < me @ cs . hmc . edu > 03 / 05 / 98 for mutt 0.90 .8
* The original code assumed that both snprintf ( ) and vsnprintf ( ) were
* missing . Some systems only have snprintf ( ) but not vsnprintf ( ) , so
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF .
*
1998-11-21 16:20:31 +03:00
* Andrew Tridgell ( tridge @ samba . org ) Oct 1998
1998-10-28 07:07:51 +03:00
* fixed handling of % .0f
* added test for HAVE_LONG_DOUBLE
*
2001-04-27 06:45:08 +04:00
* tridge @ samba . org , idra @ samba . org , April 2001
* got rid of fcvt code ( twas buggy and made testing harder )
* added C99 semantics
*
2003-04-14 11:36:19 +04:00
* date : 2002 / 12 / 19 19 : 56 : 31 ; author : herb ; state : Exp ; lines : + 2 - 0
* actually print args for % g and % e
*
* date : 2002 / 06 / 03 13 : 37 : 52 ; author : jmcd ; state : Exp ; lines : + 8 - 0
* Since includes . h isn ' t included here , VA_COPY has to be defined here . I don ' t
* see any include file that is guaranteed to be here , so I ' m defining it
* locally . Fixes AIX and Solaris builds .
*
* date : 2002 / 06 / 03 03 : 07 : 24 ; author : tridge ; state : Exp ; lines : + 5 - 13
* put the ifdef for HAVE_VA_COPY in one place rather than in lots of
* functions
*
* date : 2002 / 05 / 17 14 : 51 : 22 ; author : jmcd ; state : Exp ; lines : + 21 - 4
* Fix usage of va_list passed as an arg . Use __va_copy before using it
* when it exists .
*
* date : 2002 / 04 / 16 22 : 38 : 04 ; author : idra ; state : Exp ; lines : + 20 - 14
* Fix incorrect zpadlen handling in fmtfp .
* Thanks to Ollie Oldham < ollie . oldham @ metro - optix . com > for spotting it .
* few mods to make it easier to compile the tests .
* addedd the " Ollie " test to the floating point ones .
*
* Martin Pool ( mbp @ samba . org ) April 2003
* Remove NO_CONFIG_H so that the test case can be built within a source
* tree with less trouble .
* Remove unnecessary SAFE_FREE ( ) definition .
2003-05-12 22:12:31 +04:00
*
* Martin Pool ( mbp @ samba . org ) May 2003
* Put in a prototype for dummy_snprintf ( ) to quiet compiler warnings .
*
* Move # endif to make sure VA_COPY , LDOUBLE , etc are defined even
* if the C library has some snprintf functions already .
2005-11-19 04:14:05 +03:00
*
2006-02-07 03:50:38 +03:00
* Darren Tucker ( dtucker @ zip . com . au ) 2005
2005-11-19 04:14:05 +03:00
* Fix bug allowing read overruns of the source string with " %.*s "
* Usually harmless unless the read runs outside the process ' allocation
* ( eg if your malloc does guard pages ) in which case it will segfault .
* From OpenSSH . Also added test for same .
2006-02-07 03:50:38 +03:00
*
* Simo Sorce ( idra @ samba . org ) Jan 2006
*
* Add support for position independent parameters
* fix fmtstr now it conforms to sprintf wrt min . max
*
1998-10-27 02:40:33 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-09-19 02:49:20 +04:00
# include "replace.h"
# include "system/locale.h"
2002-07-15 14:35:28 +04:00
# ifdef TEST_SNPRINTF /* need math library headers for testing */
2003-04-14 11:36:19 +04:00
/* In test mode, we pretend that this system doesn't have any snprintf
* functions , regardless of what config . h says . */
# undef HAVE_SNPRINTF
# undef HAVE_VSNPRINTF
# undef HAVE_C99_VSNPRINTF
# undef HAVE_ASPRINTF
# undef HAVE_VASPRINTF
# include <math.h>
# endif /* TEST_SNPRINTF */
1998-10-27 02:40:33 +03:00
2001-04-28 16:27:37 +04:00
# if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF)
/* only include stdio.h if we are not re-defining snprintf or vsnprintf */
# include <stdio.h>
/* make the compiler happy with an empty file */
2003-05-12 22:12:31 +04:00
void dummy_snprintf ( void ) ;
2001-04-28 16:27:37 +04:00
void dummy_snprintf ( void ) { }
2003-05-12 22:12:31 +04:00
# endif /* HAVE_SNPRINTF, etc */
1998-10-27 02:40:33 +03:00
2006-09-19 02:49:20 +04:00
/* yes this really must be a ||. Don't muck with this (tridge) */
# if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
1998-10-27 03:07:06 +03:00
# ifdef HAVE_LONG_DOUBLE
# define LDOUBLE long double
# else
# define LDOUBLE double
# endif
1999-03-25 23:21:01 +03:00
# ifdef HAVE_LONG_LONG
# define LLONG long long
# else
# define LLONG long
# endif
2002-07-15 14:35:28 +04:00
# ifndef VA_COPY
# ifdef HAVE_VA_COPY
2003-10-24 20:08:03 +04:00
# define VA_COPY(dest, src) va_copy(dest, src)
# else
# ifdef HAVE___VA_COPY
2002-07-15 14:35:28 +04:00
# define VA_COPY(dest, src) __va_copy(dest, src)
# else
# define VA_COPY(dest, src) (dest) = (src)
# endif
2003-10-24 20:08:03 +04:00
# endif
2002-07-15 14:35:28 +04:00
1998-10-27 02:40:33 +03:00
/*
* dopr ( ) : poor man ' s version of doprintf
*/
/* format read states */
# define DP_S_DEFAULT 0
# define DP_S_FLAGS 1
# define DP_S_MIN 2
# define DP_S_DOT 3
# define DP_S_MAX 4
# define DP_S_MOD 5
# define DP_S_CONV 6
# define DP_S_DONE 7
/* format flags - Bits */
# define DP_F_MINUS (1 << 0)
# define DP_F_PLUS (1 << 1)
# define DP_F_SPACE (1 << 2)
# define DP_F_NUM (1 << 3)
# define DP_F_ZERO (1 << 4)
# define DP_F_UP (1 << 5)
# define DP_F_UNSIGNED (1 << 6)
/* Conversion Flags */
2006-02-07 03:50:38 +03:00
# define DP_C_CHAR 1
# define DP_C_SHORT 2
# define DP_C_LONG 3
# define DP_C_LDOUBLE 4
# define DP_C_LLONG 5
2006-09-04 23:53:30 +04:00
# define DP_C_SIZET 6
2006-02-07 03:50:38 +03:00
/* Chunk types */
# define CNK_FMT_STR 0
# define CNK_INT 1
# define CNK_OCTAL 2
# define CNK_UINT 3
# define CNK_HEX 4
# define CNK_FLOAT 5
# define CNK_CHAR 6
# define CNK_STRING 7
# define CNK_PTR 8
# define CNK_NUM 9
# define CNK_PRCNT 10
1998-10-27 02:40:33 +03:00
2000-07-06 05:30:41 +04:00
# define char_to_int(p) ((p)- '0')
2001-04-27 06:45:08 +04:00
# ifndef MAX
2000-07-06 05:30:41 +04:00
# define MAX(p,q) (((p) >= (q)) ? (p) : (q))
2001-04-27 06:45:08 +04:00
# endif
1998-10-27 02:40:33 +03:00
2006-02-07 03:50:38 +03:00
struct pr_chunk {
int type ; /* chunk type */
int num ; /* parameter number */
int min ;
int max ;
int flags ;
int cflags ;
int start ;
int len ;
LLONG value ;
LDOUBLE fvalue ;
char * strvalue ;
void * pnum ;
struct pr_chunk * min_star ;
struct pr_chunk * max_star ;
struct pr_chunk * next ;
} ;
struct pr_chunk_x {
struct pr_chunk * * chunks ;
int num ;
} ;
2006-09-19 02:49:20 +04:00
static int dopr ( char * buffer , size_t maxlen , const char * format ,
2003-05-14 05:10:46 +04:00
va_list args_in ) ;
static void fmtstr ( char * buffer , size_t * currlen , size_t maxlen ,
char * value , int flags , int min , int max ) ;
static void fmtint ( char * buffer , size_t * currlen , size_t maxlen ,
2006-08-21 21:58:41 +04:00
LLONG value , int base , int min , int max , int flags ) ;
2003-05-14 05:10:46 +04:00
static void fmtfp ( char * buffer , size_t * currlen , size_t maxlen ,
LDOUBLE fvalue , int min , int max , int flags ) ;
static void dopr_outch ( char * buffer , size_t * currlen , size_t maxlen , char c ) ;
2006-02-07 03:50:38 +03:00
static struct pr_chunk * new_chunk ( void ) ;
static int add_cnk_list_entry ( struct pr_chunk_x * * list ,
int max_num , struct pr_chunk * chunk ) ;
2003-05-14 05:10:46 +04:00
2006-09-19 02:49:20 +04:00
static int dopr ( char * buffer , size_t maxlen , const char * format , va_list args_in )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
char ch ;
int state ;
2006-02-07 03:50:38 +03:00
int pflag ;
int pnum ;
int pfirst ;
2001-04-27 06:45:08 +04:00
size_t currlen ;
2002-07-15 14:35:28 +04:00
va_list args ;
2006-02-07 03:50:38 +03:00
const char * base ;
struct pr_chunk * chunks = NULL ;
struct pr_chunk * cnk = NULL ;
struct pr_chunk_x * clist = NULL ;
int max_pos ;
2006-09-19 02:49:20 +04:00
int ret = - 1 ;
2002-07-15 14:35:28 +04:00
VA_COPY ( args , args_in ) ;
2006-02-07 03:50:38 +03:00
2001-04-27 06:45:08 +04:00
state = DP_S_DEFAULT ;
2006-02-07 03:50:38 +03:00
pfirst = 1 ;
pflag = 0 ;
pnum = 0 ;
max_pos = 0 ;
base = format ;
1998-10-27 02:40:33 +03:00
ch = * format + + ;
2001-04-27 06:45:08 +04:00
2006-02-07 03:50:38 +03:00
/* retrieve the string structure as chunks */
2001-04-27 06:45:08 +04:00
while ( state ! = DP_S_DONE ) {
if ( ch = = ' \0 ' )
state = DP_S_DONE ;
switch ( state ) {
case DP_S_DEFAULT :
2006-02-07 03:50:38 +03:00
if ( cnk ) {
cnk - > next = new_chunk ( ) ;
cnk = cnk - > next ;
} else {
cnk = new_chunk ( ) ;
}
if ( ! cnk ) goto done ;
if ( ! chunks ) chunks = cnk ;
if ( ch = = ' % ' ) {
2001-04-27 06:45:08 +04:00
state = DP_S_FLAGS ;
2006-02-07 03:50:38 +03:00
ch = * format + + ;
} else {
cnk - > type = CNK_FMT_STR ;
cnk - > start = format - base - 1 ;
while ( ( ch ! = ' \0 ' ) & & ( ch ! = ' % ' ) ) ch = * format + + ;
cnk - > len = format - base - cnk - > start - 1 ;
}
2001-04-27 06:45:08 +04:00
break ;
case DP_S_FLAGS :
switch ( ch ) {
case ' - ' :
2006-02-07 03:50:38 +03:00
cnk - > flags | = DP_F_MINUS ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
break ;
case ' + ' :
2006-02-07 03:50:38 +03:00
cnk - > flags | = DP_F_PLUS ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
break ;
case ' ' :
2006-02-07 03:50:38 +03:00
cnk - > flags | = DP_F_SPACE ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
break ;
case ' # ' :
2006-02-07 03:50:38 +03:00
cnk - > flags | = DP_F_NUM ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
break ;
case ' 0 ' :
2006-02-07 03:50:38 +03:00
cnk - > flags | = DP_F_ZERO ;
ch = * format + + ;
break ;
case ' I ' :
/* internationalization not supported yet */
2001-04-27 06:45:08 +04:00
ch = * format + + ;
break ;
default :
state = DP_S_MIN ;
break ;
}
break ;
case DP_S_MIN :
if ( isdigit ( ( unsigned char ) ch ) ) {
2006-02-07 03:50:38 +03:00
cnk - > min = 10 * cnk - > min + char_to_int ( ch ) ;
ch = * format + + ;
} else if ( ch = = ' $ ' ) {
if ( ! pfirst & & ! pflag ) {
/* parameters must be all positioned or none */
goto done ;
}
if ( pfirst ) {
pfirst = 0 ;
pflag = 1 ;
}
if ( cnk - > min = = 0 ) /* what ?? */
goto done ;
cnk - > num = cnk - > min ;
cnk - > min = 0 ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
} else if ( ch = = ' * ' ) {
2006-02-07 03:50:38 +03:00
if ( pfirst ) pfirst = 0 ;
cnk - > min_star = new_chunk ( ) ;
if ( ! cnk - > min_star ) /* out of memory :-( */
goto done ;
cnk - > min_star - > type = CNK_INT ;
if ( pflag ) {
int num ;
ch = * format + + ;
if ( ! isdigit ( ( unsigned char ) ch ) ) {
/* parameters must be all positioned or none */
goto done ;
}
for ( num = 0 ; isdigit ( ( unsigned char ) ch ) ; ch = * format + + ) {
num = 10 * num + char_to_int ( ch ) ;
}
cnk - > min_star - > num = num ;
if ( ch ! = ' $ ' ) /* what ?? */
goto done ;
} else {
cnk - > min_star - > num = + + pnum ;
}
max_pos = add_cnk_list_entry ( & clist , max_pos , cnk - > min_star ) ;
if ( max_pos = = 0 ) /* out of memory :-( */
goto done ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
state = DP_S_DOT ;
} else {
2006-02-07 03:50:38 +03:00
if ( pfirst ) pfirst = 0 ;
2001-04-27 06:45:08 +04:00
state = DP_S_DOT ;
}
break ;
case DP_S_DOT :
if ( ch = = ' . ' ) {
state = DP_S_MAX ;
ch = * format + + ;
} else {
state = DP_S_MOD ;
}
break ;
case DP_S_MAX :
if ( isdigit ( ( unsigned char ) ch ) ) {
2006-02-07 03:50:38 +03:00
if ( cnk - > max < 0 )
cnk - > max = 0 ;
cnk - > max = 10 * cnk - > max + char_to_int ( ch ) ;
ch = * format + + ;
} else if ( ch = = ' $ ' ) {
if ( ! pfirst & & ! pflag ) {
/* parameters must be all positioned or none */
goto done ;
}
if ( cnk - > max < = 0 ) /* what ?? */
goto done ;
cnk - > num = cnk - > max ;
cnk - > max = - 1 ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
} else if ( ch = = ' * ' ) {
2006-02-07 03:50:38 +03:00
cnk - > max_star = new_chunk ( ) ;
if ( ! cnk - > max_star ) /* out of memory :-( */
goto done ;
cnk - > max_star - > type = CNK_INT ;
if ( pflag ) {
int num ;
ch = * format + + ;
if ( ! isdigit ( ( unsigned char ) ch ) ) {
/* parameters must be all positioned or none */
goto done ;
}
for ( num = 0 ; isdigit ( ( unsigned char ) ch ) ; ch = * format + + ) {
num = 10 * num + char_to_int ( ch ) ;
}
cnk - > max_star - > num = num ;
if ( ch ! = ' $ ' ) /* what ?? */
goto done ;
} else {
cnk - > max_star - > num = + + pnum ;
}
max_pos = add_cnk_list_entry ( & clist , max_pos , cnk - > max_star ) ;
if ( max_pos = = 0 ) /* out of memory :-( */
goto done ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
state = DP_S_MOD ;
} else {
state = DP_S_MOD ;
}
break ;
case DP_S_MOD :
switch ( ch ) {
case ' h ' :
2006-02-07 03:50:38 +03:00
cnk - > cflags = DP_C_SHORT ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
2006-02-07 03:50:38 +03:00
if ( ch = = ' h ' ) {
cnk - > cflags = DP_C_CHAR ;
ch = * format + + ;
}
2001-04-27 06:45:08 +04:00
break ;
case ' l ' :
2006-02-07 03:50:38 +03:00
cnk - > cflags = DP_C_LONG ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
if ( ch = = ' l ' ) { /* It's a long long */
2006-02-07 03:50:38 +03:00
cnk - > cflags = DP_C_LLONG ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
}
break ;
2015-02-11 19:16:50 +03:00
case ' j ' :
cnk - > cflags = DP_C_LLONG ;
ch = * format + + ;
break ;
2001-04-27 06:45:08 +04:00
case ' L ' :
2006-02-07 03:50:38 +03:00
cnk - > cflags = DP_C_LDOUBLE ;
2001-04-27 06:45:08 +04:00
ch = * format + + ;
break ;
2006-09-04 23:53:30 +04:00
case ' z ' :
cnk - > cflags = DP_C_SIZET ;
ch = * format + + ;
break ;
2001-04-27 06:45:08 +04:00
default :
break ;
}
state = DP_S_CONV ;
break ;
case DP_S_CONV :
2006-02-07 03:50:38 +03:00
if ( cnk - > num = = 0 ) cnk - > num = + + pnum ;
max_pos = add_cnk_list_entry ( & clist , max_pos , cnk ) ;
if ( max_pos = = 0 ) /* out of memory :-( */
goto done ;
2001-04-27 06:45:08 +04:00
switch ( ch ) {
case ' d ' :
case ' i ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_INT ;
2001-04-27 06:45:08 +04:00
break ;
case ' o ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_OCTAL ;
cnk - > flags | = DP_F_UNSIGNED ;
2001-04-27 06:45:08 +04:00
break ;
case ' u ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_UINT ;
cnk - > flags | = DP_F_UNSIGNED ;
2001-04-27 06:45:08 +04:00
break ;
case ' X ' :
2006-02-07 03:50:38 +03:00
cnk - > flags | = DP_F_UP ;
2001-04-27 06:45:08 +04:00
case ' x ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_HEX ;
cnk - > flags | = DP_F_UNSIGNED ;
2001-04-27 06:45:08 +04:00
break ;
2006-02-07 03:50:38 +03:00
case ' A ' :
/* hex float not supported yet */
2001-04-27 06:45:08 +04:00
case ' E ' :
case ' G ' :
2006-02-07 03:50:38 +03:00
case ' F ' :
cnk - > flags | = DP_F_UP ;
case ' a ' :
/* hex float not supported yet */
case ' e ' :
case ' f ' :
2001-04-27 06:45:08 +04:00
case ' g ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_FLOAT ;
2001-04-27 06:45:08 +04:00
break ;
case ' c ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_CHAR ;
2001-04-27 06:45:08 +04:00
break ;
case ' s ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_STRING ;
2001-04-27 06:45:08 +04:00
break ;
case ' p ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_PTR ;
2009-12-19 21:43:52 +03:00
cnk - > flags | = DP_F_UNSIGNED ;
2001-04-27 06:45:08 +04:00
break ;
case ' n ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_NUM ;
2001-04-27 06:45:08 +04:00
break ;
case ' % ' :
2006-02-07 03:50:38 +03:00
cnk - > type = CNK_PRCNT ;
2001-04-27 06:45:08 +04:00
break ;
default :
2006-02-07 03:50:38 +03:00
/* Unknown, bail out*/
goto done ;
2001-04-27 06:45:08 +04:00
}
ch = * format + + ;
state = DP_S_DEFAULT ;
break ;
case DP_S_DONE :
break ;
default :
/* hmm? */
break ; /* some picky compilers need this */
}
1999-03-25 23:21:01 +03:00
}
2006-02-07 03:50:38 +03:00
2008-07-14 18:40:36 +04:00
/* retrieve the format arguments */
2006-02-07 03:50:38 +03:00
for ( pnum = 0 ; pnum < max_pos ; pnum + + ) {
int i ;
if ( clist [ pnum ] . num = = 0 ) {
/* ignoring a parameter should not be permitted
* all parameters must be matched at least once
* BUT seem some system ignore this rule . . .
* at least my glibc based system does - - SSS
*/
# ifdef DEBUG_SNPRINTF
printf ( " parameter at position %d not used \n " , pnum + 1 ) ;
# endif
/* eat the parameter */
2007-01-15 22:10:54 +03:00
va_arg ( args , int ) ;
2006-02-07 03:50:38 +03:00
continue ;
}
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
if ( clist [ pnum ] . chunks [ 0 ] - > type ! = clist [ pnum ] . chunks [ i ] - > type ) {
/* nooo noo no!
* all the references to a parameter
* must be of the same type
*/
goto done ;
}
}
cnk = clist [ pnum ] . chunks [ 0 ] ;
switch ( cnk - > type ) {
case CNK_INT :
if ( cnk - > cflags = = DP_C_SHORT )
cnk - > value = va_arg ( args , int ) ;
else if ( cnk - > cflags = = DP_C_LONG )
cnk - > value = va_arg ( args , long int ) ;
else if ( cnk - > cflags = = DP_C_LLONG )
cnk - > value = va_arg ( args , LLONG ) ;
2006-09-04 23:53:30 +04:00
else if ( cnk - > cflags = = DP_C_SIZET )
cnk - > value = va_arg ( args , ssize_t ) ;
2006-02-07 03:50:38 +03:00
else
cnk - > value = va_arg ( args , int ) ;
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
clist [ pnum ] . chunks [ i ] - > value = cnk - > value ;
}
break ;
case CNK_OCTAL :
case CNK_UINT :
case CNK_HEX :
if ( cnk - > cflags = = DP_C_SHORT )
cnk - > value = va_arg ( args , unsigned int ) ;
else if ( cnk - > cflags = = DP_C_LONG )
2006-08-29 13:49:10 +04:00
cnk - > value = ( unsigned long int ) va_arg ( args , unsigned long int ) ;
2006-02-07 03:50:38 +03:00
else if ( cnk - > cflags = = DP_C_LLONG )
cnk - > value = ( LLONG ) va_arg ( args , unsigned LLONG ) ;
2006-09-04 23:53:30 +04:00
else if ( cnk - > cflags = = DP_C_SIZET )
cnk - > value = ( size_t ) va_arg ( args , size_t ) ;
2006-02-07 03:50:38 +03:00
else
2006-08-29 13:49:10 +04:00
cnk - > value = ( unsigned int ) va_arg ( args , unsigned int ) ;
2006-02-07 03:50:38 +03:00
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
clist [ pnum ] . chunks [ i ] - > value = cnk - > value ;
}
break ;
case CNK_FLOAT :
if ( cnk - > cflags = = DP_C_LDOUBLE )
cnk - > fvalue = va_arg ( args , LDOUBLE ) ;
else
cnk - > fvalue = va_arg ( args , double ) ;
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
clist [ pnum ] . chunks [ i ] - > fvalue = cnk - > fvalue ;
}
break ;
case CNK_CHAR :
cnk - > value = va_arg ( args , int ) ;
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
clist [ pnum ] . chunks [ i ] - > value = cnk - > value ;
}
break ;
case CNK_STRING :
cnk - > strvalue = va_arg ( args , char * ) ;
if ( ! cnk - > strvalue ) cnk - > strvalue = " (NULL) " ;
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
clist [ pnum ] . chunks [ i ] - > strvalue = cnk - > strvalue ;
}
break ;
case CNK_PTR :
2006-09-19 02:49:20 +04:00
cnk - > strvalue = va_arg ( args , void * ) ;
2006-02-07 03:50:38 +03:00
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
clist [ pnum ] . chunks [ i ] - > strvalue = cnk - > strvalue ;
}
break ;
case CNK_NUM :
if ( cnk - > cflags = = DP_C_CHAR )
cnk - > pnum = va_arg ( args , char * ) ;
else if ( cnk - > cflags = = DP_C_SHORT )
cnk - > pnum = va_arg ( args , short int * ) ;
else if ( cnk - > cflags = = DP_C_LONG )
cnk - > pnum = va_arg ( args , long int * ) ;
else if ( cnk - > cflags = = DP_C_LLONG )
cnk - > pnum = va_arg ( args , LLONG * ) ;
2006-09-04 23:53:30 +04:00
else if ( cnk - > cflags = = DP_C_SIZET )
cnk - > pnum = va_arg ( args , ssize_t * ) ;
2006-02-07 03:50:38 +03:00
else
cnk - > pnum = va_arg ( args , int * ) ;
for ( i = 1 ; i < clist [ pnum ] . num ; i + + ) {
clist [ pnum ] . chunks [ i ] - > pnum = cnk - > pnum ;
}
break ;
case CNK_PRCNT :
break ;
default :
/* what ?? */
goto done ;
}
}
/* print out the actual string from chunks */
currlen = 0 ;
cnk = chunks ;
while ( cnk ) {
int len , min , max ;
if ( cnk - > min_star ) min = cnk - > min_star - > value ;
else min = cnk - > min ;
if ( cnk - > max_star ) max = cnk - > max_star - > value ;
else max = cnk - > max ;
switch ( cnk - > type ) {
case CNK_FMT_STR :
if ( maxlen ! = 0 & & maxlen > currlen ) {
if ( maxlen > ( currlen + cnk - > len ) ) len = cnk - > len ;
else len = maxlen - currlen ;
memcpy ( & ( buffer [ currlen ] ) , & ( base [ cnk - > start ] ) , len ) ;
}
currlen + = cnk - > len ;
break ;
case CNK_INT :
case CNK_UINT :
fmtint ( buffer , & currlen , maxlen , cnk - > value , 10 , min , max , cnk - > flags ) ;
break ;
case CNK_OCTAL :
fmtint ( buffer , & currlen , maxlen , cnk - > value , 8 , min , max , cnk - > flags ) ;
break ;
case CNK_HEX :
fmtint ( buffer , & currlen , maxlen , cnk - > value , 16 , min , max , cnk - > flags ) ;
break ;
case CNK_FLOAT :
fmtfp ( buffer , & currlen , maxlen , cnk - > fvalue , min , max , cnk - > flags ) ;
break ;
case CNK_CHAR :
dopr_outch ( buffer , & currlen , maxlen , cnk - > value ) ;
break ;
case CNK_STRING :
if ( max = = - 1 ) {
max = strlen ( cnk - > strvalue ) ;
}
fmtstr ( buffer , & currlen , maxlen , cnk - > strvalue , cnk - > flags , min , max ) ;
break ;
case CNK_PTR :
fmtint ( buffer , & currlen , maxlen , ( long ) ( cnk - > strvalue ) , 16 , min , max , cnk - > flags ) ;
break ;
case CNK_NUM :
if ( cnk - > cflags = = DP_C_CHAR )
* ( ( char * ) ( cnk - > pnum ) ) = ( char ) currlen ;
else if ( cnk - > cflags = = DP_C_SHORT )
* ( ( short int * ) ( cnk - > pnum ) ) = ( short int ) currlen ;
else if ( cnk - > cflags = = DP_C_LONG )
* ( ( long int * ) ( cnk - > pnum ) ) = ( long int ) currlen ;
else if ( cnk - > cflags = = DP_C_LLONG )
* ( ( LLONG * ) ( cnk - > pnum ) ) = ( LLONG ) currlen ;
2006-09-04 23:53:30 +04:00
else if ( cnk - > cflags = = DP_C_SIZET )
* ( ( ssize_t * ) ( cnk - > pnum ) ) = ( ssize_t ) currlen ;
2006-02-07 03:50:38 +03:00
else
* ( ( int * ) ( cnk - > pnum ) ) = ( int ) currlen ;
break ;
case CNK_PRCNT :
dopr_outch ( buffer , & currlen , maxlen , ' % ' ) ;
break ;
default :
/* what ?? */
goto done ;
}
cnk = cnk - > next ;
}
2001-04-27 06:45:08 +04:00
if ( maxlen ! = 0 ) {
if ( currlen < maxlen - 1 )
buffer [ currlen ] = ' \0 ' ;
else if ( maxlen > 0 )
buffer [ maxlen - 1 ] = ' \0 ' ;
}
2006-02-07 03:50:38 +03:00
ret = currlen ;
done :
2007-02-06 08:58:35 +03:00
va_end ( args ) ;
2006-02-07 03:50:38 +03:00
while ( chunks ) {
cnk = chunks - > next ;
free ( chunks ) ;
chunks = cnk ;
}
if ( clist ) {
for ( pnum = 0 ; pnum < max_pos ; pnum + + ) {
if ( clist [ pnum ] . chunks ) free ( clist [ pnum ] . chunks ) ;
}
free ( clist ) ;
}
return ret ;
1998-10-27 02:40:33 +03:00
}
2001-04-27 06:45:08 +04:00
static void fmtstr ( char * buffer , size_t * currlen , size_t maxlen ,
1998-10-27 02:40:33 +03:00
char * value , int flags , int min , int max )
{
2001-04-27 06:45:08 +04:00
int padlen , strln ; /* amount to pad */
int cnt = 0 ;
# ifdef DEBUG_SNPRINTF
printf ( " fmtstr min=%d max=%d s=[%s] \n " , min , max , value ) ;
# endif
if ( value = = 0 ) {
value = " <NULL> " ;
}
2005-11-19 04:14:05 +03:00
for ( strln = 0 ; strln < max & & value [ strln ] ; + + strln ) ; /* strlen */
2001-04-27 06:45:08 +04:00
padlen = min - strln ;
if ( padlen < 0 )
padlen = 0 ;
if ( flags & DP_F_MINUS )
padlen = - padlen ; /* Left Justify */
2006-02-07 03:50:38 +03:00
while ( padlen > 0 ) {
2001-04-27 06:45:08 +04:00
dopr_outch ( buffer , currlen , maxlen , ' ' ) ;
- - padlen ;
}
while ( * value & & ( cnt < max ) ) {
dopr_outch ( buffer , currlen , maxlen , * value + + ) ;
+ + cnt ;
}
2006-02-07 03:50:38 +03:00
while ( padlen < 0 ) {
2001-04-27 06:45:08 +04:00
dopr_outch ( buffer , currlen , maxlen , ' ' ) ;
+ + padlen ;
}
1998-10-27 02:40:33 +03:00
}
/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
2001-04-27 06:45:08 +04:00
static void fmtint ( char * buffer , size_t * currlen , size_t maxlen ,
2006-08-21 21:58:41 +04:00
LLONG value , int base , int min , int max , int flags )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
int signvalue = 0 ;
2006-08-21 21:58:41 +04:00
unsigned LLONG uvalue ;
2016-06-03 00:54:05 +03:00
char convert [ 22 + 1 ] ; /* 64-bit value in octal: 22 digits + \0 */
2001-04-27 06:45:08 +04:00
int place = 0 ;
int spadlen = 0 ; /* amount to space pad */
int zpadlen = 0 ; /* amount to zero pad */
int caps = 0 ;
if ( max < 0 )
max = 0 ;
uvalue = value ;
if ( ! ( flags & DP_F_UNSIGNED ) ) {
if ( value < 0 ) {
signvalue = ' - ' ;
uvalue = - value ;
} else {
if ( flags & DP_F_PLUS ) /* Do a sign (+/i) */
signvalue = ' + ' ;
else if ( flags & DP_F_SPACE )
signvalue = ' ' ;
}
}
1998-10-27 02:40:33 +03:00
2001-04-27 06:45:08 +04:00
if ( flags & DP_F_UP ) caps = 1 ; /* Should characters be upper case? */
do {
convert [ place + + ] =
( caps ? " 0123456789ABCDEF " : " 0123456789abcdef " )
[ uvalue % ( unsigned ) base ] ;
uvalue = ( uvalue / ( unsigned ) base ) ;
2016-06-03 00:54:05 +03:00
} while ( uvalue & & ( place < sizeof ( convert ) ) ) ;
if ( place = = sizeof ( convert ) ) place - - ;
2001-04-27 06:45:08 +04:00
convert [ place ] = 0 ;
zpadlen = max - place ;
spadlen = min - MAX ( max , place ) - ( signvalue ? 1 : 0 ) ;
if ( zpadlen < 0 ) zpadlen = 0 ;
if ( spadlen < 0 ) spadlen = 0 ;
if ( flags & DP_F_ZERO ) {
zpadlen = MAX ( zpadlen , spadlen ) ;
spadlen = 0 ;
}
if ( flags & DP_F_MINUS )
spadlen = - spadlen ; /* Left Justifty */
1998-10-27 02:40:33 +03:00
# ifdef DEBUG_SNPRINTF
2001-04-27 06:45:08 +04:00
printf ( " zpad: %d, spad: %d, min: %d, max: %d, place: %d \n " ,
zpadlen , spadlen , min , max , place ) ;
1998-10-27 02:40:33 +03:00
# endif
2001-04-27 06:45:08 +04:00
/* Spaces */
while ( spadlen > 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' ' ) ;
- - spadlen ;
}
/* Sign */
if ( signvalue )
dopr_outch ( buffer , currlen , maxlen , signvalue ) ;
/* Zeros */
if ( zpadlen > 0 ) {
while ( zpadlen > 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' 0 ' ) ;
- - zpadlen ;
}
}
/* Digits */
while ( place > 0 )
dopr_outch ( buffer , currlen , maxlen , convert [ - - place ] ) ;
1998-10-27 02:40:33 +03:00
2001-04-27 06:45:08 +04:00
/* Left Justified spaces */
while ( spadlen < 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' ' ) ;
+ + spadlen ;
}
1998-10-27 02:40:33 +03:00
}
2001-04-27 06:45:08 +04:00
static LDOUBLE abs_val ( LDOUBLE value )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
LDOUBLE result = value ;
1998-10-27 02:40:33 +03:00
2001-04-27 06:45:08 +04:00
if ( value < 0 )
result = - value ;
return result ;
1998-10-27 02:40:33 +03:00
}
2001-04-27 06:45:08 +04:00
static LDOUBLE POW10 ( int exp )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
LDOUBLE result = 1 ;
while ( exp ) {
result * = 10 ;
exp - - ;
}
1998-10-27 02:40:33 +03:00
2001-04-27 06:45:08 +04:00
return result ;
1998-10-27 02:40:33 +03:00
}
2001-04-27 06:45:08 +04:00
static LLONG ROUND ( LDOUBLE value )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
LLONG intpart ;
1998-10-27 02:40:33 +03:00
2001-04-27 06:45:08 +04:00
intpart = ( LLONG ) value ;
value = value - intpart ;
if ( value > = 0.5 ) intpart + + ;
return intpart ;
1998-10-27 02:40:33 +03:00
}
2001-05-06 19:19:25 +04:00
/* a replacement for modf that doesn't need the math library. Should
be portable , but slow */
static double my_modf ( double x0 , double * iptr )
{
int i ;
2006-09-19 02:49:20 +04:00
LLONG l = 0 ;
2001-05-06 19:19:25 +04:00
double x = x0 ;
double f = 1.0 ;
for ( i = 0 ; i < 100 ; i + + ) {
l = ( long ) x ;
if ( l < = ( x + 1 ) & & l > = ( x - 1 ) ) break ;
x * = 0.1 ;
f * = 10.0 ;
}
if ( i = = 100 ) {
/* yikes! the number is beyond what we can handle. What do we do? */
( * iptr ) = 0 ;
return 0 ;
}
if ( i ! = 0 ) {
double i2 ;
double ret ;
ret = my_modf ( x0 - l * f , & i2 ) ;
( * iptr ) = l * f + i2 ;
return ret ;
}
( * iptr ) = l ;
return x - ( * iptr ) ;
}
1998-10-27 02:40:33 +03:00
static void fmtfp ( char * buffer , size_t * currlen , size_t maxlen ,
1998-10-27 03:07:06 +03:00
LDOUBLE fvalue , int min , int max , int flags )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
int signvalue = 0 ;
double ufvalue ;
char iconvert [ 311 ] ;
char fconvert [ 311 ] ;
int iplace = 0 ;
int fplace = 0 ;
int padlen = 0 ; /* amount to pad */
int zpadlen = 0 ;
int caps = 0 ;
2003-05-14 07:32:20 +04:00
int idx ;
2001-04-27 06:45:08 +04:00
double intpart ;
double fracpart ;
double temp ;
1998-10-27 02:40:33 +03:00
2001-04-27 06:45:08 +04:00
/*
* AIX manpage says the default is 0 , but Solaris says the default
* is 6 , and sprintf on AIX defaults to 6
*/
if ( max < 0 )
max = 6 ;
ufvalue = abs_val ( fvalue ) ;
if ( fvalue < 0 ) {
signvalue = ' - ' ;
} else {
if ( flags & DP_F_PLUS ) { /* Do a sign (+/i) */
signvalue = ' + ' ;
} else {
if ( flags & DP_F_SPACE )
signvalue = ' ' ;
}
}
1998-10-27 02:40:33 +03:00
#if 0
2001-04-27 06:45:08 +04:00
if ( flags & DP_F_UP ) caps = 1 ; /* Should characters be upper case? */
1998-10-27 02:40:33 +03:00
# endif
2001-04-27 06:45:08 +04:00
#if 0
if ( max = = 0 ) ufvalue + = 0.5 ; /* if max = 0 we must round */
1998-10-27 02:40:33 +03:00
# endif
2001-04-27 06:45:08 +04:00
/*
2006-02-07 03:50:38 +03:00
* Sorry , we only support 9 digits past the decimal because of our
2001-04-27 06:45:08 +04:00
* conversion method
*/
2006-02-07 03:50:38 +03:00
if ( max > 9 )
max = 9 ;
2001-04-27 06:45:08 +04:00
/* We "cheat" by converting the fractional part to integer by
* multiplying by a factor of 10
*/
temp = ufvalue ;
2001-05-06 19:19:25 +04:00
my_modf ( temp , & intpart ) ;
2001-04-27 06:45:08 +04:00
fracpart = ROUND ( ( POW10 ( max ) ) * ( ufvalue - intpart ) ) ;
if ( fracpart > = POW10 ( max ) ) {
intpart + + ;
fracpart - = POW10 ( max ) ;
2000-02-21 21:47:21 +03:00
}
1999-12-13 16:27:58 +03:00
2001-04-27 06:45:08 +04:00
/* Convert integer part */
do {
2002-07-15 14:35:28 +04:00
temp = intpart * 0.1 ;
my_modf ( temp , & intpart ) ;
2003-05-14 07:32:20 +04:00
idx = ( int ) ( ( temp - intpart + 0.05 ) * 10.0 ) ;
/* idx = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */
/* printf ("%llf, %f, %x\n", temp, intpart, idx); */
2001-04-27 06:45:08 +04:00
iconvert [ iplace + + ] =
2003-05-14 07:32:20 +04:00
( caps ? " 0123456789ABCDEF " : " 0123456789abcdef " ) [ idx ] ;
2001-04-27 06:45:08 +04:00
} while ( intpart & & ( iplace < 311 ) ) ;
if ( iplace = = 311 ) iplace - - ;
iconvert [ iplace ] = 0 ;
/* Convert fractional part */
if ( fracpart )
{
do {
2002-07-15 14:35:28 +04:00
temp = fracpart * 0.1 ;
my_modf ( temp , & fracpart ) ;
2003-05-14 07:32:20 +04:00
idx = ( int ) ( ( temp - fracpart + 0.05 ) * 10.0 ) ;
/* idx = (int) ((((temp/10) -fracpart) +0.05) *10); */
/* printf ("%lf, %lf, %ld\n", temp, fracpart, idx ); */
2001-04-27 06:45:08 +04:00
fconvert [ fplace + + ] =
2003-05-14 07:32:20 +04:00
( caps ? " 0123456789ABCDEF " : " 0123456789abcdef " ) [ idx ] ;
2001-04-27 06:45:08 +04:00
} while ( fracpart & & ( fplace < 311 ) ) ;
if ( fplace = = 311 ) fplace - - ;
}
fconvert [ fplace ] = 0 ;
1999-12-13 16:27:58 +03:00
2001-04-27 06:45:08 +04:00
/* -1 for decimal point, another -1 if we are printing a sign */
padlen = min - iplace - max - 1 - ( ( signvalue ) ? 1 : 0 ) ;
zpadlen = max - fplace ;
if ( zpadlen < 0 ) zpadlen = 0 ;
if ( padlen < 0 )
padlen = 0 ;
if ( flags & DP_F_MINUS )
padlen = - padlen ; /* Left Justifty */
if ( ( flags & DP_F_ZERO ) & & ( padlen > 0 ) ) {
if ( signvalue ) {
dopr_outch ( buffer , currlen , maxlen , signvalue ) ;
- - padlen ;
signvalue = 0 ;
}
while ( padlen > 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' 0 ' ) ;
- - padlen ;
}
}
while ( padlen > 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' ' ) ;
- - padlen ;
}
if ( signvalue )
dopr_outch ( buffer , currlen , maxlen , signvalue ) ;
while ( iplace > 0 )
dopr_outch ( buffer , currlen , maxlen , iconvert [ - - iplace ] ) ;
1998-10-28 07:07:51 +03:00
# ifdef DEBUG_SNPRINTF
2001-04-27 06:45:08 +04:00
printf ( " fmtfp: fplace=%d zpadlen=%d \n " , fplace , zpadlen ) ;
1998-10-28 07:07:51 +03:00
# endif
2001-04-27 06:45:08 +04:00
/*
* Decimal point . This should probably use locale to find the correct
* char to print out .
*/
if ( max > 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' . ' ) ;
2002-07-15 14:35:28 +04:00
while ( zpadlen > 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' 0 ' ) ;
- - zpadlen ;
}
2001-04-27 06:45:08 +04:00
while ( fplace > 0 )
dopr_outch ( buffer , currlen , maxlen , fconvert [ - - fplace ] ) ;
}
while ( padlen < 0 ) {
dopr_outch ( buffer , currlen , maxlen , ' ' ) ;
+ + padlen ;
}
1998-10-27 02:40:33 +03:00
}
2001-04-27 06:45:08 +04:00
static void dopr_outch ( char * buffer , size_t * currlen , size_t maxlen , char c )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
if ( * currlen < maxlen ) {
buffer [ ( * currlen ) ] = c ;
}
( * currlen ) + + ;
1998-10-27 02:40:33 +03:00
}
2006-02-07 03:50:38 +03:00
static struct pr_chunk * new_chunk ( void ) {
2006-02-21 18:47:19 +03:00
struct pr_chunk * new_c = ( struct pr_chunk * ) malloc ( sizeof ( struct pr_chunk ) ) ;
2006-08-21 21:58:41 +04:00
if ( ! new_c )
2006-02-21 18:47:19 +03:00
return NULL ;
new_c - > type = 0 ;
new_c - > num = 0 ;
new_c - > min = 0 ;
new_c - > min_star = NULL ;
new_c - > max = - 1 ;
new_c - > max_star = NULL ;
new_c - > flags = 0 ;
new_c - > cflags = 0 ;
new_c - > start = 0 ;
new_c - > len = 0 ;
new_c - > value = 0 ;
new_c - > fvalue = 0 ;
new_c - > strvalue = NULL ;
new_c - > pnum = NULL ;
new_c - > next = NULL ;
return new_c ;
2006-02-07 03:50:38 +03:00
}
static int add_cnk_list_entry ( struct pr_chunk_x * * list ,
int max_num , struct pr_chunk * chunk ) {
struct pr_chunk_x * l ;
struct pr_chunk * * c ;
int max ;
int cnum ;
int i , pos ;
if ( chunk - > num > max_num ) {
max = chunk - > num ;
if ( * list = = NULL ) {
l = ( struct pr_chunk_x * ) malloc ( sizeof ( struct pr_chunk_x ) * max ) ;
pos = 0 ;
} else {
l = ( struct pr_chunk_x * ) realloc ( * list , sizeof ( struct pr_chunk_x ) * max ) ;
pos = max_num ;
}
if ( l = = NULL ) {
for ( i = 0 ; i < max ; i + + ) {
if ( ( * list ) [ i ] . chunks ) free ( ( * list ) [ i ] . chunks ) ;
}
return 0 ;
}
for ( i = pos ; i < max ; i + + ) {
l [ i ] . chunks = NULL ;
l [ i ] . num = 0 ;
}
} else {
l = * list ;
max = max_num ;
}
i = chunk - > num - 1 ;
cnum = l [ i ] . num + 1 ;
if ( l [ i ] . chunks = = NULL ) {
c = ( struct pr_chunk * * ) malloc ( sizeof ( struct pr_chunk * ) * cnum ) ;
} else {
c = ( struct pr_chunk * * ) realloc ( l [ i ] . chunks , sizeof ( struct pr_chunk * ) * cnum ) ;
}
if ( c = = NULL ) {
for ( i = 0 ; i < max ; i + + ) {
if ( l [ i ] . chunks ) free ( l [ i ] . chunks ) ;
}
return 0 ;
}
c [ l [ i ] . num ] = chunk ;
l [ i ] . chunks = c ;
l [ i ] . num = cnum ;
* list = l ;
return max ;
}
2012-05-13 05:21:34 +04:00
int rep_vsnprintf ( char * str , size_t count , const char * fmt , va_list args )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
return dopr ( str , count , fmt , args ) ;
1998-10-27 02:40:33 +03:00
}
# endif
2001-04-27 06:45:08 +04:00
2003-05-14 05:10:46 +04:00
/* yes this really must be a ||. Don't muck with this (tridge)
2001-12-10 07:35:01 +03:00
*
* The logic for these two is that we need our own definition if the
* OS * either * has no definition of * sprintf , or if it does have one
2003-07-23 07:59:57 +04:00
* that doesn ' t work properly according to the autoconf test .
2001-12-10 07:35:01 +03:00
*/
2003-05-05 07:31:50 +04:00
# if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_VSNPRINTF)
2012-05-13 05:21:34 +04:00
int rep_snprintf ( char * str , size_t count , const char * fmt , . . . )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
size_t ret ;
va_list ap ;
1998-10-27 02:40:33 +03:00
2001-04-27 06:45:08 +04:00
va_start ( ap , fmt ) ;
ret = vsnprintf ( str , count , fmt , ap ) ;
va_end ( ap ) ;
return ret ;
1998-10-27 02:40:33 +03:00
}
2006-09-19 02:49:20 +04:00
# endif
# ifndef HAVE_C99_VSNPRINTF
2012-05-13 05:21:34 +04:00
int rep_printf ( const char * fmt , . . . )
2006-09-19 02:49:20 +04:00
{
va_list ap ;
int ret ;
char * s ;
s = NULL ;
va_start ( ap , fmt ) ;
ret = vasprintf ( & s , fmt , ap ) ;
va_end ( ap ) ;
if ( s ) {
fwrite ( s , 1 , strlen ( s ) , stdout ) ;
}
free ( s ) ;
return ret ;
}
# endif
# ifndef HAVE_C99_VSNPRINTF
2012-05-13 05:21:34 +04:00
int rep_fprintf ( FILE * stream , const char * fmt , . . . )
2006-09-19 02:49:20 +04:00
{
va_list ap ;
int ret ;
char * s ;
s = NULL ;
va_start ( ap , fmt ) ;
ret = vasprintf ( & s , fmt , ap ) ;
va_end ( ap ) ;
if ( s ) {
fwrite ( s , 1 , strlen ( s ) , stream ) ;
}
free ( s ) ;
return ret ;
}
2001-02-16 22:21:18 +03:00
# endif
1998-10-28 07:07:51 +03:00
2001-04-27 06:45:08 +04:00
# endif
1998-10-28 07:07:51 +03:00
2012-11-13 17:07:11 +04:00
# if !defined(HAVE_VASPRINTF) || !defined(HAVE_C99_VSNPRINTF)
2012-05-13 05:21:34 +04:00
int rep_vasprintf ( char * * ptr , const char * format , va_list ap )
2001-04-27 06:45:08 +04:00
{
int ret ;
2002-07-15 14:35:28 +04:00
va_list ap2 ;
VA_COPY ( ap2 , ap ) ;
ret = vsnprintf ( NULL , 0 , format , ap2 ) ;
2007-02-06 08:58:35 +03:00
va_end ( ap2 ) ;
2008-03-15 23:47:22 +03:00
if ( ret < 0 ) return ret ;
2001-04-28 17:49:34 +04:00
2001-04-28 16:27:37 +04:00
( * ptr ) = ( char * ) malloc ( ret + 1 ) ;
if ( ! * ptr ) return - 1 ;
2002-07-15 14:35:28 +04:00
VA_COPY ( ap2 , ap ) ;
ret = vsnprintf ( * ptr , ret + 1 , format , ap2 ) ;
2007-02-06 08:58:35 +03:00
va_end ( ap2 ) ;
2001-04-28 17:49:34 +04:00
return ret ;
}
# endif
2012-11-13 17:07:11 +04:00
# if !defined(HAVE_ASPRINTF) || !defined(HAVE_C99_VSNPRINTF)
2012-05-13 05:21:34 +04:00
int rep_asprintf ( char * * ptr , const char * format , . . . )
2001-04-28 17:49:34 +04:00
{
va_list ap ;
int ret ;
2002-01-19 20:29:32 +03:00
* ptr = NULL ;
2001-04-28 17:49:34 +04:00
va_start ( ap , format ) ;
ret = vasprintf ( ptr , format , ap ) ;
2001-04-27 06:45:08 +04:00
va_end ( ap ) ;
2001-04-28 16:27:37 +04:00
return ret ;
2001-04-27 06:45:08 +04:00
}
1998-10-27 02:40:33 +03:00
# endif
2001-04-27 06:45:08 +04:00
# ifdef TEST_SNPRINTF
int sprintf ( char * str , const char * fmt , . . . ) ;
2006-09-04 23:53:30 +04:00
int printf ( const char * fmt , . . . ) ;
2001-04-27 06:45:08 +04:00
1998-10-28 15:04:34 +03:00
int main ( void )
1998-10-27 02:40:33 +03:00
{
2001-04-27 06:45:08 +04:00
char buf1 [ 1024 ] ;
char buf2 [ 1024 ] ;
2005-11-19 04:14:05 +03:00
char * buf3 ;
2001-04-27 06:45:08 +04:00
char * fp_fmt [ ] = {
" %1.1f " ,
" %-1.5f " ,
" %1.5f " ,
" %123.9f " ,
" %10.5f " ,
" % 10.5f " ,
" %+22.9f " ,
" %+4.9f " ,
" %01.3f " ,
" %4f " ,
" %3.1f " ,
" %3.2f " ,
" %.0f " ,
" %f " ,
2006-02-07 03:50:38 +03:00
" %-8.8f " ,
" %-9.9f " ,
2001-04-27 06:45:08 +04:00
NULL
} ;
2003-04-14 11:36:19 +04:00
double fp_nums [ ] = { 6442452944.1234 , - 1.5 , 134.21 , 91340.2 , 341.1234 , 203.9 , 0.96 , 0.996 ,
0.9996 , 1.996 , 4.136 , 5.030201 , 0.00205 ,
/* END LIST */ 0 } ;
2001-04-27 06:45:08 +04:00
char * int_fmt [ ] = {
" %-1.5d " ,
" %1.5d " ,
" %123.9d " ,
" %5.5d " ,
" %10.5d " ,
" % 10.5d " ,
" %+22.33d " ,
" %01.3d " ,
" %4d " ,
" %d " ,
NULL
} ;
2006-08-21 21:58:41 +04:00
long int_nums [ ] = { - 1 , 134 , 91340 , 341 , 0203 , 1234567890 , 0 } ;
2001-04-27 06:45:08 +04:00
char * str_fmt [ ] = {
2006-02-07 03:50:38 +03:00
" %10.5s " ,
" %-10.5s " ,
" %5.10s " ,
" %-5.10s " ,
" %10.1s " ,
" %0.10s " ,
" %10.0s " ,
" %1.10s " ,
2001-04-27 06:45:08 +04:00
" %s " ,
" %.1s " ,
" %.10s " ,
" %10s " ,
NULL
} ;
char * str_vals [ ] = { " hello " , " a " , " " , " a longer string " , NULL } ;
2006-08-21 21:58:41 +04:00
# ifdef HAVE_LONG_LONG
char * ll_fmt [ ] = {
" %llu " ,
NULL
} ;
LLONG ll_nums [ ] = { 134 , 91340 , 341 , 0203 , 1234567890 , 128006186140000000LL , 0 } ;
# endif
2001-04-27 06:45:08 +04:00
int x , y ;
int fail = 0 ;
int num = 0 ;
2006-02-07 03:50:38 +03:00
int l1 , l2 ;
2006-09-04 23:53:30 +04:00
char * ss_fmt [ ] = {
" %zd " ,
" %zu " ,
NULL
} ;
size_t ss_nums [ ] = { 134 , 91340 , 123456789 , 0203 , 1234567890 , 0 } ;
2001-04-27 06:45:08 +04:00
printf ( " Testing snprintf format codes against system sprintf... \n " ) ;
for ( x = 0 ; fp_fmt [ x ] ; x + + ) {
for ( y = 0 ; fp_nums [ y ] ! = 0 ; y + + ) {
2006-02-07 03:50:38 +03:00
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
2006-09-04 23:53:30 +04:00
l1 = snprintf ( buf1 , sizeof ( buf1 ) , fp_fmt [ x ] , fp_nums [ y ] ) ;
l2 = sprintf ( buf2 , fp_fmt [ x ] , fp_nums [ y ] ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
2006-02-07 03:50:38 +03:00
if ( strcmp ( buf1 , buf2 ) | | ( l1 ! = l2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
fp_fmt [ x ] , l1 , buf1 , l2 , buf2 ) ;
2001-04-27 06:45:08 +04:00
fail + + ;
}
num + + ;
}
}
for ( x = 0 ; int_fmt [ x ] ; x + + ) {
for ( y = 0 ; int_nums [ y ] ! = 0 ; y + + ) {
2006-02-07 03:50:38 +03:00
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
2006-09-04 23:53:30 +04:00
l1 = snprintf ( buf1 , sizeof ( buf1 ) , int_fmt [ x ] , int_nums [ y ] ) ;
l2 = sprintf ( buf2 , int_fmt [ x ] , int_nums [ y ] ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
2006-02-07 03:50:38 +03:00
if ( strcmp ( buf1 , buf2 ) | | ( l1 ! = l2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
int_fmt [ x ] , l1 , buf1 , l2 , buf2 ) ;
2001-04-27 06:45:08 +04:00
fail + + ;
}
num + + ;
}
}
for ( x = 0 ; str_fmt [ x ] ; x + + ) {
for ( y = 0 ; str_vals [ y ] ! = 0 ; y + + ) {
2006-02-07 03:50:38 +03:00
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
2006-09-04 23:53:30 +04:00
l1 = snprintf ( buf1 , sizeof ( buf1 ) , str_fmt [ x ] , str_vals [ y ] ) ;
l2 = sprintf ( buf2 , str_fmt [ x ] , str_vals [ y ] ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
2006-02-07 03:50:38 +03:00
if ( strcmp ( buf1 , buf2 ) | | ( l1 ! = l2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
str_fmt [ x ] , l1 , buf1 , l2 , buf2 ) ;
2001-04-27 06:45:08 +04:00
fail + + ;
}
num + + ;
}
}
2006-08-21 21:58:41 +04:00
# ifdef HAVE_LONG_LONG
for ( x = 0 ; ll_fmt [ x ] ; x + + ) {
for ( y = 0 ; ll_nums [ y ] ! = 0 ; y + + ) {
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
2006-09-04 23:53:30 +04:00
l1 = snprintf ( buf1 , sizeof ( buf1 ) , ll_fmt [ x ] , ll_nums [ y ] ) ;
l2 = sprintf ( buf2 , ll_fmt [ x ] , ll_nums [ y ] ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
if ( strcmp ( buf1 , buf2 ) | | ( l1 ! = l2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
ll_fmt [ x ] , l1 , buf1 , l2 , buf2 ) ;
fail + + ;
}
num + + ;
}
}
# endif
2005-11-19 04:14:05 +03:00
# define BUFSZ 2048
2006-02-07 03:50:38 +03:00
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
2005-11-19 04:14:05 +03:00
if ( ( buf3 = malloc ( BUFSZ ) ) = = NULL ) {
fail + + ;
} else {
num + + ;
memset ( buf3 , ' a ' , BUFSZ ) ;
snprintf ( buf1 , sizeof ( buf1 ) , " %.*s " , 1 , buf3 ) ;
2006-02-07 03:50:38 +03:00
buf1 [ 1023 ] = ' \0 ' ;
2005-11-19 04:14:05 +03:00
if ( strcmp ( buf1 , " a " ) ! = 0 ) {
printf ( " length limit buf1 '%s' expected 'a' \n " , buf1 ) ;
fail + + ;
}
}
2006-02-07 03:50:38 +03:00
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
l1 = snprintf ( buf1 , sizeof ( buf1 ) , " %4$*1$d %2$s %3$*1$.*1$f " , 3 , " pos test " , 12.3456 , 9 ) ;
l2 = sprintf ( buf2 , " %4$*1$d %2$s %3$*1$.*1$f " , 3 , " pos test " , 12.3456 , 9 ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
2006-02-07 03:50:38 +03:00
if ( strcmp ( buf1 , buf2 ) | | ( l1 ! = l2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
" %4$*1$d %2$s %3$*1$.*1$f " , l1 , buf1 , l2 , buf2 ) ;
fail + + ;
}
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
l1 = snprintf ( buf1 , sizeof ( buf1 ) , " %4$*4$d %2$s %3$*4$.*4$f " , 3 , " pos test " , 12.3456 , 9 ) ;
l2 = sprintf ( buf2 , " %4$*4$d %2$s %3$*4$.*4$f " , 3 , " pos test " , 12.3456 , 9 ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
2006-02-07 03:50:38 +03:00
if ( strcmp ( buf1 , buf2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
" %4$*1$d %2$s %3$*1$.*1$f " , l1 , buf1 , l2 , buf2 ) ;
fail + + ;
}
2006-09-04 23:53:30 +04:00
for ( x = 0 ; ss_fmt [ x ] ; x + + ) {
for ( y = 0 ; ss_nums [ y ] ! = 0 ; y + + ) {
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
l1 = snprintf ( buf1 , sizeof ( buf1 ) , ss_fmt [ x ] , ss_nums [ y ] ) ;
l2 = sprintf ( buf2 , ss_fmt [ x ] , ss_nums [ y ] ) ;
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
if ( strcmp ( buf1 , buf2 ) | | ( l1 ! = l2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
ss_fmt [ x ] , l1 , buf1 , l2 , buf2 ) ;
fail + + ;
}
num + + ;
}
}
2006-02-07 03:50:38 +03:00
#if 0
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
l1 = snprintf ( buf1 , sizeof ( buf1 ) , " %lld " , ( LLONG ) 1234567890 ) ;
l2 = sprintf ( buf2 , " %lld " , ( LLONG ) 1234567890 ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
2006-02-07 03:50:38 +03:00
if ( strcmp ( buf1 , buf2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
" %lld " , l1 , buf1 , l2 , buf2 ) ;
fail + + ;
}
buf1 [ 0 ] = buf2 [ 0 ] = ' \0 ' ;
l1 = snprintf ( buf1 , sizeof ( buf1 ) , " %Lf " , ( LDOUBLE ) 890.1234567890123 ) ;
l2 = sprintf ( buf2 , " %Lf " , ( LDOUBLE ) 890.1234567890123 ) ;
2006-08-21 21:58:41 +04:00
buf1 [ 1023 ] = buf2 [ 1023 ] = ' \0 ' ;
2006-02-07 03:50:38 +03:00
if ( strcmp ( buf1 , buf2 ) ) {
printf ( " snprintf doesn't match Format: %s \n \t snprintf(%d) = [%s] \n \t sprintf(%d) = [%s] \n " ,
" %Lf " , l1 , buf1 , l2 , buf2 ) ;
fail + + ;
}
# endif
2001-04-27 06:45:08 +04:00
printf ( " %d tests failed out of %d. \n " , fail , num ) ;
printf ( " seeing how many digits we support \n " ) ;
{
double v0 = 0.12345678901234567890123456789012345678901 ;
for ( x = 0 ; x < 100 ; x + + ) {
2002-07-15 14:35:28 +04:00
double p = pow ( 10 , x ) ;
double r = v0 * p ;
snprintf ( buf1 , sizeof ( buf1 ) , " %1.1f " , r ) ;
sprintf ( buf2 , " %1.1f " , r ) ;
2001-04-27 06:45:08 +04:00
if ( strcmp ( buf1 , buf2 ) ) {
printf ( " we seem to support %d digits \n " , x - 1 ) ;
break ;
}
}
}
return 0 ;
1998-10-27 02:40:33 +03:00
}
2003-04-14 11:36:19 +04:00
# endif /* TEST_SNPRINTF */