Tcl_Obj Deep Copy

Here's my code which shows how to (in short) do a deep copy of a Tcl_Obj structure from C.

It stores any unknown type as string and the following in internal represenatation:

  • list (including sublists)
  • int
  • long
  • double
  • strings (this is the default handler)

Here's the main code:

[deepcopy.h]

    #ifndef DEEPCOPY_H
    #define DEEPCOPY_H 1

    #include <tcl.h>

    void *Dc_GetTclObj(Tcl_Obj *obj);
    Tcl_Obj *Dc_NewTclObj(void *ptr);
    void Dc_FreeDcObj(void *ptr);
    unsigned long Dc_ObjSize(void *ptr);

    #endif

[deepcopyInt.h]

    #ifndef DEEPCOPYINT_H
    #define DEEPCOPYINT_H 1

    #define DCO_STRING      1
    #define DCO_INTEGER     2
    #define DCO_LONG        3
    #define DCO_DOUBLE      4
    #define DCO_LIST        5

    #ifdef  AOLSERVER
    #define dc_Malloc       Ns_Alloc
    #define dc_Free         Ns_Free
    #else
    #define dc_Malloc       Tcl_Alloc
    #define dc_Free         Tcl_Free
    #endif

    /* objhead */
    struct dcObjHead {
        short type;
        unsigned long size;         // including dcObjHead
    };

    /* int object */
    struct dcIntObj {
        struct dcObjHead head;
        int value;
    };

    static struct dcObjHead *int_GetTclObj(Tcl_Obj *to);
    Tcl_Obj *int_NewTclObj(struct dcObjHead *o);
    static void int_Free(struct dcObjHead *o);

    /* long object */
    struct dcLongObj {
        struct dcObjHead head;
        long value;
    };

    static struct dcObjHead *long_GetTclObj(Tcl_Obj *to);
    Tcl_Obj *long_NewTclObj(struct dcObjHead *o);
    static void long_Free(struct dcObjHead *o);

    /* double object */
    struct dcDoubleObj {
        struct dcObjHead head;
        double value;
    };

    static struct dcObjHead *double_GetTclObj(Tcl_Obj *to);
    Tcl_Obj *double_NewTclObj(struct dcObjHead *o);
    static void double_Free(struct dcObjHead *o);

    /* string object */
    struct dcStrObj {
        struct dcObjHead head;
        char string[1];
    };

    static struct dcObjHead *str_GetTclObj(Tcl_Obj *to);
    Tcl_Obj *str_NewTclObj(struct dcObjHead *o);
    static void str_Free(struct dcObjHead *o);

    /* list object */
    struct dcListObj {
        struct dcObjHead head;
        int nelem;
        struct dcObjHead *list[1];
    };

    static struct dcObjHead *list_GetTclObj(Tcl_Obj *to);
    Tcl_Obj *list_NewTclObj(struct dcObjHead *o);
    static void list_Free(struct dcObjHead *o);

    #endif

