0

Based on my reading of the C standard, if snprinf reaches maxlen while writing, it will return either a positive or negative value (it's not actually clear) that is equal in magnitude to the number of characters which failed to be written. The spec also says that the function will return a negative value for any error. So my question is: what does it return in the following two circumstances...

Maxlen > 0, no characters are written before an error occurs and snprintf bails

Maxlen > 0, all characters are written, an error of some kind occurs and snprintf bails

I realize that the last one, in particular, is unlikely. But I wanted to better understand the specification. It seems to me that snprintf's return makes it ambiguous whether you correctly wrote part of a string or not.

        • Edit

Apparently this is not clear. Let me elaborate:

*> Which negative number doesn't matter. An error is an error, it doesn't

matter how many bytes were written before it occurred. That may be the practical case for most people. I saw that snprintf is supposed to return the number of excess characters, however, and I wanted to know if that value could be used. It seemed strange to me that the spec would go through the trouble of reporting something useful, only to leave it ambiguous as to whether it was really returning something irrelevant. Does that not make sense? I wanted to know if there was more to the convention that made it possible to determine if snprintf failed because it was given too much input.*

I get it now. I see that any error other than truncation is negative, success is 0 < R <= maxsize, truncation is 0 < maxsize < R. Thanks everyone who responded.

SaburoutaMishima
  • 269
  • 4
  • 15
  • "If the output was truncated due to this limit then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available." What's ambiguous about that? – Kevin Oct 05 '13 at 18:20
  • You did not read the two lines in the middle of my question. – SaburoutaMishima Oct 05 '13 at 18:22
  • 1
    I did, and I'm still not sure why you're confused. – Kevin Oct 05 '13 at 18:23
  • 1
    History: before snprintf() and friends were standardised by C89/C90, there were two conventions for the return value of the (_not-yet standard_) functions. One always returned -1, the other used values larger than the 2nd argument to give the caller a hint. The last one was adopted by the standard. (but you still have to check for return values < 0 anyway) – wildplasser Oct 05 '13 at 18:48
  • 1. The spec suggests that truncation is a kind of error, but then the comment about glibcn versions suggests that truncation returns a positive value and doesn't indicate an error. More to the point, it never explicitly says truncation is an error. 2. Whether or not the standard is ambiguous, the information in the return value seems to be. – SaburoutaMishima Oct 05 '13 at 18:52

4 Answers4

2

That's not how I read the standard. From the n1570 draft (final free one, I think):

The snprintf function returns the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a neg ative value if an encoding error occurred. Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n.

Their prototype is:

int sprintf(char * restrict s, const char * restrict format, ...);

...so their "n" is presumably your "Maxlen" value.

So, there's no problem. If there's an error, you get -42 or some other arbitrary negative number. If not, you get the length of all data to be written. The portion that fits was written to the buffer, and there was a truncation if the return value is greater than or equal to n.

The only unspecified bit I see is that since n is a size_t, it's possible for the result to be larger than will fit in a positive int return value. I can't think of an application that would affected by that, though.

Mike Housky
  • 3,959
  • 1
  • 17
  • 31
1

I wanted to know if there was more to the convention that made it possible to determine if snprintf failed because it was given too much input.

It is straightforward. For errors, a negative number is returned. For truncation, a number >= size is returned. Whether you consider truncation an error or not, you test for both conditions and handle as you see fit.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • I had to reread that spec a number of times until I found what I was looking for. I thought there was a problem with what you said until I reread the line about the size of output. Of course there is no way to confuse a successful run with truncation because a successful return value is at most size. I didn't think about that so I thought that truncation was considered an error and returned as a negative value. – SaburoutaMishima Oct 05 '13 at 19:01
  • A slight correction to your comment. a successful return value is *less than* `size`. The `size` count includes the terminating null, but the return value only returns the characters output, excluding the terminating null. – Mark Tolonen Oct 05 '13 at 19:14
  • Oh, thank you. I hadn't noticed the difference in the size and return. – SaburoutaMishima Oct 05 '13 at 19:35
0

The description from the man page:

Return value

Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).

The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to this limit then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available. Thus, a return value of size or more means that the output was truncated. (See also below under NOTES.)

If an output error is encountered, a negative value is returned.

So for your cases:

Maxlen > 0, no characters are written before an error occurs and snprintf bails

snprintf returns a negative number. Which negative number doesn't matter.

Maxlen > 0, all characters are written, an error of some kind occurs and snprintf bails

snprintf returns a negative number. Which negative number doesn't matter. An error is an error, it doesn't matter how many bytes were written before it occurred.

Community
  • 1
  • 1
Kevin
  • 53,822
  • 15
  • 101
  • 132
0

As a further comment, when using snprintf repeatedly to build a string incrementally (e.g. within a loop) - or anywhere where the length of the resultant string must be determined, I suggest this as a "safe" method...

int foo( char * b, size_t bLen ) {
  size_t sLen = 0;
  for(int i = 0; i < 1000; i++) {
    int res = snprintf(&b[sLen], bLen - sLen, "%d. A string ", sLen);
    // sLen:         Inc by +ive res,      (upto safe maximum)        
    sLen = std::min( sLen + (res>0?res:0), (bLen?bLen-1:bLen) );
  }
  return sLen;
}

The line after the snprintf will safely increment the string length variable (sLen), ignoring negative results (res) and guarding against overflow by limiting to the buffer length (bLen). I've used C++ "min" in my example, but stack overflow provides 'C' alternatives: MIN and MAX in C

Community
  • 1
  • 1
GreyMattR
  • 333
  • 2
  • 6