2 * Implements the internals of the format command for jim
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and documentation
31 * are those of the authors and should not be interpreted as representing
32 * official policies, either expressed or implied, of the Jim Tcl Project.
34 * Based on code originally from Tcl 8.5:
36 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
37 * Copyright (c) 1999 by Scriptics Corporation.
39 * See the file "tcl.license.terms" for information on usage and redistribution of
40 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
46 #include "jimautoconf.h"
50 #define JIM_INTEGER_SPACE 24
51 #define MAX_FLOAT_WIDTH 320
54 * Apply the printf-like format in fmtObjPtr with the given arguments.
56 * Returns a new object with zero reference count if OK, or NULL on error.
58 Jim_Obj
*Jim_FormatString(Jim_Interp
*interp
, Jim_Obj
*fmtObjPtr
, int objc
, Jim_Obj
*const *objv
)
60 const char *span
, *format
, *formatEnd
, *msg
;
61 int numBytes
= 0, objIndex
= 0, gotXpg
= 0, gotSequential
= 0;
62 static const char * const mixedXPG
=
63 "cannot mix \"%\" and \"%n$\" conversion specifiers";
64 static const char * const badIndex
[2] = {
65 "not enough arguments for all format specifiers",
66 "\"%n$\" argument index out of range"
71 /* A single buffer is used to store numeric fields (with sprintf())
72 * This buffer is allocated/reallocated as necessary
74 char *num_buffer
= NULL
;
75 int num_buffer_size
= 0;
77 span
= format
= Jim_GetString(fmtObjPtr
, &formatLen
);
78 formatEnd
= format
+ formatLen
;
79 resultPtr
= Jim_NewStringObj(interp
, "", 0);
81 while (format
!= formatEnd
) {
83 int gotMinus
, sawFlag
;
84 int gotPrecision
, useShort
;
85 long width
, precision
;
91 char spec
[2*JIM_INTEGER_SPACE
+ 12];
96 const char *formatted_buf
;
98 step
= utf8_tounicode(format
, &ch
);
105 Jim_AppendString(interp
, resultPtr
, span
, numBytes
);
110 * Saw a % : process the format specifier.
112 * Step 0. Handle special case of escaped format marker (i.e., %%).
115 step
= utf8_tounicode(format
, &ch
);
124 * Step 1. XPG3 position specifier
129 int position
= strtoul(format
, &end
, 10);
132 objIndex
= position
- 1;
134 step
= utf8_tounicode(format
, &ch
);
150 if ((objIndex
< 0) || (objIndex
>= objc
)) {
151 msg
= badIndex
[gotXpg
];
156 * Step 2. Set of flags. Also build up the sprintf spec.
181 step
= utf8_tounicode(format
, &ch
);
185 * Step 3. Minimum field width.
190 width
= strtoul(format
, &end
, 10);
192 step
= utf8_tounicode(format
, &ch
);
193 } else if (ch
== '*') {
194 if (objIndex
>= objc
- 1) {
195 msg
= badIndex
[gotXpg
];
198 if (Jim_GetLong(interp
, objv
[objIndex
], &width
) != JIM_OK
) {
210 step
= utf8_tounicode(format
, &ch
);
217 gotPrecision
= precision
= 0;
221 step
= utf8_tounicode(format
, &ch
);
224 precision
= strtoul(format
, &end
, 10);
226 step
= utf8_tounicode(format
, &ch
);
227 } else if (ch
== '*') {
228 if (objIndex
>= objc
- 1) {
229 msg
= badIndex
[gotXpg
];
232 if (Jim_GetLong(interp
, objv
[objIndex
], &precision
) != JIM_OK
) {
237 * TODO: Check this truncation logic.
245 step
= utf8_tounicode(format
, &ch
);
249 * Step 5. Length modifier.
256 step
= utf8_tounicode(format
, &ch
);
257 } else if (ch
== 'l') {
258 /* Just for compatibility. All non-short integers are wide. */
260 step
= utf8_tounicode(format
, &ch
);
263 step
= utf8_tounicode(format
, &ch
);
271 * Step 6. The actual conversion character.
280 /* Each valid conversion will set:
281 * formatted_buf - the result to be added
282 * formatted_chars - the length of formatted_buf in characters
283 * formatted_bytes - the length of formatted_buf in bytes
287 msg
= "format string ended in middle of field specifier";
290 formatted_buf
= Jim_GetString(objv
[objIndex
], &formatted_bytes
);
291 formatted_chars
= Jim_Utf8Length(interp
, objv
[objIndex
]);
292 if (gotPrecision
&& (precision
< formatted_chars
)) {
293 /* Need to build a (null terminated) truncated string */
294 formatted_chars
= precision
;
295 formatted_bytes
= utf8_index(formatted_buf
, precision
);
302 if (Jim_GetWide(interp
, objv
[objIndex
], &code
) != JIM_OK
) {
305 /* Just store the value in the 'spec' buffer */
306 formatted_bytes
= utf8_fromunicode(spec
, code
);
307 formatted_buf
= spec
;
328 /* Fill in the width and precision */
330 p
+= sprintf(p
, "%ld", width
);
333 p
+= sprintf(p
, ".%ld", precision
);
336 /* Now the modifier, and get the actual value here */
338 if (Jim_GetDouble(interp
, objv
[objIndex
], &d
) != JIM_OK
) {
341 length
= MAX_FLOAT_WIDTH
;
344 if (Jim_GetWide(interp
, objv
[objIndex
], &w
) != JIM_OK
) {
347 length
= JIM_INTEGER_SPACE
;
354 w
= (unsigned short)w
;
359 #ifdef HAVE_LONG_LONG
360 if (sizeof(long long) == sizeof(jim_wide
)) {
370 /* Adjust length for width and precision */
371 if (width
> length
) {
378 /* Increase the size of the buffer if needed */
379 if (num_buffer_size
< length
+ 1) {
380 num_buffer_size
= length
+ 1;
381 num_buffer
= Jim_Realloc(num_buffer
, num_buffer_size
);
385 snprintf(num_buffer
, length
+ 1, spec
, d
);
388 formatted_bytes
= snprintf(num_buffer
, length
+ 1, spec
, w
);
390 formatted_chars
= formatted_bytes
= strlen(num_buffer
);
391 formatted_buf
= num_buffer
;
396 /* Just reuse the 'spec' buffer */
399 Jim_SetResultFormatted(interp
, "bad field specifier \"%s\"", spec
);
405 while (formatted_chars
< width
) {
406 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
411 Jim_AppendString(interp
, resultPtr
, formatted_buf
, formatted_bytes
);
413 while (formatted_chars
< width
) {
414 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
418 objIndex
+= gotSequential
;
421 Jim_AppendString(interp
, resultPtr
, span
, numBytes
);
424 Jim_Free(num_buffer
);
428 Jim_SetResultString(interp
, msg
, -1);
430 Jim_FreeNewObj(interp
, resultPtr
);
431 Jim_Free(num_buffer
);