uuid.tcl

Go to the documentation of this file.
00001 /*  uuid.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>*/
00002 /* */
00003 /*  UUIDs are 128 bit values that attempt to be unique in time and space.*/
00004 /* */
00005 /*  Reference:*/
00006 /*    http://www.opengroup.org/dce/info/draft-leach-uuids-guids-01.txt*/
00007 /* */
00008 /*  uuid: scheme:*/
00009 /*  http://www.globecom.net/ietf/draft/draft-kindel-uuid-uri-00.html*/
00010 /* */
00011 /*  Usage: uuid::uuid generate*/
00012 /*         uuid::uuid equal $idA $idB*/
00013 
00014 namespace uuid {
00015     variable version 1.0.1
00016     variable accel
00017     array  accel =  {critcl 0}
00018 
00019     namespace export uuid
00020 
00021     variable uid
00022     if {![info exists uid]} {
00023          uid =  1
00024     }
00025 
00026     if {[package vcompare [package provide Tcl] 8.4] < 0} {
00027         package require struct::list
00028         interp alias {} ::uuid::l {} ::struct = ::list::l
00029     }
00030 
00031     proc =  K {a b} { a = }
00032 }
00033 
00034 /*  Generates a binary UUID as per the draft spec. We generate a pseudo-random*/
00035 /*  type uuid (type 4). See section 3.4*/
00036 /* */
00037 ret  ::uuid::generate_tcl () {
00038     package require md5 2
00039     variable uid
00040 
00041     set tok [md5::MD5Init]
00042     md5::MD5Update $tok [clock seconds]; # timestamp
00043     md5::MD5Update $tok [clock clicks];  # system incrementing counter
00044     md5::MD5Update $tok [incr uid];      # package incrementing counter 
00045     md5::MD5Update $tok [info hostname]; # spatial unique id (poor)
00046     md5::MD5Update $tok [pid];           # additional entropy
00047     md5::MD5Update $tok [array get ::tcl_platform]
00048     
00049     # More spatial information -- better than hostname.
00050     # bug 1150714: opening a server socket may raise a warning messagebox
00051     #   with WinXP firewall, using ipconfig will return all IP addresses
00052     #   including ipv6 ones if available. ipconfig is OK on win98+
00053     if {[string equal $::tcl_platform(platform) "windows"]} {
00054         catch {exec ipconfig} config
00055         md5::MD5Update $tok $config
00056     } else {
00057         catch {
00058             set s [socket -server void -myaddr [info hostname] 0]
00059             K [fconfigure $s -sockname] [close $s]
00060         } r
00061         md5::MD5Update $tok $r
00062     }
00063 
00064     if {[package provide Tk] != {}} {
00065         md5::MD5Update $tok [winfo pointerxy .]
00066         md5::MD5Update $tok [winfo id .]
00067     }
00068 
00069     set r [md5::MD5Final $tok]
00070     binary scan $r c* r
00071     
00072     # 3.4: set uuid versioning fields
00073     lset r 8 [expr {([lindex $r 8] & 0x7F) | 0x40}]
00074     lset r 6 [expr {([lindex $r 6] & 0x0F) | 0x40}]
00075     
00076     return [binary format c* $r]
00077 }
00078 
00079 if {[string equal $tcl_platform(platform) "windows"] 
00080         && [package provide critcl] != {}} {
00081     namespace uuid {
00082         critcl::ccode {
00083             /* define WIN32_LEAN_AND_MEAN*/
00084             /* define STRICT*/
00085             /* include <windows.h>*/
00086             /* include <ole2.h>*/
00087             typedef long (__stdcall *LPFNUUIDCREATE)(UUID *);
00088             typedef const unsigned char cu_char;
00089         }
00090         critcl::cret  generate_c (type Tcl_, type Interp* , type interp) ok {
00091             HRESULT hr = S_OK;
00092             int r = TCL_OK;
00093             UUID uuid = {0};
00094             HMODULE hLib;
00095             LPFNUUIDCREATE lpfnUuidCreate = NULL;
00096 
00097             hLib = LoadLibrary(_T("rpcrt4.dll"));
00098             if (hLib)
00099                 lpfnUuidCreate = (LPFNUUIDCREATE)
00100                     GetProcAddress(hLib, "UuidCreate");
00101             if (lpfnUuidCreate) {
00102                 Tcl_Obj *obj;
00103                 lpfnUuidCreate(&uuid);
00104                 obj = Tcl_NewByteArrayObj((cu_char *)&uuid, sizeof(uuid));
00105                 Tcl_SetObjResult(interp, obj);
00106             } else {
00107                 Tcl_SetResult(interp, "error: failed to create a guid",
00108                               TCL_STATIC);
00109                 r = TCL_ERROR;
00110             }
00111             return r;
00112         }
00113     }
00114 }
00115 
00116 /*  Convert a binary uuid into its string representation.*/
00117 /* */
00118 ret  ::uuid::tostring (type uuid) {
00119     binary scan $uuid H* s
00120     foreach {a b} {0 7 8 11 12 15 16 19 20 end} {
00121         append r [string range $s $a $b] -
00122     }
00123     return [string tolower [string trimright $r -]]
00124 }
00125 
00126 /*  Convert a string representation of a uuid into its binary format.*/
00127 /* */
00128 ret  ::uuid::fromstring (type uuid) {
00129     return [binary format H* [string map {- {}} $uuid]]
00130 }
00131 
00132 /*  Compare two uuids for equality.*/
00133 /* */
00134 ret  ::uuid::equal (type left , type right) {
00135     set l [fromstring $left]
00136     set r [fromstring $right]
00137     return [string equal $l $r]
00138 }
00139 
00140 /*  Call our generate uuid implementation*/
00141 ret  ::uuid::generate () {
00142     variable accel
00143     if {$accel(critcl)} {
00144         return [generate_c]
00145     } else {
00146         return [generate_tcl]
00147     }
00148 }
00149 
00150 /*  uuid generate -> string rep of a new uuid*/
00151 /*  uuid equal uuid1 uuid2*/
00152 /* */
00153 ret  uuid::uuid (type cmd , type args) {
00154     switch -exact -- $cmd {
00155         generate {
00156             if {[llength $args] != 0} {
00157                 return -code error "wrong # args:\
00158                     should be \"uuid generate\""
00159             }
00160             return [tostring [generate]]
00161         }
00162         equal {
00163             if {[llength $args] != 2} {
00164                 return -code error "wrong \# args:\
00165                     should be \"uuid equal uuid1 uuid2\""
00166             }
00167             return [eval [linsert $args 0 equal]]
00168         }
00169         default {
00170             return -code error "bad option \"$cmd\":\
00171                 must be generate or equal"
00172         }
00173     }
00174 }
00175 
00176 /*  -------------------------------------------------------------------------*/
00177 
00178 /*  LoadAccelerator --*/
00179 /* */
00180 /*  This package can make use of a number of compiled extensions to*/
00181 /*  accelerate the digest computation. This procedure manages the*/
00182 /*  use of these extensions within the package. During normal usage*/
00183 /*  this should not be called, but the test package manipulates the*/
00184 /*  list of enabled accelerators.*/
00185 /* */
00186 ret  ::uuid::LoadAccelerator (type name) {
00187     variable accel
00188     set r 0
00189     switch -exact -- $name {
00190         critcl {
00191             if {![catch {package require tcllibc}]} {
00192                 set r [expr {[info command ::uuid::generate_c] != {}}]
00193             }
00194         }
00195         default {
00196             return -code error "invalid accelerator package:\
00197                 must be one of [join [array names accel] {, }]"
00198         }
00199     }
00200     set accel($name) $r
00201 }
00202 
00203 /*  -------------------------------------------------------------------------*/
00204 
00205 /*  Try and load a compiled extension to help.*/
00206 namespace ::uuid {
00207     foreach e {critcl} { if {[LoadAccelerator $e]} { break } }
00208 }
00209 
00210 package provide uuid $::uuid::version
00211 
00212 /*  -------------------------------------------------------------------------*/
00213 /*  Local variables:*/
00214 /*    mode: tcl*/
00215 /*    indent-tabs-mode: nil*/
00216 /*  End:*/
00217 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1