NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: bin/60499: printf(1) doesn't support positional arguments (%n$...)



The following reply was made to PR bin/60499; it has been noted by GNATS.

From: Robert Elz <kre%munnari.OZ.AU@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc: 
Subject: Re: bin/60499: printf(1) doesn't support positional arguments (%n$...)
Date: Sat, 25 Jul 2026 09:23:12 +0700

     Date:        Fri, 24 Jul 2026 21:30:01 +0000 (UTC)
     From:        "uwe%stderr.spb.ru@localhost via gnats" <gnats-admin%NetBSD.org@localhost>
     Message-ID:  <20260724213001.C55141A923D%mollari.NetBSD.org@localhost>
 
   | printf(1) doesn't seem to support positional parameters "%n$..."
   | Issue 8 seems to prescribe it:
 
 It does.   If it didn't, we would most likely have support!
 
 The problem is that I simply find the spec in POSIX absurd, so rather
 that implement that, I implemented (committed really) nothing.
 
 Note that the issue doesn't affect any normal use of the things,
 but would have affected your test case (potentially):
 
   |   printf '\tR%1$d, G%1$d = R%1$d,\n' $(seq 0 7)
 
 That is, when there are more args (8 in that case) than are used
 by the format string (just 1 here).
 
 printf(3) is not affected at all by these issues, it never attempts
 to repeat the format string, to use up all the args (it has no way to
 determine how many of them there were) - one potential solution for
 printf(1) would be to make it the same, so if there are any positional
 args, the format string would only be used once.
 
 The would easily deal with all the issues when the positional args are
 being used for their original purpose - supporting output of i18n strings,
 where different cultures output messages with the parameters in different
 orders, and not necessarily using all of the args (any of them might be
 omitted).
 
 It wouldn't however handle people who have decided that printf(1)'s
 positional params can be (ab)used to do all kinds of other tricks, like
 the one you are attempting above, which could just as easily be
 written
 
 	for A in $(seq 0 7)
 	do
 		printf '\tR%d, G%d = R%d,\n' "$A" "$A" "$A"
 	done
 
 If you do it that way (and something like this is *always* possible
 in these kinds of cases) there is never any ambiguity about what is
 intended - there's just a little less opportunity to show off just
 how clever you are.
 
 There are other (not as simple as that one, but still relatively sane)
 potential solutions, which are not as broken as what POSIX prescribes
 however -- personally though I kind of like the simple elegance of
 simply ignoring stray extra args when there are positional params, but
 none happens to select the final args -- which can easily happen when
 an internationised printf usage (where the format string comes from a
 message catalog) and the language in question happens not to need the
 final arg.   printf(3) wouldn't care, printf(1) probably shouldn't either.
 
   | $ printf: %1$: invalid directive
   | R$
 
   | PS: note also, how the format is not checked in advance and the initial
   | prefix is printed.
 
 That is actually a requirement.  I agree in most cases a fairly silly one,
 but in general printf(1) is always required to print everything it has
 accumulated up to the time an error is detected (including bogus results
 of conversions in many cases).
 
 kre
 



Home | Main Index | Thread Index | Old Index