@ -2,6 +2,7 @@
* Copyright ( c ) 1983 , 1995 , 1996 Eric P . Allman
* Copyright ( c ) 1988 , 1993
* The Regents of the University of California . All rights reserved .
* Portions Copyright ( c ) 1996 - 2018 , PostgreSQL Global Development Group
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
@ -49,8 +50,8 @@
* SNPRINTF , VSNPRINTF and friends
*
* These versions have been grabbed off the net . They have been
* cleaned up to compile properly and support for most of the Single Unix
* S pecification has been added . Remaining unimplemented features are :
* cleaned up to compile properly and support for most of the C99
* s pecification has been added . Remaining unimplemented features are :
*
* 1. No locale support : the radix character is always ' . ' and the '
* ( single quote ) format flag is ignored .
@ -64,25 +65,24 @@
* 5. Space and ' # ' flags are not implemented .
*
*
* T he result values of these functions are not the same across different
* platforms . This implementation is compatible with the Single Unix Spec :
* Historically t he result values of sprintf / snprintf varied across platforms .
* This implementation now follows the C99 standard :
*
* 1. - 1 is returned only if processing is abandoned due to an invalid
* parameter , such as incorrect format string . ( Although not required by
* the spec , this happens only when no characters have yet been transmitted
* to the destination . )
* 1. - 1 is returned if an error is detected in the format string , or if
* a write to the target stream fails ( as reported by fwrite ) . Note that
* overrunning snprintf ' s target buffer is * not * an error .
*
* 2. For snprintf and sprintf , 0 is returned if str = = NULL or count = = 0 ;
* no data has been stor ed.
* 2. For successful writes to streams , the actual number of bytes written
* to the stream is return ed.
*
* 3. Otherwise , the number of bytes actually transmitted to the destination
* is returned ( excluding the trailing ' \0 ' for snprintf and sprintf ) .
* 3. For successful sprintf / snprintf , the number of bytes that would have
* been written to an infinite - size buffer ( excluding the trailing ' \0 ' )
* is returned . snprintf will truncate its output to fit in the buffer
* ( ensuring a trailing ' \0 ' unless count = = 0 ) , but this is not reflected
* in the function result .
*
* For snprintf with nonzero count , the result cannot be more than count - 1
* ( a trailing ' \0 ' is always stored ) ; it is not possible to distinguish
* buffer overrun from exact fit . This is unlike some implementations that
* return the number of bytes that would have been needed for the complete
* result string .
* snprintf buffer overrun can be detected by checking for function result
* greater than or equal to the supplied count .
*/
/**************************************************************
@ -101,15 +101,27 @@
# undef fprintf
# undef printf
/* Info about where the formatted output is going */
/*
* Info about where the formatted output is going .
*
* dopr and subroutines will not write at / past bufend , but snprintf
* reserves one byte , ensuring it may place the trailing ' \0 ' there .
*
* In snprintf , we use nchars to count the number of bytes dropped on the
* floor due to buffer overrun . The correct result of snprintf is thus
* ( bufptr - bufstart ) + nchars . ( This isn ' t as inconsistent as it might
* seem : nchars is the number of emitted bytes that are not in the buffer now ,
* either because we sent them to the stream or because we couldn ' t fit them
* into the buffer to begin with . )
*/
typedef struct
{
char * bufptr ; /* next buffer output position */
char * bufstart ; /* first buffer element */
char * bufend ; /* last buffer element, or NULL */
char * bufend ; /* last+1 buffer element, or NULL */
/* bufend == NULL is for sprintf, where we assume buf is big enough */
FILE * stream ; /* eventual output destination, or NULL */
int nchars ; /* # chars already sent to stream */
int nchars ; /* # chars sent to stream, or dropped */
bool failed ; /* call is a failure; errno is set */
} PrintfTarget ;
@ -147,17 +159,28 @@ int
pg_vsnprintf ( char * str , size_t count , const char * fmt , va_list args )
{
PrintfTarget target ;
char onebyte [ 1 ] ;
if ( str = = NULL | | count = = 0 )
return 0 ;
/*
* C99 allows the case str = = NULL when count = = 0. Rather than
* special - casing this situation further down , we substitute a one - byte
* local buffer . Callers cannot tell , since the function result doesn ' t
* depend on count .
*/
if ( count = = 0 )
{
str = onebyte ;
count = 1 ;
}
target . bufstart = target . bufptr = str ;
target . bufend = str + count - 1 ;
target . stream = NULL ;
/* target.nchars is unused in this case */
target . nchars = 0 ;
target . failed = false ;
dopr ( & target , fmt , args ) ;
* ( target . bufptr ) = ' \0 ' ;
return target . failed ? - 1 : ( target . bufptr - target . bufstart ) ;
return target . failed ? - 1 : ( target . bufptr - target . bufstart
+ target . nchars ) ;
}
int
@ -177,16 +200,15 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
{
PrintfTarget target ;
if ( str = = NULL )
return 0 ;
target . bufstart = target . bufptr = str ;
target . bufend = NULL ;
target . stream = NULL ;
/* target.nchars is un used in this case */
target . nchars = 0 ; /* not really used in this case */
target . failed = false ;
dopr ( & target , fmt , args ) ;
* ( target . bufptr ) = ' \0 ' ;
return target . failed ? - 1 : ( target . bufptr - target . bufstart ) ;
return target . failed ? - 1 : ( target . bufptr - target . bufstart
+ target . nchars ) ;
}
int
@ -213,7 +235,7 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
return - 1 ;
}
target . bufstart = target . bufptr = buffer ;
target . bufend = buffer + sizeof ( buffer ) - 1 ;
target . bufend = buffer + sizeof ( buffer ) ; /* use the whole buffer */
target . stream = stream ;
target . nchars = 0 ;
target . failed = false ;
@ -256,6 +278,10 @@ flushbuffer(PrintfTarget *target)
{
size_t nc = target - > bufptr - target - > bufstart ;
/*
* Don ' t write anything if we already failed ; this is to ensure we
* preserve the original failure ' s errno .
*/
if ( ! target - > failed & & nc > 0 )
{
size_t written ;
@ -1035,7 +1061,10 @@ dostr(const char *str, int slen, PrintfTarget *target)
{
/* buffer full, can we dump to stream? */
if ( target - > stream = = NULL )
return ; /* no, lose the data */
{
target - > nchars + = slen ; /* no, lose the data */
return ;
}
flushbuffer ( target ) ;
continue ;
}
@ -1054,7 +1083,10 @@ dopr_outch(int c, PrintfTarget *target)
{
/* buffer full, can we dump to stream? */
if ( target - > stream = = NULL )
return ; /* no, lose the data */
{
target - > nchars + + ; /* no, lose the data */
return ;
}
flushbuffer ( target ) ;
}
* ( target - > bufptr + + ) = c ;