mirror of
https://github.com/neovim/neovim.git
synced 2025-10-06 01:46:29 +00:00
vim-patch:9.0.1704: Cannot use positional arguments for printf() (#24719)
Problem: Cannot use positional arguments for printf()
Solution: Support positional arguments in string formatting
closes: vim/vim#12140
0c6181fec4
Co-authored-by: Christ van Willegen <cvwillegen@gmail.com>
This commit is contained in:
111
runtime/doc/builtin.txt
generated
111
runtime/doc/builtin.txt
generated
@@ -5010,7 +5010,11 @@ printf({fmt}, {expr1} ...) *printf()*
|
|||||||
The "%" starts a conversion specification. The following
|
The "%" starts a conversion specification. The following
|
||||||
arguments appear in sequence:
|
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
|
flags
|
||||||
Zero or more of the following flags:
|
Zero or more of the following flags:
|
||||||
@@ -5079,6 +5083,13 @@ printf({fmt}, {expr1} ...) *printf()*
|
|||||||
< This limits the length of the text used from "line" to
|
< This limits the length of the text used from "line" to
|
||||||
"width" bytes.
|
"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:
|
The conversion specifiers and their meanings are:
|
||||||
|
|
||||||
*printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
|
*printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
|
||||||
@@ -5166,6 +5177,104 @@ printf({fmt}, {expr1} ...) *printf()*
|
|||||||
of "%" items. If there are not sufficient or too many
|
of "%" items. If there are not sufficient or too many
|
||||||
arguments an error is given. Up to 18 arguments can be used.
|
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: >vim
|
||||||
|
|
||||||
|
#, 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. >vim
|
||||||
|
|
||||||
|
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 >vim
|
||||||
|
|
||||||
|
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. >vim
|
||||||
|
|
||||||
|
echo printf("%1$*2$.*3$d", 1, 2, 3)
|
||||||
|
< 001 >vim
|
||||||
|
echo printf("%2$*3$.*1$d", 1, 2, 3)
|
||||||
|
< 2 >vim
|
||||||
|
echo printf("%3$*1$.*2$d", 1, 2, 3)
|
||||||
|
< 03 >vim
|
||||||
|
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: >vim
|
||||||
|
|
||||||
|
echo printf("%1$4.*2$f", 1.4142135, 6)
|
||||||
|
< 1.414214 >vim
|
||||||
|
echo printf("%1$*2$.4f", 1.4142135, 6)
|
||||||
|
< 1.4142 >vim
|
||||||
|
echo printf("%1$*2$.*3$f", 1.4142135, 6, 2)
|
||||||
|
< 1.41
|
||||||
|
|
||||||
|
*E1400*
|
||||||
|
You cannot mix positional and non-positional arguments: >vim
|
||||||
|
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: >vim
|
||||||
|
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: >vim
|
||||||
|
echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2)
|
||||||
|
< 1 at width 2 is: 01
|
||||||
|
|
||||||
|
However, you can't use it as a different type: >vim
|
||||||
|
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
|
||||||
|
|
||||||
|
*E1403*
|
||||||
|
When a positional argument is used, but not the correct number
|
||||||
|
or arguments is given, an error is raised: >vim
|
||||||
|
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
|
||||||
|
|
||||||
|
Only the first error is reported: >vim
|
||||||
|
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: >vim
|
||||||
|
echo printf("%1$s %2$s %1$s", "One", "Two")
|
||||||
|
< One Two One
|
||||||
|
|
||||||
|
However, you can't use a different type the second time: >vim
|
||||||
|
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: >vim
|
||||||
|
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()*
|
prompt_getprompt({buf}) *prompt_getprompt()*
|
||||||
Returns the effective prompt text for buffer {buf}. {buf} can
|
Returns the effective prompt text for buffer {buf}. {buf} can
|
||||||
be a buffer name or number. See |prompt-buffer|.
|
be a buffer name or number. See |prompt-buffer|.
|
||||||
|
111
runtime/lua/vim/_meta/vimfn.lua
generated
111
runtime/lua/vim/_meta/vimfn.lua
generated
@@ -5989,7 +5989,11 @@ function vim.fn.prevnonblank(lnum) end
|
|||||||
--- The "%" starts a conversion specification. The following
|
--- The "%" starts a conversion specification. The following
|
||||||
--- arguments appear in sequence:
|
--- 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
|
--- flags
|
||||||
--- Zero or more of the following flags:
|
--- Zero or more of the following flags:
|
||||||
@@ -6058,6 +6062,13 @@ function vim.fn.prevnonblank(lnum) end
|
|||||||
--- <This limits the length of the text used from "line" to
|
--- <This limits the length of the text used from "line" to
|
||||||
--- "width" bytes.
|
--- "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:
|
--- The conversion specifiers and their meanings are:
|
||||||
---
|
---
|
||||||
--- *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
|
--- *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
|
||||||
@@ -6145,6 +6156,104 @@ function vim.fn.prevnonblank(lnum) end
|
|||||||
--- of "%" items. If there are not sufficient or too many
|
--- of "%" items. If there are not sufficient or too many
|
||||||
--- arguments an error is given. Up to 18 arguments can be used.
|
--- 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: >vim
|
||||||
|
---
|
||||||
|
--- #, 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. >vim
|
||||||
|
---
|
||||||
|
--- 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 >vim
|
||||||
|
---
|
||||||
|
--- 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. >vim
|
||||||
|
---
|
||||||
|
--- echo printf("%1$*2$.*3$d", 1, 2, 3)
|
||||||
|
--- < 001 >vim
|
||||||
|
--- echo printf("%2$*3$.*1$d", 1, 2, 3)
|
||||||
|
--- < 2 >vim
|
||||||
|
--- echo printf("%3$*1$.*2$d", 1, 2, 3)
|
||||||
|
--- < 03 >vim
|
||||||
|
--- 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: >vim
|
||||||
|
---
|
||||||
|
--- echo printf("%1$4.*2$f", 1.4142135, 6)
|
||||||
|
--- < 1.414214 >vim
|
||||||
|
--- echo printf("%1$*2$.4f", 1.4142135, 6)
|
||||||
|
--- < 1.4142 >vim
|
||||||
|
--- echo printf("%1$*2$.*3$f", 1.4142135, 6, 2)
|
||||||
|
--- < 1.41
|
||||||
|
---
|
||||||
|
--- *E1400*
|
||||||
|
--- You cannot mix positional and non-positional arguments: >vim
|
||||||
|
--- 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: >vim
|
||||||
|
--- 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: >vim
|
||||||
|
--- echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2)
|
||||||
|
--- < 1 at width 2 is: 01
|
||||||
|
---
|
||||||
|
--- However, you can't use it as a different type: >vim
|
||||||
|
--- 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
|
||||||
|
---
|
||||||
|
--- *E1403*
|
||||||
|
--- When a positional argument is used, but not the correct number
|
||||||
|
--- or arguments is given, an error is raised: >vim
|
||||||
|
--- 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
|
||||||
|
---
|
||||||
|
--- Only the first error is reported: >vim
|
||||||
|
--- 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: >vim
|
||||||
|
--- echo printf("%1$s %2$s %1$s", "One", "Two")
|
||||||
|
--- < One Two One
|
||||||
|
---
|
||||||
|
--- However, you can't use a different type the second time: >vim
|
||||||
|
--- 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: >vim
|
||||||
|
--- 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
|
||||||
|
---
|
||||||
--- @param fmt any
|
--- @param fmt any
|
||||||
--- @param expr1? any
|
--- @param expr1? any
|
||||||
--- @return any
|
--- @return any
|
||||||
|
@@ -7251,7 +7251,11 @@ M.funcs = {
|
|||||||
The "%" starts a conversion specification. The following
|
The "%" starts a conversion specification. The following
|
||||||
arguments appear in sequence:
|
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
|
flags
|
||||||
Zero or more of the following flags:
|
Zero or more of the following flags:
|
||||||
@@ -7320,6 +7324,13 @@ M.funcs = {
|
|||||||
<This limits the length of the text used from "line" to
|
<This limits the length of the text used from "line" to
|
||||||
"width" bytes.
|
"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:
|
The conversion specifiers and their meanings are:
|
||||||
|
|
||||||
*printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
|
*printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X*
|
||||||
@@ -7406,6 +7417,104 @@ M.funcs = {
|
|||||||
The number of {exprN} arguments must exactly match the number
|
The number of {exprN} arguments must exactly match the number
|
||||||
of "%" items. If there are not sufficient or too many
|
of "%" items. If there are not sufficient or too many
|
||||||
arguments an error is given. Up to 18 arguments can be used.
|
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: >vim
|
||||||
|
|
||||||
|
#, 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. >vim
|
||||||
|
|
||||||
|
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 >vim
|
||||||
|
|
||||||
|
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. >vim
|
||||||
|
|
||||||
|
echo printf("%1$*2$.*3$d", 1, 2, 3)
|
||||||
|
< 001 >vim
|
||||||
|
echo printf("%2$*3$.*1$d", 1, 2, 3)
|
||||||
|
< 2 >vim
|
||||||
|
echo printf("%3$*1$.*2$d", 1, 2, 3)
|
||||||
|
< 03 >vim
|
||||||
|
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: >vim
|
||||||
|
|
||||||
|
echo printf("%1$4.*2$f", 1.4142135, 6)
|
||||||
|
< 1.414214 >vim
|
||||||
|
echo printf("%1$*2$.4f", 1.4142135, 6)
|
||||||
|
< 1.4142 >vim
|
||||||
|
echo printf("%1$*2$.*3$f", 1.4142135, 6, 2)
|
||||||
|
< 1.41
|
||||||
|
|
||||||
|
*E1400*
|
||||||
|
You cannot mix positional and non-positional arguments: >vim
|
||||||
|
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: >vim
|
||||||
|
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: >vim
|
||||||
|
echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2)
|
||||||
|
< 1 at width 2 is: 01
|
||||||
|
|
||||||
|
However, you can't use it as a different type: >vim
|
||||||
|
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
|
||||||
|
|
||||||
|
*E1403*
|
||||||
|
When a positional argument is used, but not the correct number
|
||||||
|
or arguments is given, an error is raised: >vim
|
||||||
|
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
|
||||||
|
|
||||||
|
Only the first error is reported: >vim
|
||||||
|
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: >vim
|
||||||
|
echo printf("%1$s %2$s %1$s", "One", "Two")
|
||||||
|
< One Two One
|
||||||
|
|
||||||
|
However, you can't use a different type the second time: >vim
|
||||||
|
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: >vim
|
||||||
|
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
|
||||||
]=],
|
]=],
|
||||||
name = 'printf',
|
name = 'printf',
|
||||||
params = { { 'fmt', 'any' }, { 'expr1', 'any' } },
|
params = { { 'fmt', 'any' }, { 'expr1', 'any' } },
|
||||||
|
@@ -33,8 +33,15 @@ func! GetMline()
|
|||||||
" remove '%' used for plural forms.
|
" remove '%' used for plural forms.
|
||||||
let idline = substitute(idline, '\\nPlural-Forms: .\+;\\n', '', '')
|
let idline = substitute(idline, '\\nPlural-Forms: .\+;\\n', '', '')
|
||||||
|
|
||||||
|
" remove duplicate positional format arguments
|
||||||
|
let idline2 = ""
|
||||||
|
while idline2 != idline
|
||||||
|
let idline2 = idline
|
||||||
|
let idline = substitute(idline, '%\([1-9][0-9]*\)\$\([-+ #''.*]*[0-9]*l\=[dsuxXpoc%]\)\(.*\)%\1$\([-+ #''.*]*\)\(l\=[dsuxXpoc%]\)', '%\1$\2\3\4', 'g')
|
||||||
|
endwhile
|
||||||
|
|
||||||
" remove everything but % items.
|
" remove everything but % items.
|
||||||
return substitute(idline, '[^%]*\(%[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g')
|
return substitute(idline, '[^%]*\(%([1-9][0-9]*\$)\=[-+ #''.0-9*]*l\=[dsuxXpoc%]\)\=', '\1', 'g')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" This only works when 'wrapscan' is not set.
|
" This only works when 'wrapscan' is not set.
|
||||||
|
@@ -32,6 +32,32 @@
|
|||||||
#include "nvim/types.h"
|
#include "nvim/types.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
|
|
||||||
|
static char e_cannot_mix_positional_and_non_positional_str[]
|
||||||
|
= N_("E1400: Cannot mix positional and non-positional arguments: %s");
|
||||||
|
static char e_fmt_arg_nr_unused_str[]
|
||||||
|
= N_("E1401: format argument %d unused in $-style format: %s");
|
||||||
|
static char e_positional_num_field_spec_reused_str_str[]
|
||||||
|
= N_("E1402: Positional argument %d used as field width reused as different type: %s/%s");
|
||||||
|
static char e_positional_nr_out_of_bounds_str[]
|
||||||
|
= N_("E1403: Positional argument %d out of bounds: %s");
|
||||||
|
static char e_positional_arg_num_type_inconsistent_str_str[]
|
||||||
|
= N_("E1404: Positional argument %d type used inconsistently: %s/%s");
|
||||||
|
static char e_invalid_format_specifier_str[]
|
||||||
|
= N_("E1405: Invalid format specifier: %s");
|
||||||
|
|
||||||
|
static char typename_unknown[] = N_("unknown");
|
||||||
|
static char typename_int[] = N_("int");
|
||||||
|
static char typename_longint[] = N_("long int");
|
||||||
|
static char typename_longlongint[] = N_("long long int");
|
||||||
|
static char typename_unsignedint[] = N_("unsigned int");
|
||||||
|
static char typename_unsignedlongint[] = N_("unsigned long int");
|
||||||
|
static char typename_unsignedlonglongint[] = N_("unsigned long long int");
|
||||||
|
static char typename_pointer[] = N_("pointer");
|
||||||
|
static char typename_percent[] = N_("percent");
|
||||||
|
static char typename_char[] = N_("char");
|
||||||
|
static char typename_string[] = N_("string");
|
||||||
|
static char typename_float[] = N_("float");
|
||||||
|
|
||||||
/// Copy up to `len` bytes of `string` into newly allocated memory and
|
/// Copy up to `len` bytes of `string` into newly allocated memory and
|
||||||
/// terminate with a NUL. The allocated memory always has size `len + 1`, even
|
/// terminate with a NUL. The allocated memory always has size `len + 1`, even
|
||||||
/// when `string` is shorter.
|
/// when `string` is shorter.
|
||||||
@@ -717,6 +743,571 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
|
|||||||
return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
|
return vim_vsnprintf_typval(str, str_m, fmt, ap, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TYPE_UNKNOWN = -1,
|
||||||
|
TYPE_INT,
|
||||||
|
TYPE_LONGINT,
|
||||||
|
TYPE_LONGLONGINT,
|
||||||
|
TYPE_UNSIGNEDINT,
|
||||||
|
TYPE_UNSIGNEDLONGINT,
|
||||||
|
TYPE_UNSIGNEDLONGLONGINT,
|
||||||
|
TYPE_POINTER,
|
||||||
|
TYPE_PERCENT,
|
||||||
|
TYPE_CHAR,
|
||||||
|
TYPE_STRING,
|
||||||
|
TYPE_FLOAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Types that can be used in a format string
|
||||||
|
static int format_typeof(const char *type, bool usetvs)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
// allowed values: \0, h, l, L
|
||||||
|
char length_modifier = '\0';
|
||||||
|
|
||||||
|
// current conversion specifier character
|
||||||
|
char fmt_spec = '\0';
|
||||||
|
|
||||||
|
// parse 'h', 'l' and 'll' length modifiers
|
||||||
|
if (*type == 'h' || *type == 'l') {
|
||||||
|
length_modifier = *type;
|
||||||
|
type++;
|
||||||
|
if (length_modifier == 'l' && *type == 'l') {
|
||||||
|
// double l = long long
|
||||||
|
length_modifier = 'L';
|
||||||
|
type++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt_spec = *type;
|
||||||
|
|
||||||
|
// common synonyms:
|
||||||
|
switch (fmt_spec) {
|
||||||
|
case 'i':
|
||||||
|
fmt_spec = 'd'; break;
|
||||||
|
case '*':
|
||||||
|
fmt_spec = 'd'; length_modifier = 'h'; break;
|
||||||
|
case 'D':
|
||||||
|
fmt_spec = 'd'; length_modifier = 'l'; break;
|
||||||
|
case 'U':
|
||||||
|
fmt_spec = 'u'; length_modifier = 'l'; break;
|
||||||
|
case 'O':
|
||||||
|
fmt_spec = 'o'; length_modifier = 'l'; break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usetvs) {
|
||||||
|
switch (fmt_spec) {
|
||||||
|
case 'd':
|
||||||
|
case 'u':
|
||||||
|
case 'o':
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
if (length_modifier == '\0') {
|
||||||
|
length_modifier = 'L';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get parameter value, do initial processing
|
||||||
|
switch (fmt_spec) {
|
||||||
|
// '%' and 'c' behave similar to 's' regarding flags and field
|
||||||
|
// widths
|
||||||
|
case '%':
|
||||||
|
return TYPE_PERCENT;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
return TYPE_CHAR;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
case 'S':
|
||||||
|
return TYPE_STRING;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
case 'u':
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
case 'o':
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
case 'p':
|
||||||
|
// NOTE: the u, b, o, x, X and p conversion specifiers
|
||||||
|
// imply the value is unsigned; d implies a signed
|
||||||
|
// value
|
||||||
|
|
||||||
|
// 0 if numeric argument is zero (or if pointer is
|
||||||
|
// NULL for 'p'), +1 if greater than zero (or nonzero
|
||||||
|
// for unsigned arguments), -1 if negative (unsigned
|
||||||
|
// argument is never negative)
|
||||||
|
|
||||||
|
if (fmt_spec == 'p') {
|
||||||
|
return TYPE_POINTER;
|
||||||
|
} else if (fmt_spec == 'b' || fmt_spec == 'B') {
|
||||||
|
return TYPE_UNSIGNEDINT;
|
||||||
|
} else if (fmt_spec == 'd') {
|
||||||
|
// signed
|
||||||
|
switch (length_modifier) {
|
||||||
|
case '\0':
|
||||||
|
case 'h':
|
||||||
|
// char and short arguments are passed as int.
|
||||||
|
return TYPE_INT;
|
||||||
|
case 'l':
|
||||||
|
return TYPE_LONGINT;
|
||||||
|
case 'L':
|
||||||
|
return TYPE_LONGLONGINT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// unsigned
|
||||||
|
switch (length_modifier) {
|
||||||
|
case '\0':
|
||||||
|
case 'h':
|
||||||
|
return TYPE_UNSIGNEDINT;
|
||||||
|
case 'l':
|
||||||
|
return TYPE_UNSIGNEDLONGINT;
|
||||||
|
case 'L':
|
||||||
|
return TYPE_UNSIGNEDLONGLONGINT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
return TYPE_FLOAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *format_typename(const char *type)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
switch (format_typeof(type, false)) {
|
||||||
|
case TYPE_INT:
|
||||||
|
return _(typename_int);
|
||||||
|
case TYPE_LONGINT:
|
||||||
|
return _(typename_longint);
|
||||||
|
case TYPE_LONGLONGINT:
|
||||||
|
return _(typename_longlongint);
|
||||||
|
case TYPE_UNSIGNEDINT:
|
||||||
|
return _(typename_unsignedint);
|
||||||
|
case TYPE_UNSIGNEDLONGINT:
|
||||||
|
return _(typename_unsignedlongint);
|
||||||
|
case TYPE_UNSIGNEDLONGLONGINT:
|
||||||
|
return _(typename_unsignedlonglongint);
|
||||||
|
case TYPE_POINTER:
|
||||||
|
return _(typename_pointer);
|
||||||
|
case TYPE_PERCENT:
|
||||||
|
return _(typename_percent);
|
||||||
|
case TYPE_CHAR:
|
||||||
|
return _(typename_char);
|
||||||
|
case TYPE_STRING:
|
||||||
|
return _(typename_string);
|
||||||
|
case TYPE_FLOAT:
|
||||||
|
return _(typename_float);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _(typename_unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const char *type)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
if (*ap_types == NULL || *num_posarg < arg) {
|
||||||
|
const char **new_types = *ap_types == NULL
|
||||||
|
? xcalloc(sizeof(const char *), (size_t)arg)
|
||||||
|
: xrealloc(*ap_types, (size_t)arg * sizeof(const char *));
|
||||||
|
|
||||||
|
for (int idx = *num_posarg; idx < arg; idx++) {
|
||||||
|
new_types[idx] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ap_types = new_types;
|
||||||
|
*num_posarg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((*ap_types)[arg - 1] != NULL) {
|
||||||
|
if ((*ap_types)[arg - 1][0] == '*' || type[0] == '*') {
|
||||||
|
const char *pt = type;
|
||||||
|
if (pt[0] == '*') {
|
||||||
|
pt = (*ap_types)[arg - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt[0] != '*') {
|
||||||
|
switch (pt[0]) {
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
semsg(_(e_positional_num_field_spec_reused_str_str), arg,
|
||||||
|
format_typename((*ap_types)[arg - 1]), format_typename(type));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (format_typeof(type, false) != format_typeof((*ap_types)[arg - 1], false)) {
|
||||||
|
semsg(_(e_positional_arg_num_type_inconsistent_str_str), arg,
|
||||||
|
format_typename(type), format_typename((*ap_types)[arg - 1]));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*ap_types)[arg - 1] = type;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *fmt, typval_T *tvs)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||||
|
{
|
||||||
|
const char *p = fmt;
|
||||||
|
const char *arg = NULL;
|
||||||
|
|
||||||
|
int any_pos = 0;
|
||||||
|
int any_arg = 0;
|
||||||
|
|
||||||
|
#define CHECK_POS_ARG \
|
||||||
|
do { \
|
||||||
|
if (any_pos && any_arg) { \
|
||||||
|
semsg(_(e_cannot_mix_positional_and_non_positional_str), fmt); \
|
||||||
|
goto error; \
|
||||||
|
} \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
if (p == NULL) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*p != NUL) {
|
||||||
|
if (*p != '%') {
|
||||||
|
char *q = strchr(p + 1, '%');
|
||||||
|
size_t n = (q == NULL) ? strlen(p) : (size_t)(q - p);
|
||||||
|
|
||||||
|
p += n;
|
||||||
|
} else {
|
||||||
|
// allowed values: \0, h, l, L
|
||||||
|
char length_modifier = '\0';
|
||||||
|
|
||||||
|
// variable for positional arg
|
||||||
|
int pos_arg = -1;
|
||||||
|
|
||||||
|
p++; // skip '%'
|
||||||
|
|
||||||
|
// First check to see if we find a positional
|
||||||
|
// argument specifier
|
||||||
|
const char *ptype = p;
|
||||||
|
|
||||||
|
while (ascii_isdigit(*ptype)) {
|
||||||
|
ptype++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*ptype == '$') {
|
||||||
|
if (*p == '0') {
|
||||||
|
// 0 flag at the wrong place
|
||||||
|
semsg(_(e_invalid_format_specifier_str), fmt);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Positional argument
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
pos_arg = (int)uj;
|
||||||
|
|
||||||
|
any_pos = 1;
|
||||||
|
CHECK_POS_ARG;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse flags
|
||||||
|
while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
|
||||||
|
|| *p == '#' || *p == '\'') {
|
||||||
|
switch (*p) {
|
||||||
|
case '0':
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
case ' ': // If both the ' ' and '+' flags appear, the ' '
|
||||||
|
// flag should be ignored
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
// If the '0' and '-' flags both appear, the '0' flag should be
|
||||||
|
// ignored.
|
||||||
|
|
||||||
|
// parse field width
|
||||||
|
if (*(arg = p) == '*') {
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (ascii_isdigit((int)(*p))) {
|
||||||
|
// Positional argument field width
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p != '$') {
|
||||||
|
semsg(_(e_invalid_format_specifier_str), fmt);
|
||||||
|
goto error;
|
||||||
|
} else {
|
||||||
|
p++;
|
||||||
|
any_pos = 1;
|
||||||
|
CHECK_POS_ARG;
|
||||||
|
|
||||||
|
if (adjust_types(ap_types, (int)uj, num_posarg, arg) == FAIL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
any_arg = 1;
|
||||||
|
CHECK_POS_ARG;
|
||||||
|
}
|
||||||
|
} else if (ascii_isdigit((int)(*(arg = p)))) {
|
||||||
|
// size_t could be wider than unsigned int; make sure we treat
|
||||||
|
// argument like common implementations do
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '$') {
|
||||||
|
semsg(_(e_invalid_format_specifier_str), fmt);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse precision
|
||||||
|
if (*p == '.') {
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (*(arg = p) == '*') {
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (ascii_isdigit((int)(*p))) {
|
||||||
|
// Parse precision
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '$') {
|
||||||
|
any_pos = 1;
|
||||||
|
CHECK_POS_ARG;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (adjust_types(ap_types, (int)uj, num_posarg, arg) == FAIL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
semsg(_(e_invalid_format_specifier_str), fmt);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
any_arg = 1;
|
||||||
|
CHECK_POS_ARG;
|
||||||
|
}
|
||||||
|
} else if (ascii_isdigit((int)(*(arg = p)))) {
|
||||||
|
// size_t could be wider than unsigned int; make sure we
|
||||||
|
// treat argument like common implementations do
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '$') {
|
||||||
|
semsg(_(e_invalid_format_specifier_str), fmt);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos_arg != -1) {
|
||||||
|
any_pos = 1;
|
||||||
|
CHECK_POS_ARG;
|
||||||
|
|
||||||
|
ptype = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse 'h', 'l' and 'll' length modifiers
|
||||||
|
if (*p == 'h' || *p == 'l') {
|
||||||
|
length_modifier = *p;
|
||||||
|
p++;
|
||||||
|
if (length_modifier == 'l' && *p == 'l') {
|
||||||
|
// double l = long long
|
||||||
|
length_modifier = 'L';
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*p) {
|
||||||
|
// Check for known format specifiers. % is special!
|
||||||
|
case 'i':
|
||||||
|
case '*':
|
||||||
|
case 'd':
|
||||||
|
case 'u':
|
||||||
|
case 'o':
|
||||||
|
case 'D':
|
||||||
|
case 'U':
|
||||||
|
case 'O':
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
case 'c':
|
||||||
|
case 's':
|
||||||
|
case 'S':
|
||||||
|
case 'p':
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
if (pos_arg != -1) {
|
||||||
|
if (adjust_types(ap_types, pos_arg, num_posarg, ptype) == FAIL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
any_arg = 1;
|
||||||
|
CHECK_POS_ARG;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (pos_arg != -1) {
|
||||||
|
semsg(_(e_cannot_mix_positional_and_non_positional_str), fmt);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p != NUL) {
|
||||||
|
p++; // step over the just processed conversion specifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int arg_idx = 0; arg_idx < *num_posarg; arg_idx++) {
|
||||||
|
if ((*ap_types)[arg_idx] == NULL) {
|
||||||
|
semsg(_(e_fmt_arg_nr_unused_str), arg_idx + 1, fmt);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tvs != NULL && tvs[arg_idx].v_type == VAR_UNKNOWN) {
|
||||||
|
semsg(_(e_positional_nr_out_of_bounds_str), arg_idx + 1, fmt);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
|
||||||
|
error:
|
||||||
|
xfree(*ap_types);
|
||||||
|
*ap_types = NULL;
|
||||||
|
*num_posarg = 0;
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void skip_to_arg(const char **ap_types, va_list ap_start, va_list *ap, int *arg_idx,
|
||||||
|
int *arg_cur)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(3, 4, 5)
|
||||||
|
{
|
||||||
|
int arg_min = 0;
|
||||||
|
|
||||||
|
if (*arg_cur + 1 == *arg_idx) {
|
||||||
|
(*arg_cur)++;
|
||||||
|
(*arg_idx)++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*arg_cur >= *arg_idx) {
|
||||||
|
// Reset ap to ap_start and skip arg_idx - 1 types
|
||||||
|
va_end(*ap);
|
||||||
|
va_copy(*ap, ap_start);
|
||||||
|
} else {
|
||||||
|
// Skip over any we should skip
|
||||||
|
arg_min = *arg_cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (*arg_cur = arg_min; *arg_cur < *arg_idx - 1; (*arg_cur)++) {
|
||||||
|
assert(ap_types != NULL);
|
||||||
|
const char *p = ap_types[*arg_cur];
|
||||||
|
|
||||||
|
int fmt_type = format_typeof(p, true);
|
||||||
|
|
||||||
|
// get parameter value, do initial processing
|
||||||
|
switch (fmt_type) {
|
||||||
|
case TYPE_PERCENT:
|
||||||
|
case TYPE_UNKNOWN:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_CHAR:
|
||||||
|
va_arg(*ap, int);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_STRING:
|
||||||
|
va_arg(*ap, const char *);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_POINTER:
|
||||||
|
va_arg(*ap, void *);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_INT:
|
||||||
|
va_arg(*ap, int);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_LONGINT:
|
||||||
|
va_arg(*ap, long);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_LONGLONGINT:
|
||||||
|
va_arg(*ap, long long); // NOLINT(runtime/int)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_UNSIGNEDINT:
|
||||||
|
va_arg(*ap, unsigned);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_UNSIGNEDLONGINT:
|
||||||
|
va_arg(*ap, unsigned long);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_UNSIGNEDLONGLONGINT:
|
||||||
|
va_arg(*ap, unsigned long long); // NOLINT(runtime/int)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TYPE_FLOAT:
|
||||||
|
va_arg(*ap, double);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because we know that after we return from this call,
|
||||||
|
// a va_arg() call is made, we can pre-emptively
|
||||||
|
// increment the current argument index.
|
||||||
|
(*arg_cur)++;
|
||||||
|
(*arg_idx)++;
|
||||||
|
}
|
||||||
|
|
||||||
/// Write formatted value to the string
|
/// Write formatted value to the string
|
||||||
///
|
///
|
||||||
/// @param[out] str String to write to.
|
/// @param[out] str String to write to.
|
||||||
@@ -728,12 +1319,23 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap)
|
|||||||
///
|
///
|
||||||
/// @return Number of bytes excluding NUL byte that would be written to the
|
/// @return Number of bytes excluding NUL byte that would be written to the
|
||||||
/// string if str_m was greater or equal to the return value.
|
/// string if str_m was greater or equal to the return value.
|
||||||
int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, typval_T *const tvs)
|
int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_start,
|
||||||
|
typval_T *const tvs)
|
||||||
{
|
{
|
||||||
size_t str_l = 0;
|
size_t str_l = 0;
|
||||||
bool str_avail = str_l < str_m;
|
bool str_avail = str_l < str_m;
|
||||||
const char *p = fmt;
|
const char *p = fmt;
|
||||||
|
int arg_cur = 0;
|
||||||
|
int num_posarg = 0;
|
||||||
int arg_idx = 1;
|
int arg_idx = 1;
|
||||||
|
va_list ap;
|
||||||
|
const char **ap_types = NULL;
|
||||||
|
|
||||||
|
if (parse_fmt_types(&ap_types, &num_posarg, fmt, tvs) == FAIL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_copy(ap, ap_start);
|
||||||
|
|
||||||
if (!p) {
|
if (!p) {
|
||||||
p = "";
|
p = "";
|
||||||
@@ -789,8 +1391,31 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
// buffer for 's' and 'S' specs
|
// buffer for 's' and 'S' specs
|
||||||
char *tofree = NULL;
|
char *tofree = NULL;
|
||||||
|
|
||||||
|
// variable for positional arg
|
||||||
|
int pos_arg = -1;
|
||||||
|
|
||||||
p++; // skip '%'
|
p++; // skip '%'
|
||||||
|
|
||||||
|
// First check to see if we find a positional
|
||||||
|
// argument specifier
|
||||||
|
const char *ptype = p;
|
||||||
|
|
||||||
|
while (ascii_isdigit(*ptype)) {
|
||||||
|
ptype++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*ptype == '$') {
|
||||||
|
// Positional argument
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
pos_arg = (int)uj;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
// parse flags
|
// parse flags
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
@@ -817,7 +1442,24 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
// parse field width
|
// parse field width
|
||||||
if (*p == '*') {
|
if (*p == '*') {
|
||||||
p++;
|
p++;
|
||||||
const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
|
|
||||||
|
if (ascii_isdigit((int)(*p))) {
|
||||||
|
// Positional argument field width
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
arg_idx = (int)uj;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int j = (tvs
|
||||||
|
? (int)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, int)));
|
||||||
|
|
||||||
if (j >= 0) {
|
if (j >= 0) {
|
||||||
min_field_width = (size_t)j;
|
min_field_width = (size_t)j;
|
||||||
} else {
|
} else {
|
||||||
@@ -839,16 +1481,8 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
if (*p == '.') {
|
if (*p == '.') {
|
||||||
p++;
|
p++;
|
||||||
precision_specified = 1;
|
precision_specified = 1;
|
||||||
if (*p == '*') {
|
|
||||||
const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
|
if (ascii_isdigit((int)(*p))) {
|
||||||
p++;
|
|
||||||
if (j >= 0) {
|
|
||||||
precision = (size_t)j;
|
|
||||||
} else {
|
|
||||||
precision_specified = 0;
|
|
||||||
precision = 0;
|
|
||||||
}
|
|
||||||
} else if (ascii_isdigit((int)(*p))) {
|
|
||||||
// size_t could be wider than unsigned int; make sure we
|
// size_t could be wider than unsigned int; make sure we
|
||||||
// treat argument like common implementations do
|
// treat argument like common implementations do
|
||||||
unsigned uj = (unsigned)(*p++ - '0');
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
@@ -857,6 +1491,32 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
}
|
}
|
||||||
precision = uj;
|
precision = uj;
|
||||||
|
} else if (*p == '*') {
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (ascii_isdigit((int)(*p))) {
|
||||||
|
// positional argument
|
||||||
|
unsigned uj = (unsigned)(*p++ - '0');
|
||||||
|
|
||||||
|
while (ascii_isdigit((int)(*p))) {
|
||||||
|
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||||
|
}
|
||||||
|
arg_idx = (int)uj;
|
||||||
|
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int j = (tvs
|
||||||
|
? (int)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, int)));
|
||||||
|
|
||||||
|
if (j >= 0) {
|
||||||
|
precision = (size_t)j;
|
||||||
|
} else {
|
||||||
|
precision_specified = 0;
|
||||||
|
precision = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,8 +1524,9 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
if (*p == 'h' || *p == 'l' || *p == 'z') {
|
if (*p == 'h' || *p == 'l' || *p == 'z') {
|
||||||
length_modifier = *p;
|
length_modifier = *p;
|
||||||
p++;
|
p++;
|
||||||
if (length_modifier == 'l' && *p == 'l') { // ll, encoded as 2
|
if (length_modifier == 'l' && *p == 'l') {
|
||||||
length_modifier = '2';
|
// double l = long long
|
||||||
|
length_modifier = 'L';
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -895,10 +1556,14 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
case 'x':
|
case 'x':
|
||||||
case 'X':
|
case 'X':
|
||||||
if (tvs && length_modifier == '\0') {
|
if (tvs && length_modifier == '\0') {
|
||||||
length_modifier = '2';
|
length_modifier = 'L';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pos_arg != -1) {
|
||||||
|
arg_idx = pos_arg;
|
||||||
|
}
|
||||||
|
|
||||||
// get parameter value, do initial processing
|
// get parameter value, do initial processing
|
||||||
switch (fmt_spec) {
|
switch (fmt_spec) {
|
||||||
// '%' and 'c' behave similar to 's' regarding flags and field widths
|
// '%' and 'c' behave similar to 's' regarding flags and field widths
|
||||||
@@ -913,7 +1578,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c': {
|
case 'c': {
|
||||||
const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int);
|
const int j = (tvs
|
||||||
|
? (int)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, int)));
|
||||||
|
|
||||||
// standard demands unsigned char
|
// standard demands unsigned char
|
||||||
uchar_arg = (unsigned char)j;
|
uchar_arg = (unsigned char)j;
|
||||||
str_arg = (char *)&uchar_arg;
|
str_arg = (char *)&uchar_arg;
|
||||||
@@ -922,8 +1591,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
case 'S':
|
case 'S':
|
||||||
str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree)
|
str_arg = (tvs
|
||||||
: va_arg(ap, const char *);
|
? tv_str(tvs, &arg_idx, &tofree)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, const char *)));
|
||||||
|
|
||||||
if (!str_arg) {
|
if (!str_arg) {
|
||||||
str_arg = "[NULL]";
|
str_arg = "[NULL]";
|
||||||
str_arg_l = 6;
|
str_arg_l = 6;
|
||||||
@@ -990,7 +1662,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
const void *ptr_arg = NULL;
|
const void *ptr_arg = NULL;
|
||||||
|
|
||||||
if (fmt_spec == 'p') {
|
if (fmt_spec == 'p') {
|
||||||
ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *);
|
ptr_arg = (tvs
|
||||||
|
? tv_ptr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, void *)));
|
||||||
|
|
||||||
if (ptr_arg) {
|
if (ptr_arg) {
|
||||||
arg_sign = 1;
|
arg_sign = 1;
|
||||||
}
|
}
|
||||||
@@ -998,23 +1674,36 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
// signed
|
// signed
|
||||||
switch (length_modifier) {
|
switch (length_modifier) {
|
||||||
case '\0':
|
case '\0':
|
||||||
arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int));
|
arg = (tvs
|
||||||
|
? (int)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, int)));
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
// char and short arguments are passed as int16_t
|
// char and short arguments are passed as int16_t
|
||||||
arg = (int16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int));
|
arg = (int16_t)
|
||||||
|
(tvs
|
||||||
|
? (int)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, int)));
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
arg = (tvs ? (long)tv_nr(tvs, &arg_idx) : va_arg(ap, long));
|
arg = (tvs
|
||||||
|
? (long)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, long)));
|
||||||
break;
|
break;
|
||||||
case '2':
|
case 'L':
|
||||||
arg = (
|
arg = (tvs
|
||||||
tvs
|
? (long long)tv_nr(tvs, &arg_idx) // NOLINT(runtime/int)
|
||||||
? (long long)tv_nr(tvs, &arg_idx) // NOLINT (runtime/int)
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
: va_arg(ap, long long)); // NOLINT (runtime/int)
|
va_arg(ap, long long))); // NOLINT(runtime/int)
|
||||||
break;
|
break;
|
||||||
case 'z':
|
case 'z':
|
||||||
arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) : va_arg(ap, ptrdiff_t));
|
arg = (tvs
|
||||||
|
? (ptrdiff_t)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, ptrdiff_t)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (arg > 0) {
|
if (arg > 0) {
|
||||||
@@ -1026,23 +1715,35 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
// unsigned
|
// unsigned
|
||||||
switch (length_modifier) {
|
switch (length_modifier) {
|
||||||
case '\0':
|
case '\0':
|
||||||
uarg = (unsigned)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned));
|
uarg = (tvs
|
||||||
|
? (unsigned)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, unsigned)));
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned));
|
uarg = (uint16_t)
|
||||||
|
(tvs
|
||||||
|
? (unsigned)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, unsigned)));
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long));
|
uarg = (tvs
|
||||||
|
? (unsigned long)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, unsigned long)));
|
||||||
break;
|
break;
|
||||||
case '2':
|
case 'L':
|
||||||
uarg = (uintmax_t)(unsigned long long)( // NOLINT (runtime/int)
|
uarg = (tvs
|
||||||
tvs
|
? (unsigned long long)tv_nr(tvs, &arg_idx) // NOLINT(runtime/int)
|
||||||
? ((unsigned long long) // NOLINT (runtime/int)
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
tv_nr(tvs, &arg_idx))
|
va_arg(ap, unsigned long long))); // NOLINT(runtime/int)
|
||||||
: va_arg(ap, unsigned long long)); // NOLINT (runtime/int)
|
|
||||||
break;
|
break;
|
||||||
case 'z':
|
case 'z':
|
||||||
uarg = (tvs ? (size_t)tv_nr(tvs, &arg_idx) : va_arg(ap, size_t));
|
uarg = (tvs
|
||||||
|
? (size_t)tv_nr(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, size_t)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
arg_sign = (uarg != 0);
|
arg_sign = (uarg != 0);
|
||||||
@@ -1177,7 +1878,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
char format[40];
|
char format[40];
|
||||||
int remove_trailing_zeroes = false;
|
int remove_trailing_zeroes = false;
|
||||||
|
|
||||||
double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double);
|
double f = (tvs
|
||||||
|
? tv_float(tvs, &arg_idx)
|
||||||
|
: (skip_to_arg(ap_types, ap_start, &ap, &arg_idx, &arg_cur),
|
||||||
|
va_arg(ap, double)));
|
||||||
|
|
||||||
double abs_f = f < 0 ? -f : f;
|
double abs_f = f < 0 ? -f : f;
|
||||||
|
|
||||||
if (fmt_spec == 'g' || fmt_spec == 'G') {
|
if (fmt_spec == 'g' || fmt_spec == 'G') {
|
||||||
@@ -1395,10 +2100,14 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t
|
|||||||
str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
|
str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tvs && tvs[arg_idx - 1].v_type != VAR_UNKNOWN) {
|
if (tvs != NULL
|
||||||
|
&& tvs[num_posarg != 0 ? num_posarg : arg_idx - 1].v_type != VAR_UNKNOWN) {
|
||||||
emsg(_("E767: Too many arguments to printf()"));
|
emsg(_("E767: Too many arguments to printf()"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xfree(ap_types);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
// return the number of characters formatted (excluding trailing nul
|
// return the number of characters formatted (excluding trailing nul
|
||||||
// character); that is, the number of characters that would have been
|
// character); that is, the number of characters that would have been
|
||||||
// written to the buffer if it were large enough.
|
// written to the buffer if it were large enough.
|
||||||
|
@@ -239,6 +239,8 @@ func Test_printf_misc()
|
|||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
call assert_equal('123', printf('123'))
|
call assert_equal('123', printf('123'))
|
||||||
|
|
||||||
|
call assert_equal('', printf('%'))
|
||||||
|
call assert_equal('', printf('%.0d', 0))
|
||||||
call assert_equal('123', printf('%d', 123))
|
call assert_equal('123', printf('%d', 123))
|
||||||
call assert_equal('123', printf('%i', 123))
|
call assert_equal('123', printf('%i', 123))
|
||||||
call assert_equal('123', printf('%D', 123))
|
call assert_equal('123', printf('%D', 123))
|
||||||
|
361
test/old/testdir/test_format.vim
Normal file
361
test/old/testdir/test_format.vim
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
" Tests for expressions.
|
||||||
|
|
||||||
|
source check.vim
|
||||||
|
source vim9.vim
|
||||||
|
|
||||||
|
func Test_printf_pos_misc()
|
||||||
|
let lines =<< trim END
|
||||||
|
call assert_equal('123', printf('%1$d', 123))
|
||||||
|
call assert_equal('', printf('%1$.0d', 0))
|
||||||
|
call assert_equal('00005', printf('%1$5.5d', 5))
|
||||||
|
call assert_equal('00005', printf('%1$*1$.5d', 5))
|
||||||
|
call assert_equal('00005', printf('%1$5.*1$d', 5))
|
||||||
|
call assert_equal('00005', printf('%1$*1$.*1$d', 5))
|
||||||
|
call assert_equal('00005', printf('%1$*10$.5d%2$.0d%3$.0d%4$.0d%5$.0d%6$.0d%7$.0d%8$.0d%9$.0d', 5, 0, 0, 0, 0, 0, 0, 0, 0, 5))
|
||||||
|
call assert_equal('00005', printf('%1$5.*10$d%2$.0d%3$.0d%4$.0d%5$.0d%6$.0d%7$.0d%8$.0d%9$.0d', 5, 0, 0, 0, 0, 0, 0, 0, 0, 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('Printing 1 at width 1 gives: 1', 1->printf("Printing %1$d at width %1$d gives: %1$*1$d"))
|
||||||
|
call assert_equal('Printing 2 at width 2 gives: 2', 2->printf("Printing %1$d at width %1$d gives: %1$*1$d"))
|
||||||
|
call assert_equal('Printing 3 at width 3 gives: 3', 3->printf("Printing %1$d at width %1$d gives: %1$*1$d"))
|
||||||
|
call assert_equal('Printing 1 at width/precision 1.1 gives: 1', 1->printf("Printing %1$d at width/precision %1$d.%1$d gives: %1$*1$.*1$d"))
|
||||||
|
call assert_equal('Printing 2 at width/precision 2.2 gives: 02', 2->printf("Printing %1$d at width/precision %1$d.%1$d gives: %1$*1$.*1$d"))
|
||||||
|
call assert_equal('Printing 3 at width/precision 3.3 gives: 003', 3->printf("Printing %1$d at width/precision %1$d.%1$d gives: %1$*1$.*1$d"))
|
||||||
|
|
||||||
|
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'))
|
||||||
|
|
||||||
|
call assert_equal('1%', printf('%1$d%%', 1))
|
||||||
|
call assert_notequal('', printf('%1$p', "abc"))
|
||||||
|
call assert_notequal('', printf('%2$d %1$p %3$s', "abc", 2, "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))
|
||||||
|
|
||||||
|
#" Try out lots of combinations of argument types to skip
|
||||||
|
call assert_equal('9 12345 7654321', printf('%2$ld %1$d %3$lu', 12345, 9, 7654321))
|
||||||
|
call assert_equal('9 1234567 7654321', printf('%2$d %1$ld %3$lu', 1234567, 9, 7654321))
|
||||||
|
call assert_equal('9 1234567 7654321', printf('%2$d %1$lld %3$lu', 1234567, 9, 7654321))
|
||||||
|
call assert_equal('9 12345 7654321', printf('%2$ld %1$u %3$lu', 12345, 9, 7654321))
|
||||||
|
call assert_equal('9 1234567 7654321', printf('%2$d %1$lu %3$lu', 1234567, 9, 7654321))
|
||||||
|
call assert_equal('9 1234567 7654321', printf('%2$d %1$llu %3$lu', 1234567, 9, 7654321))
|
||||||
|
call assert_equal('9 1234567 7654321', printf('%2$d %1$llu %3$lu', 1234567, 9, 7654321))
|
||||||
|
call assert_equal('9 deadbeef 7654321', printf('%2$d %1$x %3$lu', 0xdeadbeef, 9, 7654321))
|
||||||
|
call assert_equal('9 c 7654321', printf('%2$ld %1$c %3$lu', 99, 9, 7654321))
|
||||||
|
call assert_equal('9 hi 7654321', printf('%2$ld %1$s %3$lu', "hi", 9, 7654321))
|
||||||
|
call assert_equal('9 0.000000e+00 7654321', printf('%2$ld %1$e %3$lu', 0.0, 9, 7654321))
|
||||||
|
END
|
||||||
|
call CheckLegacyAndVim9Success(lines)
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 1, 3, 4)"], "E767:")
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%2$*1$d%d', 1, 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%d%2$*1$d', 1, 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%2$.*1$d%d', 1, 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%d%2$.*1$d', 1, 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$%')"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$')"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$_')"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*3$.*d', 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*.*2$d', 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*.*d', 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%*.*1$d', 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%*1$.*d', 3)"], "E1400:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%*1$.*1$d', 3)"], "E1400:")
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1401:")
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%2$*1$d %1$ld', 3, 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$p %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$f %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$lud %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$llud %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$lld %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$c %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$*1$d', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$ld %2$*1$d', 3, 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1402:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1402:")
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1403:")
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$s', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$ud %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$s %1$f', 3.0)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*1$d %1$ld', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$p %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$f %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$lud %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$llud %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$lld %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$c %1$d', 3)"], "E1404:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$d', 3)"], "E1404:")
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$.2$d', 3)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%01$d', 3)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%01$0d', 3)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*3.*2$d', 3)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*3$.2$d', 3)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1405:")
|
||||||
|
call CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1405:")
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_printf_pos_float()
|
||||||
|
let lines =<< trim END
|
||||||
|
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))
|
||||||
|
|
||||||
|
#" When precision is 0, the dot should be omitted.
|
||||||
|
call assert_equal(' 2', printf('%1$*2$.f', 7.0 / 3.0, 3))
|
||||||
|
call assert_equal(' 2', printf('%2$*1$.f', 3, 7.0 / 3.0))
|
||||||
|
call assert_equal(' 2', printf('%1$*2$.g', 7.0 / 3.0, 3))
|
||||||
|
call assert_equal(' 2', printf('%2$*1$.g', 3, 7.0 / 3.0))
|
||||||
|
call assert_equal(' 2e+00', printf('%1$*2$.e', 7.0 / 3.0, 7))
|
||||||
|
call assert_equal(' 2e+00', printf('%2$*1$.e', 7, 7.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))
|
||||||
|
call assert_equal(' inf', printf('%1$*2$f', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' -inf', printf('%1$*2$f', -1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' inf', printf('%1$*2$g', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' -inf', printf('%1$*2$g', -1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' +inf', printf('%1$+*2$f', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' inf', printf('%1$ *2$f', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' +inf', printf('%1$+0*2$f', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal('inf ', printf('%1$-*2$f', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal('-inf ', printf('%1$-*2$f', -1.0 / 0.0, 6))
|
||||||
|
call assert_equal('+inf ', printf('%1$-+*2$f', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' inf ', printf('%1$- *2$f', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal('-INF ', printf('%1$-*2$F', -1.0 / 0.0, 6))
|
||||||
|
call assert_equal('+INF ', printf('%1$-+*2$F', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' INF ', printf('%1$- *2$F', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal('INF ', printf('%1$-*2$G', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal('-INF ', printf('%1$-*2$G', -1.0 / 0.0, 6))
|
||||||
|
call assert_equal('INF ', printf('%1$-*2$E', 1.0 / 0.0, 6))
|
||||||
|
call assert_equal('-INF ', printf('%1$-*2$E', -1.0 / 0.0, 6))
|
||||||
|
call assert_equal(' inf', printf('%2$*1$f', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal(' -inf', printf('%2$*1$f', 6, -1.0 / 0.0))
|
||||||
|
call assert_equal(' inf', printf('%2$*1$g', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal(' -inf', printf('%2$*1$g', 6, -1.0 / 0.0))
|
||||||
|
call assert_equal(' +inf', printf('%2$+*1$f', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal(' inf', printf('%2$ *1$f', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal(' +inf', printf('%2$+0*1$f', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal('inf ', printf('%2$-*1$f', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal('-inf ', printf('%2$-*1$f', 6, -1.0 / 0.0))
|
||||||
|
call assert_equal('+inf ', printf('%2$-+*1$f', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal(' inf ', printf('%2$- *1$f', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal('-INF ', printf('%2$-*1$F', 6, -1.0 / 0.0))
|
||||||
|
call assert_equal('+INF ', printf('%2$-+*1$F', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal(' INF ', printf('%2$- *1$F', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal('INF ', printf('%2$-*1$G', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal('-INF ', printf('%2$-*1$G', 6, -1.0 / 0.0))
|
||||||
|
call assert_equal('INF ', printf('%2$-*1$E', 6, 1.0 / 0.0))
|
||||||
|
call assert_equal('-INF ', printf('%2$-*1$E', 6, -1.0 / 0.0))
|
||||||
|
call assert_equal("str2float('inf')", printf('%1$s', 1.0 / 0.0))
|
||||||
|
call assert_equal("-str2float('inf')", printf('%1$s', -1.0 / 0.0))
|
||||||
|
|
||||||
|
#" Test special case where max precision is truncated at 340.
|
||||||
|
call assert_equal('1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%1$.*2$f', 1.0, 330))
|
||||||
|
call assert_equal('1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%2$.*1$f', 330, 1.0))
|
||||||
|
call assert_equal('1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%1$.*2$f', 1.0, 340))
|
||||||
|
call assert_equal('1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%2$.*1$f', 340, 1.0))
|
||||||
|
call assert_equal('1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%1$.*2$f', 1.0, 350))
|
||||||
|
call assert_equal('1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%2$.*1$f', 350, 1.0))
|
||||||
|
|
||||||
|
#" Float nan (not a number) has no sign.
|
||||||
|
call assert_equal('nan', printf('%1$f', sqrt(-1.0)))
|
||||||
|
call assert_equal('nan', printf('%1$f', 0.0 / 0.0))
|
||||||
|
call assert_equal('nan', printf('%1$f', -0.0 / 0.0))
|
||||||
|
call assert_equal('nan', printf('%1$g', 0.0 / 0.0))
|
||||||
|
call assert_equal('nan', printf('%1$e', 0.0 / 0.0))
|
||||||
|
call assert_equal('NAN', printf('%1$F', 0.0 / 0.0))
|
||||||
|
call assert_equal('NAN', printf('%1$G', 0.0 / 0.0))
|
||||||
|
call assert_equal('NAN', printf('%1$E', 0.0 / 0.0))
|
||||||
|
call assert_equal('NAN', printf('%1$F', -0.0 / 0.0))
|
||||||
|
call assert_equal('NAN', printf('%1$G', -0.0 / 0.0))
|
||||||
|
call assert_equal('NAN', printf('%1$E', -0.0 / 0.0))
|
||||||
|
call assert_equal(' nan', printf('%1$*2$f', 0.0 / 0.0, 6))
|
||||||
|
call assert_equal(' nan', printf('%1$0*2$f', 0.0 / 0.0, 6))
|
||||||
|
call assert_equal('nan ', printf('%1$-*2$f', 0.0 / 0.0, 6))
|
||||||
|
call assert_equal('nan ', printf('%1$- *2$f', 0.0 / 0.0, 6))
|
||||||
|
call assert_equal(' nan', printf('%2$*1$f', 6, 0.0 / 0.0))
|
||||||
|
call assert_equal(' nan', printf('%2$0*1$f', 6, 0.0 / 0.0))
|
||||||
|
call assert_equal('nan ', printf('%2$-*1$f', 6, 0.0 / 0.0))
|
||||||
|
call assert_equal('nan ', printf('%2$- *1$f', 6, 0.0 / 0.0))
|
||||||
|
call assert_equal("str2float('nan')", printf('%1$s', 0.0 / 0.0))
|
||||||
|
call assert_equal("str2float('nan')", printf('%1$s', -0.0 / 0.0))
|
||||||
|
call assert_equal("str2float('nan')", printf('%1$S', 0.0 / 0.0))
|
||||||
|
call assert_equal("str2float('nan')", printf('%1$S', -0.0 / 0.0))
|
||||||
|
END
|
||||||
|
call CheckLegacyAndVim9Success(lines)
|
||||||
|
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%f", "a")'], 'E807:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_printf_pos_errors()
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%1$d", {})'], 'E728:')
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%1$d", [])'], 'E745:')
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%1$d", 1, 2)'], 'E767:')
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%*d", 1)'], 'E766:')
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%1$s")'], 'E1403:')
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%1$d", 1.2)'], 'E805:')
|
||||||
|
call CheckLegacyAndVim9Failure(['echo printf("%1$f")'], 'E1403:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_printf_pos_64bit()
|
||||||
|
let lines =<< trim END
|
||||||
|
call assert_equal("123456789012345", printf('%1$d', 123456789012345))
|
||||||
|
END
|
||||||
|
call CheckLegacyAndVim9Success(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_printf_pos_spec_s()
|
||||||
|
let lines =<< trim END
|
||||||
|
#" number
|
||||||
|
call assert_equal("1234567890", printf('%1$s', 1234567890))
|
||||||
|
|
||||||
|
#" string
|
||||||
|
call assert_equal("abcdefgi", printf('%1$s', "abcdefgi"))
|
||||||
|
|
||||||
|
#" float
|
||||||
|
call assert_equal("1.23", printf('%1$s', 1.23))
|
||||||
|
|
||||||
|
#" list
|
||||||
|
VAR lvalue = [1, 'two', ['three', 4]]
|
||||||
|
call assert_equal(string(lvalue), printf('%1$s', lvalue))
|
||||||
|
|
||||||
|
#" dict
|
||||||
|
VAR dvalue = {'key1': 'value1', 'key2': ['list', 'lvalue'], 'key3': {'dict': 'lvalue'}}
|
||||||
|
call assert_equal(string(dvalue), printf('%1$s', dvalue))
|
||||||
|
|
||||||
|
#" funcref
|
||||||
|
call assert_equal('printf', printf('%1$s', 'printf'->function()))
|
||||||
|
|
||||||
|
#" partial
|
||||||
|
call assert_equal(string(function('printf', ['%1$s'])), printf('%1$s', function('printf', ['%1$s'])))
|
||||||
|
END
|
||||||
|
call CheckLegacyAndVim9Success(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_printf_pos_spec_b()
|
||||||
|
let lines =<< trim END
|
||||||
|
call assert_equal("0", printf('%1$b', 0))
|
||||||
|
call assert_equal("00001100", printf('%1$0*2$b', 12, 8))
|
||||||
|
call assert_equal("11111111", printf('%1$0*2$b', 0xff, 8))
|
||||||
|
call assert_equal(" 1111011", printf('%1$*2$b', 123, 10))
|
||||||
|
call assert_equal("0001111011", printf('%1$0*2$b', 123, 10))
|
||||||
|
call assert_equal(" 0b1111011", printf('%1$#*2$b', 123, 10))
|
||||||
|
call assert_equal("0B01111011", printf('%1$#0*2$B', 123, 10))
|
||||||
|
call assert_equal("00001100", printf('%2$0*1$b', 8, 12))
|
||||||
|
call assert_equal("11111111", printf('%2$0*1$b', 8, 0xff))
|
||||||
|
call assert_equal(" 1111011", printf('%2$*1$b', 10, 123))
|
||||||
|
call assert_equal("0001111011", printf('%2$0*1$b', 10, 123))
|
||||||
|
call assert_equal(" 0b1111011", printf('%2$#*1$b', 10, 123))
|
||||||
|
call assert_equal("0B01111011", printf('%2$#0*1$B', 10, 123))
|
||||||
|
call assert_equal("1001001100101100000001011010010", printf('%1$b', 1234567890))
|
||||||
|
call assert_equal("11100000100100010000110000011011101111101111001", printf('%1$b', 123456789012345))
|
||||||
|
call assert_equal("1111111111111111111111111111111111111111111111111111111111111111", printf('%1$b', -1))
|
||||||
|
END
|
||||||
|
call CheckLegacyAndVim9Success(lines)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" vim: shiftwidth=2 sts=2 expandtab
|
@@ -1439,7 +1439,7 @@ describe('typval.c', function()
|
|||||||
eq('3', tv_list_find_str(l, 2))
|
eq('3', tv_list_find_str(l, 2))
|
||||||
eq('3', tv_list_find_str(l, -3))
|
eq('3', tv_list_find_str(l, -3))
|
||||||
|
|
||||||
alloc_log:check({a.freed(alloc_log.null)})
|
alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)})
|
||||||
end)
|
end)
|
||||||
itp('returns string when used with VAR_STRING items', function()
|
itp('returns string when used with VAR_STRING items', function()
|
||||||
local l = list('1', '2', '3', '4', '5')
|
local l = list('1', '2', '3', '4', '5')
|
||||||
@@ -1768,7 +1768,7 @@ describe('typval.c', function()
|
|||||||
local s44 = check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end,
|
local s44 = check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end,
|
||||||
nil)
|
nil)
|
||||||
eq('44.0', ffi.string(s44))
|
eq('44.0', ffi.string(s44))
|
||||||
alloc_log:check({a.freed(alloc_log.null)})
|
alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)})
|
||||||
end)
|
end)
|
||||||
itp('allocates a string copy when requested', function()
|
itp('allocates a string copy when requested', function()
|
||||||
local function tv_dict_get_string_alloc(d, key, emsg, is_float)
|
local function tv_dict_get_string_alloc(d, key, emsg, is_float)
|
||||||
@@ -1779,7 +1779,7 @@ describe('typval.c', function()
|
|||||||
if not emsg then
|
if not emsg then
|
||||||
if s_ret then
|
if s_ret then
|
||||||
if is_float then
|
if is_float then
|
||||||
alloc_log:check({a.freed(alloc_log.null), a.str(ret, s_ret)})
|
alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null), a.str(ret, s_ret)})
|
||||||
else
|
else
|
||||||
alloc_log:check({a.str(ret, s_ret)})
|
alloc_log:check({a.str(ret, s_ret)})
|
||||||
end
|
end
|
||||||
@@ -1810,7 +1810,7 @@ describe('typval.c', function()
|
|||||||
local s_ret = (ret ~= nil) and ffi.string(ret) or nil
|
local s_ret = (ret ~= nil) and ffi.string(ret) or nil
|
||||||
if not emsg then
|
if not emsg then
|
||||||
if is_float then
|
if is_float then
|
||||||
alloc_log:check({a.freed(alloc_log.null)})
|
alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)})
|
||||||
else
|
else
|
||||||
alloc_log:check({})
|
alloc_log:check({})
|
||||||
end
|
end
|
||||||
@@ -1856,7 +1856,7 @@ describe('typval.c', function()
|
|||||||
local s_ret = (ret ~= nil) and ffi.string(ret) or nil
|
local s_ret = (ret ~= nil) and ffi.string(ret) or nil
|
||||||
if not emsg then
|
if not emsg then
|
||||||
if is_float then
|
if is_float then
|
||||||
alloc_log:check({a.freed(alloc_log.null)})
|
alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)})
|
||||||
else
|
else
|
||||||
alloc_log:check({})
|
alloc_log:check({})
|
||||||
end
|
end
|
||||||
@@ -3018,7 +3018,7 @@ describe('typval.c', function()
|
|||||||
if emsg then
|
if emsg then
|
||||||
alloc_log:clear()
|
alloc_log:clear()
|
||||||
elseif tv.v_type == lib.VAR_FLOAT then
|
elseif tv.v_type == lib.VAR_FLOAT then
|
||||||
alloc_log:check({a.freed(alloc_log.null)})
|
alloc_log:check({a.freed(alloc_log.null), a.freed(alloc_log.null)})
|
||||||
else
|
else
|
||||||
alloc_log:check({})
|
alloc_log:check({})
|
||||||
end
|
end
|
||||||
|
@@ -140,37 +140,83 @@ describe('vim_strchr()', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe('vim_snprintf()', function()
|
describe('vim_snprintf()', function()
|
||||||
itp('truncation', function()
|
local function a(expected, buf, bsize, fmt, ...)
|
||||||
local function check(expected, buf, bsize, fmt, ...)
|
eq(#expected, strings.vim_snprintf(buf, bsize, fmt, ...))
|
||||||
eq(#expected, strings.vim_snprintf(buf, bsize, fmt, ...))
|
if bsize > 0 then
|
||||||
if bsize > 0 then
|
local actual = ffi.string(buf, math.min(#expected + 1, bsize))
|
||||||
local actual = ffi.string(buf, math.min(#expected + 1, bsize))
|
eq(expected:sub(1, bsize - 1) .. '\0', actual)
|
||||||
eq(expected:sub(1, bsize - 1) .. '\0', actual)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function i(n) return ffi.cast('int', n) end
|
||||||
|
local function l(n) return ffi.cast('long', n) end
|
||||||
|
local function u(n) return ffi.cast('unsigned', n) end
|
||||||
|
local function ll(n) return ffi.cast('long long', n) end
|
||||||
|
local function ul(n) return ffi.cast('unsigned long', n) end
|
||||||
|
local function ull(n) return ffi.cast('unsigned long long', n) end
|
||||||
|
|
||||||
|
itp('truncation', function()
|
||||||
for bsize = 0, 14 do
|
for bsize = 0, 14 do
|
||||||
local buf = ffi.gc(strings.xmalloc(bsize), strings.xfree)
|
local buf = ffi.gc(strings.xmalloc(bsize), strings.xfree)
|
||||||
check('1234567', buf, bsize, '%d', ffi.cast('int', 1234567))
|
a('1.00000001e7', buf, bsize, '%.8g', 10000000.1)
|
||||||
check('1234567', buf, bsize, '%ld', ffi.cast('long', 1234567))
|
a('1234567', buf, bsize, '%d', i(1234567))
|
||||||
check(' 1234567', buf, bsize, '%9ld', ffi.cast('long', 1234567))
|
a('1234567', buf, bsize, '%ld', l(1234567))
|
||||||
check('1234567 ', buf, bsize, '%-9ld', ffi.cast('long', 1234567))
|
a(' 1234567', buf, bsize, '%9ld', l(1234567))
|
||||||
check('deadbeef', buf, bsize, '%x', ffi.cast('unsigned', 0xdeadbeef))
|
a('1234567 ', buf, bsize, '%-9ld', l(1234567))
|
||||||
check('001100', buf, bsize, '%06b', ffi.cast('int', 12))
|
a('deadbeef', buf, bsize, '%x', u(0xdeadbeef))
|
||||||
check('1.234000', buf, bsize, '%f', ffi.cast('double', 1.234))
|
a('001100', buf, bsize, '%06b', u(12))
|
||||||
check('1.234000e+00', buf, bsize, '%e', ffi.cast('double', 1.234))
|
a('one two', buf, bsize, '%s %s', 'one', 'two')
|
||||||
check('nan', buf, bsize, '%f', ffi.cast('double', 0.0 / 0.0))
|
a('1.234000', buf, bsize, '%f', 1.234)
|
||||||
check('inf', buf, bsize, '%f', ffi.cast('double', 1.0 / 0.0))
|
a('1.234000e+00', buf, bsize, '%e', 1.234)
|
||||||
check('-inf', buf, bsize, '%f', ffi.cast('double', -1.0 / 0.0))
|
a('nan', buf, bsize, '%f', 0.0 / 0.0)
|
||||||
check('-0.000000', buf, bsize, '%f', ffi.cast('double', -0.0))
|
a('inf', buf, bsize, '%f', 1.0 / 0.0)
|
||||||
check('漢語', buf, bsize, '%s', '漢語')
|
a('-inf', buf, bsize, '%f', -1.0 / 0.0)
|
||||||
check(' 漢語', buf, bsize, '%8s', '漢語')
|
a('-0.000000', buf, bsize, '%f', -0.0)
|
||||||
check('漢語 ', buf, bsize, '%-8s', '漢語')
|
a('漢語', buf, bsize, '%s', '漢語')
|
||||||
check('漢', buf, bsize, '%.3s', '漢語')
|
a(' 漢語', buf, bsize, '%8s', '漢語')
|
||||||
check(' foo', buf, bsize, '%5S', 'foo')
|
a('漢語 ', buf, bsize, '%-8s', '漢語')
|
||||||
check('%%%', buf, bsize, '%%%%%%')
|
a('漢', buf, bsize, '%.3s', '漢語')
|
||||||
check('0x87654321', buf, bsize, '%p', ffi.cast('char *', 0x87654321))
|
a(' foo', buf, bsize, '%5S', 'foo')
|
||||||
check('0x0087654321', buf, bsize, '%012p', ffi.cast('char *', 0x87654321))
|
a('%%%', buf, bsize, '%%%%%%')
|
||||||
|
a('0x87654321', buf, bsize, '%p', ffi.cast('char *', 0x87654321))
|
||||||
|
a('0x0087654321', buf, bsize, '%012p', ffi.cast('char *', 0x87654321))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
itp('positional arguments', function()
|
||||||
|
for bsize = 0, 24 do
|
||||||
|
local buf = ffi.gc(strings.xmalloc(bsize), strings.xfree)
|
||||||
|
a('1234567 ', buf, bsize, '%1$*2$ld', l(1234567), i(-9))
|
||||||
|
a('1234567 ', buf, bsize, '%1$*2$.*3$ld', l(1234567), i(-9), i(5))
|
||||||
|
a('1234567 ', buf, bsize, '%1$*3$.*2$ld', l(1234567), i(5), i(-9))
|
||||||
|
a('1234567 ', buf, bsize, '%3$*1$.*2$ld', i(-9), i(5), l(1234567))
|
||||||
|
a('1234567', buf, bsize, '%1$ld', l(1234567))
|
||||||
|
a(' 1234567', buf, bsize, '%1$*2$ld', l(1234567), i(9))
|
||||||
|
a('9 12345 7654321', buf, bsize, '%2$ld %1$d %3$lu', i(12345), l(9), ul(7654321))
|
||||||
|
a('9 1234567 7654321', buf, bsize, '%2$d %1$ld %3$lu', l(1234567), i(9), ul(7654321))
|
||||||
|
a('9 1234567 7654321', buf, bsize, '%2$d %1$lld %3$lu', ll(1234567), i(9), ul(7654321))
|
||||||
|
a('9 12345 7654321', buf, bsize, '%2$ld %1$u %3$lu', u(12345), l(9), ul(7654321))
|
||||||
|
a('9 1234567 7654321', buf, bsize, '%2$d %1$lu %3$lu', ul(1234567), i(9), ul(7654321))
|
||||||
|
a('9 1234567 7654321', buf, bsize, '%2$d %1$llu %3$lu', ull(1234567), i(9), ul(7654321))
|
||||||
|
a('9 1234567 7654321', buf, bsize, '%2$d %1$llu %3$lu', ull(1234567), i(9), ul(7654321))
|
||||||
|
a('9 deadbeef 7654321', buf, bsize, '%2$d %1$x %3$lu', u(0xdeadbeef), i(9), ul(7654321))
|
||||||
|
a('9 c 7654321', buf, bsize, '%2$ld %1$c %3$lu', i(('c'):byte()), l(9), ul(7654321))
|
||||||
|
a('9 hi 7654321', buf, bsize, '%2$ld %1$s %3$lu', 'hi', l(9), ul(7654321))
|
||||||
|
a('9 0.000000e+00 7654321', buf, bsize, '%2$ld %1$e %3$lu', 0.0, l(9), ul(7654321))
|
||||||
|
a('two one two', buf, bsize, '%2$s %1$s %2$s', 'one', 'two', 'three')
|
||||||
|
a('three one two', buf, bsize, '%3$s %1$s %2$s', 'one', 'two', 'three')
|
||||||
|
a('1234567', buf, bsize, '%1$d', i(1234567))
|
||||||
|
a('deadbeef', buf, bsize, '%1$x', u(0xdeadbeef))
|
||||||
|
a('001100', buf, bsize, '%1$0.*2$b', u(12), i(6))
|
||||||
|
a('one two', buf, bsize, '%1$s %2$s', 'one', 'two')
|
||||||
|
a('001100', buf, bsize, '%06b', u(12))
|
||||||
|
a('two one', buf, bsize, '%2$s %1$s', 'one', 'two')
|
||||||
|
a('1.234000', buf, bsize, '%1$f', 1.234)
|
||||||
|
a('1.234000e+00', buf, bsize, '%1$e', 1.234)
|
||||||
|
a('nan', buf, bsize, '%1$f', 0.0 / 0.0)
|
||||||
|
a('inf', buf, bsize, '%1$f', 1.0 / 0.0)
|
||||||
|
a('-inf', buf, bsize, '%1$f', -1.0 / 0.0)
|
||||||
|
a('-0.000000', buf, bsize, '%1$f', -0.0)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Reference in New Issue
Block a user