• Tcl_NewObj & manual bytes/length setting vs Tcl_NewStringObj

    From oleg.o.nemanov@gmail.com@21:1/5 to All on Mon Oct 4 09:37:26 2021
    Hello.

    I have some strangeness with string representation of a value. It is truncated to (length - 1) if i use "set" command. The code:

    ```
    Tcl_CreateObjCommand(interp, "ulist", ulist, NULL, NULL);

    ...
    static int
    ulist(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
    {
    Tcl_Obj *str_o;

    if (objc != 2) {
    Tcl_SetObjResult(interp, Tcl_NewStringObj("Wrong number of arguments", -1));
    return TCL_ERROR;
    }
    str_o = Tcl_NewObj();
    Tcl_InvalidateStringRep(str_o);
    str_o->typePtr = &ulist_type;
    str_o->bytes = ckalloc(6);
    strcat(str_o->bytes, "ulist");
    str_o->length = 5;
    fprintf(stderr, "mk: len=%d, bytes=%s\n", str_o->length, str_o->bytes);

    Tcl_SetObjResult(interp, str_o);
    return TCL_OK;
    }
    ```

    tclsh:
    % load libulist.so
    % ulist 0
    mk: len=5, bytes=ulist
    ulist
    % set a [ulist 0]
    mk: len=5, bytes=ulist
    ulis
    % puts $a
    ulis
    %

    Note, "set" command returns "ulis" instead of "ulist".

    If i replace Tcl_NewObj with Tcl_NewStringObj:

    ```
    static int
    ulist(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
    {
    Tcl_Obj *str_o;

    if (objc != 2) {
    Tcl_SetObjResult(interp, Tcl_NewStringObj("Wrong number of arguments", -1));
    return TCL_ERROR;
    }
    str_o = Tcl_NewStringObj("ulist", 5);
    str_o->typePtr = &ulist_type;
    fprintf(stderr, "mk: len=%d, bytes=%s\n", str_o->length, str_o->bytes);

    Tcl_SetObjResult(interp, str_o);
    return TCL_OK;
    }
    ```
    tclsh:
    % load libulist.so
    % ulist 0
    mk: len=5, bytes=ulist
    ulist
    % set a [ulist 0]
    mk: len=5, bytes=ulist
    ulist
    % puts $a
    ulist
    %

    Where i mistake?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From clt.to.davebr@dfgh.net@21:1/5 to All on Tue Oct 5 14:35:09 2021
    I'm not sure what is causing the problem, but I have some comments:


    Tcl_CreateObjCommand(interp, "ulist", ulist, NULL, NULL);

    ...
    static int
    ulist(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) >{
    Tcl_Obj *str_o;

    if (objc != 2) {
    Tcl_SetObjResult(interp, Tcl_NewStringObj("Wrong number of arguments", -1));
    return TCL_ERROR;
    }
    str_o = Tcl_NewObj();
    Tcl_InvalidateStringRep(str_o);

    I believe a new object already has an empty string representation.

    str_o->typePtr = &ulist_type;

    It looks like you have defined a new Tcl type. If so why not set a string value (as below) then use Tcl_ConvertToType instead of directly manipulating the Tcl_Obj structure?

    str_o->bytes = ckalloc(6);
    strcat(str_o->bytes, "ulist");

    using strcat on what looks like uninitalized memory looks unsafe, why not strcpy?

    str_o->length = 5;
    fprintf(stderr, "mk: len=%d, bytes=%s\n", str_o->length, str_o->bytes);

    Tcl_SetObjResult(interp, str_o);
    return TCL_OK;
    }



    static int
    ulist(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) >{
    Tcl_Obj *str_o;

    if (objc != 2) {
    Tcl_SetObjResult(interp, Tcl_NewStringObj("Wrong number of arguments", -1));
    return TCL_ERROR;
    }
    str_o = Tcl_NewStringObj("ulist", 5);
    str_o->typePtr = &ulist_type;

    This appears to work, but you could use Tcl_ConvertToType instead of manipulating the Tcl_Obj structure outside of the routines in the Tcl_ObjType structure.

    fprintf(stderr, "mk: len=%d, bytes=%s\n", str_o->length, str_o->bytes);

    Tcl_SetObjResult(interp, str_o);
    return TCL_OK;
    }


    Dave B

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From oleg.o.nemanov@gmail.com@21:1/5 to All on Tue Oct 5 12:10:05 2021
    вторник, 5 октября 2021 г. в 17:34:46 UTC+3, clt.to...@dfgh.net:
    Tcl_InvalidateStringRep(str_o);
    I believe a new object already has an empty string representation.

    Yes, i saw this in tclInt.h. Forgot to remove this line :-).

    str_o->typePtr = &ulist_type;

    It looks like you have defined a new Tcl type.

    Yes. I'm trying to create custom tcl type.

    If so why not set a string value (as below) then use Tcl_ConvertToType instead of directly manipulating the Tcl_Obj structure?

    If i understand correctly, in the case with Tcl_ConvertToType i need in any case do directly manipulation of Tcl_Obj, but inside a setFromAnyProc, isn't it?

    str_o->bytes = ckalloc(6);
    strcat(str_o->bytes, "ulist");
    using strcat on what looks like uninitalized memory looks unsafe, why not strcpy?

    Oh, yes! This is a very stupid error. I need more sleep :-D. Thanks very much! Now(with str_o->bytes[0] = '\0' before strcat() or with strcpy() instead of strcat()) everything works well.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)