[vim/vim] *printf vs vim_*printf for translation (Issue #10577)

21 views
Skip to first unread message

依云

unread,
Jun 15, 2022, 5:53:57 AM6/15/22
to vim/vim, Subscribed

Is your feature request about something that is currently impossible or hard to do? Please describe the problem.

I see both libc *printf and vim's own vim_*printf are used. The latter doesn't support format specifiers like %1$s which specifies which argument to use.

This is used to rephrase translation messages because different languages have different ordering.

Describe the solution you'd like

Use libc's *printf throughout the codebase.

Most systems including Linux (glibc), FreeBSD, OpenBSD, MacOS support this as I read their man pages. Windows may not support this but I plan to fallback to foreign order for it.

Describe alternatives you've considered

  • Use an unnatural, non-native order in the translation. This will make the translated messages awkward to read. Translation should make messages easier to read, not the other way around.
  • Implement %1$s in vim_*printf. Someone (not me) needs to write the code.


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/10577@github.com>

Bram Moolenaar

unread,
Jun 15, 2022, 6:55:25 AM6/15/22
to vim/vim, Subscribed

The reason we use vim_snprintf() is that snprintf() is not available or not properly implemented on some platforms. We could use the library snprintf() when it works, but then the translations would fail, and possibly cause a crash, when vim_snprintf() is used.

A generic solution would be to make the "%1$s" form work with vim_snprintf(). I don't know how complicated this is, getting the right argument from through va_arg().


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/10577/1156321234@github.com>

Christ van Willegen

unread,
Jun 15, 2022, 7:48:56 AM6/15/22
to vim...@googlegroups.com, reply+ACY5DGDSJAIUHFSHVT...@reply.github.com
Hi, 

Op wo 15 jun. 2022 12:55 schreef Bram Moolenaar <vim-dev...@256bit.org>:

A generic solution would be to make the "%1$s" form work with vim_snprintf(). I don't know how complicated this is, getting the right argument from through va_arg().


I'll try to see if I can come up with something...

Christ van Willegen

vim-dev ML

unread,
Jun 15, 2022, 7:49:13 AM6/15/22
to vim/vim, vim-dev ML, Your activity

Hi,

Op wo 15 jun. 2022 12:55 schreef Bram Moolenaar ***@***.***>:


> A generic solution would be to make the "%1$s" form work with
> vim_snprintf(). I don't know how complicated this is, getting the right
> argument from through va_arg().
>

I'll try to see if I can come up with something...

Christ van Willegen


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/10577/1156372790@github.com>

Christ van Willegen

unread,
Feb 26, 2023, 9:45:53 AM2/26/23
to vim...@googlegroups.com, reply+ACY5DGAUA5UID7PVC6...@reply.github.com


Op wo 15 jun. 2022 13:49 schreef vim-dev ML <vim-dev...@256bit.org>:
Hi,

Op wo 15 jun. 2022 12:55 schreef Bram Moolenaar ***@***.***>:

> A generic solution would be to make the "%1$s" form work with
> vim_snprintf(). I don't know how complicated this is, getting the right
> argument from through va_arg().
>

I'll try to see if I can come up with something...

Christ van Willegen

I didn't realize this was on my to do list so long ago already!

Anyway, I'm (finally!) working on this, and got quite a bit working already. Here are a few of my test cases:

in src/message_test.c

n = vim_snprintf(buf, bsize, "%1$*2$ld", 1234567L, -9);
n = vim_snprintf(buf, bsize, "%1$*2$.*3$ld", 1234567L, -9, 5);
n = vim_snprintf(buf, bsize, "%1$*3$.*2$ld", 1234567L, 5, -9);
n = vim_snprintf(buf, bsize, "%3$*1$.*2$ld", -9, 5, 1234567L);
n = vim_snprintf(buf, bsize, "%1$ld", 1234567L);
n = vim_snprintf(buf, bsize, "%1$*2$ld", 1234567L, 9);
n = vim_snprintf(buf, bsize, "%2$s %1$s %2$s", "one", "two", "three");
n = vim_snprintf(buf, bsize, "%3$s %1$s %2$s", "one", "two", "three");
n = vim_snprintf(buf, bsize, "%1$d", 1234567);
n = vim_snprintf(buf, bsize, "%1$x", 0xdeadbeef);
n = vim_snprintf(buf, bsize, "%01$.*2$b", (uvarnumber_T)12, 6);
n = vim_snprintf(buf, bsize, "%1$s %2$s", "one", "two");

in src/testdit/test_format.vim

call assert_equal('123', printf('%1$d', 123))
call assert_equal('00005', printf('%1$*1$.*1$d', 5))
call assert_equal('123', printf('%1$i', 123))
call assert_equal('123', printf('%1$D', 123))
call assert_equal('123', printf('%1$U', 123))
call assert_equal('173', printf('%1$o', 123))
call assert_equal('173', printf('%1$O', 123))
call assert_equal('7b', printf('%1$x', 123))
call assert_equal('7B', printf('%1$X', 123))

call assert_equal('123', printf('%1$hd', 123))
call assert_equal('-123', printf('%1$hd', -123))
call assert_equal('-1', printf('%1$hd', 0xFFFF))
call assert_equal('-1', printf('%1$hd', 0x1FFFFF))

call assert_equal('123', printf('%1$hu', 123))
call assert_equal('65413', printf('%1$hu', -123))
call assert_equal('65535', printf('%1$hu', 0xFFFF))
call assert_equal('65535', printf('%1$hu', 0x1FFFFF))

call assert_equal('123', printf('%1$ld', 123))
call assert_equal('-123', printf('%1$ld', -123))
call assert_equal('65535', printf('%1$ld', 0xFFFF))
call assert_equal('131071', printf('%1$ld', 0x1FFFF))

call assert_equal('{', printf('%1$c', 123))
call assert_equal('abc', printf('%1$s', 'abc'))
call assert_equal('abc', printf('%1$S', 'abc'))

call assert_equal('+123', printf('%+1$d', 123))
call assert_equal('-123', printf('%+1$d', -123))

call assert_equal('+123', printf('%+ 1$d', 123))
call assert_equal(' 123', printf('% 1$d', 123))
call assert_equal(' 123', printf('%  1$d', 123))
call assert_equal('-123', printf('% 1$d', -123))

call assert_equal('  123', printf('%2$*1$d', 5, 123))
call assert_equal('123  ', printf('%2$*1$d', -5, 123))
call assert_equal('00123', printf('%2$.*1$d', 5, 123))
call assert_equal('  123', printf('% 2$*1$d', 5, 123))
call assert_equal(' +123', printf('%+ 2$*1$d', 5, 123))

call assert_equal('  123', printf('%1$*2$d', 123, 5))
call assert_equal('123  ', printf('%1$*2$d', 123, -5))
call assert_equal('00123', printf('%1$.*2$d', 123, 5))
call assert_equal('  123', printf('% 1$*2$d', 123, 5))
call assert_equal(' +123', printf('%+ 1$*2$d', 123, 5))

call assert_equal('foobar', printf('%2$.*1$s',  9, 'foobar'))
call assert_equal('foo',    printf('%2$.*1$s',  3, 'foobar'))
call assert_equal('',       printf('%2$.*1$s',  0, 'foobar'))
call assert_equal('foobar', printf('%2$.*1$s', -1, 'foobar'))

#" Unrecognized format specifier kept as-is.
call assert_equal('_123', printf("%_%1$d", 123))

#" Test alternate forms.
call assert_equal('0x7b', printf('%#1$x', 123))
call assert_equal('0X7B', printf('%#1$X', 123))
call assert_equal('0173', printf('%#1$o', 123))
call assert_equal('0173', printf('%#1$O', 123))
call assert_equal('abc', printf('%#1$s', 'abc'))
call assert_equal('abc', printf('%#1$S', 'abc'))

#" Try argument re-use and argument swapping
call assert_equal('one two one', printf('%1$s %2$s %1$s', "one", "two"))
call assert_equal('Screen height: 400', printf('%1$s height: %2$d', "Screen", 400))
call assert_equal('400 is: Screen height', printf('%2$d is: %1$s height', "Screen", 400))

call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1400:")
call v9.CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1400:")
call v9.CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1401:")
call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1402:")
call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1402:")
call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1403:")
call v9.CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1404:")
call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1405:")
call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1405:")


//// Errors are defined as:

EXTERN char e_cannot_mix_positional_and_non_positional_str[]
        INIT(= N_("E1400: Cannot mix positional and non-positional arguments: %s"));
EXTERN char e_fmt_arg_nr_unused_str[]
        INIT(= N_("E1401: format argument %d unused in $-style format: %s"));
EXTERN char e_positional_num_field_spec_reused_str_str[]
        INIT(= N_("E1402: Positional argument %d used as field width reused as different type: %s/%s"));
EXTERN char e_positional_nr_out_of_bounds_str[]
        INIT(= N_("E1403: Positional argument %d out of bounds: %s"));
EXTERN char e_positional_arg_num_type_inconsistent_str_str[]
        INIT(= N_("E1404: Positional argument %d type used inconsistently: %s/%s"));
EXTERN char e_invalid_format_specifier_str[]
        INIT(= N_("E1405: Invalid format specifier: %s"));

//// End of error definitions

call assert_equal('1.000000', printf('%1$f', 1))
call assert_equal('1.230000', printf('%1$f', 1.23))
call assert_equal('1.230000', printf('%1$F', 1.23))
call assert_equal('9999999.9', printf('%1$g', 9999999.9))
call assert_equal('9999999.9', printf('%1$G', 9999999.9))
call assert_equal('1.230000e+00', printf('%1$e', 1.23))
call assert_equal('1.230000E+00', printf('%1$E', 1.23))
call assert_equal('1.200000e-02', printf('%1$e', 0.012))
call assert_equal('-1.200000e-02', printf('%1$e', -0.012))
call assert_equal('0.33', printf('%1$.2f', 1.0 / 3.0))

#" Float zero can be signed.
call assert_equal('+0.000000', printf('%+1$f', 0.0))
call assert_equal('0.000000', printf('%1$f', 1.0 / (1.0 / 0.0)))
call assert_equal('-0.000000', printf('%1$f', 1.0 / (-1.0 / 0.0)))
call assert_equal('0.0', printf('%1$s', 1.0 / (1.0 / 0.0)))
call assert_equal('-0.0', printf('%1$s', 1.0 / (-1.0 / 0.0)))
call assert_equal('0.0', printf('%1$S', 1.0 / (1.0 / 0.0)))
call assert_equal('-0.0', printf('%1$S', 1.0 / (-1.0 / 0.0)))

#" Float infinity can be signed.
call assert_equal('inf', printf('%1$f', 1.0 / 0.0))
call assert_equal('-inf', printf('%1$f', -1.0 / 0.0))
call assert_equal('inf', printf('%1$g', 1.0 / 0.0))
call assert_equal('-inf', printf('%1$g', -1.0 / 0.0))
call assert_equal('inf', printf('%1$e', 1.0 / 0.0))
call assert_equal('-inf', printf('%1$e', -1.0 / 0.0))
call assert_equal('INF', printf('%1$F', 1.0 / 0.0))
call assert_equal('-INF', printf('%1$F', -1.0 / 0.0))
call assert_equal('INF', printf('%1$E', 1.0 / 0.0))
call assert_equal('-INF', printf('%1$E', -1.0 / 0.0))
call assert_equal('INF', printf('%1$E', 1.0 / 0.0))
call assert_equal('-INF', printf('%1$G', -1.0 / 0.0))
call assert_equal('+inf', printf('%+1$f', 1.0 / 0.0))
call assert_equal('-inf', printf('%+1$f', -1.0 / 0.0))
call assert_equal(' inf', printf('% 1$f',  1.0 / 0.0))

There are more tests, but this is the largest set with the best coverage I can find at the moment.

Of course, the 'normal' format strings without positional arguments are still supported...

Before sending a PR, I would like to have more test cases, both valid ones, and ones being invalid.

Can the Vim community help me to cover more test cases?

Christ van Willegen

vim-dev ML

unread,
Feb 26, 2023, 9:46:09 AM2/26/23
to vim/vim, vim-dev ML, Your activity

Op wo 15 jun. 2022 13:49 schreef vim-dev ML ***@***.***>:


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/10577/1445379342@github.com>

依云

unread,
Mar 2, 2023, 5:20:57 AM3/2/23
to vim/vim, vim-dev ML, Comment

Hi Christ van Willegen, where is your patch that I can try out?


Reply to this email directly, view it on GitHub.

You are receiving this because you commented.Message ID: <vim/vim/issues/10577/1451631862@github.com>

Christ van Willegen

unread,
Mar 2, 2023, 6:05:27 AM3/2/23
to vim...@googlegroups.com, reply+ACY5DGGAQHBTBB3ZEH...@reply.github.com
Hi,

Op do 2 mrt. 2023 11:20 schreef 依云 <vim-dev...@256bit.org>:

Hi Christ van Willegen, where is your patch that I can try out?


On my computer so far :-)

