diff options
| author | Philipp Stephani | 2017-06-03 11:16:21 +0200 |
|---|---|---|
| committer | Philipp Stephani | 2017-06-03 11:19:02 +0200 |
| commit | 7d413cb4da89e0bdd70068e6a5e1dbc57190ed10 (patch) | |
| tree | 6ca93c8b51c29a3189d00abdc5d1f419981b8b81 | |
| parent | 0147cdd4d96f1eaeef720ee0b89bddd27eaf4233 (diff) | |
| download | emacs-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.c | 150 | ||||
| -rw-r--r-- | test/src/editfns-tests.el | 3 |
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 |