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.
48 #define JIM_INTEGER_SPACE 24
49 #define MAX_FLOAT_WIDTH 320
52 * Apply the printf-like format in fmtObjPtr with the given arguments.
54 * Returns a new object with zero reference count if OK, or NULL on error.
56 Jim_Obj
*Jim_FormatString(Jim_Interp
*interp
, Jim_Obj
*fmtObjPtr
, int objc
, Jim_Obj
*const *objv
)
58 const char *span
, *format
, *formatEnd
, *msg
;
59 int numBytes
= 0, objIndex
= 0, gotXpg
= 0, gotSequential
= 0;
60 static const char * const mixedXPG
=
61 "cannot mix \"%\" and \"%n$\" conversion specifiers";
62 static const char * const badIndex
[2] = {
63 "not enough arguments for all format specifiers",
64 "\"%n$\" argument index out of range"
69 /* A single buffer is used to store numeric fields (with sprintf())
70 * This buffer is allocated/reallocated as necessary
72 char *num_buffer
= NULL
;
73 int num_buffer_size
= 0;
75 span
= format
= Jim_GetString(fmtObjPtr
, &formatLen
);
76 formatEnd
= format
+ formatLen
;
77 resultPtr
= Jim_NewEmptyStringObj(interp
);
79 while (format
!= formatEnd
) {
81 int gotMinus
, sawFlag
;
82 int gotPrecision
, useShort
;
83 long width
, precision
;
89 char spec
[2*JIM_INTEGER_SPACE
+ 12];
94 const char *formatted_buf
;
96 step
= utf8_tounicode(format
, &ch
);
103 Jim_AppendString(interp
, resultPtr
, span
, numBytes
);
108 * Saw a % : process the format specifier.
110 * Step 0. Handle special case of escaped format marker (i.e., %%).
113 step
= utf8_tounicode(format
, &ch
);
122 * Step 1. XPG3 position specifier
127 int position
= strtoul(format
, &end
, 10);
130 objIndex
= position
- 1;
132 step
= utf8_tounicode(format
, &ch
);
148 if ((objIndex
< 0) || (objIndex
>= objc
)) {
149 msg
= badIndex
[gotXpg
];
154 * Step 2. Set of flags. Also build up the sprintf spec.
179 step
= utf8_tounicode(format
, &ch
);
183 * Step 3. Minimum field width.
188 width
= strtoul(format
, &end
, 10);
190 step
= utf8_tounicode(format
, &ch
);
191 } else if (ch
== '*') {
192 if (objIndex
>= objc
- 1) {
193 msg
= badIndex
[gotXpg
];
196 if (Jim_GetLong(interp
, objv
[objIndex
], &width
) != JIM_OK
) {
208 step
= utf8_tounicode(format
, &ch
);
215 gotPrecision
= precision
= 0;
219 step
= utf8_tounicode(format
, &ch
);
222 precision
= strtoul(format
, &end
, 10);
224 step
= utf8_tounicode(format
, &ch
);
225 } else if (ch
== '*') {
226 if (objIndex
>= objc
- 1) {
227 msg
= badIndex
[gotXpg
];
230 if (Jim_GetLong(interp
, objv
[objIndex
], &precision
) != JIM_OK
) {
235 * TODO: Check this truncation logic.
243 step
= utf8_tounicode(format
, &ch
);
247 * Step 5. Length modifier.
254 step
= utf8_tounicode(format
, &ch
);
255 } else if (ch
== 'l') {
256 /* Just for compatibility. All non-short integers are wide. */
258 step
= utf8_tounicode(format
, &ch
);
261 step
= utf8_tounicode(format
, &ch
);
269 * Step 6. The actual conversion character.
278 /* Each valid conversion will set:
279 * formatted_buf - the result to be added
280 * formatted_chars - the length of formatted_buf in characters
281 * formatted_bytes - the length of formatted_buf in bytes
285 msg
= "format string ended in middle of field specifier";
288 formatted_buf
= Jim_GetString(objv
[objIndex
], &formatted_bytes
);
289 formatted_chars
= Jim_Utf8Length(interp
, objv
[objIndex
]);
290 if (gotPrecision
&& (precision
< formatted_chars
)) {
291 /* Need to build a (null terminated) truncated string */
292 formatted_chars
= precision
;
293 formatted_bytes
= utf8_index(formatted_buf
, precision
);
300 if (Jim_GetWide(interp
, objv
[objIndex
], &code
) != JIM_OK
) {
303 /* Just store the value in the 'spec' buffer */
304 formatted_bytes
= utf8_getchars(spec
, code
);
305 formatted_buf
= spec
;
315 if (Jim_GetWide(interp
, objv
[objIndex
], (jim_wide
*)&w
) != JIM_OK
) {
318 length
= sizeof(w
) * 8;
320 /* XXX: width and precision not yet implemented for binary
321 * also flags in 'spec', e.g. #, 0, -
324 /* Increase the size of the buffer if needed */
325 if (num_buffer_size
< length
+ 1) {
326 num_buffer_size
= length
+ 1;
327 num_buffer
= Jim_Realloc(num_buffer
, num_buffer_size
);
331 for (i
= length
; i
> 0; ) {
333 if (w
& ((unsigned jim_wide
)1 << i
)) {
334 num_buffer
[j
++] = '1';
336 else if (j
|| i
== 0) {
337 num_buffer
[j
++] = '0';
341 formatted_chars
= formatted_bytes
= j
;
342 formatted_buf
= num_buffer
;
362 /* Fill in the width and precision */
364 p
+= sprintf(p
, "%ld", width
);
367 p
+= sprintf(p
, ".%ld", precision
);
370 /* Now the modifier, and get the actual value here */
372 if (Jim_GetDouble(interp
, objv
[objIndex
], &d
) != JIM_OK
) {
375 length
= MAX_FLOAT_WIDTH
;
378 if (Jim_GetWide(interp
, objv
[objIndex
], &w
) != JIM_OK
) {
381 length
= JIM_INTEGER_SPACE
;
387 w
= (unsigned short)w
;
391 #ifdef HAVE_LONG_LONG
392 if (sizeof(long long) == sizeof(jim_wide
)) {
401 /* Adjust length for width and precision */
402 if (width
> length
) {
409 /* Increase the size of the buffer if needed */
410 if (num_buffer_size
< length
+ 1) {
411 num_buffer_size
= length
+ 1;
412 num_buffer
= Jim_Realloc(num_buffer
, num_buffer_size
);
416 snprintf(num_buffer
, length
+ 1, spec
, d
);
419 formatted_bytes
= snprintf(num_buffer
, length
+ 1, spec
, w
);
421 formatted_chars
= formatted_bytes
= strlen(num_buffer
);
422 formatted_buf
= num_buffer
;
427 /* Just reuse the 'spec' buffer */
430 Jim_SetResultFormatted(interp
, "bad field specifier \"%s\"", spec
);
436 while (formatted_chars
< width
) {
437 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
442 Jim_AppendString(interp
, resultPtr
, formatted_buf
, formatted_bytes
);
444 while (formatted_chars
< width
) {
445 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
449 objIndex
+= gotSequential
;
452 Jim_AppendString(interp
, resultPtr
, span
, numBytes
);
455 Jim_Free(num_buffer
);
459 Jim_SetResultString(interp
, msg
, -1);
461 Jim_FreeNewObj(interp
, resultPtr
);
462 Jim_Free(num_buffer
);