#4118 StringObj.3 Tcl_ObjPrintf inaccuracies

obsolete: 8.6a2

StringObj.3 gives this example:
long x = 5;
Tcl_Obj *objPtr = Tcl_ObjPrintf("Value is %d", x);

This is incorrect, because the Tcl_ObjPrintf code will expect an int, and use va_arg with int.

As can be seen in the AppendPrintfToObjVA:
case 'd':
seekingConversion = 0;
switch (size) {
case -1:
case 0:
Tcl_ListObjAppendElement(NULL, list, Tcl_NewLongObj((long int)va_arg(argList, int)));

The issue is that if the user uses %d for a long, the va_list will have the wrong offset. The example will work in the 1 argument case, and even the multiple argument case if sizeof(int) = sizeof(long). It however will fail in the case of sizeof(long) > sizeof(int), because the va_list will have the wrong offset. Thus you'd be getting parts of the integer. This can have disasterous consequences if the offset is wrong, because the next va_arg call becomes wrong, which if it's a pointer may lead to segfaults.

Tcl works with some platforms where a long is 64-bit and an int is 32-bit. #ifdef TCL_WIDE_INT_IS_LONG as I understand it refers to this.

The next issue is that the format specifiers are not exactly the same as the [format] command, and probably shouldn't be.

The format command uses (as documented) %ld for truncation to a 64-bit integer. %lld for no truncation (BigNum) and %d for a 32-bit integer. This is incomptible with AppendPrintfToObjVA() (used by Tcl_ObjPrintf), because it currently accepts a long for %ld which may or may not be 64-bit depending on the platform. The wide and BigNum cases are also not handled by AppendPrintfToObjVA(). Thus the suggestion in the documentation that Tcl_ObjPrintf has the same syntax as the format command are wrong.


  • Don Porter

    Don Porter - 2008-09-02
    • labels: 105688 --> 10. Objects
    • priority: 5 --> 7
    • assigned_to: hobbs --> dgp
  • Don Porter

    Don Porter - 2009-02-16
    • priority: 7 --> 9
  • Alexandre Ferrieux

    Indeed, there's a trace of more ambition in APTOVA:

    /* TODO: support for wide (and bignum?) arguments */

    at present, what's implemented is closer to sprintf. Could someone review to see how exactly it differs ? Is the %h (length -1) case correct ?

  • Alexandre Ferrieux

    After a second dive, it looks like the doc is currently an outright lie:
    - The %h prefix for [ciudoxX] is completely ignored (va_arg(int) in all cases)
    (I also don't understand the absence of a "p++" in the %h case)
    - As George mentioned, generalization to %ll et al is not a good idea (not really suited for vararg handling)

    So I'd advocate to fix the doc so that it admits taking printf() as a model and diverging only on %h.
    (maybe an explicit list of specifiers and modifiers should be added, since printf() has a variable ambition on different unix flavours).

  • Donal K. Fellows

    • priority: 9 --> 8
  • Donal K. Fellows

    Corrected the example in the docs, but the real problems persist.


Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:

No, thanks