Base WG Resolution Ref: bwg98-006
Topic: snprintf


This is an approved Base Working Group Resolution for XSH5.

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