If I send you the output of "git diff", is that enough to test?

Christ van Willegen

vim-dev ML

unread,
Mar 2, 2023, 6:05:47 AM3/2/23
to vim/vim, vim-dev ML, Your activity

Hi,

Op do 2 mrt. 2023 11:20 schreef 依云 ***@***.***>:


> Hi Christ van Willegen, where is your patch that I can try out?
>

On my computer so far :-)

If I send you the output of "git diff", is that enough to test?

Christ van Willegen

>


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/10577/1451689335@github.com>

依云

unread,
Mar 2, 2023, 7:08:08 AM3/2/23
to vim/vim, vim-dev ML, Comment

If I send you the output of "git diff", is that enough to test?

Yes!


Reply to this email directly, view it on GitHub.

You are receiving this because you commented.Message ID: <vim/vim/issues/10577/1451765414@github.com>

Christ van Willegen

unread,
Mar 2, 2023, 7:10:58 AM3/2/23
to vim...@googlegroups.com, reply+ACY5DGBKEMQ6NJANZ2...@reply.github.com


Op do 2 mrt. 2023 13:08 schreef 依云 <vim-dev...@256bit.org>:

If I send you the output of "git diff", is that enough to test?

Yes!


I'll be home in about 4.5 hours. That would, probably, be in the middle of the night for you...

