aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Stephani2017-06-03 11:16:21 +0200
committerPhilipp Stephani2017-06-03 11:19:02 +0200
commit7d413cb4da89e0bdd70068e6a5e1dbc57190ed10 (patch)
tree6ca93c8b51c29a3189d00abdc5d1f419981b8b81
parent0147cdd4d96f1eaeef720ee0b89bddd27eaf4233 (diff)
downloademacs-7d413cb4da89e0bdd70068e6a5e1dbc57190ed10.tar.gz
emacs-7d413cb4da89e0bdd70068e6a5e1dbc57190ed10.zip
Fix a bug when using format field numbers
Previously styled_format overwrite the argument vector. This is no longer possible because there might be more than one specification per argument. Use the existing auxiliary info array instead. * src/editfns.c (styled_format): Record arguments in the info structure instead of overwriting them. * test/src/editfns-tests.el (format-with-field): Add unit test.
-rw-r--r--src/editfns.c150
-rw-r--r--test/src/editfns-tests.el3
2 files changed, 96 insertions, 57 deletions
diff --git a/src/editfns.c b/src/editfns.c
index a5088b0a1fd..1ce5e8ca6f0 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -3980,12 +3980,14 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
3980 bool arg_intervals = false; 3980 bool arg_intervals = false;
3981 USE_SAFE_ALLOCA; 3981 USE_SAFE_ALLOCA;
3982 3982
3983 /* Each element records, for one argument, 3983 /* Each element records, for one field,
3984 the corresponding argument,
3984 the start and end bytepos in the output string, 3985 the start and end bytepos in the output string,
3985 whether the argument has been converted to string (e.g., due to "%S"), 3986 whether the argument has been converted to string (e.g., due to "%S"),
3986 and whether the argument is a string with intervals. */ 3987 and whether the argument is a string with intervals. */
3987 struct info 3988 struct info
3988 { 3989 {
3990 Lisp_Object argument;
3989 ptrdiff_t start, end; 3991 ptrdiff_t start, end;
3990 bool_bf converted_to_string : 1; 3992 bool_bf converted_to_string : 1;
3991 bool_bf intervals : 1; 3993 bool_bf intervals : 1;
@@ -3995,9 +3997,16 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
3995 char *format_start = SSDATA (args[0]); 3997 char *format_start = SSDATA (args[0]);
3996 ptrdiff_t formatlen = SBYTES (args[0]); 3998 ptrdiff_t formatlen = SBYTES (args[0]);
3997 3999
4000 /* The number of percent characters is a safe upper bound for the
4001 number of format fields. */
4002 ptrdiff_t num_percent = 0;
4003 for (ptrdiff_t i = 0; i < formatlen; ++i)
4004 if (format_start[i] == '%')
4005 ++num_percent;
4006
3998 /* Allocate the info and discarded tables. */ 4007 /* Allocate the info and discarded tables. */
3999 ptrdiff_t alloca_size; 4008 ptrdiff_t alloca_size;
4000 if (INT_MULTIPLY_WRAPV (nargs, sizeof *info, &alloca_size) 4009 if (INT_MULTIPLY_WRAPV (num_percent, sizeof *info, &alloca_size)
4001 || INT_ADD_WRAPV (sizeof *info, alloca_size, &alloca_size) 4010 || INT_ADD_WRAPV (sizeof *info, alloca_size, &alloca_size)
4002 || INT_ADD_WRAPV (formatlen, alloca_size, &alloca_size) 4011 || INT_ADD_WRAPV (formatlen, alloca_size, &alloca_size)
4003 || SIZE_MAX < alloca_size) 4012 || SIZE_MAX < alloca_size)
@@ -4005,12 +4014,15 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4005 /* info[0] is unused. Unused elements have -1 for start. */ 4014 /* info[0] is unused. Unused elements have -1 for start. */
4006 info = SAFE_ALLOCA (alloca_size); 4015 info = SAFE_ALLOCA (alloca_size);
4007 memset (info, 0, alloca_size); 4016 memset (info, 0, alloca_size);
4008 for (ptrdiff_t i = 0; i < nargs + 1; i++) 4017 for (ptrdiff_t i = 0; i < num_percent + 1; i++)
4009 info[i].start = -1; 4018 {
4019 info[i].argument = Qunbound;
4020 info[i].start = -1;
4021 }
4010 /* discarded[I] is 1 if byte I of the format 4022 /* discarded[I] is 1 if byte I of the format
4011 string was not copied into the output. 4023 string was not copied into the output.
4012 It is 2 if byte I was not the first byte of its character. */ 4024 It is 2 if byte I was not the first byte of its character. */
4013 char *discarded = (char *) &info[nargs + 1]; 4025 char *discarded = (char *) &info[num_percent + 1];
4014 4026
4015 /* Try to determine whether the result should be multibyte. 4027 /* Try to determine whether the result should be multibyte.
4016 This is not always right; sometimes the result needs to be multibyte 4028 This is not always right; sometimes the result needs to be multibyte
@@ -4029,13 +4041,18 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4029 4041
4030 int quoting_style = message ? text_quoting_style () : -1; 4042 int quoting_style = message ? text_quoting_style () : -1;
4031 4043
4044 ptrdiff_t ispec;
4045
4032 /* If we start out planning a unibyte result, 4046 /* If we start out planning a unibyte result,
4033 then discover it has to be multibyte, we jump back to retry. */ 4047 then discover it has to be multibyte, we jump back to retry. */
4034 retry: 4048 retry:
4035 4049
4036 p = buf; 4050 p = buf;
4037 nchars = 0; 4051 nchars = 0;
4052
4053 /* N is the argument index, ISPEC is the specification index. */
4038 n = 0; 4054 n = 0;
4055 ispec = 0;
4039 4056
4040 /* Scan the format and store result in BUF. */ 4057 /* Scan the format and store result in BUF. */
4041 format = format_start; 4058 format = format_start;
@@ -4044,8 +4061,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4044 4061
4045 while (format != end) 4062 while (format != end)
4046 { 4063 {
4047 /* The values of N and FORMAT when the loop body is entered. */ 4064 /* The values of N, ISPEC, and FORMAT when the loop body is
4065 entered. */
4048 ptrdiff_t n0 = n; 4066 ptrdiff_t n0 = n;
4067 ptrdiff_t ispec0 = ispec;
4049 char *format0 = format; 4068 char *format0 = format;
4050 char const *convsrc = format; 4069 char const *convsrc = format;
4051 unsigned char format_char = *format++; 4070 unsigned char format_char = *format++;
@@ -4136,20 +4155,28 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4136 if (! (n < nargs)) 4155 if (! (n < nargs))
4137 error ("Not enough arguments for format string"); 4156 error ("Not enough arguments for format string");
4138 4157
4158 eassert (ispec < num_percent);
4159 ++ispec;
4160
4161 if (EQ (info[ispec].argument, Qunbound))
4162 info[ispec].argument = args[n];
4163
4139 /* For 'S', prin1 the argument, and then treat like 's'. 4164 /* For 'S', prin1 the argument, and then treat like 's'.
4140 For 's', princ any argument that is not a string or 4165 For 's', princ any argument that is not a string or
4141 symbol. But don't do this conversion twice, which might 4166 symbol. But don't do this conversion twice, which might
4142 happen after retrying. */ 4167 happen after retrying. */
4143 if ((conversion == 'S' 4168 if ((conversion == 'S'
4144 || (conversion == 's' 4169 || (conversion == 's'
4145 && ! STRINGP (args[n]) && ! SYMBOLP (args[n])))) 4170 && ! STRINGP (info[ispec].argument)
4171 && ! SYMBOLP (info[ispec].argument))))
4146 { 4172 {
4147 if (! info[n].converted_to_string) 4173 if (! info[ispec].converted_to_string)
4148 { 4174 {
4149 Lisp_Object noescape = conversion == 'S' ? Qnil : Qt; 4175 Lisp_Object noescape = conversion == 'S' ? Qnil : Qt;
4150 args[n] = Fprin1_to_string (args[n], noescape); 4176 info[ispec].argument =
4151 info[n].converted_to_string = true; 4177 Fprin1_to_string (info[ispec].argument, noescape);
4152 if (STRING_MULTIBYTE (args[n]) && ! multibyte) 4178 info[ispec].converted_to_string = true;
4179 if (STRING_MULTIBYTE (info[ispec].argument) && ! multibyte)
4153 { 4180 {
4154 multibyte = true; 4181 multibyte = true;
4155 goto retry; 4182 goto retry;
@@ -4159,26 +4186,29 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4159 } 4186 }
4160 else if (conversion == 'c') 4187 else if (conversion == 'c')
4161 { 4188 {
4162 if (INTEGERP (args[n]) && ! ASCII_CHAR_P (XINT (args[n]))) 4189 if (INTEGERP (info[ispec].argument)
4190 && ! ASCII_CHAR_P (XINT (info[ispec].argument)))
4163 { 4191 {
4164 if (!multibyte) 4192 if (!multibyte)
4165 { 4193 {
4166 multibyte = true; 4194 multibyte = true;
4167 goto retry; 4195 goto retry;
4168 } 4196 }
4169 args[n] = Fchar_to_string (args[n]); 4197 info[ispec].argument =
4170 info[n].converted_to_string = true; 4198 Fchar_to_string (info[ispec].argument);
4199 info[ispec].converted_to_string = true;
4171 } 4200 }
4172 4201
4173 if (info[n].converted_to_string) 4202 if (info[ispec].converted_to_string)
4174 conversion = 's'; 4203 conversion = 's';
4175 zero_flag = false; 4204 zero_flag = false;
4176 } 4205 }
4177 4206
4178 if (SYMBOLP (args[n])) 4207 if (SYMBOLP (info[ispec].argument))
4179 { 4208 {
4180 args[n] = SYMBOL_NAME (args[n]); 4209 info[ispec].argument =
4181 if (STRING_MULTIBYTE (args[n]) && ! multibyte) 4210 SYMBOL_NAME (info[ispec].argument);
4211 if (STRING_MULTIBYTE (info[ispec].argument) && ! multibyte)
4182 { 4212 {
4183 multibyte = true; 4213 multibyte = true;
4184 goto retry; 4214 goto retry;
@@ -4209,11 +4239,12 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4209 else 4239 else
4210 { 4240 {
4211 ptrdiff_t nch, nby; 4241 ptrdiff_t nch, nby;
4212 width = lisp_string_width (args[n], prec, &nch, &nby); 4242 width = lisp_string_width (info[ispec].argument,
4243 prec, &nch, &nby);
4213 if (prec < 0) 4244 if (prec < 0)
4214 { 4245 {
4215 nchars_string = SCHARS (args[n]); 4246 nchars_string = SCHARS (info[ispec].argument);
4216 nbytes = SBYTES (args[n]); 4247 nbytes = SBYTES (info[ispec].argument);
4217 } 4248 }
4218 else 4249 else
4219 { 4250 {
@@ -4223,8 +4254,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4223 } 4254 }
4224 4255
4225 convbytes = nbytes; 4256 convbytes = nbytes;
4226 if (convbytes && multibyte && ! STRING_MULTIBYTE (args[n])) 4257 if (convbytes && multibyte &&
4227 convbytes = count_size_as_multibyte (SDATA (args[n]), nbytes); 4258 ! STRING_MULTIBYTE (info[ispec].argument))
4259 convbytes =
4260 count_size_as_multibyte (SDATA (info[ispec].argument),
4261 nbytes);
4228 4262
4229 ptrdiff_t padding 4263 ptrdiff_t padding
4230 = width < field_width ? field_width - width : 0; 4264 = width < field_width ? field_width - width : 0;
@@ -4240,18 +4274,20 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4240 p += padding; 4274 p += padding;
4241 nchars += padding; 4275 nchars += padding;
4242 } 4276 }
4243 info[n].start = nchars; 4277 info[ispec].start = nchars;
4244 4278
4245 if (p > buf 4279 if (p > buf
4246 && multibyte 4280 && multibyte
4247 && !ASCII_CHAR_P (*((unsigned char *) p - 1)) 4281 && !ASCII_CHAR_P (*((unsigned char *) p - 1))
4248 && STRING_MULTIBYTE (args[n]) 4282 && STRING_MULTIBYTE (info[ispec].argument)
4249 && !CHAR_HEAD_P (SREF (args[n], 0))) 4283 && !CHAR_HEAD_P (SREF (info[ispec].argument, 0)))
4250 maybe_combine_byte = true; 4284 maybe_combine_byte = true;
4251 4285
4252 p += copy_text (SDATA (args[n]), (unsigned char *) p, 4286 p += copy_text (SDATA (info[ispec].argument),
4287 (unsigned char *) p,
4253 nbytes, 4288 nbytes,
4254 STRING_MULTIBYTE (args[n]), multibyte); 4289 STRING_MULTIBYTE (info[ispec].argument),
4290 multibyte);
4255 4291
4256 nchars += nchars_string; 4292 nchars += nchars_string;
4257 4293
@@ -4261,12 +4297,12 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4261 p += padding; 4297 p += padding;
4262 nchars += padding; 4298 nchars += padding;
4263 } 4299 }
4264 info[n].end = nchars; 4300 info[ispec].end = nchars;
4265 4301
4266 /* If this argument has text properties, record where 4302 /* If this argument has text properties, record where
4267 in the result string it appears. */ 4303 in the result string it appears. */
4268 if (string_intervals (args[n])) 4304 if (string_intervals (info[ispec].argument))
4269 info[n].intervals = arg_intervals = true; 4305 info[ispec].intervals = arg_intervals = true;
4270 4306
4271 continue; 4307 continue;
4272 } 4308 }
@@ -4277,8 +4313,8 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4277 || conversion == 'X')) 4313 || conversion == 'X'))
4278 error ("Invalid format operation %%%c", 4314 error ("Invalid format operation %%%c",
4279 STRING_CHAR ((unsigned char *) format - 1)); 4315 STRING_CHAR ((unsigned char *) format - 1));
4280 else if (! (INTEGERP (args[n]) 4316 else if (! (INTEGERP (info[ispec].argument)
4281 || (FLOATP (args[n]) && conversion != 'c'))) 4317 || (FLOATP (info[ispec].argument) && conversion != 'c')))
4282 error ("Format specifier doesn't match argument type"); 4318 error ("Format specifier doesn't match argument type");
4283 else 4319 else
4284 { 4320 {
@@ -4340,7 +4376,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4340 if (INT_AS_LDBL) 4376 if (INT_AS_LDBL)
4341 { 4377 {
4342 *f = 'L'; 4378 *f = 'L';
4343 f += INTEGERP (args[n]); 4379 f += INTEGERP (info[ispec].argument);
4344 } 4380 }
4345 } 4381 }
4346 else if (conversion != 'c') 4382 else if (conversion != 'c')
@@ -4372,22 +4408,22 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4372 ptrdiff_t sprintf_bytes; 4408 ptrdiff_t sprintf_bytes;
4373 if (float_conversion) 4409 if (float_conversion)
4374 { 4410 {
4375 if (INT_AS_LDBL && INTEGERP (args[n])) 4411 if (INT_AS_LDBL && INTEGERP (info[ispec].argument))
4376 { 4412 {
4377 /* Although long double may have a rounding error if 4413 /* Although long double may have a rounding error if
4378 DIG_BITS_LBOUND * LDBL_MANT_DIG < FIXNUM_BITS - 1, 4414 DIG_BITS_LBOUND * LDBL_MANT_DIG < FIXNUM_BITS - 1,
4379 it is more accurate than plain 'double'. */ 4415 it is more accurate than plain 'double'. */
4380 long double x = XINT (args[n]); 4416 long double x = XINT (info[ispec].argument);
4381 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x); 4417 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, x);
4382 } 4418 }
4383 else 4419 else
4384 sprintf_bytes = sprintf (sprintf_buf, convspec, prec, 4420 sprintf_bytes = sprintf (sprintf_buf, convspec, prec,
4385 XFLOATINT (args[n])); 4421 XFLOATINT (info[ispec].argument));
4386 } 4422 }
4387 else if (conversion == 'c') 4423 else if (conversion == 'c')
4388 { 4424 {
4389 /* Don't use sprintf here, as it might mishandle prec. */ 4425 /* Don't use sprintf here, as it might mishandle prec. */
4390 sprintf_buf[0] = XINT (args[n]); 4426 sprintf_buf[0] = XINT (info[ispec].argument);
4391 sprintf_bytes = prec != 0; 4427 sprintf_bytes = prec != 0;
4392 } 4428 }
4393 else if (conversion == 'd' || conversion == 'i') 4429 else if (conversion == 'd' || conversion == 'i')
@@ -4396,11 +4432,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4396 instead so it also works for values outside 4432 instead so it also works for values outside
4397 the integer range. */ 4433 the integer range. */
4398 printmax_t x; 4434 printmax_t x;
4399 if (INTEGERP (args[n])) 4435 if (INTEGERP (info[ispec].argument))
4400 x = XINT (args[n]); 4436 x = XINT (info[ispec].argument);
4401 else 4437 else
4402 { 4438 {
4403 double d = XFLOAT_DATA (args[n]); 4439 double d = XFLOAT_DATA (info[ispec].argument);
4404 if (d < 0) 4440 if (d < 0)
4405 { 4441 {
4406 x = TYPE_MINIMUM (printmax_t); 4442 x = TYPE_MINIMUM (printmax_t);
@@ -4420,11 +4456,11 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4420 { 4456 {
4421 /* Don't sign-extend for octal or hex printing. */ 4457 /* Don't sign-extend for octal or hex printing. */
4422 uprintmax_t x; 4458 uprintmax_t x;
4423 if (INTEGERP (args[n])) 4459 if (INTEGERP (info[ispec].argument))
4424 x = XUINT (args[n]); 4460 x = XUINT (info[ispec].argument);
4425 else 4461 else
4426 { 4462 {
4427 double d = XFLOAT_DATA (args[n]); 4463 double d = XFLOAT_DATA (info[ispec].argument);
4428 if (d < 0) 4464 if (d < 0)
4429 x = 0; 4465 x = 0;
4430 else 4466 else
@@ -4505,7 +4541,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4505 exponent_bytes = src + sprintf_bytes - e; 4541 exponent_bytes = src + sprintf_bytes - e;
4506 } 4542 }
4507 4543
4508 info[n].start = nchars; 4544 info[ispec].start = nchars;
4509 if (! minus_flag) 4545 if (! minus_flag)
4510 { 4546 {
4511 memset (p, ' ', padding); 4547 memset (p, ' ', padding);
@@ -4536,7 +4572,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4536 p += padding; 4572 p += padding;
4537 nchars += padding; 4573 nchars += padding;
4538 } 4574 }
4539 info[n].end = nchars; 4575 info[ispec].end = nchars;
4540 4576
4541 continue; 4577 continue;
4542 } 4578 }
@@ -4622,6 +4658,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4622 p = buf + used; 4658 p = buf + used;
4623 format = format0; 4659 format = format0;
4624 n = n0; 4660 n = n0;
4661 ispec = ispec0;
4625 } 4662 }
4626 4663
4627 if (bufsize < p - buf) 4664 if (bufsize < p - buf)
@@ -4644,7 +4681,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4644 if (CONSP (props)) 4681 if (CONSP (props))
4645 { 4682 {
4646 ptrdiff_t bytepos = 0, position = 0, translated = 0; 4683 ptrdiff_t bytepos = 0, position = 0, translated = 0;
4647 ptrdiff_t argn = 1; 4684 ptrdiff_t fieldn = 1;
4648 4685
4649 /* Adjust the bounds of each text property 4686 /* Adjust the bounds of each text property
4650 to the proper start and end in the output string. */ 4687 to the proper start and end in the output string. */
@@ -4674,10 +4711,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4674 else if (discarded[bytepos] == 1) 4711 else if (discarded[bytepos] == 1)
4675 { 4712 {
4676 position++; 4713 position++;
4677 if (translated == info[argn].start) 4714 if (translated == info[fieldn].start)
4678 { 4715 {
4679 translated += info[argn].end - info[argn].start; 4716 translated += info[fieldn].end - info[fieldn].start;
4680 argn++; 4717 fieldn++;
4681 } 4718 }
4682 } 4719 }
4683 } 4720 }
@@ -4694,10 +4731,10 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4694 else if (discarded[bytepos] == 1) 4731 else if (discarded[bytepos] == 1)
4695 { 4732 {
4696 position++; 4733 position++;
4697 if (translated == info[argn].start) 4734 if (translated == info[fieldn].start)
4698 { 4735 {
4699 translated += info[argn].end - info[argn].start; 4736 translated += info[fieldn].end - info[fieldn].start;
4700 argn++; 4737 fieldn++;
4701 } 4738 }
4702 } 4739 }
4703 } 4740 }
@@ -4710,12 +4747,13 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message)
4710 4747
4711 /* Add text properties from arguments. */ 4748 /* Add text properties from arguments. */
4712 if (arg_intervals) 4749 if (arg_intervals)
4713 for (ptrdiff_t i = 1; i < nargs; i++) 4750 for (ptrdiff_t i = 1; i < num_percent; i++)
4714 if (info[i].intervals) 4751 if (info[i].intervals)
4715 { 4752 {
4716 len = make_number (SCHARS (args[i])); 4753 len = make_number (SCHARS (info[i].argument));
4717 Lisp_Object new_len = make_number (info[i].end - info[i].start); 4754 Lisp_Object new_len = make_number (info[i].end - info[i].start);
4718 props = text_property_list (args[i], make_number (0), len, Qnil); 4755 props = text_property_list (info[i].argument,
4756 make_number (0), len, Qnil);
4719 props = extend_property_ranges (props, len, new_len); 4757 props = extend_property_ranges (props, len, new_len);
4720 /* If successive arguments have properties, be sure that 4758 /* If successive arguments have properties, be sure that
4721 the value of `composition' property be the copy. */ 4759 the value of `composition' property be the copy. */
diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el
index 54fb743e192..3073e371933 100644
--- a/test/src/editfns-tests.el
+++ b/test/src/editfns-tests.el
@@ -205,6 +205,7 @@
205 (should (equal (should-error (format "a %$s b" 11)) 205 (should (equal (should-error (format "a %$s b" 11))
206 '(error "Invalid format operation %$"))) 206 '(error "Invalid format operation %$")))
207 (should (equal (should-error (format "a %-1$s b" 11)) 207 (should (equal (should-error (format "a %-1$s b" 11))
208 '(error "Invalid format operation %$")))) 208 '(error "Invalid format operation %$")))
209 (should (equal (format "%1$c %1$s" ?±) "± 177")))
209 210
210;;; editfns-tests.el ends here 211;;; editfns-tests.el ends here