Path: csiph.com!weretis.net!feeder8.news.weretis.net!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: Tim Rentsch
Newsgroups: comp.lang.c
Subject: Re: Why does C allow structs to have a tag?
Date: Fri, 01 Oct 2021 15:04:41 -0700
Organization: A noiseless patient Spider
Lines: 116
Message-ID: <8635pktn9i.fsf@linuxsc.com>
References: <87czqnlp2m.fsf@bsb.me.uk> <875ywci8k0.fsf@bsb.me.uk> <63773d19-8176-43a0-9c6d-47f72b1e1c10n@googlegroups.com> <87tujwgldp.fsf@bsb.me.uk>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Injection-Info: reader02.eternal-september.org; posting-host="394007ef4ff0447a8819fcdafe9c3245"; logging-data="22331"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/0iAhKj7qOhu9f5V9p3PQXIe9D1n06lfI="
User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux)
Cancel-Lock: sha1:Wojrm41GlUlkqLlWHRYYl02g/uk= sha1:haGCTXl1oWhiljHiIAtRJqhY9+g=
Xref: csiph.com comp.lang.c:162923
Michael S writes:
[...]
> The purpose of my_pritntf() is to prevent buffer overflows [...]
> rather than to always output something meaningful. [...]
Sounds useful and practical.
> [..some whitespace added in what follows..]
>
> #include
> #include
> #include
>
> #include "my_snprintf.h"
>
> // my_snprintf - Write formatted output to sized buffer
> // my_snprintf is similar to snprintf() with following modifications
> //
> // 1. Argument n is signed. For n <= 0 the function does nothing
> // and returns 0
> //
> // 2. Return value is always a number of characters that have been
> // into s, not counting the terminating null character.
> //
> // The rest of description applies to case of positive n.
> // Unlike snprintf(), if an encoding error occurs, a 0 is returned
> // and the terminating null character is written to s[0].
> // Unlike snprintf(), if n had not been sufficiently large, a
> // value of n-1 is returned and the terminating null character is
> // written to s[n-1].
>
> int
> my_snprintf(char * s, ptrdiff_t n, const char * format, ...)
> {
> if (n <= 0)
> return 0;
> if (n > INT_MAX)
> n = INT_MAX;
>
> va_list args;
> va_start(args, format);
> int ret = vsnprintf(s, n, format, args);
> if (ret < 0)
> { // encoding error.
> // since our code uses no UNICODE, it should never happen,
> // but let's handle it nevertheless
> s[0] = 0; // may be, unnecessary
> return 0;
> }
>
> if (ret >= n) {
> s[n-1] = 0; // most likely unnecessary
> return n-1;
> }
>
> return ret;
> }
Some short comments:
One: signed types have issues. Rather than using a signed type
for the parameter 'n', use a large unsigned type such as unsigned
long long. Doing that still allows arguments having a signed
type to be checked for negative values, following the rules for
conversion of a signed value to an unsigned type.
Two: va_start() always requires a corresponding va_end().
Three: when supplying a *printf() function, it is customary to
supply also a corresponding *vprintf() function, which makes
writing the non-v form straightforward. It's two functions for
the price of one (plus only a little bit). Also it makes the
habit of using va_end() along with va_start() fall out with
essentially no effort.
To illustrate these points in C code, here is a revised writing
of my_snprintf(), along with my_vsnprintf();
// --- cut here ---
#include
#include
#include
typedef unsigned long long ULL;
extern int my_vsnprintf( char *, ULL, const char *, va_list );
int
my_snprintf( char * s, ULL n, const char * format, ... ){
va_list args;
int r;
va_start( args, format );
r = my_vsnprintf( s, n, format, args );
va_end( args );
return r;
}
int
my_vsnprintf( char * s, ULL n0, const char * format, va_list args ){
if( n0 > -1ULL>>1 || n0 == 0 ) return 0;
size_t n = n0 > INT_MAX ? INT_MAX : n0;
int ret = vsnprintf( s, n, format, args );
return
ret < 0 ? s[0] = 0, 0 :
ret >= n ? s[n-1] = 0, n-1 :
ret;
}