I'll send it via direct email...

Christ van Willegen

vim-dev ML

unread,
Mar 2, 2023, 7:11:17 AM3/2/23
to vim/vim, vim-dev ML, Your activity

Op do 2 mrt. 2023 13:08 schreef 依云 ***@***.***>:


> If I send you the output of "git diff", is that enough to test?
>
> Yes!
>

I'll be home in about 4.5 hours. That would, probably, be in the middle of
the night for you...

I'll send it via direct email...

Christ van Willegen

>


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/10577/1451769517@github.com>

cvwillegen

unread,
Mar 12, 2023, 3:50:58 PM3/12/23
to vim/vim, vim-dev ML, Comment

Tentatively fixed by #12140


Reply to this email directly, view it on GitHub.

You are receiving this because you commented.Message ID: <vim/vim/issues/10577/1465284296@github.com>

cvwillegen

unread,
Mar 20, 2023, 6:19:53 AM3/20/23
to vim/vim, vim-dev ML, Comment

Hi, Op do 2 mrt. 2023 11:20 schreef 依云 @.***>:


Hi Christ van Willegen, where is your patch that I can try out?

Please check out the latest commit on #12140, I've caught an error (I had read documentation that was .. not as detailed as I wanted it to, and had reversed the [flags] and [pos-arguments] part of the format string).


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.Message ID: <vim/vim/issues/10577/1475965253@github.com>

