Last update: 22 September,1998
98 #006 _____________________________________________________________________________ Topic: snprintf Relevant Sections: snprint Spec: XSH5 Resolution Request: ------------------- (1) snprintf and BSD behaviour I am concerned about the specification of snprintf. That specification is quite different from the 6 or 7 year old BSD implimentation; it makes it very difficult for programmers to use the interface in a portable fashion. The BSD spec says: Snprintf(), vsnprintf(), asnprintf() and vasnprintf() will write at most size-1 of the characters printed into the output string (the size'th character then gets the terminating `\0'); if the return value is greater than or equal to the size argument, the string was too short and some of the printed characters were discarded. The XPG spec says that snprintf returns the number of characters it placed, and -1 otherwise. They have also made a passed in length of 0 a special case. Since snprintf returns an int (ie. signed), there are obvious and serious API flaws here which make things very complicated for the programmer. Suddenly the code which we use if (snprintf(buf, len, ...) > len) overflow Isn't correct code. And the code they want if (snprintf(buf, len, ...) < 0) overflow Isn't correct code for the BSD API. int err; err = snprintf(buf, len, ...); if (err < 1 || err > len) overflow Is perhaps the only thing that closely approximates, but it requires a local variable to be declared. As far as we know, BSD snprintf never returns 0 or -1. It always returns the number of bytes it wanted. What XPG is changing things to is too complicated for an interface of this kind, and my belief is that programmers who are switching over to using this interface for more careful buffer management in their software are going to slack off and use it incorrectly. (2) XSH5 does not match historic practice or the C9x draft. Unfortunately, the C9x draft doesn't match historic practice either. For reference the synopses for snprintf are: XSH5: int snprintf(char *s, size_t n, const char *format, /* args */ ...); C9x: int snprintf(char * restrict s, size_t n, const char * restrict format, ..); There are three cases that need consideration: 1. What happens when n==0? Apparently historic practice was not consistent. XSH5 allows any unspecified value less than 1 (apparently allowing 0 or EOF). The C9x draft says that the return value will be the size that would be needed to hold all of the output (not including the terminating null byte). A system could return EOF without setting errno. 2. What happens when n>INT_MAX or the size of the buffer needed to hold the output is larger than INT_MAX bytes? This is an issue since the n parameter is of type size_t (an unsigned type), and the return value is a signed int. Both XSH5 and the C9x draft are silent on this issue. A system could set errno to EOVERFLOW and return EOF for both of these conditions. If there is agreement on this, it should be clearly specified; if not, we should at least note that the results are unspecified in these cases. 3. What happens when 0 < n < size needed to hold the output? XSH5 requires the return value to be n-1. The C9x draft says that the return value will be the size that would be needed to hold all of the output (not including the terminating null byte). Resolution: =========== Firstly, there is no error if the number of bytes needed to output the data with the given format is larger than n when 0 < n <= INT_MAX. 1. Change the last paragraph of the RETURN VALUE section of fprintf() on XSH5, P260 from: If the value is zero on a call to snprintf(), an unspecified value less than 1 is returned. to: If the value of n is zero on a call to snprintf(), an unspecified value is returned. Rationale: XSH5 conforming applications shouldn't be calling snprintf() with n == 0 anyway (except for test suites). This will allow implementations to conform to SUSv2 and C9x, if C9x is ratified. (And, this seems like much more rational behavior.) We could also add some notes to APPLICATON USAGE or FUTURE DIRECTIONS pointing out historic behavior and the C9x draft requirement. 2. We add an additional part to the end of the ERRORS section on XSH5, P260: In addition, snprintf() will fail if: [EOVERFLOW] The value of n is greater than INT_MAX or the number of bytes needed to hold the output excluding the terminating null is greater than INT_MAX. Rationale: We should let applications know there is a potential problem here. If not all implemententation support this test, we can change the "will fail" above to "may fail". 3. We change the first paragraph of the RETURN VALUE section of fprintf() on XSH5, P260 from: Upon successful completion, these functions return the number of bytes transmitted excluding the terminating null in the case of sprintf() or snprintf() or a negative value if an output error was encountered. to: Upon successful completion, the fprintf() and printf() functions return the number of bytes transmitted. Upon successful completion, the sprintf() function returns the number of bytes written to s excluding the terminating null byte. Upon successful completion, the snprintf() function returns the number of bytes that would be written to s had n been sufficiently large excluding the terminating null byte. If an output error was encountered, these functions return a negative value. and add to the end of the second paragraph of the DESCRIPTION section of fprintf() on XSH5, P256: If n is greater than zero, but not large enough to hold all output bytes specified by the format, output bytes beyond the n-1st are discarded rather than being written into the array. and add a new paragraph following the second paragraph of the DESCRIPTION section of fprintf() on XSH5, P256: If copying takes place between objects that overlap as a result of a call to sprintf() or snprintf(), the results are undefined. Rationale: When we added snprintf() to XSH5, we were trying to document existing practice. These changes will make the description match implementations and align with the C9x draft. Its assumed we missed the fact that existing implementations disagreed with the first paragraph of the RETURN VALUE section when n wasn't large enough to hold the full output. Approved: Sep 22 1998