aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2017-06-04 08:39:37 -0700
committerPaul Eggert2017-06-04 08:42:53 -0700
commitd5fcf9e458053af1c50f0d4dad4c59db056790e5 (patch)
treef6fe65eb1823c49d771d520c4e6d66b92525ac2d
parent6ad42aecc243d378d38468fd3efd89bc1fbfb187 (diff)
downloademacs-d5fcf9e458053af1c50f0d4dad4c59db056790e5.tar.gz
emacs-d5fcf9e458053af1c50f0d4dad4c59db056790e5.zip
Tune ‘format’ after recent fix
* doc/lispref/strings.texi (Formatting Strings): * src/editfns.c (Fformat): Format field numbers no longer need to be unique, reverting the previous doc change since that has now been fixed. Also, document that %% should not have modifiers. * src/editfns.c (styled_format): Improve performance. Remove the need for the new prepass over the format string, by using a typically-more-generous bound for the info array size. Initialize the info array lazily. Move string inspection to the same area to help caching. Avoid the need for a converted_to_string bitfield by using EQ. Cache arg in a local and avoid some potential aliasing issues to help the compiler. Info array is now 0-origin, not 1-origin.
-rw-r--r--doc/lispref/strings.texi13
-rw-r--r--src/editfns.c154
2 files changed, 72 insertions, 95 deletions
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index f365c80493d..23961f99efd 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -926,7 +926,8 @@ digit.
926 926
927@item %% 927@item %%
928Replace the specification with a single @samp{%}. This format 928Replace the specification with a single @samp{%}. This format
929specification is unusual in that it does not use a value. For example, 929specification is unusual in that its only form is plain
930@samp{%%} and that it does not use a value. For example,
930@code{(format "%% %d" 30)} returns @code{"% 30"}. 931@code{(format "%% %d" 30)} returns @code{"% 30"}.
931@end table 932@end table
932 933
@@ -965,10 +966,9 @@ extra values to be formatted are ignored.
965decimal number immediately after the initial @samp{%}, followed by a 966decimal number immediately after the initial @samp{%}, followed by a
966literal dollar sign @samp{$}. It causes the format specification to 967literal dollar sign @samp{$}. It causes the format specification to
967convert the argument with the given number instead of the next 968convert the argument with the given number instead of the next
968argument. Field numbers start at 1. A field number should differ 969argument. Field numbers start at 1. A format can contain either
969from the other field numbers in the same format. A format can contain 970numbered or unnumbered format specifications but not both, except that
970either numbered or unnumbered format specifications but not both, 971@samp{%%} can be mixed with numbered specifications.
971except that @samp{%%} can be mixed with numbered specifications.
972 972
973@example 973@example
974(format "%2$s, %3$s, %%, %1$s" "x" "y" "z") 974(format "%2$s, %3$s, %%, %1$s" "x" "y" "z")
@@ -1026,8 +1026,7 @@ ignored.
1026 A specification can have a @dfn{width}, which is a decimal number 1026 A specification can have a @dfn{width}, which is a decimal number
1027that appears after any field number and flags. If the printed 1027that appears after any field number and flags. If the printed
1028representation of the object contains fewer characters than this 1028representation of the object contains fewer characters than this
1029width, @code{format} extends it with padding. The width is 1029width, @code{format} extends it with padding. Any padding introduced by
1030ignored for the @samp{%%} specification. Any padding introduced by
1031the width normally consists of spaces inserted on the left: 1030the width normally consists of spaces inserted on the left:
1032 1031
1033@example 1032@example
diff --git a/src/editfns.c b/src/editfns.c
index 56aa8ce1a72..43b17f9f116 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -3891,8 +3891,8 @@ the next available argument, or the argument explicitly specified:
3891The argument used for %d, %o, %x, %e, %f, %g or %c must be a number. 3891The argument used for %d, %o, %x, %e, %f, %g or %c must be a number.
3892Use %% to put a single % into the output. 3892Use %% to put a single % into the output.
3893 3893
3894A %-sequence may contain optional field number, flag, width, and 3894A %-sequence other than %% may contain optional field number, flag,
3895precision specifiers, as follows: 3895width, and precision specifiers, as follows:
3896 3896
3897 %<field><flags><width><precision>character 3897 %<field><flags><width><precision>character
3898 3898
@@ -3901,10 +3901,9 @@ where field is [0-9]+ followed by a literal dollar "$", flags is
3901followed by [0-9]+. 3901followed by [0-9]+.
3902 3902
3903If a %-sequence is numbered with a field with positive value N, the 3903If a %-sequence is numbered with a field with positive value N, the
3904Nth argument is substituted instead of the next one. A field number 3904Nth argument is substituted instead of the next one. A format can
3905should differ from the other field numbers in the same format. A 3905contain either numbered or unnumbered %-sequences but not both, except
3906format can contain either numbered or unnumbered %-sequences but not 3906that %% can be mixed with numbered %-sequences.
3907both, except that %% can be mixed with numbered %-sequences.
3908 3907
3909The + flag character inserts a + before any positive number, while a 3908The + flag character inserts a + before any positive number, while a
3910space inserts a space before any positive number; these flags only 3909space inserts a space before any positive number; these flags only
@@ -3980,49 +3979,40 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
3980 bool arg_intervals = false; 3979 bool arg_intervals = false;
3981 USE_SAFE_ALLOCA; 3980 USE_SAFE_ALLOCA;
3982 3981
3983 /* Each element records, for one field, 3982 /* Information recorded for each format spec. */
3984 the corresponding argument,
3985 the start and end bytepos in the output string,
3986 whether the argument has been converted to string (e.g., due to "%S"),
3987 and whether the argument is a string with intervals. */
3988 struct info 3983 struct info
3989 { 3984 {
3985 /* The corresponding argument, converted to string if conversion
3986 was needed. */
3990 Lisp_Object argument; 3987 Lisp_Object argument;
3988
3989 /* The start and end bytepos in the output string. */
3991 ptrdiff_t start, end; 3990 ptrdiff_t start, end;
3992 bool_bf converted_to_string : 1; 3991
3992 /* Whether the argument is a string with intervals. */
3993 bool_bf intervals : 1; 3993 bool_bf intervals : 1;
3994 } *info; 3994 } *info;
3995 3995
3996 CHECK_STRING (args[0]); 3996 CHECK_STRING (args[0]);
3997 char *format_start = SSDATA (args[0]); 3997 char *format_start = SSDATA (args[0]);
3998 bool multibyte_format = STRING_MULTIBYTE (args[0]);
3998 ptrdiff_t formatlen = SBYTES (args[0]); 3999 ptrdiff_t formatlen = SBYTES (args[0]);
3999 4000
4000 /* The number of percent characters is a safe upper bound for the 4001 /* Upper bound on number of format specs. Each uses at least 2 chars. */
4001 number of format fields. */ 4002 ptrdiff_t nspec_bound = SCHARS (args[0]) >> 1;
4002 ptrdiff_t num_percent = 0;
4003 for (ptrdiff_t i = 0; i < formatlen; ++i)
4004 if (format_start[i] == '%')
4005 ++num_percent;
4006 4003
4007 /* Allocate the info and discarded tables. */ 4004 /* Allocate the info and discarded tables. */
4008 ptrdiff_t alloca_size; 4005 ptrdiff_t alloca_size;
4009 if (INT_MULTIPLY_WRAPV (num_percent, sizeof *info, &alloca_size) 4006 if (INT_MULTIPLY_WRAPV (nspec_bound, sizeof *info, &alloca_size)
4010 || INT_ADD_WRAPV (sizeof *info, alloca_size, &alloca_size)
4011 || INT_ADD_WRAPV (formatlen, alloca_size, &alloca_size) 4007 || INT_ADD_WRAPV (formatlen, alloca_size, &alloca_size)
4012 || SIZE_MAX < alloca_size) 4008 || SIZE_MAX < alloca_size)
4013 memory_full (SIZE_MAX); 4009 memory_full (SIZE_MAX);
4014 /* info[0] is unused. Unused elements have -1 for start. */
4015 info = SAFE_ALLOCA (alloca_size); 4010 info = SAFE_ALLOCA (alloca_size);
4016 memset (info, 0, alloca_size);
4017 for (ptrdiff_t i = 0; i < num_percent + 1; i++)
4018 {
4019 info[i].argument = Qunbound;
4020 info[i].start = -1;
4021 }
4022 /* discarded[I] is 1 if byte I of the format 4011 /* discarded[I] is 1 if byte I of the format
4023 string was not copied into the output. 4012 string was not copied into the output.
4024 It is 2 if byte I was not the first byte of its character. */ 4013 It is 2 if byte I was not the first byte of its character. */
4025 char *discarded = (char *) &info[num_percent + 1]; 4014 char *discarded = (char *) &info[nspec_bound];
4015 memset (discarded, 0, formatlen);
4026 4016
4027 /* Try to determine whether the result should be multibyte. 4017 /* Try to determine whether the result should be multibyte.
4028 This is not always right; sometimes the result needs to be multibyte 4018 This is not always right; sometimes the result needs to be multibyte
@@ -4030,8 +4020,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4030 or because a grave accent or apostrophe is requoted, 4020 or because a grave accent or apostrophe is requoted,
4031 and in that case, we won't know it here. */ 4021 and in that case, we won't know it here. */
4032 4022
4033 /* True if the format is multibyte. */
4034 bool multibyte_format = STRING_MULTIBYTE (args[0]);
4035 /* True if the output should be a multibyte string, 4023 /* True if the output should be a multibyte string,
4036 which is true if any of the inputs is one. */ 4024 which is true if any of the inputs is one. */
4037 bool multibyte = multibyte_format; 4025 bool multibyte = multibyte_format;
@@ -4042,6 +4030,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4042 int quoting_style = message ? text_quoting_style () : -1; 4030 int quoting_style = message ? text_quoting_style () : -1;
4043 4031
4044 ptrdiff_t ispec; 4032 ptrdiff_t ispec;
4033 ptrdiff_t nspec = 0;
4045 4034
4046 /* If we start out planning a unibyte result, 4035 /* If we start out planning a unibyte result,
4047 then discover it has to be multibyte, we jump back to retry. */ 4036 then discover it has to be multibyte, we jump back to retry. */
@@ -4155,11 +4144,14 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4155 if (! (n < nargs)) 4144 if (! (n < nargs))
4156 error ("Not enough arguments for format string"); 4145 error ("Not enough arguments for format string");
4157 4146
4158 eassert (ispec < num_percent); 4147 struct info *spec = &info[ispec++];
4159 ++ispec; 4148 if (nspec < ispec)
4160 4149 {
4161 if (EQ (info[ispec].argument, Qunbound)) 4150 spec->argument = args[n];
4162 info[ispec].argument = args[n]; 4151 spec->intervals = false;
4152 nspec = ispec;
4153 }
4154 Lisp_Object arg = spec->argument;
4163 4155
4164 /* For 'S', prin1 the argument, and then treat like 's'. 4156 /* For 'S', prin1 the argument, and then treat like 's'.
4165 For 's', princ any argument that is not a string or 4157 For 's', princ any argument that is not a string or
@@ -4167,16 +4159,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4167 happen after retrying. */ 4159 happen after retrying. */
4168 if ((conversion == 'S' 4160 if ((conversion == 'S'
4169 || (conversion == 's' 4161 || (conversion == 's'
4170 && ! STRINGP (info[ispec].argument) 4162 && ! STRINGP (arg) && ! SYMBOLP (arg))))
4171 && ! SYMBOLP (info[ispec].argument))))
4172 { 4163 {
4173 if (! info[ispec].converted_to_string) 4164 if (EQ (arg, args[n]))
4174 { 4165 {
4175 Lisp_Object noescape = conversion == 'S' ? Qnil : Qt; 4166 Lisp_Object noescape = conversion == 'S' ? Qnil : Qt;
4176 info[ispec].argument = 4167 spec->argument = arg = Fprin1_to_string (arg, noescape);
4177 Fprin1_to_string (info[ispec].argument, noescape); 4168 if (STRING_MULTIBYTE (arg) && ! multibyte)
4178 info[ispec].converted_to_string = true;
4179 if (STRING_MULTIBYTE (info[ispec].argument) && ! multibyte)
4180 { 4169 {
4181 multibyte = true; 4170 multibyte = true;
4182 goto retry; 4171 goto retry;
@@ -4186,29 +4175,25 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4186 } 4175 }
4187 else if (conversion == 'c') 4176 else if (conversion == 'c')
4188 { 4177 {
4189 if (INTEGERP (info[ispec].argument) 4178 if (INTEGERP (arg) && ! ASCII_CHAR_P (XINT (arg)))
4190 && ! ASCII_CHAR_P (XINT (info[ispec].argument)))
4191 { 4179 {
4192 if (!multibyte) 4180 if (!multibyte)
4193 { 4181 {
4194 multibyte = true; 4182 multibyte = true;
4195 goto retry; 4183 goto retry;
4196 } 4184 }
4197 info[ispec].argument = 4185 spec->argument = arg = Fchar_to_string (arg);
4198 Fchar_to_string (info[ispec].argument);
4199 info[ispec].converted_to_string = true;
4200 } 4186 }
4201 4187
4202 if (info[ispec].converted_to_string) 4188 if (!EQ (arg, args[n]))
4203 conversion = 's'; 4189 conversion = 's';
4204 zero_flag = false; 4190 zero_flag = false;
4205 } 4191 }
4206 4192
4207 if (SYMBOLP (info[ispec].argument)) 4193 if (SYMBOLP (arg))
4208 { 4194 {
4209 info[ispec].argument = 4195 spec->argument = arg = SYMBOL_NAME (arg);
4210 SYMBOL_NAME (info[ispec].argument); 4196 if (STRING_MULTIBYTE (arg) && ! multibyte)
4211 if (STRING_MULTIBYTE (info[ispec].argument) && ! multibyte)
4212 { 4197 {
4213 multibyte = true; 4198 multibyte = true;
4214 goto retry; 4199 goto retry;
@@ -4239,12 +4224,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4239 else 4224 else
4240 { 4225 {
4241 ptrdiff_t nch, nby; 4226 ptrdiff_t nch, nby;
4242 width = lisp_string_width (info[ispec].argument, 4227 width = lisp_string_width (arg, prec, &nch, &nby);
4243 prec, &nch, &nby);
4244 if (prec < 0) 4228 if (prec < 0)
4245 { 4229 {
4246 nchars_string = SCHARS (info[ispec].argument); 4230 nchars_string = SCHARS (arg);
4247 nbytes = SBYTES (info[ispec].argument); 4231 nbytes = SBYTES (arg);
4248 } 4232 }
4249 else 4233 else
4250 { 4234 {
@@ -4254,11 +4238,8 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4254 } 4238 }
4255 4239
4256 convbytes = nbytes; 4240 convbytes = nbytes;
4257 if (convbytes && multibyte && 4241 if (convbytes && multibyte && ! STRING_MULTIBYTE (arg))
4258 ! STRING_MULTIBYTE (info[ispec].argument)) 4242 convbytes = count_size_as_multibyte (SDATA (arg), nbytes);
4259 convbytes =
4260 count_size_as_multibyte (SDATA (info[ispec].argument),
4261 nbytes);
4262 4243
4263 ptrdiff_t padding 4244 ptrdiff_t padding
4264 = width < field_width ? field_width - width : 0; 4245 = width < field_width ? field_width - width : 0;
@@ -4274,20 +4255,18 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4274 p += padding; 4255 p += padding;
4275 nchars += padding; 4256 nchars += padding;
4276 } 4257 }
4277 info[ispec].start = nchars; 4258 spec->start = nchars;
4278 4259
4279 if (p > buf 4260 if (p > buf
4280 && multibyte 4261 && multibyte
4281 && !ASCII_CHAR_P (*((unsigned char *) p - 1)) 4262 && !ASCII_CHAR_P (*((unsigned char *) p - 1))
4282 && STRING_MULTIBYTE (info[ispec].argument) 4263 && STRING_MULTIBYTE (arg)
4283 && !CHAR_HEAD_P (SREF (info[ispec].argument, 0))) 4264 && !CHAR_HEAD_P (SREF (arg, 0)))
4284 maybe_combine_byte = true; 4265 maybe_combine_byte = true;
4285 4266
4286 p += copy_text (SDATA (info[ispec].argument), 4267 p += copy_text (SDATA (arg), (unsigned char *) p,
4287 (unsigned char *) p,
4288 nbytes, 4268 nbytes,
4289 STRING_MULTIBYTE (info[ispec].argument), 4269 STRING_MULTIBYTE (arg), multibyte);
4290 multibyte);
4291 4270
4292 nchars += nchars_string; 4271 nchars += nchars_string;
4293 4272
@@ -4297,12 +4276,12 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4297 p += padding; 4276 p += padding;
4298 nchars += padding; 4277 nchars += padding;
4299 } 4278 }
4300 info[ispec].end = nchars; 4279 spec->end = nchars;
4301 4280
4302 /* If this argument has text properties, record where 4281 /* If this argument has text properties, record where
4303 in the result string it appears. */ 4282 in the result string it appears. */
4304 if (string_intervals (info[ispec].argument)) 4283 if (string_intervals (arg))
4305 info[ispec].intervals = arg_intervals = true; 4284 spec->intervals = arg_intervals = true;
4306 4285
4307 continue; 4286 continue;
4308 } 4287 }
@@ -4313,8 +4292,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4313 || conversion == 'X')) 4292 || conversion == 'X'))
4314 error ("Invalid format operation %%%c", 4293 error ("Invalid format operation %%%c",
4315 STRING_CHAR ((unsigned char *) format - 1)); 4294 STRING_CHAR ((unsigned char *) format - 1));
4316 else if (! (INTEGERP (info[ispec].argument) 4295 else if (! (INTEGERP (arg) || (FLOATP (arg) && conversion != 'c')))
4317 || (FLOATP (info[ispec].argument) && conversion != 'c')))
4318 error ("Format specifier doesn't match argument type"); 4296 error ("Format specifier doesn't match argument type");
4319 else 4297 else
4320 { 4298 {
@@ -4376,7 +4354,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4376 if (INT_AS_LDBL) 4354 if (INT_AS_LDBL)
4377 { 4355 {
4378 *f = 'L'; 4356 *f = 'L';
4379 f += INTEGERP (info[ispec].argument); 4357 f += INTEGERP (arg);
4380 } 4358 }
4381 } 4359 }
4382 else if (conversion != 'c') 4360 else if (conversion != 'c')
@@ -4408,22 +4386,22 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4408 ptrdiff_t sprintf_bytes; 4386 ptrdiff_t sprintf_bytes;
4409 if (float_conversion) 4387 if (float_conversion)
4410 { 4388 {
4411 if (INT_AS_LDBL && INTEGERP (info[ispec].argument)) 4389 if (INT_AS_LDBL && INTEGERP (arg))
4412 { 4390 {
4413 /* Although long double may have a rounding error if 4391 /* Although long double may have a rounding error if
4414 DIG_BITS_LBOUND * LDBL_MANT_DIG < FIXNUM_BITS - 1, 4392 DIG_BITS_LBOUND * LDBL_MANT_DIG < FIXNUM_BITS - 1,
4415 it is more accurate than plain 'double'. */ 4393 it is more accurate than plain 'double'. */
4416 long double x = XINT (info[ispec].argument); 4394 long double x = XINT (arg);
4417 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x); 4395 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
4418 } 4396 }
4419 else 4397 else
4420 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, 4398 sprintf_bytes = sprintf (sprintf_buf, convspec, prec,
4421 XFLOATINT (info[ispec].argument)); 4399 XFLOATINT (arg));
4422 } 4400 }
4423 else if (conversion == 'c') 4401 else if (conversion == 'c')
4424 { 4402 {
4425 /* Don't use sprintf here, as it might mishandle prec. */ 4403 /* Don't use sprintf here, as it might mishandle prec. */
4426 sprintf_buf[0] = XINT (info[ispec].argument); 4404 sprintf_buf[0] = XINT (arg);
4427 sprintf_bytes = prec != 0; 4405 sprintf_bytes = prec != 0;
4428 } 4406 }
4429 else if (conversion == 'd' || conversion == 'i') 4407 else if (conversion == 'd' || conversion == 'i')
@@ -4432,11 +4410,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4432 instead so it also works for values outside 4410 instead so it also works for values outside
4433 the integer range. */ 4411 the integer range. */
4434 printmax_t x; 4412 printmax_t x;
4435 if (INTEGERP (info[ispec].argument)) 4413 if (INTEGERP (arg))
4436 x = XINT (info[ispec].argument); 4414 x = XINT (arg);
4437 else 4415 else
4438 { 4416 {
4439 double d = XFLOAT_DATA (info[ispec].argument); 4417 double d = XFLOAT_DATA (arg);
4440 if (d < 0) 4418 if (d < 0)
4441 { 4419 {
4442 x = TYPE_MINIMUM (printmax_t); 4420 x = TYPE_MINIMUM (printmax_t);
@@ -4456,11 +4434,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4456 { 4434 {
4457 /* Don't sign-extend for octal or hex printing. */ 4435 /* Don't sign-extend for octal or hex printing. */
4458 uprintmax_t x; 4436 uprintmax_t x;
4459 if (INTEGERP (info[ispec].argument)) 4437 if (INTEGERP (arg))
4460 x = XUINT (info[ispec].argument); 4438 x = XUINT (arg);
4461 else 4439 else
4462 { 4440 {
4463 double d = XFLOAT_DATA (info[ispec].argument); 4441 double d = XFLOAT_DATA (arg);
4464 if (d < 0) 4442 if (d < 0)
4465 x = 0; 4443 x = 0;
4466 else 4444 else
@@ -4541,7 +4519,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4541 exponent_bytes = src + sprintf_bytes - e; 4519 exponent_bytes = src + sprintf_bytes - e;
4542 } 4520 }
4543 4521
4544 info[ispec].start = nchars; 4522 spec->start = nchars;
4545 if (! minus_flag) 4523 if (! minus_flag)
4546 { 4524 {
4547 memset (p, ' ', padding); 4525 memset (p, ' ', padding);
@@ -4572,7 +4550,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4572 p += padding; 4550 p += padding;
4573 nchars += padding; 4551 nchars += padding;
4574 } 4552 }
4575 info[ispec].end = nchars; 4553 spec->end = nchars;
4576 4554
4577 continue; 4555 continue;
4578 } 4556 }
@@ -4681,7 +4659,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4681 if (CONSP (props)) 4659 if (CONSP (props))
4682 { 4660 {
4683 ptrdiff_t bytepos = 0, position = 0, translated = 0; 4661 ptrdiff_t bytepos = 0, position = 0, translated = 0;
4684 ptrdiff_t fieldn = 1; 4662 ptrdiff_t fieldn = 0;
4685 4663
4686 /* Adjust the bounds of each text property 4664 /* Adjust the bounds of each text property
4687 to the proper start and end in the output string. */ 4665 to the proper start and end in the output string. */
@@ -4747,7 +4725,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4747 4725
4748 /* Add text properties from arguments. */ 4726 /* Add text properties from arguments. */
4749 if (arg_intervals) 4727 if (arg_intervals)
4750 for (ptrdiff_t i = 1; i <= num_percent; i++) 4728 for (ptrdiff_t i = 0; i < nspec; i++)
4751 if (info[i].intervals) 4729 if (info[i].intervals)
4752 { 4730 {
4753 len = make_number (SCHARS (info[i].argument)); 4731 len = make_number (SCHARS (info[i].argument));