Posted to tcl by emiliano at Thu Oct 31 16:11:33 GMT 2024view pretty

#include <tcl.h>
#include <tk.h>

typedef int (HashSubCmd)(ClientData, Tcl_Interp *, int, Tcl_Obj *const []);
static HashSubCmd GetOp;
static HashSubCmd SetOp;
static HashSubCmd DeleteOp;

static int
Hash_Cmd(ClientData clientdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
    static const struct HashCmds {
	char *name;
	HashSubCmd *subCmd;
    } hashCmds[] = {
	{"delete", DeleteOp},
	{"get",    GetOp},
	{"set",    SetOp},
	{NULL, NULL}
    };
    int index;

    if (objc < 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?");
	return TCL_ERROR;
    }

    if (Tcl_GetIndexFromObjStruct(interp, objv[1], hashCmds,
	sizeof(struct HashCmds), "subcommand", 0, &index) != TCL_OK) {
	return TCL_ERROR;
    }

    return hashCmds[index].subCmd(clientdata, interp, objc, objv);
}

static int
SetOp(ClientData clientdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
    Tcl_HashTable *tablePtr = (Tcl_HashTable *) clientdata;
    Tk_Window tkwin;
    int isNew;
    Tcl_Size size;
    Tcl_HashEntry *entryPtr;

    if (objc != 4) {
	Tcl_WrongNumArgs(interp, 2, objv, "window dict");
	return TCL_ERROR;
    }

    tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), Tk_MainWindow(interp));
    if (! tkwin) {
	return TCL_ERROR;
    }

    if ( Tcl_DictObjSize(interp, objv[3], &size) != TCL_OK) {
	return TCL_ERROR;
    }

    entryPtr = Tcl_CreateHashEntry(tablePtr, tkwin, &isNew);
    if (! isNew) {
	Tcl_DecrRefCount(Tcl_GetHashValue(entryPtr));
    }

    Tcl_IncrRefCount(objv[3]);
    Tcl_SetHashValue(entryPtr, objv[3]);
    Tcl_SetObjResult(interp, objv[3]);
    return TCL_OK;
}

static int
GetOp(ClientData clientdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
    Tcl_HashTable *tablePtr = (Tcl_HashTable *) clientdata;
    Tk_Window tkwin;
    Tcl_HashEntry *entryPtr;

    if (objc != 3) {
	Tcl_WrongNumArgs(interp, 2, objv, "window");
	return TCL_ERROR;
    }

    tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), Tk_MainWindow(interp));
    if (! tkwin) {
	return TCL_ERROR;
    }

    entryPtr = Tcl_FindHashEntry(tablePtr, tkwin);
    if (! entryPtr) {
	Tcl_SetObjResult(interp, Tcl_ObjPrintf("can't find entry for window %s",
	    Tcl_GetString(objv[2])));
	return TCL_ERROR;
    }

    Tcl_SetObjResult(interp, Tcl_GetHashValue(entryPtr));
    return TCL_OK;
}

static int
DeleteOp(ClientData clientdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
    Tcl_HashTable *tablePtr = (Tcl_HashTable *) clientdata;
    Tk_Window tkwin;
    Tcl_HashEntry *entryPtr;

    if (objc != 3) {
	Tcl_WrongNumArgs(interp, 2, objv, "window");
	return TCL_ERROR;
    }

    tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[2]), Tk_MainWindow(interp));
    if (! tkwin) {
	return TCL_ERROR;
    }

    entryPtr = Tcl_FindHashEntry(tablePtr, tkwin);
    if (! entryPtr) {
	Tcl_SetObjResult(interp, Tcl_ObjPrintf("can't find entry for window %s",
	    Tcl_GetString(objv[2])));
	return TCL_ERROR;
    }

    Tcl_DecrRefCount(Tcl_GetHashValue(entryPtr));
    Tcl_DeleteHashEntry(entryPtr);
    return TCL_OK;
}

void
Hash_DeleteProc(ClientData clientData)
{
    Tcl_HashTable *tablePtr = (Tcl_HashTable *) clientData;
    Tcl_HashSearch search;
    Tcl_HashEntry *entryPtr;

    for (entryPtr = Tcl_FirstHashEntry(tablePtr, &search); entryPtr != NULL;
	    entryPtr = Tcl_NextHashEntry(&search)) {
	Tcl_DecrRefCount(Tcl_GetHashValue(entryPtr));
	Tcl_DeleteHashEntry(entryPtr);
    }

    Tcl_DeleteHashTable(tablePtr);
    ckfree(tablePtr);
    return;
}

/*
 * Hashsample_Init -- Called when Tcl loads your extension.
 */
int DLLEXPORT
Hashsample_Init(Tcl_Interp *interp)
{
    Tcl_HashTable *tablePtr;

    if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
        return TCL_ERROR;
    }
    if (Tk_InitStubs(interp, TCL_VERSION, 0) == NULL) {
        return TCL_ERROR;
    }
    if (Tcl_PkgProvide(interp, "Hashsample", "1.0") == TCL_ERROR) {
        return TCL_ERROR;
    }

    tablePtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable));
    Tcl_InitHashTable(tablePtr, TCL_ONE_WORD_KEYS);

    Tcl_CreateObjCommand(interp, "hash", Hash_Cmd, tablePtr, Hash_DeleteProc);
    return TCL_OK;
}

/*

emiliano@LE-PB01:~$ gcc -shared -fPIC -o libhashsample.so -DUSE_TCL_STUBS -DUSE_TK_STUBS -I/home/emiliano/tcl9/include hash_sample.c -L/home/emiliano/tcl9/lib -ltclstub -ltkstub

emiliano@LE-PB01:~$ /home/emiliano/tcl9/bin/tclsh9.0
% load ./libhashsample.so
% hash get .
can't find entry for window .
% hash set . foo
missing value to go with key
% hash set . "this is a test"
this is a test
% hash get .
this is a test
% hash delete .
% hash get .
can't find entry for window .

*/