uuid.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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
00035
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
00084
00085
00086
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
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
00127
00128 ret ::uuid::fromstring (type uuid) {
00129 return [binary format H* [string map {- {}} $uuid]]
00130 }
00131
00132
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
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
00151
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
00179
00180
00181
00182
00183
00184
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
00206 namespace ::uuid {
00207 foreach e {critcl} { if {[LoadAccelerator $e]} { break } }
00208 }
00209
00210 package provide uuid $::uuid::version
00211
00212
00213
00214
00215
00216
00217