[deepcopy.c]

    #include <tcl.h>
    #include <deepcopy.h>
    #include <deepcopyInt.h>

    /* external API */

    void *Dc_GetTclObj(Tcl_Obj *obj) {
        void *rc;
        char *otype = "";

        if (obj->typePtr!=NULL) {
            otype = obj->typePtr->name;
        }
        if (!strcmp(otype,"int")) {
            rc = int_GetTclObj(obj);
        } else if (!strcmp(otype,"long")) {
            rc = long_GetTclObj(obj);
        } else if (!strcmp(otype,"double")) {
            rc = double_GetTclObj(obj);
        } else if (!strcmp(otype,"list")) {
            rc = list_GetTclObj(obj);
        } else {
            rc = str_GetTclObj(obj);
        }
        return rc;
    }

    Tcl_Obj *Dc_NewTclObj(void *ptr) {
        struct dcObjHead *oh = ptr;
        Tcl_Obj *rc = NULL;
        switch (oh->type) {
            case DCO_STRING:
                rc = str_NewTclObj(ptr);
                break;
            case DCO_LIST:
                rc = list_NewTclObj(ptr);
                break;
            case DCO_INTEGER:
                rc = int_NewTclObj(ptr);
                break;
            case DCO_DOUBLE:
                rc = double_NewTclObj(ptr);
                break;
            case DCO_LONG:
                rc = long_NewTclObj(ptr);
                break;
        }
        return rc;
    }

    void Dc_FreeDcObj(void *ptr) {
        struct dcObjHead *oh = ptr;
        switch (oh->type) {
            case DCO_STRING:
                str_Free(ptr);
                break;
            case DCO_LIST:
                list_Free(ptr);
                break;
            case DCO_INTEGER:
                int_Free(ptr);
                break;
            case DCO_LONG:
                long_Free(ptr);
                break;
            case DCO_DOUBLE:
                double_Free(ptr);
                break;
        }
    }

    unsigned long Dc_ObjSize(void *ptr) {
        struct dcObjHead *oh = ptr;
        return oh->size;
    }


    /* internal type representations */

    static struct dcObjHead *str_GetTclObj(Tcl_Obj *to) {
        struct dcStrObj *rc;
        int strlen;
        char *str = Tcl_GetStringFromObj(to, &strlen);
        unsigned long size = strlen+1+sizeof(struct dcObjHead);

        if (rc=(struct dcStrObj *) dc_Malloc(size)) {
            memcpy(rc->string, str, strlen);
            rc->string[strlen] = '\0';
            rc->head.type = DCO_STRING;
            rc->head.size = size;
        }
        return ((struct dcObjHead *) rc);
    }

    Tcl_Obj *str_NewTclObj(struct dcObjHead *o) {
        struct dcStrObj *so = (struct dcStrObj *) o;
        Tcl_Obj *rc = NULL;
        if (o->type==DCO_STRING) {
            rc = Tcl_NewStringObj(so->string,
                    o->size-sizeof(struct dcObjHead)-1);
        }
        return rc;
    }

    static void str_Free(struct dcObjHead *o) {
        if (o->type==DCO_STRING) {
            dc_Free(o);
        }
    }


    static struct dcObjHead *int_GetTclObj(Tcl_Obj *to) {
        struct dcIntObj *rc = (struct dcIntObj *)
                dc_Malloc(sizeof(struct dcIntObj));
        if (rc != NULL) {
            Tcl_GetIntFromObj(NULL, to, &rc->value);
            rc->head.type = DCO_INTEGER;
            rc->head.size = sizeof(struct dcIntObj);
        }
        return ((struct dcObjHead *) rc);
    }

    Tcl_Obj *int_NewTclObj(struct dcObjHead *o) {
        struct dcIntObj *so = (struct dcIntObj *) o;
        Tcl_Obj *rc = NULL;
        if (o->type==DCO_INTEGER) {
            rc = Tcl_NewIntObj(so->value);
        }
        return rc;
    }

    static void int_Free(struct dcObjHead *o) {
        if (o->type==DCO_INTEGER) {
            dc_Free((void *) o);
        }
    }


    static struct dcObjHead *long_GetTclObj(Tcl_Obj *to) {
        struct dcLongObj *rc = (struct dcLongObj *)
                dc_Malloc(sizeof(struct dcLongObj));
        if (rc != NULL) {
            Tcl_GetLongFromObj(NULL, to, &rc->value);
            rc->head.type = DCO_LONG;
            rc->head.size = sizeof(struct dcLongObj);
        }
        return ((struct dcObjHead *) rc);
    }

    Tcl_Obj *long_NewTclObj(struct dcObjHead *o) {
        struct dcLongObj *so = (struct dcLongObj *) o;
        Tcl_Obj *rc = NULL;
        if (o->type==DCO_LONG) {
            rc = Tcl_NewLongObj(so->value);
        }
        return rc;
    }

    static void long_Free(struct dcObjHead *o) {
        if (o->type==DCO_LONG) {
            dc_Free((void *) o);
        }
    }


    static struct dcObjHead *double_GetTclObj(Tcl_Obj *to) {
        struct dcDoubleObj *rc = (struct dcDoubleObj *)
                dc_Malloc(sizeof(struct dcDoubleObj));
        if (rc != NULL) {
            Tcl_GetDoubleFromObj(NULL, to, &rc->value);
            rc->head.type = DCO_DOUBLE;
            rc->head.size = sizeof(struct dcDoubleObj);
        }
        return ((struct dcObjHead *) rc);
    }

    Tcl_Obj *double_NewTclObj(struct dcObjHead *o) {
        struct dcDoubleObj *so = (struct dcDoubleObj *) o;
        Tcl_Obj *rc = NULL;
        if (o->type==DCO_DOUBLE) {
            rc = Tcl_NewDoubleObj(so->value);
        }
        return rc;
    }

    static void double_Free(struct dcObjHead *o) {
        if (o->type==DCO_DOUBLE) {
            dc_Free((void *) o);
        }
    }


    static struct dcObjHead *list_GetTclObj(Tcl_Obj *to) {
        Tcl_Obj *elem;
        struct dcListObj *rc;
        int size, llen, i;

        if (Tcl_ListObjLength(NULL,to,&llen) != TCL_OK)
            return NULL;

        size = sizeof(struct dcListObj)+(llen-1)*sizeof(struct dcListObj *);
        rc = (struct dcListObj *) dc_Malloc(size);
        if (rc != NULL) {
            rc->head.type = DCO_LIST;
            rc->nelem = llen;
            for (i=0 ; i<llen ; i++) {
                if (Tcl_ListObjIndex(NULL,to,i,&elem)!=TCL_OK) {
                    rc->list[i] = NULL;
                } else {
                    rc->list[i] = Dc_GetTclObj(elem);
                    size += rc->list[i]->size;
                }
            }
            rc->head.size = size;
        }
        return ((struct dcObjHead *) rc);
    }

    Tcl_Obj *list_NewTclObj(struct dcObjHead *o) {
        struct dcListObj *so = (struct dcListObj *) o;
        Tcl_Obj *rc = NULL, **tbl;
        int llen,i;

        if (o->type==DCO_LIST) {
            llen = so->nelem;
            tbl = dc_Malloc(llen*sizeof(Tcl_Obj *));
            if (tbl!=NULL) {
                for (i=0 ; i<llen ; i++) {
                    tbl[i] = Dc_NewTclObj(so->list[i]);
                }
                rc = Tcl_NewListObj(llen,tbl);
                dc_Free(tbl);
            }
        }
        return rc;
    }

    static void list_Free(struct dcObjHead *o) {
        struct dcListObj *so = o;
        int i;
        if (o->type==DCO_LIST) {
            for (i=0 ; i<so->nelem ; i++) {
                Dc_FreeDcObj(so->list[i]);
            }
            dc_Free((void *) o);
        }
    }