Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.arch.embedded > #28418 > unrolled thread

snprintf return value

Started bypozz <pozzugno@gmail.com>
First post2018-12-10 11:20 +0100
Last post2018-12-11 13:31 +1100
Articles 13 — 8 participants

Back to article view | Back to comp.arch.embedded


Contents

  snprintf return value pozz <pozzugno@gmail.com> - 2018-12-10 11:20 +0100
    Re: snprintf return value Reinhardt Behm <rbehm@hushmail.com> - 2018-12-10 18:24 +0800
      Re: snprintf return value pozz <pozzugno@gmail.com> - 2018-12-10 11:32 +0100
        Re: snprintf return value Philipp Klaus Krause <pkk@spth.de> - 2019-01-01 12:59 +0100
    Re: snprintf return value David Brown <david.brown@hesbynett.no> - 2018-12-10 13:15 +0100
    Re: snprintf return value Clifford Heath <no.spam@please.net> - 2018-12-11 08:49 +1100
      Re: snprintf return value Phil Hobbs <pcdhSpamMeSenseless@electrooptical.net> - 2018-12-11 11:41 -0500
        Re: snprintf return value Clifford Heath <no.spam@please.net> - 2018-12-12 10:34 +1100
          Re: snprintf return value David Brown <david.brown@hesbynett.no> - 2018-12-12 09:09 +0100
            Re: snprintf return value Clifford Heath <no.spam@please.net> - 2018-12-12 21:38 +1100
          Re: snprintf return value Les Cargill <lcargill99@comcast.com> - 2018-12-30 11:23 -0600
    Re: snprintf return value Hans-Bernhard Bröker <HBBroeker@t-online.de> - 2018-12-11 00:47 +0100
      Re: snprintf return value Clifford Heath <no.spam@please.net> - 2018-12-11 13:31 +1100

#28418 — snprintf return value

Frompozz <pozzugno@gmail.com>
Date2018-12-10 11:20 +0100
Subjectsnprintf return value
Message-ID<pulekl$n55$1@dont-email.me>
I usually have a static buffer and I don't want to use a dynamic buffer.

char buf[BUFLEN];

I have to construct a long string, and it is quite easy to call snprintf 
multiple times:

   int n = 0;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   n += snprintf(&buf[n], BUFLEN - n, ...);
   n += snprintf(&buf[n], BUFLEN - n, ...);
   n += snprintf(&buf[n], BUFLEN - n, ...);

I was convinced the return value was the real number of characters 
written in buf, not counting final '\0'. In this case, the above code works.

I just noticed the return value of snprintf() is the number of 
characters of the generated string as if the buffer was long enough. So 
the above code doesn't work.

The return value of snprintf() is useful if you want to dynamically 
reallocate the buffer so it can store all the string. In my case I would 
prefer to truncate the string at the end of the static buffer. The above 
code should be redesigned as:

   int n = 0;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;
   n += snprintf(&buf[n], BUFLEN - n, ...);
   if (n >= BUFLEN - n) return;

The code starts being not readable. I'm thinking to create a new 
snprintf2():

   int
   snprintf2(char *str, size_t size, const char *format, ...) {
       va_list args;

       va_start(args, format);
       int n = vsnprintf(str, size, format, args);
       va_end(args);
       return (n >= size) ? (size - 1) : n;
   }

[toc] | [next] | [standalone]


#28419

FromReinhardt Behm <rbehm@hushmail.com>
Date2018-12-10 18:24 +0800
Message-ID<puleth$npt$1@dont-email.me>
In reply to#28418
AT Monday 10 December 2018 18:20, pozz wrote:

