Tcl crypt -- An Example for Embedding Tcl Download example as gzipped tar file.

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.

tcl_crypt.c


#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 ;
}

The function Tcl_Crypt() is installed as a Tcl command by Tcl_CreateCommand() in the function Crypt_Init(), which is to be called by Tcl_AppInit(). In a simple case like this, I would usually call Tcl_CreateCommand() directly from Tcl_AppInit(), but using a separate init function for each module is the cleaner way in general.

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):

tkAppInit.c-patch


*** /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;

The file tcl_crypt.h just contains the function prototype for Crypt_Init() to make it known to tkAppInit.c:

tcl_crypt.h


int Crypt_Init(Tcl_Interp *interp) ;

Now we have to compile everything and build the interpreter, which will be called "cwish". For this we use the following Makefile:

Makefile


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

Of course the macro setting depends on your local Tcl/Tk installation and the compiler you use. This setting is for an HP workstation with HP-UX 9 and the HP ANSI C compiler.

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.

crypt-it.tcl


#!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.entry  Doit

# 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]]
}

This example application lets you type a string into the text entry. When you press the Return key or activate the "Crypt" button, the string is crypted and the resulting string is displayed.

The application looks like this:


Back to other Tcl hacks.

Jürgen Nickelsen <ni@jnickelsen.de>, 8.12.95