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_NewEmptyStringObj(interp
);
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_getchars(spec
, code
);
306 formatted_buf
= spec
;
316 if (Jim_GetWide(interp
, objv
[objIndex
], (jim_wide
*)&w
) != JIM_OK
) {
319 length
= sizeof(w
) * 8;
321 /* XXX: width and precision not yet implemented for binary
322 * also flags in 'spec', e.g. #, 0, -
325 /* Increase the size of the buffer if needed */
326 if (num_buffer_size
< length
+ 1) {
327 num_buffer_size
= length
+ 1;
328 num_buffer
= Jim_Realloc(num_buffer
, num_buffer_size
);
332 for (i
= length
; i
> 0; ) {
334 if (w
& ((unsigned jim_wide
)1 << i
)) {
335 num_buffer
[j
++] = '1';
337 else if (j
|| i
== 0) {
338 num_buffer
[j
++] = '0';
342 formatted_chars
= formatted_bytes
= j
;
343 formatted_buf
= num_buffer
;
363 /* Fill in the width and precision */
365 p
+= sprintf(p
, "%ld", width
);
368 p
+= sprintf(p
, ".%ld", precision
);
371 /* Now the modifier, and get the actual value here */
373 if (Jim_GetDouble(interp
, objv
[objIndex
], &d
) != JIM_OK
) {
376 length
= MAX_FLOAT_WIDTH
;
379 if (Jim_GetWide(interp
, objv
[objIndex
], &w
) != JIM_OK
) {
382 length
= JIM_INTEGER_SPACE
;
389 w
= (unsigned short)w
;
394 #ifdef HAVE_LONG_LONG
395 if (sizeof(long long) == sizeof(jim_wide
)) {
405 /* Adjust length for width and precision */
406 if (width
> length
) {
413 /* Increase the size of the buffer if needed */
414 if (num_buffer_size
< length
+ 1) {
415 num_buffer_size
= length
+ 1;
416 num_buffer
= Jim_Realloc(num_buffer
, num_buffer_size
);
420 snprintf(num_buffer
, length
+ 1, spec
, d
);
423 formatted_bytes
= snprintf(num_buffer
, length
+ 1, spec
, w
);
425 formatted_chars
= formatted_bytes
= strlen(num_buffer
);
426 formatted_buf
= num_buffer
;
431 /* Just reuse the 'spec' buffer */
434 Jim_SetResultFormatted(interp
, "bad field specifier \"%s\"", spec
);
440 while (formatted_chars
< width
) {
441 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
446 Jim_AppendString(interp
, resultPtr
, formatted_buf
, formatted_bytes
);
448 while (formatted_chars
< width
) {
449 Jim_AppendString(interp
, resultPtr
, &pad
, 1);
453 objIndex
+= gotSequential
;
456 Jim_AppendString(interp
, resultPtr
, span
, numBytes
);
459 Jim_Free(num_buffer
);
463 Jim_SetResultString(interp
, msg
, -1);
465 Jim_FreeNewObj(interp
, resultPtr
);
466 Jim_Free(num_buffer
);