cvwillegen

unread,
Apr 10, 2023, 8:10:43 AM4/10/23
to vim/vim, vim-dev ML, Comment

Hi, Op do 2 mrt. 2023 11:20 schreef 依云 @.***>:
Hi Christ van Willegen, where is your patch that I can try out?

Please check out the latest commit on #12140, I've caught an error (I had read documentation that was .. not as detailed as I wanted it to, and had reversed the [flags] and [pos-arguments] part of the format string).

A generic solution would be to make the "%1$s" form work with vim_snprintf(). I don't know how complicated this is, getting the right argument from through va_arg().

I implemented this in this pr (#12140), but I'm not sure if you caught that...

Christ van Willegen


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.Message ID: <vim/vim/issues/10577/1501747663@github.com>

Yegappan Lakshmanan

unread,
Aug 24, 2023, 12:43:27 AM8/24/23
to vim/vim, vim-dev ML, Comment

Now that PR #12140 is included, can this issue be closed?


Reply to this email directly, view it on GitHub.

You are receiving this because you commented.Message ID: <vim/vim/issues/10577/1690991553@github.com>

依云

unread,
Aug 24, 2023, 1:11:54 AM8/24/23
to vim/vim, vim-dev ML, Comment

Closed #10577 as completed.


Reply to this email directly, view it on GitHub.

You are receiving this because you commented.Message ID: <vim/vim/issue/10577/issue_event/10176908341@github.com>

Reply all
Reply to author
Forward
0 new messages