On 8/5/24, Paul Eggert wrote:

If snprintf (buf, sizeof buf, ...) returns an integer I such that (0 <= I < sizeof buf) then the snprintf worked, the contents of buf[0] through buf[I] are valid, and buf[I] == '\0'. Otherwise snprintf didn't work and buf's contents are indeterminate.

In practice, then "... contents are indeterminate" is too pessimistic.
Nearly always the contents are a good approximation to what is desired.
Often it is necessary to make progress despite imperfect snprintf.

Over several decades of porting, maintaining, and developing apps,
I used these three techniques to make progress with a large handful
of different implementations (and specifications!) of snprintf:

1) Terminate the buffer yourself, anyway:
     snprintf(buffer, length, format, args...);
     buffer[-1 + length] = '\0';
  This defends against implementations that forget to terminate
  the buffer on overflow.

2) If the resulting length is needed, then apply strlen() anyway:
     len1 = snprintf(buffer, length, format, args...);
     buffer[ -1 + length] = '\0';
     len2 = strlen(buffer);
  This "extra work" will be fast enough because the buffer
  will be in the cache.  You can be fooled by printing a NUL
  byte ('\0') using a "%c" format specifier. But in 2 of my 3
  actual cases, this helped discover a bug in the caller:
  the argument should not have been a NUL byte.
  You also can be fooled by a bug in formatting an extreme
  floating point value (+/- infinity, NaN, denormal).

3) Use two canary bytes:
     buffer[-1 + length] = '\xA5';  /* any non-NUL byte */
     buffer[-2 + length] = '\0';
     len1 = snprintf(buffer, length, format, args...);
     if ('\0' != buffer[-2 + length]) {
          /* Check both canaries and len1 carefully. */
          /* Details omitted here. */
     }
     buffer[-1 + length] = '\0';  /* Force terminate. */

--


Reply via email to