> I usually have a static buffer and I don't want to use a dynamic buffer.
> 
> char buf[BUFLEN];
> 
> I have to construct a long string, and it is quite easy to call snprintf
> multiple times:
> 
>    int n = 0;
>    n += snprintf(&buf[n], BUFLEN - n, ...);
>    n += snprintf(&buf[n], BUFLEN - n, ...);
>    n += snprintf(&buf[n], BUFLEN - n, ...);
>    n += snprintf(&buf[n], BUFLEN - n, ...);
> 
> I was convinced the return value was the real number of characters
> written in buf, not counting final '\0'. In this case, the above code
> works.
> 
> I just noticed the return value of snprintf() is the number of
> characters of the generated string as if the buffer was long enough. So
> the above code doesn't work.
> 
> The return value of snprintf() is useful if you want to dynamically
> reallocate the buffer so it can store all the string. In my case I would
> prefer to truncate the string at the end of the static buffer. The above
> code should be redesigned as:
> 
>    int n = 0;
>    n += snprintf(&buf[n], BUFLEN - n, ...);
>    if (n >= BUFLEN - n) return;
>    n += snprintf(&buf[n], BUFLEN - n, ...);
>    if (n >= BUFLEN - n) return;
>    n += snprintf(&buf[n], BUFLEN - n, ...);
>    if (n >= BUFLEN - n) return;
>    n += snprintf(&buf[n], BUFLEN - n, ...);
>    if (n >= BUFLEN - n) return;
> 
> The code starts being not readable. I'm thinking to create a new
> snprintf2():
> 
>    int
>    snprintf2(char *str, size_t size, const char *format, ...) {
>        va_list args;
> 
>        va_start(args, format);
>        int n = vsnprintf(str, size, format, args);
>        va_end(args);
>        return (n >= size) ? (size - 1) : n;
>    }

Keep in mind that snprintf can even return negative values as error codes.

-- 
Reinhardt

[toc] | [prev] | [next] | [standalone]


#28420

Frompozz <pozzugno@gmail.com>
Date2018-12-10 11:32 +0100
Message-ID<pulfc4$q14$1@dont-email.me>
In reply to#28419
Il 10/12/2018 11:24, Reinhardt Behm ha scritto:
> AT Monday 10 December 2018 18:20, pozz wrote:
> 
>> I usually have a static buffer and I don't want to use a dynamic buffer.
>>
>> char buf[BUFLEN];
>>
>> I have to construct a long string, and it is quite easy to call snprintf
>> multiple times:
>>
>>     int n = 0;
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>
>> I was convinced the return value was the real number of characters
>> written in buf, not counting final '\0'. In this case, the above code
>> works.
>>
>> I just noticed the return value of snprintf() is the number of
>> characters of the generated string as if the buffer was long enough. So
>> the above code doesn't work.
>>
>> The return value of snprintf() is useful if you want to dynamically
>> reallocate the buffer so it can store all the string. In my case I would
>> prefer to truncate the string at the end of the static buffer. The above
>> code should be redesigned as:
>>
>>     int n = 0;
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>     if (n >= BUFLEN - n) return;
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>     if (n >= BUFLEN - n) return;
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>     if (n >= BUFLEN - n) return;
>>     n += snprintf(&buf[n], BUFLEN - n, ...);
>>     if (n >= BUFLEN - n) return;
>>
>> The code starts being not readable. I'm thinking to create a new
>> snprintf2():
>>
>>     int
>>     snprintf2(char *str, size_t size, const char *format, ...) {
>>         va_list args;
>>
>>         va_start(args, format);
>>         int n = vsnprintf(str, size, format, args);
>>         va_end(args);
>>         return (n >= size) ? (size - 1) : n;
>>     }
> 
> Keep in mind that snprintf can even return negative values as error codes.

Could you provide a test case for a negative return value from snprintf()?

[toc] | [prev] | [next] | [standalone]


#28465

FromPhilipp Klaus Krause <pkk@spth.de>
Date2019-01-01 12:59 +0100
Message-ID<q0fkmj$pe1$1@solani.org>
In reply to#28420
Am 10.12.18 um 11:32 schrieb pozz:
>> Keep in mind that snprintf can even return negative values as error
>> codes.
> 
> Could you provide a test case for a negative return value from snprintf()?
> 

The standard allows negative return values only for encoding errors. So
for typical systems (i.e. sizeof(char) < sizeof(int), wchar_t is Unicode
code points), a %c with an argument outside the range of char should
work. Or a %lc with an argument that is not a Unicode code point. Or a
%ls with an argument that is an array containing values that are not
valid Unicode code points.

Philipp

[toc] | [prev] | [next] | [standalone]


#28423

