diff mbox series

[1/2] gettext API users: correct use of casts for Q_()

Message ID patch-1.2-83659fbc459-20220307T113707Z-avarab@gmail.com (mailing list archive)
State Superseded
Headers show
Series string-list.h: make "nr" and "alloc" a "size_t" | expand

Commit Message

Ævar Arnfjörð Bjarmason March 7, 2022, 11:38 a.m. UTC
Change users of the inline gettext.h Q_() function to cast its
argument to "unsigned long" instead of "int" or "unsigned int".

The ngettext() function (which Q_() resolves to) takes an "unsigned
long int", and so does our Q_() wrapper for it, see 0c9ea33b90f (i18n:
add stub Q_() wrapper for ngettext, 2011-03-09).

In a subsequent commit we'll be making more use of this pattern of:

    func(Q_(..%"PRIuMAX..., (unsigned long)x), (uintmax_t)x);

By making this change we ensure that this case isn't the odd one out
in that post-image.

This:

 * Corrects code added in 7171a0b0cf5 (index-pack: correct "len" type
   in unpack_data(), 2016-07-13) to cast the "off_t len" to an
   "unsigned long int" rather than an "unsigned int".

 * Does the same for code in add-interactive.c added in several
   commits starting with a8c45be939d (built-in add -i: implement the
   `update` command, 2019-11-29).

 * Likewise for a case in 9254bdfb4f9 (built-in add -p: implement the
   'g' ("goto") command, 2019-12-13) where only the err() argument had
   a cast, but not the same argument to Q_().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c    | 15 +++++++++------
 add-patch.c          |  8 ++++----
 builtin/index-pack.c |  2 +-
 3 files changed, 14 insertions(+), 11 deletions(-)

Comments

Derrick Stolee March 7, 2022, 1:41 p.m. UTC | #1
On 3/7/2022 6:38 AM, Ævar Arnfjörð Bjarmason wrote:
> Change users of the inline gettext.h Q_() function to cast its
> argument to "unsigned long" instead of "int" or "unsigned int".
> 
> The ngettext() function (which Q_() resolves to) takes an "unsigned
> long int", and so does our Q_() wrapper for it, see 0c9ea33b90f (i18n:
> add stub Q_() wrapper for ngettext, 2011-03-09).
> 
> In a subsequent commit we'll be making more use of this pattern of:
> 
>     func(Q_(..%"PRIuMAX..., (unsigned long)x), (uintmax_t)x);
> 
> By making this change we ensure that this case isn't the odd one out
> in that post-image.


>  	if (!res)
> -		printf(Q_("updated %d path\n",
> -			  "updated %d paths\n", count), (int)count);
> +		printf(Q_("updated %"PRIuMAX" path\n",
> +			  "updated %"PRIuMAX" paths\n", (unsigned long)count),
> +		       (uintmax_t)count);

Why are we adding more uses of "unsigned long" which is not consistent
in its size across 64-bit Linux and 64-bit Windows? Specifically, on
Windows "unsigned long" is _not_ uintmax_t. Shouldn't we be using
uintmax_t everywhere instead?

Thanks,
-Stolee
Ævar Arnfjörð Bjarmason March 7, 2022, 1:54 p.m. UTC | #2
On Mon, Mar 07 2022, Derrick Stolee wrote:

> On 3/7/2022 6:38 AM, Ævar Arnfjörð Bjarmason wrote:
>> Change users of the inline gettext.h Q_() function to cast its
>> argument to "unsigned long" instead of "int" or "unsigned int".
>> 
>> The ngettext() function (which Q_() resolves to) takes an "unsigned
>> long int", and so does our Q_() wrapper for it, see 0c9ea33b90f (i18n:
>> add stub Q_() wrapper for ngettext, 2011-03-09).
>> 
>> In a subsequent commit we'll be making more use of this pattern of:
>> 
>>     func(Q_(..%"PRIuMAX..., (unsigned long)x), (uintmax_t)x);
>> 
>> By making this change we ensure that this case isn't the odd one out
>> in that post-image.
>
>
>>  	if (!res)
>> -		printf(Q_("updated %d path\n",
>> -			  "updated %d paths\n", count), (int)count);
>> +		printf(Q_("updated %"PRIuMAX" path\n",
>> +			  "updated %"PRIuMAX" paths\n", (unsigned long)count),
>> +		       (uintmax_t)count);
>
> Why are we adding more uses of "unsigned long" which is not consistent
> in its size across 64-bit Linux and 64-bit Windows? Specifically, on
> Windows "unsigned long" is _not_ uintmax_t. Shouldn't we be using
> uintmax_t everywhere instead?

Whatever we do with "unsigned long" v.s. "size_t" or "uintmax_t" here
we'll need to call the ngettext() function, which takes "unsigned long".

Since you're quoting the part of the commit message that's explaining
that I'm not sure if you're meaning this as a suggestion that the
explanation should be clearer/more explicit, or just missed that
ngettext() isn't ours...

I did wonder if we should just skip the casts here and instead do:
	
	diff --git a/gettext.h b/gettext.h
	index d209911ebb8..095bf6b0e5e 100644
	--- a/gettext.h
	+++ b/gettext.h
	@@ -49,8 +49,10 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
	 }
	 
	 static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)
	-const char *Q_(const char *msgid, const char *plu, unsigned long n)
	+const char *Q_(const char *msgid, const char *plu, size_t n)
	 {
	+	if (n > ULONG_MAX)
	+		n = ULONG_MAX;
	 	return ngettext(msgid, plu, n);
	 }

Which I suppose would be more correct than a cast, but the edge case
where we'd need that ULONG_MAX is so obscure that I don't think it's
worth worrying about it.

I think for this series it probably makes more sense to drop the casts
for the "n" argument entirely, what do you think?
Derrick Stolee March 7, 2022, 3:53 p.m. UTC | #3
On 3/7/2022 8:54 AM, Ævar Arnfjörð Bjarmason wrote:
> 
> On Mon, Mar 07 2022, Derrick Stolee wrote:
> 
>> On 3/7/2022 6:38 AM, Ævar Arnfjörð Bjarmason wrote:
>>> Change users of the inline gettext.h Q_() function to cast its
>>> argument to "unsigned long" instead of "int" or "unsigned int".
>>>
>>> The ngettext() function (which Q_() resolves to) takes an "unsigned
>>> long int", and so does our Q_() wrapper for it, see 0c9ea33b90f (i18n:
>>> add stub Q_() wrapper for ngettext, 2011-03-09).
>>>
>>> In a subsequent commit we'll be making more use of this pattern of:
>>>
>>>     func(Q_(..%"PRIuMAX..., (unsigned long)x), (uintmax_t)x);
>>>
>>> By making this change we ensure that this case isn't the odd one out
>>> in that post-image.
>>
>>
>>>  	if (!res)
>>> -		printf(Q_("updated %d path\n",
>>> -			  "updated %d paths\n", count), (int)count);
>>> +		printf(Q_("updated %"PRIuMAX" path\n",
>>> +			  "updated %"PRIuMAX" paths\n", (unsigned long)count),
>>> +		       (uintmax_t)count);
>>
>> Why are we adding more uses of "unsigned long" which is not consistent
>> in its size across 64-bit Linux and 64-bit Windows? Specifically, on
>> Windows "unsigned long" is _not_ uintmax_t. Shouldn't we be using
>> uintmax_t everywhere instead?
> 
> Whatever we do with "unsigned long" v.s. "size_t" or "uintmax_t" here
> we'll need to call the ngettext() function, which takes "unsigned long".
> 
> Since you're quoting the part of the commit message that's explaining
> that I'm not sure if you're meaning this as a suggestion that the
> explanation should be clearer/more explicit, or just missed that
> ngettext() isn't ours...

Mostly missed which parts we had control over or not.

> I did wonder if we should just skip the casts here and instead do:
> 	
> 	diff --git a/gettext.h b/gettext.h
> 	index d209911ebb8..095bf6b0e5e 100644
> 	--- a/gettext.h
> 	+++ b/gettext.h
> 	@@ -49,8 +49,10 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
> 	 }
> 	 
> 	 static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)
> 	-const char *Q_(const char *msgid, const char *plu, unsigned long n)
> 	+const char *Q_(const char *msgid, const char *plu, size_t n)
> 	 {
> 	+	if (n > ULONG_MAX)
> 	+		n = ULONG_MAX;
> 	 	return ngettext(msgid, plu, n);
> 	 }
> 
> Which I suppose would be more correct than a cast, but the edge case
> where we'd need that ULONG_MAX is so obscure that I don't think it's
> worth worrying about it.
> 
> I think for this series it probably makes more sense to drop the casts
> for the "n" argument entirely, what do you think?

I agree that this is a better approach. It avoids polluting all of
the callers with down-casts and instead puts it here. The only
difference is that we will truncate at ULONG_MAX instead of blindly
ignoring the significant bits, which is probably better.

Thanks,
-Stolee
diff mbox series

Patch

diff --git a/add-interactive.c b/add-interactive.c
index e1ab39cce30..6da781004ad 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -707,8 +707,9 @@  static int run_update(struct add_i_state *s, const struct pathspec *ps,
 		res = error(_("could not write index"));
 
 	if (!res)
-		printf(Q_("updated %d path\n",
-			  "updated %d paths\n", count), (int)count);
+		printf(Q_("updated %"PRIuMAX" path\n",
+			  "updated %"PRIuMAX" paths\n", (unsigned long)count),
+		       (uintmax_t)count);
 
 	putchar('\n');
 	return res;
@@ -814,8 +815,9 @@  static int run_revert(struct add_i_state *s, const struct pathspec *ps,
 						   NULL, NULL, NULL);
 
 	if (!res)
-		printf(Q_("reverted %d path\n",
-			  "reverted %d paths\n", count), (int)count);
+		printf(Q_("reverted %"PRIuMAX" path\n",
+			  "reverted %"PRIuMAX" paths\n", (unsigned long)count),
+		       (uintmax_t)count);
 
 finish_revert:
 	putchar('\n');
@@ -896,8 +898,9 @@  static int run_add_untracked(struct add_i_state *s, const struct pathspec *ps,
 		res = error(_("could not write index"));
 
 	if (!res)
-		printf(Q_("added %d path\n",
-			  "added %d paths\n", count), (int)count);
+		printf(Q_("added %"PRIuMAX" path\n",
+			  "added %"PRIuMAX" paths\n", (unsigned long)count),
+		       (uintmax_t)count);
 
 finish_add_untracked:
 	putchar('\n');
diff --git a/add-patch.c b/add-patch.c
index 55d719f7845..dfef00e5680 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -1569,10 +1569,10 @@  static int patch_update_file(struct add_p_state *s,
 			else if (0 < response && response <= file_diff->hunk_nr)
 				hunk_index = response - 1;
 			else
-				err(s, Q_("Sorry, only %d hunk available.",
-					  "Sorry, only %d hunks available.",
-					  file_diff->hunk_nr),
-				    (int)file_diff->hunk_nr);
+				err(s, Q_("Sorry, only %"PRIuMAX" hunk available.",
+					  "Sorry, only %"PRIuMAX" hunks available.",
+					  (unsigned long)file_diff->hunk_nr),
+				    (uintmax_t)file_diff->hunk_nr);
 		} else if (s->answer.buf[0] == '/') {
 			regex_t regex;
 			int ret;
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 3c2e6aee3cc..f15b59e22b0 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -579,7 +579,7 @@  static void *unpack_data(struct object_entry *obj,
 		if (!n)
 			die(Q_("premature end of pack file, %"PRIuMAX" byte missing",
 			       "premature end of pack file, %"PRIuMAX" bytes missing",
-			       (unsigned int)len),
+			       (unsigned long)len),
 			    (uintmax_t)len);
 		from += n;
 		len -= n;