Minor improvements to format field numbers

* src/editfns.c (styled_format): Allow field numbers in a %% spec.
No need for a special diagnostic for field numbers greater than
PTRDIFF_MAX.  Reword diagnostic for field 0.
* test/src/editfns-tests.el (format-with-field): Adjust to match.
This commit is contained in:
Paul Eggert 2017-05-31 22:09:39 -07:00 committed by Philipp Stephani
parent 0dd1bbb0bb
commit 5324710841
4 changed files with 89 additions and 118 deletions

View file

@ -864,15 +864,6 @@ below, as the first argument, and the string as the second, like this:
(format "%s" @var{arbitrary-string})
@end example
If @var{string} contains more than one format specification and none
of the format specifications contain an explicit field number, the
format specifications correspond to successive values from
@var{objects}. Thus, the first format specification in @var{string}
uses the first such value, the second format specification uses the
second such value, and so on. Any extra format specifications (those
for which there are no corresponding values) cause an error. Any
extra values to be formatted are ignored.
Certain format specifications require values of particular types. If
you supply a value that doesn't fit the requirements, an error is
signaled.
@ -962,33 +953,84 @@ operation} error.
@end group
@end example
@cindex field numbers in format spec
A specification can have a @dfn{field number}, which is a decimal
number after the initial @samp{%}, followed by a literal dollar sign
@samp{$}. If you provide a field number, then the argument to be
printed corresponds to the given field number instead of the next
argument. Field numbers start at 1.
By default, format specifications correspond to successive values from
@var{objects}. Thus, the first format specification in @var{string}
uses the first such value, the second format specification uses the
second such value, and so on. Any extra format specifications (those
for which there are no corresponding values) cause an error. Any
extra values to be formatted are ignored.
You can mix specifications with and without field numbers. A
@cindex field numbers in format spec
A format specification can have a @dfn{field number}, which is a
decimal number immediately after the initial @samp{%}, followed by a
literal dollar sign @samp{$}. It causes the format specification to
convert the argument with the given number instead of the next
argument. Argument 1 is the argument just after the format.
You can mix specifications with and without field numbers. A
specification without a field number that follows a specification with
a field number will convert the argument after the one specified by
the field number:
@example
(format "First argument %2$s, then %s, then %1$s" 1 2 3)
@result{} "First argument 2, then 3, then 1"
(format "Argument %2$s, then %s, then %1$s" "x" "y" "z")
@result{} "Argument y, then z, then x"
@end example
You can't use field numbers in a @samp{%%} specification.
@cindex flags in format specifications
After the @samp{%} and any field number, you can put certain
@dfn{flag characters}.
The flag @samp{+} inserts a plus sign before a positive number, so
that it always has a sign. A space character as flag inserts a space
before a positive number. (Otherwise, positive numbers start with the
first digit.) These flags are useful for ensuring that positive
numbers and negative numbers use the same number of columns. They are
ignored except for @samp{%d}, @samp{%e}, @samp{%f}, @samp{%g}, and if
both flags are used, @samp{+} takes precedence.
The flag @samp{#} specifies an alternate form which depends on
the format in use. For @samp{%o}, it ensures that the result begins
with a @samp{0}. For @samp{%x} and @samp{%X}, it prefixes the result
with @samp{0x} or @samp{0X}. For @samp{%e} and @samp{%f}, the
@samp{#} flag means include a decimal point even if the precision is
zero. For @samp{%g}, it always includes a decimal point, and also
forces any trailing zeros after the decimal point to be left in place
where they would otherwise be removed.
The flag @samp{0} ensures that the padding consists of @samp{0}
characters instead of spaces. This flag is ignored for non-numerical
specification characters like @samp{%s}, @samp{%S} and @samp{%c}.
These specification characters accept the @samp{0} flag, but still pad
with @emph{spaces}.
The flag @samp{-} causes any padding inserted by the width,
if specified, to be inserted on the right rather than the left.
If both @samp{-} and @samp{0} are present, the @samp{0} flag is
ignored.
@example
@group
(format "%06d is padded on the left with zeros" 123)
@result{} "000123 is padded on the left with zeros"
(format "'%-6d' is padded on the right" 123)
@result{} "'123 ' is padded on the right"
(format "The word '%-7s' actually has %d letters in it."
"foo" (length "foo"))
@result{} "The word 'foo ' actually has 3 letters in it."
@end group
@end example
@cindex field width
@cindex padding
A specification can have a @dfn{width}, which is a decimal number
between the @samp{%} and the specification character. If the printed
that appears after any field number and flags. If the printed
representation of the object contains fewer characters than this
width, @code{format} extends it with padding. The width specifier is
width, @code{format} extends it with padding. The width is
ignored for the @samp{%%} specification. Any padding introduced by
the width specifier normally consists of spaces inserted on the left:
the width normally consists of spaces inserted on the left:
@example
(format "%5d is padded on the left with spaces" 123)
@ -1016,60 +1058,9 @@ is not truncated.
@end group
@end example
If you want to use both a field number and a width, place the field
number before the width. For example, in @samp{%2$7s}, @samp{2} is
the field number and @samp{7} is the width.
@cindex flags in format specifications
After the @samp{%} and before the optional width specifier, you can
also put certain @dfn{flag characters}. The flag characters need to
come directly after a potential field number.
The flag @samp{+} inserts a plus sign before a positive number, so
that it always has a sign. A space character as flag inserts a space
before a positive number. (Otherwise, positive numbers start with the
first digit.) These flags are useful for ensuring that positive
numbers and negative numbers use the same number of columns. They are
ignored except for @samp{%d}, @samp{%e}, @samp{%f}, @samp{%g}, and if
both flags are used, @samp{+} takes precedence.
The flag @samp{#} specifies an alternate form which depends on
the format in use. For @samp{%o}, it ensures that the result begins
with a @samp{0}. For @samp{%x} and @samp{%X}, it prefixes the result
with @samp{0x} or @samp{0X}. For @samp{%e} and @samp{%f}, the
@samp{#} flag means include a decimal point even if the precision is
zero. For @samp{%g}, it always includes a decimal point, and also
forces any trailing zeros after the decimal point to be left in place
where they would otherwise be removed.
The flag @samp{0} ensures that the padding consists of @samp{0}
characters instead of spaces. This flag is ignored for non-numerical
specification characters like @samp{%s}, @samp{%S} and @samp{%c}.
These specification characters accept the @samp{0} flag, but still pad
with @emph{spaces}.
The flag @samp{-} causes the padding inserted by the width
specifier, if any, to be inserted on the right rather than the left.
If both @samp{-} and @samp{0} are present, the @samp{0} flag is
ignored.
@example
@group
(format "%06d is padded on the left with zeros" 123)
@result{} "000123 is padded on the left with zeros"
(format "'%-6d' is padded on the right" 123)
@result{} "'123 ' is padded on the right"
(format "The word '%-7s' actually has %d letters in it."
"foo" (length "foo"))
@result{} "The word 'foo ' actually has 3 letters in it."
@end group
@end example
@cindex precision in format specifications
All the specification characters allow an optional @dfn{precision}
before the character (after the width, if present). The precision is
after the field number, flags and width, if present. The precision is
a decimal-point @samp{.} followed by a digit-string. For the
floating-point specifications (@samp{%e} and @samp{%f}), the
precision specifies how many digits following the decimal point to

View file

@ -369,7 +369,7 @@ libraries: 'find-library-other-window' and 'find-library-other-frame'.
display of raw bytes from octal to hex.
** You can now provide explicit field numbers in format specifiers.
For example, '(format "%2$s %1$s" 1 2)' produces "2 1".
For example, '(format "%2$s %1$s" "X" "Y")' produces "Y X".
* Editing Changes in Emacs 26.1

View file

@ -4046,9 +4046,8 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
field-width ::= [0-9]+
precision ::= '.' [0-9]*
If a field-number is specified, it specifies the argument
number to substitute. Otherwise, the next argument is
taken.
If present, a field-number specifies the argument number
to substitute. Otherwise, the next argument is taken.
If a field-width is specified, it specifies to which width
the output should be padded with blanks, if the output
@ -4058,28 +4057,20 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
digits to print after the '.' for floats, or the max.
number of chars to print from a string. */
char *field_end;
uintmax_t raw_field = strtoumax (format, &field_end, 10);
bool has_field = false;
if (c_isdigit (*format) && *field_end == '$')
{
if (raw_field < 1 || raw_field >= PTRDIFF_MAX)
{
/* doprnt doesn't support %.*s, so we need to copy
the field number string. */
ptrdiff_t length = field_end - format;
eassert (length > 0);
eassert (length < PTRDIFF_MAX);
char *field = SAFE_ALLOCA (length + 1);
memcpy (field, format, length);
field[length] = '\0';
error ("Invalid field number `%s'", field);
}
has_field = true;
/* n is incremented below. */
n = raw_field - 1;
format = field_end + 1;
}
uintmax_t num;
char *num_end;
if (c_isdigit (*format))
{
num = strtoumax (format, &num_end, 10);
if (*num_end == '$')
{
if (num == 0)
error ("Invalid format field number 0");
n = min (num, PTRDIFF_MAX);
n--;
format = num_end + 1;
}
}
bool minus_flag = false;
bool plus_flag = false;
@ -4104,11 +4095,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
space_flag &= ! plus_flag;
zero_flag &= ! minus_flag;
char *num_end;
uintmax_t raw_field_width = strtoumax (format, &num_end, 10);
if (max_bufsize <= raw_field_width)
num = strtoumax (format, &num_end, 10);
if (max_bufsize <= num)
string_overflow ();
ptrdiff_t field_width = raw_field_width;
ptrdiff_t field_width = num;
bool precision_given = *num_end == '.';
uintmax_t precision = (precision_given
@ -4123,13 +4113,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
memset (&discarded[format0 - format_start], 1,
format - format0 - (conversion == '%'));
if (conversion == '%')
{
if (has_field)
/* FIXME: `error' doesn't appear to support `%%'. */
error ("Field number specified together with `%c' conversion",
'%');
goto copy_char;
}
goto copy_char;
++n;
if (! (n < nargs))

View file

@ -186,13 +186,9 @@
(should (equal (should-error (format "a %999999$s b" 11))
'(error "Not enough arguments for format string")))
(should (equal (should-error (format "a %$s b" 11))
;; FIXME: there shouldn't be two % in the error
;; string!
'(error "Invalid format operation %%$")))
'(error "Invalid format operation %$")))
(should (equal (should-error (format "a %0$s b" 11))
'(error "Invalid field number `0'")))
(should (equal
(should-error (format "a %1$% %s b" 11))
'(error "Field number specified together with `%' conversion"))))
'(error "Invalid format field number 0")))
(should (equal (format "a %1$% %s b" 11) "a % 11 b")))
;;; editfns-tests.el ends here