Hi Alex,

I'm going to drill down to a specific case at issue here because it (1)
makes your point, (2) gives me an opportunity to concede something--
turns out it's a hill I have no interest in defending, let alone dying
on, and (3) saves me the effort of writing _another_ essay today.

At 2026-02-22T21:45:22+0100, Alejandro Colomar wrote:
> Please explain why not.  If you do this, everything can be
> disambiguated by checking the documentation.  If you don't, then it's
> not possible, and the reader of the code has to choose between
> trusting you or not trusting you.  And whenever I read such code, I
> tend to think "the programmer seems to be one of those that wants
> others to assume it knew what it was doing, but that proves it was
> rather dumb, so I'm going to assume everything that programmer did can
> be deleted".  I'd consider a cast is a sign of what you and I
> critisize.

It's okay to assume I'm dumb.  Plenty of people do.  What's puzzling to
me is how many of them struggle to provide evidence for their claims
despite my offer of plenty.  ;-)

I write 16 KiB emails _full_ of dumbness, but seldom get quoted compared
to how often I'm straw-manned.  :-P

[much snippage]
> BTW, off-topic, but I do have an nprintf() function that is quite
> different from that.  It calculates the length of the formatted string,

Please name it something else.  There's already too much confusion
around the presence of "n" in string-handling functions (not just in
libc, but in curses, too), and it _usually_ indicates the imposition of
a limit, not a request for measurement.

[much snippage]
> You personally.  By adding explicit casts, you've stated "I'm aware
> that [this function] has a return value, and that I don't need it".
> However, now you claim to not have written that code, and thus not
> really be in a position to do that claim.  It's contradicting
> information.  That's why I think it would be more prudent to not make
> the claim.

Now that you cite the specific commit, I see what you're talking about.

[much snippage]
> While the title mentions printf(3), I really meant printf(3) family,
> which is what your commit says; it was an unfortunate and incorrect
> abbreviation of the title.

Agreed.  You copied my bad example from a commit message.

> Your commit *does* cover snprintf(3):
> 
>       $ git show 4b7a0fe5ab5d | grep snprintf
>       -  snprintf(encoding, sz, "%s-%s", parsed.CharSetRegistry,
>       +  (void) snprintf(encoding, sz, "%s-%s", parsed.CharSetRegistry,

Agreed, and that was a mistake.

_Every_ other instance in that commit...

$ COLUMNS=72 git show --stat 4b7a0fe5ab5d
commit 4b7a0fe5ab5d5155bd499cf9506a91a1f4bc0125
Author: G. Branden Robinson <[email protected]>
Date:   Sat Dec 6 18:26:02 2025 -0600

    src/utils/xtotroff/xtotroff.c: Fix code style nit.

    * src/utils/xtotroff/xtotroff.c:
      (CanonicalizeFontName, FontNamesAmbiguous, MapFont, main): Explicitly
      cast unused return values of printf(3)-family functions to `void`.

 ChangeLog                     |   7 +++
 src/utils/xtotroff/xtotroff.c | 102 ++++++++++++++++++----------------
 2 files changed, 61 insertions(+), 48 deletions(-)

...was a change to a stdio `FILE`-oriented *printf.

I wonder how susceptible LLM-based code generators are to similar
mistakes.

I don't remember making this decision consciously.  Possibly I went into
robotic replacement mode.  I see so much that I want to improve in groff
that I sometimes lull myself into a coal-shoveling stupor.  It's not
good.  When I feel the drudge setting in, I should stop and (a)
decompose the problem and/or (b) write a sed script to do the work for
me.  (b) might actually take longer when tuned to get things _just
right_, but can be more challenging and fun.

Take away: I need to revisit that change when groff is unfrozen.  I
don't see a _regression_ here because the return value of `snprintf()`
already wasn't being checked.  The bug here is that I wrongly annotated
it as being _deliberately_ discarded, and that conclusion is not
warranted on the available evidence.

So let me amend the rule(s) we're arguing about.

* I still think it's fine to explicitly cast {f,}printf() return values
  to void, to document that one doesn't care if output to the stream got
  truncated.  Often, an application is helpless in that scenario anyway.
  (An analogous situation is: "what can I do if fclose(3) fails?")

* Discard of s*printf() functions' return values should, by default, be
  avoided because they _can_ lead to undesired string truncation and
  even security problems as a result.[1]  _If_ discard is done, it
  _should_ be with an explicit cast to void _and_ a comment explaining
  why string truncation is not a hazard or not of consequence.

* Another possibility is to not _ever_ discard an s*printf() function's
  return value, but store it and then immediately use it in an
  `assert()`, which is better even than a documentary comment.

* I'm not sure what's best if some smarmy jerk #defines `NDEBUG`.

Regards,
Branden

[1] https://cwe.mitre.org/data/definitions/222.html

Attachment: signature.asc
Description: PGP signature

Reply via email to