3201 |
register int n; /* The number of the next arg to substitute */ |
register int n; /* The number of the next arg to substitute */ |
3202 |
register int total; /* An estimate of the final length */ |
register int total; /* An estimate of the final length */ |
3203 |
char *buf, *p; |
char *buf, *p; |
3204 |
register unsigned char *format, *end; |
register unsigned char *format, *end, *format_start; |
3205 |
int nchars; |
int nchars; |
3206 |
/* Nonzero if the output should be a multibyte string, |
/* Nonzero if the output should be a multibyte string, |
3207 |
which is true if any of the inputs is one. */ |
which is true if any of the inputs is one. */ |
3220 |
int *precision = (int *) (alloca(nargs * sizeof (int))); |
int *precision = (int *) (alloca(nargs * sizeof (int))); |
3221 |
int longest_format; |
int longest_format; |
3222 |
Lisp_Object val; |
Lisp_Object val; |
3223 |
|
int arg_intervals = 0; |
3224 |
|
|
3225 |
|
/* discarded[I] is 1 if byte I of the format |
3226 |
|
string was not copied into the output. |
3227 |
|
It is 2 if byte I was not the first byte of its character. */ |
3228 |
|
char *discarded; |
3229 |
|
|
3230 |
|
/* Each element records, for one argument, |
3231 |
|
the start and end bytepos in the output string, |
3232 |
|
and whether the argument is a string with intervals. |
3233 |
|
info[0] is unused. Unused elements have -1 for start. */ |
3234 |
struct info |
struct info |
3235 |
{ |
{ |
3236 |
int start, end; |
int start, end, intervals; |
3237 |
} *info = 0; |
} *info = 0; |
3238 |
|
|
3239 |
/* It should not be necessary to GCPRO ARGS, because |
/* It should not be necessary to GCPRO ARGS, because |
3243 |
This is not always right; sometimes the result needs to be multibyte |
This is not always right; sometimes the result needs to be multibyte |
3244 |
because of an object that we will pass through prin1, |
because of an object that we will pass through prin1, |
3245 |
and in that case, we won't know it here. */ |
and in that case, we won't know it here. */ |
3246 |
for (n = 0; n < nargs; n++) { |
for (n = 0; n < nargs; n++) |
3247 |
if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n])) |
{ |
3248 |
multibyte = 1; |
if (STRINGP (args[n]) && STRING_MULTIBYTE (args[n])) |
3249 |
/* Piggyback on this loop to initialize precision[N]. */ |
multibyte = 1; |
3250 |
precision[n] = -1; |
/* Piggyback on this loop to initialize precision[N]. */ |
3251 |
} |
precision[n] = -1; |
3252 |
|
} |
3253 |
|
|
3254 |
CHECK_STRING (args[0]); |
CHECK_STRING (args[0]); |
3255 |
/* We may have to change "%S" to "%s". */ |
/* We may have to change "%S" to "%s". */ |
3260 |
retry: |
retry: |
3261 |
|
|
3262 |
format = SDATA (args[0]); |
format = SDATA (args[0]); |
3263 |
|
format_start = format; |
3264 |
end = format + SBYTES (args[0]); |
end = format + SBYTES (args[0]); |
3265 |
longest_format = 0; |
longest_format = 0; |
3266 |
|
|
3267 |
/* Make room in result for all the non-%-codes in the control string. */ |
/* Make room in result for all the non-%-codes in the control string. */ |
3268 |
total = 5 + CONVERTED_BYTE_SIZE (multibyte, args[0]); |
total = 5 + CONVERTED_BYTE_SIZE (multibyte, args[0]); |
3269 |
|
|
3270 |
|
/* Allocate the info and discarded tables. */ |
3271 |
|
{ |
3272 |
|
int nbytes = nargs * sizeof *info; |
3273 |
|
int i; |
3274 |
|
info = (struct info *) alloca (nbytes); |
3275 |
|
bzero (info, nbytes); |
3276 |
|
for (i = 0; i <= nargs; i++) |
3277 |
|
info[i].start = -1; |
3278 |
|
discarded = (char *) alloca (SBYTES (args[0])); |
3279 |
|
bzero (discarded, SBYTES (args[0])); |
3280 |
|
} |
3281 |
|
|
3282 |
/* Add to TOTAL enough space to hold the converted arguments. */ |
/* Add to TOTAL enough space to hold the converted arguments. */ |
3283 |
|
|
3284 |
n = 0; |
n = 0; |
3483 |
int negative = 0; |
int negative = 0; |
3484 |
unsigned char *this_format_start = format; |
unsigned char *this_format_start = format; |
3485 |
|
|
3486 |
|
discarded[format - format_start] = 1; |
3487 |
format++; |
format++; |
3488 |
|
|
3489 |
/* Process a numeric arg and skip it. */ |
/* Process a numeric arg and skip it. */ |
3497 |
fixed. */ |
fixed. */ |
3498 |
while ((*format >= '0' && *format <= '9') |
while ((*format >= '0' && *format <= '9') |
3499 |
|| *format == '-' || *format == ' ' || *format == '.') |
|| *format == '-' || *format == ' ' || *format == '.') |
3500 |
format++; |
{ |
3501 |
|
discarded[format - format_start] = 1; |
3502 |
|
format++; |
3503 |
|
} |
3504 |
|
|
3505 |
if (*format++ == '%') |
if (*format++ == '%') |
3506 |
{ |
{ |
3511 |
|
|
3512 |
++n; |
++n; |
3513 |
|
|
3514 |
|
discarded[format - format_start - 1] = 1; |
3515 |
|
info[n].start = nchars; |
3516 |
|
|
3517 |
if (STRINGP (args[n])) |
if (STRINGP (args[n])) |
3518 |
{ |
{ |
3519 |
/* handle case (precision[n] >= 0) */ |
/* handle case (precision[n] >= 0) */ |
3573 |
/* If this argument has text properties, record where |
/* If this argument has text properties, record where |
3574 |
in the result string it appears. */ |
in the result string it appears. */ |
3575 |
if (STRING_INTERVALS (args[n])) |
if (STRING_INTERVALS (args[n])) |
3576 |
{ |
info[n].intervals = arg_intervals = 1; |
|
if (!info) |
|
|
{ |
|
|
int nbytes = nargs * sizeof *info; |
|
|
info = (struct info *) alloca (nbytes); |
|
|
bzero (info, nbytes); |
|
|
} |
|
|
|
|
|
info[n].start = start; |
|
|
info[n].end = end; |
|
|
} |
|
3577 |
} |
} |
3578 |
else if (INTEGERP (args[n]) || FLOATP (args[n])) |
else if (INTEGERP (args[n]) || FLOATP (args[n])) |
3579 |
{ |
{ |
3600 |
p += this_nchars; |
p += this_nchars; |
3601 |
nchars += this_nchars; |
nchars += this_nchars; |
3602 |
} |
} |
3603 |
|
|
3604 |
|
info[n].end = nchars; |
3605 |
} |
} |
3606 |
else if (STRING_MULTIBYTE (args[0])) |
else if (STRING_MULTIBYTE (args[0])) |
3607 |
{ |
{ |
3612 |
&& !CHAR_HEAD_P (*format)) |
&& !CHAR_HEAD_P (*format)) |
3613 |
maybe_combine_byte = 1; |
maybe_combine_byte = 1; |
3614 |
*p++ = *format++; |
*p++ = *format++; |
3615 |
while (! CHAR_HEAD_P (*format)) *p++ = *format++; |
while (! CHAR_HEAD_P (*format)) |
3616 |
|
{ |
3617 |
|
discarded[format - format_start] = 2; |
3618 |
|
*p++ = *format++; |
3619 |
|
} |
3620 |
nchars++; |
nchars++; |
3621 |
} |
} |
3622 |
else if (multibyte) |
else if (multibyte) |
3647 |
arguments has text properties, set up text properties of the |
arguments has text properties, set up text properties of the |
3648 |
result string. */ |
result string. */ |
3649 |
|
|
3650 |
if (STRING_INTERVALS (args[0]) || info) |
if (STRING_INTERVALS (args[0]) || arg_intervals) |
3651 |
{ |
{ |
3652 |
Lisp_Object len, new_len, props; |
Lisp_Object len, new_len, props; |
3653 |
struct gcpro gcpro1; |
struct gcpro gcpro1; |
3659 |
|
|
3660 |
if (CONSP (props)) |
if (CONSP (props)) |
3661 |
{ |
{ |
3662 |
new_len = make_number (SCHARS (val)); |
int bytepos = 0, position = 0, translated = 0, argn = 1; |
3663 |
extend_property_ranges (props, len, new_len); |
Lisp_Object list; |
3664 |
|
|
3665 |
|
/* Adjust the bounds of each text property |
3666 |
|
to the proper start and end in the output string. */ |
3667 |
|
/* We take advantage of the fact that the positions in PROPS |
3668 |
|
are in increasing order, so that we can do (effectively) |
3669 |
|
one scan through the position space of the format string. |
3670 |
|
|
3671 |
|
BYTEPOS is the byte position in the format string, |
3672 |
|
POSITION is the untranslated char position in it, |
3673 |
|
TRANSLATED is the translated char position in BUF, |
3674 |
|
and ARGN is the number of the next arg we will come to. */ |
3675 |
|
for (list = props; CONSP (list); list = XCDR (list)) |
3676 |
|
{ |
3677 |
|
Lisp_Object item, pos; |
3678 |
|
|
3679 |
|
item = XCAR (list); |
3680 |
|
|
3681 |
|
/* First adjust the property start position. */ |
3682 |
|
pos = XINT (XCAR (item)); |
3683 |
|
|
3684 |
|
/* Advance BYTEPOS, POSITION, TRANSLATED and ARGN |
3685 |
|
up to this position. */ |
3686 |
|
for (; position < pos; bytepos++) |
3687 |
|
{ |
3688 |
|
if (! discarded[bytepos]) |
3689 |
|
position++, translated++; |
3690 |
|
else if (discarded[bytepos] == 1) |
3691 |
|
{ |
3692 |
|
position++; |
3693 |
|
if (translated == info[argn].start) |
3694 |
|
{ |
3695 |
|
translated += info[argn].end - info[argn].start; |
3696 |
|
argn++; |
3697 |
|
} |
3698 |
|
} |
3699 |
|
} |
3700 |
|
|
3701 |
|
XSETCAR (item, make_number (translated)); |
3702 |
|
|
3703 |
|
/* Likewise adjust the property end position. */ |
3704 |
|
pos = XINT (XCAR (XCDR (item))); |
3705 |
|
|
3706 |
|
for (; bytepos < pos; bytepos++) |
3707 |
|
{ |
3708 |
|
if (! discarded[bytepos]) |
3709 |
|
position++, translated++; |
3710 |
|
else if (discarded[bytepos] == 1) |
3711 |
|
{ |
3712 |
|
position++; |
3713 |
|
if (translated == info[argn].start) |
3714 |
|
{ |
3715 |
|
translated += info[argn].end - info[argn].start; |
3716 |
|
argn++; |
3717 |
|
} |
3718 |
|
} |
3719 |
|
} |
3720 |
|
|
3721 |
|
XSETCAR (XCDR (item), make_number (translated)); |
3722 |
|
} |
3723 |
|
|
3724 |
add_text_properties_from_list (val, props, make_number (0)); |
add_text_properties_from_list (val, props, make_number (0)); |
3725 |
} |
} |
3726 |
|
|
3727 |
/* Add text properties from arguments. */ |
/* Add text properties from arguments. */ |
3728 |
if (info) |
if (arg_intervals) |
3729 |
for (n = 1; n < nargs; ++n) |
for (n = 1; n < nargs; ++n) |
3730 |
if (info[n].end) |
if (info[n].intervals) |
3731 |
{ |
{ |
3732 |
len = make_number (SCHARS (args[n])); |
len = make_number (SCHARS (args[n])); |
3733 |
new_len = make_number (info[n].end - info[n].start); |
new_len = make_number (info[n].end - info[n].start); |