FromDavid Brown <david.brown@hesbynett.no>
Date2018-12-10 13:15 +0100
Message-ID<pulldd$sl3$1@dont-email.me>
In reply to#28418
On 10/12/18 11:20, pozz wrote:
> I usually have a static buffer and I don't want to use a dynamic buffer.
> 
> char buf[BUFLEN];
> 
> I have to construct a long string, and it is quite easy to call snprintf
> multiple times:
> 
>   int n = 0;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   n += snprintf(&buf[n], BUFLEN - n, ...);
> 
> I was convinced the return value was the real number of characters
> written in buf, not counting final '\0'. In this case, the above code
> works.
> 
> I just noticed the return value of snprintf() is the number of
> characters of the generated string as if the buffer was long enough. So
> the above code doesn't work.
> 
> The return value of snprintf() is useful if you want to dynamically
> reallocate the buffer so it can store all the string. In my case I would
> prefer to truncate the string at the end of the static buffer. The above
> code should be redesigned as:
> 
>   int n = 0;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;
> 
> The code starts being not readable. I'm thinking to create a new
> snprintf2():
> 
>   int
>   snprintf2(char *str, size_t size, const char *format, ...) {
>       va_list args;
> 
>       va_start(args, format);
>       int n = vsnprintf(str, size, format, args);
>       va_end(args);
>       return (n >= size) ? (size - 1) : n;
>   }

This is going to add a good extra layer of inefficiency, stop
optimisations that your compiler could do (such as converting some cases
to strcat), and stop the format checking provided by gcc unless you
manually add the right attributes.

I'd be tempted to put things in a macro here:

#define snprintf2(str, size, format...) \
	({ int n = snprintf(str, size, ## format); \
	   (n >= size) ? (size - 1) : n })

Yes, it's a gcc extension - but the point is to get the gcc features of
warnings and optimisation.  If you are not using gcc, then I guess your
function is okay.

[toc] | [prev] | [next] | [standalone]


#28431

FromClifford Heath <no.spam@please.net>
Date2018-12-11 08:49 +1100
Message-ID<HbBPD.1230070$Vl2.662518@fx46.iad>
In reply to#28418
On 10/12/18 9:20 pm, pozz wrote:
> The return value of snprintf() is useful if you want to dynamically 
> reallocate the buffer so it can store all the string. In my case I would 
> prefer to truncate the string at the end of the static buffer.

I'd be seriously asking yourself why you prefer that.
Arbitrary unexpected truncation of data should not be tolerated.

Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#28441

FromPhil Hobbs <pcdhSpamMeSenseless@electrooptical.net>
Date2018-12-11 11:41 -0500
Message-ID<VLmdnSQkm65OdZLBnZ2dnUU78d2dnZ2d@supernews.com>
In reply to#28431
On 12/10/18 4:49 PM, Clifford Heath wrote:
> On 10/12/18 9:20 pm, pozz wrote:
>> The return value of snprintf() is useful if you want to dynamically 
>> reallocate the buffer so it can store all the string. In my case I 
>> would prefer to truncate the string at the end of the static buffer.
> 
> I'd be seriously asking yourself why you prefer that.
> Arbitrary unexpected truncation of data should not be tolerated.
> 
> Clifford Heath.

Well, overrunning a buffer is a bug, so it's just a choice of which type 
of misbehaviour you prefer.  If I'm writing to some logfile, for 
instance, truncating the message is a lot better than overwriting memory!

Cheers

Phil Hobbs

-- 
Dr Philip C D Hobbs
Principal Consultant
ElectroOptical Innovations LLC
Optics, Electro-optics, Photonics, Analog Electronics

160 North State Road #203
Briarcliff Manor NY 10510

hobbs at electrooptical dot net
http://electrooptical.net

[toc] | [prev] | [next] | [standalone]


#28443

FromClifford Heath <no.spam@please.net>
Date2018-12-12 10:34 +1100
Message-ID<nQXPD.950180$ZJ2.2926@fx33.iad>
In reply to#28441
On 12/12/18 3:41 am, Phil Hobbs wrote:
> On 12/10/18 4:49 PM, Clifford Heath wrote:
>> On 10/12/18 9:20 pm, pozz wrote:
>>> The return value of snprintf() is useful if you want to dynamically 
>>> reallocate the buffer so it can store all the string. In my case I 
>>> would prefer to truncate the string at the end of the static buffer.
>>
>> I'd be seriously asking yourself why you prefer that.
>> Arbitrary unexpected truncation of data should not be tolerated.
>>
>> Clifford Heath.
> 
> Well, overrunning a buffer is a bug, so it's just a choice of which type 
> of misbehaviour you prefer.  If I'm writing to some logfile, for 
> instance, truncating the message is a lot better than overwriting memory!

Right, overflow protection is important. So is data integrity.

So make sure you have a big enough buffer for your data, possibly by 
truncating the data before you snprintf(), or by using %.23s or the 
like. Either way you should handle truncation *deliberately*, not by 
simply clamping to a buffer length.

Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#28446

FromDavid Brown <david.brown@hesbynett.no>
Date2018-12-12 09:09 +0100
Message-ID<puqfon$vde$1@dont-email.me>
In reply to#28443
On 12/12/18 00:34, Clifford Heath wrote:
> On 12/12/18 3:41 am, Phil Hobbs wrote:
>> On 12/10/18 4:49 PM, Clifford Heath wrote:
>>> On 10/12/18 9:20 pm, pozz wrote:
>>>> The return value of snprintf() is useful if you want to dynamically
>>>> reallocate the buffer so it can store all the string. In my case I
>>>> would prefer to truncate the string at the end of the static buffer.
>>>
>>> I'd be seriously asking yourself why you prefer that.
>>> Arbitrary unexpected truncation of data should not be tolerated.
>>>
>>> Clifford Heath.
>>
>> Well, overrunning a buffer is a bug, so it's just a choice of which
>> type of misbehaviour you prefer.  If I'm writing to some logfile, for
>> instance, truncating the message is a lot better than overwriting memory!
> 
> Right, overflow protection is important. So is data integrity.
> 
> So make sure you have a big enough buffer for your data, possibly by
> truncating the data before you snprintf(), or by using %.23s or the
> like. Either way you should handle truncation *deliberately*, not by
> simply clamping to a buffer length.
> 

Not all data is equally valuable.  And not all data is of a size that
you know you can handle fully.  I have no idea what the OP's original
needs are here, but a prime example is, as Phil said, a log file.  Often
the details of what are in the log file are not critical - but avoiding
buffer overruns or limiting the bandwidth used /is/ critical.  So
techniques like truncation or rate limiting are entirely reasonable here.


[toc] | [prev] | [next] | [standalone]


#28447

FromClifford Heath <no.spam@please.net>
Date2018-12-12 21:38 +1100
Message-ID<Yy5QD.90920$2g1.87583@fx41.iad>
In reply to#28446
On 12/12/18 7:09 pm, David Brown wrote:
> On 12/12/18 00:34, Clifford Heath wrote:
>> On 12/12/18 3:41 am, Phil Hobbs wrote:
>>> On 12/10/18 4:49 PM, Clifford Heath wrote:
>>>> On 10/12/18 9:20 pm, pozz wrote:
>>>>> The return value of snprintf() is useful if you want to dynamically
>>>>> reallocate the buffer so it can store all the string. In my case I
>>>>> would prefer to truncate the string at the end of the static buffer.
>>>>
>>>> I'd be seriously asking yourself why you prefer that.
>>>> Arbitrary unexpected truncation of data should not be tolerated.
>>>>
>>>> Clifford Heath.
>>>
>>> Well, overrunning a buffer is a bug, so it's just a choice of which
>>> type of misbehaviour you prefer.  If I'm writing to some logfile, for
>>> instance, truncating the message is a lot better than overwriting memory!
>>
>> Right, overflow protection is important. So is data integrity.
>>
>> So make sure you have a big enough buffer for your data, possibly by
>> truncating the data before you snprintf(), or by using %.23s or the
>> like. Either way you should handle truncation *deliberately*, not by
>> simply clamping to a buffer length.
>>
> 
> Not all data is equally valuable.  And not all data is of a size that
> you know you can handle fully.  I have no idea what the OP's original
> needs are here, but a prime example is, as Phil said, a log file.  Often
> the details of what are in the log file are not critical - but avoiding
> buffer overruns or limiting the bandwidth used /is/ critical.  So
> techniques like truncation or rate limiting are entirely reasonable here.

And as long as that's deliberate, that's fine. But the OP's code was 
appending some number of strings to some buffer. It looked far less 
deliberate than it ought, though the effect may be as intended. It also 
splits the buffer-bounds check into many pieces, so any slip fails the 
whole thing.

We used to have a printf that took a putchar function as argument. You 
could pass one that filled a buffer with truncation. Even easier now 
that C++ supports lambda closures.

Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#28464

FromLes Cargill <lcargill99@comcast.com>
Date2018-12-30 11:23 -0600
Message-ID<q0auq8$757$1@dont-email.me>
In reply to#28443
Clifford Heath wrote:
> On 12/12/18 3:41 am, Phil Hobbs wrote:
>> On 12/10/18 4:49 PM, Clifford Heath wrote:
>>> On 10/12/18 9:20 pm, pozz wrote:
>>>> The return value of snprintf() is useful if you want to dynamically 
>>>> reallocate the buffer so it can store all the string. In my case I 
>>>> would prefer to truncate the string at the end of the static buffer.
>>>
>>> I'd be seriously asking yourself why you prefer that.
>>> Arbitrary unexpected truncation of data should not be tolerated.
>>>
>>> Clifford Heath.
>>
>> Well, overrunning a buffer is a bug, so it's just a choice of which 
>> type of misbehaviour you prefer.  If I'm writing to some logfile, for 
>> instance, truncating the message is a lot better than overwriting memory!
> 
> Right, overflow protection is important. So is data integrity.
> 
> So make sure you have a big enough buffer for your data, possibly by 
> truncating the data before you snprintf(), or by using %.23s or the 
> like. Either way you should handle truncation *deliberately*, not by 
> simply clamping to a buffer length.
> 
> Clifford Heath.

SO it seems you can have both in this case - if said buffer is truncated 
you can detect that and reallocate. That being said, prevention is
better, IMO - don't set it up to where it can overrun nor be truncated.
If you have a variation on %s in there, length-check the string. All 
other cases should enable the use of like %32.23lf to limit things.

The one nasty thing about using a static bnuffer is that you'd need to
be able to test it thoroughly and any "reallocation" would require a
recompile.

-- 
Les Cargill

[toc] | [prev] | [next] | [standalone]


#28434

FromHans-Bernhard Bröker <HBBroeker@t-online.de>
Date2018-12-11 00:47 +0100
Message-ID<g78c9gFctonU1@mid.dfncis.de>
In reply to#28418
Am 10.12.2018 um 11:20 schrieb pozz:
> I usually have a static buffer and I don't want to use a dynamic buffer.
> 
> char buf[BUFLEN];
> 
> I have to construct a long string, and it is quite easy to call snprintf
> multiple times:
> 
>   int n = 0;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   n += snprintf(&buf[n], BUFLEN - n, ...);

What exactly do you expect to gain by doing it that way?

> I was convinced the return value was the real number of characters
> written in buf, 

Convinced by what?  I mean, come on, what do we (as a community) pay
documentation writers for, if nobody can be bothered to read what they
created?

> The return value of snprintf() is useful if you want to dynamically
> reallocate the buffer so it can store all the string. 

And that's not at all a coincidence.  It's precisely the reason why the
return value of v?snprintf() is defined slightly differently from that
of all the other *printf() functions.

> In my case I would
> prefer to truncate the string at the end of the static buffer. The above
> code should be redesigned as:
> 
>   int n = 0;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;
>   n += snprintf(&buf[n], BUFLEN - n, ...);
>   if (n >= BUFLEN - n) return;

Or much simpler as

   snprintf(buf, BUFLEN,
      ...
      ...
      ...
      ...
   );
   buf[BUFLEN - 1] = '\0';  // just in case we hit bottom

That said, if you absolutely have to, there's always the %n output format...

[toc] | [prev] | [next] | [standalone]


#28436

FromClifford Heath <no.spam@please.net>
Date2018-12-11 13:31 +1100
Message-ID<QjFPD.494212$9O2.164489@fx03.iad>
In reply to#28434
On 11/12/18 10:47 am, Hans-Bernhard Bröker wrote:
> Or much simpler as
> 
>     snprintf(buf, BUFLEN,
>        ...
>        ...
>        ...
>        ...
>     );

One would assume there's some logic between appending bits of string.

>     buf[BUFLEN - 1] = '\0';  // just in case we hit bottom

Now who needs to read the documentation? snprintf guarantees always to 
null-terminate.

Clifford Heath.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.arch.embedded


csiph-web