Skip to content

Commit

Permalink
patch 9.0.1704: Cannot use positional arguments for printf()
Browse files Browse the repository at this point in the history
Problem: Cannot use positional arguments for printf()
Solution: Support positional arguments in string formatting

closes: #12140

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Christ van Willegen <cvwillegen@gmail.com>
  • Loading branch information
cvwillegen authored and chrisbra committed Aug 13, 2023
1 parent 1688938 commit 0c6181f
Show file tree
Hide file tree
Showing 10 changed files with 1,468 additions and 29 deletions.
110 changes: 109 additions & 1 deletion runtime/doc/builtin.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6590,7 +6590,11 @@ printf({fmt}, {expr1} ...) *printf()*
The "%" starts a conversion specification. The following
arguments appear in sequence:

% [flags] [field-width] [.precision] type
% [pos-argument] [flags] [field-width] [.precision] type

pos-argument
At most one positional argument specifier. These
take the form {n$}, where n is >= 1.

flags
Zero or more of the following flags:
Expand Down Expand Up @@ -6662,6 +6666,13 @@ printf({fmt}, {expr1} ...) *printf()*
< This limits the length of the text used from "line" to
"width" bytes.

If the argument to be formatted is specified using a posional
argument specifier, and a '*' is used to indicate that a
number argument is to be used to specify the width or
precision, the argument(s) to be used must also be specified
using a {n$} positional argument specifier. See |printf-$|.


The conversion specifiers and their meanings are:

*printf-d* *printf-b* *printf-B* *printf-o*
Expand Down Expand Up @@ -6751,6 +6762,103 @@ printf({fmt}, {expr1} ...) *printf()*
of "%" items. If there are not sufficient or too many
arguments an error is given. Up to 18 arguments can be used.

*printf-$*
In certain languages, error and informative messages are
more readable when the order of words is different from the
corresponding message in English. To accomodate translations
having a different word order, positional arguments may be
used to indicate this. For instance: >
#, c-format
msgid "%s returning %s"
msgstr "waarde %2$s komt terug van %1$s"
<
In this example, the sentence has its 2 string arguments reversed
in the output. >
echo printf(
"In The Netherlands, vim's creator's name is: %1$s %2$s",
"Bram", "Moolenaar")
< In The Netherlands, vim's creator's name is: Bram Moolenaar >
echo printf(
"In Belgium, vim's creator's name is: %2$s %1$s",
"Bram", "Moolenaar")
< In Belgium, vim's creator's name is: Moolenaar Bram

Width (and precision) can be specified using the '*' specifier.
In this case, you must specify the field width position in the
argument list. >
echo printf("%1$*2$.*3$d", 1, 2, 3)
< 001 >
echo printf("%2$*3$.*1$d", 1, 2, 3)
< 2 >
echo printf("%3$*1$.*2$d", 1, 2, 3)
< 03 >
echo printf("%1$*2$.*3$g", 1.4142, 2, 3)
< 1.414

You can mix specifying the width and/or precision directly
and via positional arguments: >
echo printf("%1$4.*2$f", 1.4142135, 6)
< 1.414214 >
echo printf("%1$*2$.4f", 1.4142135, 6)
< 1.4142 >
echo printf("%1$*2$.*3$f", 1.4142135, 6, 2)
< 1.41

*E1400*
You cannot mix positional and non-positional arguments: >
echo printf("%s%1$s", "One", "Two")
< E1400: Cannot mix positional and non-positional
arguments: %s%1$s

*E1401*
You cannot skip a positional argument in a format string: >
echo printf("%3$s%1$s", "One", "Two", "Three")
< E1401: format argument 2 unused in $-style
format: %3$s%1$s

*E1402*
You can re-use a [field-width] (or [precision]) argument: >
echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2)
< 1 at width 2 is: 01

This comment has been minimized.

Copy link
@RestorerZ

RestorerZ Aug 14, 2023

Contributor

This example returns error E1405
E1405: Invalid format specifier: %1$d at width %2$d is: %01$*2$d


However, you can't use it as a different type: >
echo printf("%1$d at width %2$ld is: %01$*2$d", 1, 2)
< E1402: Positional argument 2 used as field
width reused as different type: long int/int

This comment has been minimized.

Copy link
@RestorerZ

RestorerZ Aug 14, 2023

Contributor

This example returns error E1405 and not as expected error E1402
E1405: Invalid format specifier: %1$d at width %2$ld is: %01$*2$d


*E1403*
When a positional argument is used, but not the correct number
or arguments is given, an error is raised: >
echo printf("%1$d at width %2$d is: %01$*2$.*3$d", 1, 2)
< E1403: Positional argument 3 out of bounds:
%1$d at width %2$d is: %01$*2$.*3$d

This comment has been minimized.

Copy link
@RestorerZ

RestorerZ Aug 14, 2023

Contributor

This example returns error E1405 and not expected error E1403
E1405: Invalid format specifier: %1$d at width %2$d is: %01$*2$.*3$d

This comment has been minimized.

Copy link
@vim-ml

vim-ml via email Aug 31, 2023


Only the first error is reported: >
echo printf("%01$*2$.*3$d %4$d", 1, 2)
< E1403: Positional argument 3 out of bounds:
%01$*2$.*3$d %4$d

*E1404*
A positional argument can be used more than once: >
echo printf("%1$s %2$s %1$s", "One", "Two")
< One Two One

However, you can't use a different type the second time: >
echo printf("%1$s %2$s %1$d", "One", "Two")
< E1404: Positional argument 1 type used
inconsistently: int/string

*E1405*
Various other errors that lead to a format string being
wrongly formatted lead to: >
echo printf("%1$d at width %2$d is: %01$*2$.3$d", 1, 2)
< E1405: Invalid format specifier:
%1$d at width %2$d is: %01$*2$.3$d

prompt_getprompt({buf}) *prompt_getprompt()*
Returns the effective prompt text for buffer {buf}. {buf} can
Expand Down
14 changes: 14 additions & 0 deletions src/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -3477,3 +3477,17 @@ EXTERN char e_incomplete_type[]
#endif
EXTERN char e_warning_pointer_block_corrupted[]
INIT(= N_("E1364: Warning: Pointer block corrupted"));
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"));

// E1365 - E1399 unused
13 changes: 13 additions & 0 deletions src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -1674,6 +1674,19 @@ EXTERN int cmdwin_result INIT(= 0); // result of cmdline window or 0

EXTERN char_u no_lines_msg[] INIT(= N_("--No lines in buffer--"));

EXTERN char typename_unknown[] INIT(= N_("unknown"));
EXTERN char typename_int[] INIT(= N_("int"));
EXTERN char typename_longint[] INIT(= N_("long int"));
EXTERN char typename_longlongint[] INIT(= N_("long long int"));
EXTERN char typename_unsignedint[] INIT(= N_("unsigned int"));
EXTERN char typename_unsignedlongint[] INIT(= N_("unsigned long int"));
EXTERN char typename_unsignedlonglongint[] INIT(= N_("unsigned long long int"));
EXTERN char typename_pointer[] INIT(= N_("pointer"));
EXTERN char typename_percent[] INIT(= N_("percent"));
EXTERN char typename_char[] INIT(= N_("char"));
EXTERN char typename_string[] INIT(= N_("string"));
EXTERN char typename_float[] INIT(= N_("float"));

/*
* When ":global" is used to number of substitutions and changed lines is
* accumulated until it's finished.
Expand Down
Loading

0 comments on commit 0c6181f

Please sign in to comment.