When Tcl/Tk was new to me, I missed a small, but complete and working example on how to integrate C functions into Tcl and Tk. John Ousterhout's book on Tcl and Tk describes the C and Tcl parts of the task well, but does not show how to put a working interpreter together.
This example makes the Unix crypt(3) function available as a Tcl command. It shows how to write a C function that can be called from Tcl, how to integrate it into the Tcl interpreter (wish, in this case), how to build the interpreter executable, and how to use the newly created command from Tcl. In commenting this example, I assume that you are familiar with the appropriate chapters of John Ousterhout's book.
The Tcl command is named "crypt". The first argument is the password to be encrypted, the optional second argument is the salt to use in the encryption. If the second argument is not given, a salt is chosen by the crypt command.
The C function is contained in the file tcl_crypt.c, which is shown below.
#include <unistd.h> #include <tcl.h> #include "tcl_crypt.h" Tcl_CmdProc Tcl_Crypt ; int Crypt_Init(Tcl_Interp *interp) { Tcl_CreateCommand(interp, "crypt", Tcl_Crypt, 0, 0) ; return TCL_OK ; } int Tcl_Crypt(ClientData dummy, Tcl_Interp *interp, int argc, char *argv[]) { char salt[2] ; switch (argc) { case 2: memcpy(salt, "AA", 2) ; break ; case 3: memcpy(salt, argv[2], 2) ; break ; default: interp->result = "Wrong # args: usage is `crypt password ?salt?'" ; return TCL_ERROR ; } strcpy(interp->result, crypt(argv[1], salt)) ; return TCL_OK ; }
Tcl_Crypt first checks its argc. If the number of arguments is incorrect, an error is raised with an informative message. With the correct number of arguments the salt is initialized and crypt(3) is called. The result is stored in interp->result. (We can do this safely, because we know that the resulting string is short.)
To call Crypt_Init() on interpreter startup, make the following changes to tkAppInit.c (or tclAppInit.c if you don't want to use Tk):
*** /usr/contrib/packages/tcl/lib/tkAppInit.c Fri Feb 11 18:27:10 1994 --- tkAppInit.c Wed Apr 13 20:57:40 1994 *************** *** 30,35 **** --- 30,36 ---- #endif /* not lint */ #include "tk.h" + #include "tcl_crypt.h" /* * The following variable is a special hack that allows applications *************** *** 78,83 **** --- 79,87 ---- * * where "Mod" is the name of the module. */ + if (Crypt_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } if (Tcl_Init(interp) == TCL_ERROR) { return TCL_ERROR;
int Crypt_Init(Tcl_Interp *interp) ;
CC = c89 CFLAGS = -g $(INCPATH) INCPATH = -I$(TCLBASE)/include -I/usr/include/X11R5 TCLBASE = /usr/contrib/packages/tcl LDFLAGS = LIBPATH = -L/usr/lib/X11R5 -L$(TCLBASE)/lib TKLIBS = -ltk -ltcl SYSLIBS = -lX11 -lm LIBS = $(TKLIBS) $(SYSLIBS) OBJECTS = tcl_crypt.o tkAppInit.o HEADERS = tcl_crypt.h TARGET = cwish all : $(TARGET) $(TARGET) : $(OBJECTS) $(CC) $(LDFLAGS) -o $(TARGET) $(OBJECTS) $(LIBPATH) $(LIBS) $(OBJECTS) : $(HEADERS) clean : rm -f $(TARGET) *~ *.o
From the Makefile we can see that no C source file containing a main() function is used here. main() is already contained in the Tk (and Tcl) library. Be sure to link them in the correct order -- the main() in libtcl.a will not initialize Tk!
Now we can use the crypt function from Tcl:
% crypt murdlyba @b @bL4fOe.I6c8g
To complete this example, I also show a tiny Tk application using the crypt command. In this case, crypt is called without the second argument, so that the fixed salt "AA" is used.
#!cwish # Build frame to hold prompt label and text entry. frame .top pack .top # Insert prompt label. label .top.l -text "Enter String:" pack .top.l -side left # Insert text entry, bind command to Return key. entry .top.entry -width 15 -relief sunken pack .top.entry -side left bind .top.entryDoit # Build frame to hold command button and display label. frame .bottom pack .bottom -fill x # Insert command button. button .bottom.b -text Crypt -command Doit pack .bottom.b -side left # Insert display label. label .bottom.l -text {} pack .bottom.l -side left -fill x -expand 1 # Define procedure to call crypt with text and display result. proc Doit {} { .bottom.l configure -text [crypt [.top.entry get]] }
The application looks like this: