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.
49 #define JIM_INTEGER_SPACE 24
50 #define MAX_FLOAT_WIDTH 320
53 * Apply the printf-like format in fmtObjPtr with the given arguments.
55 * Returns a new object with zero reference count if OK, or NULL on error.
57 Jim_Obj
*Jim_FormatString(Jim_Interp
*interp
, Jim_Obj
*fmtObjPtr
, int objc
, Jim_Obj
*const *objv
)
59 const char *span
, *format
, *formatEnd
, *msg
;
60 int numBytes
= 0, objIndex
= 0, gotXpg
= 0, gotSequential
= 0;
61 static const char * const mixedXPG
=
62 "cannot mix \"%\" and \"%n$\" conversion specifiers";
63 static const char * const badIndex
[2] = {
64 "not enough arguments for all format specifiers",
65 "\"%n$\" argument index out of range"
70 /* A single buffer is used to store numeric fields (with sprintf())
71 * This buffer is allocated/reallocated as necessary
73 char *num_buffer
= NULL
;
74 int num_buffer_size
= 0;
76 span
= format
= Jim_GetString(fmtObjPtr
, &formatLen
);
77 formatEnd
= format
+ formatLen
;
78 resultPtr
= Jim_NewStringObj(interp
, "", 0);
80 while (format
!= formatEnd
) {
82 int gotMinus
, sawFlag
;
83 int gotPrecision
, useShort
;
84 long width
, precision
;
90 char spec
[2*JIM_INTEGER_SPACE
+ 12];
95 const char *formatted_buf
;
97 step
= utf8_tounicode(format
, &ch
);
104 Jim_AppendString(interp
, resultPtr
, span
, numBytes
);
109 * Saw a % : process the format specifier.
111 * Step 0. Handle special case of escaped format marker (i.e., %%).
114 step
= utf8_tounicode(format
, &ch
);
123 * Step 1. XPG3 position specifier
128 int position
= strtoul(format
, &end
, 10);
131 objIndex
= position
- 1;
133 step
= utf8_tounicode(format
, &ch
);
149 if ((objIndex
< 0) || (objIndex
>= objc
)) {
150 msg
= badIndex
[gotXpg
];
155 * Step 2. Set of flags. Also build up the sprintf spec.
180 step
= utf8_tounicode(format
, &ch
);
184 * Step 3. Minimum field width.
189 width
= strtoul(format
, &end
, 10);
191 step
= utf8_tounicode(format
, &ch
);
192 } else if (ch
== '*') {
193 if (objIndex
>= objc
- 1) {
194 msg
= badIndex
[gotXpg
];
197 if (Jim_GetLong(interp
, objv
[objIndex
], &width
) != JIM_OK
) {
209 step
= utf8_tounicode(format
, &ch
);
216 gotPrecision
= precision
= 0;
220 step
= utf8_tounicode(format
, &ch
);
223 precision
= strtoul(format
, &end
, 10);
225 step
= utf8_tounicode(format
, &ch
);
226 } else if (ch
== '*') {
227 if (objIndex
>= objc
- 1) {
228 msg
= badIndex
[gotXpg
];
231 if (Jim_GetLong(interp
, objv
[objIndex
], &precision
) != JIM_OK
) {
236 * TODO: Check this truncation logic.
244 step
= utf8_tounicode(format
, &ch
);
248 * Step 5. Length modifier.
255 step
= utf8_tounicode(format
, &ch
);
256 } else if (ch
== 'l') {
257 /* Just for compatibility. All non-short integers are wide. */
259 step
= utf8_tounicode(format
, &ch
);
262 step
= utf8_tounicode(format
, &ch
);
270 * Step 6. The actual conversion character.
279 /* Each valid conversion will set:
280 * formatted_buf - the result to be added
281 * formatted_chars - the length of formatted_buf in characters
282 * formatted_bytes - the length of formatted_buf in bytes
286 msg
= "format string ended in middle of field specifier";
289 formatted_buf
= Jim_GetString(objv
[objIndex
], &formatted_bytes
);
290 formatted_chars
= Jim_Utf8Length(interp
, objv
[objIndex
]);
291 if (gotPrecision
&& (precision
< formatted_chars
)) {
292 /* Need to build a (null terminated) truncated string */
293 formatted_chars
= precision
;
294 formatted_bytes
= utf8_index(formatted_buf
, precision
);
301 if (Jim_GetWide(interp
, objv
[objIndex
], &code
) != JIM_OK
) {
304 /* Just store the value in the 'spec' buffer */
305 formatted_bytes
= utf8_fromunicode(spec
, code
);
306 formatted_buf
= spec
;
327 /* Fill in the width and precision */
329 p
+= sprintf(p
, "%ld", width
);
332 p
+= sprintf(p
, ".%ld", precision
);
335 /* Now the modifier, and get the actual value here */
337 if (Jim_GetDouble(interp
, objv
[objIndex
], &d
) != JIM_OK
) {
340 length
= MAX_FLOAT_WIDTH
;
343 if (Jim_GetWide(interp
, objv
[objIndex
], &w
) != JIM_OK
) {
346 length
= JIM_INTEGER_SPACE
;
353 w
= (unsigned short)w
;
358 #ifdef HAVE_LONG_LONG
359 if (sizeof(long long) == sizeof(jim_wide
)) {
369 /* Adjust length for width and precision */
370 if (width
> length
) {
377 /* Increase the size of the buffer if needed */
378 if (num_buffer_size
< length
+ 1) {
379 num_buffer_size
= length
+ 1;
380 num_buffer
= Jim_Realloc(num_buffer
, num_buffer_size
);
384 snprintf(num_buffer
, length
+ 1, spec
, d
);
387 formatted_bytes
= snprintf(num_buffer
, length
+ 1, spec
, w
);
389 formatted_chars
= formatted_bytes
= strlen(num_buffer
);
390 formatted_buf
= num_buffer
;
395 /* Just reuse the 'spec' buffer */
398 Jim_SetResultFormatted(interp
, "bad field specifier \"%s\"", spec
);
404 while (formatted_chars
< width
) {
405 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
410 Jim_AppendString(interp
, resultPtr
, formatted_buf
, formatted_bytes
);
412 while (formatted_chars
< width
) {
413 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
417 objIndex
+= gotSequential
;
420 Jim_AppendString(interp
, resultPtr
, span
, numBytes
);
423 Jim_Free(num_buffer
);
427 Jim_SetResultString(interp
, msg
, -1);
429 Jim_FreeNewObj(interp
, resultPtr
);
430 Jim_Free(num_buffer
);