1# 2# CTables - code to generate Tcl C extensions that implement tables out of 3# C structures 4# 5# 6# $Id$ 7# 8 9namespace eval ctable { 10 variable ctablePackageVersion 11 variable table 12 variable tables 13 variable booleans 14 variable nonBooleans 15 variable fields 16 variable fieldList 17 variable keyField 18 variable keyFieldName 19 variable ctableTypes 20 variable ctableErrorInfo 21 variable withPgtcl 22 variable withCasstcl 23 variable withSharedTables 24 variable withSharedTclExtension 25 variable sharedTraceFile ;# default none 26 variable poolRatio ;# default 16 27 variable withDirty 28 variable reservedWords 29 variable errorDebug 30 31 variable genCompilerDebug 32 variable showCompilerCommands 33 variable withPipe 34 variable memDebug 35 variable sharedGuard 36 variable sharedLog 37 variable sanityChecks 38 variable keyCompileVariables 39 40 # If loaded directly, rather than as a package 41 if {![info exists srcDir]} { 42 set srcDir . 43 } 44 45 source [file join $srcDir sysconfig.tcl] 46 47 source [file join $srcDir config.tcl] 48 49 set ctablePackageVersion $sysconfig(ctablePackageVersion) 50 51 set withPgtcl [info exists sysconfig(pgtclprefix)] 52 set withCasstcl [info exists sysconfig(casstclprefix)] 53 54 55 variable leftCurly 56 variable rightCurly 57 58 set leftCurly \173 59 set rightCurly \175 60 61 # Important compile settings, used in generating the ID 62 set keyCompileVariables { 63 fullInline 64 fullStatic 65 withPgtcl 66 withCasstcl 67 withSharedTables 68 withSharedTclExtension 69 sharedTraceFile 70 sharedBase 71 withDirty 72 genCompilerDebug 73 memDebug 74 sanityChecks 75 sharedGuard 76 ctablePackageVersion 77 } 78 79 set ctableErrorInfo "" 80 81 set tables "" 82 83 namespace eval fields {} 84 85 set cvsID {#CTable generator ID: $Id$} 86 87 ## ctableTypes must line up with the enumerated typedef "ctable_types" 88 ## in ctable.h 89 set ctableTypes "boolean fixedstring varstring char mac short int long wide float double inet tclobj key" 90 91 set reservedWords "bool char short int long wide float double" 92 93set fp [open $srcDir/template.c-subst] 94set metaTableSource [read $fp] 95close $fp 96 97set fp [open $srcDir/init-exten.c-subst] 98set initExtensionSource [read $fp] 99close $fp 100 101set fp [open $srcDir/exten-frag.c-subst] 102set extensionFragmentSource [read $fp] 103close $fp 104 105# 106# cmdBodySource - code we run subst over to generate the second chunk of the 107# body that implements the methods that work on the table. 108# 109set fp [open $srcDir/command-body.c-subst] 110set cmdBodySource [read $fp] 111close $fp 112 113# 114# emit - emit a string to the file being generated 115# 116proc emit {text} { 117 variable ofp 118 119 puts $ofp $text 120} 121 122# 123# cquote -- quote a string so the C compiler will see the same thing 124# if it occurs inside double-quotes 125# 126proc cquote {string {meta {"}}} { 127 # first, escape the metacharacters (quote) and backslash 128 append meta {\\} 129 regsub -all "\[$meta]" $string {\\&} string 130 131 # Now loop over the string looking for nonprinting characters 132 set quoted "" 133 while { 134 [regexp {([[:graph:]]*)([^[:graph:]])(.*)} $string _ plain char string] 135 } { 136 append quoted $plain 137 # gratuitously make \n and friends look nice 138 set index [string first $char "\r\n\t\b\f "] 139 if {$index == -1} { 140 scan $char %c decimal 141 set plain [format {\%03o} $decimal] 142 } else { 143 set plain [lindex {{\r} {\n} {\t} {\b} {\f} { }} $index] 144 } 145 append quoted $plain 146 } 147 append quoted $string 148 return $quoted 149} 150 151# 152# Special normally-illegal field names 153# 154variable specialFieldNames { 155 _key 156 _dirty 157} 158 159# 160# is_key - is this field a "key" or a normal field 161# 162proc is_key {fieldName} { 163 # If called before special "_key" field is set up. 164 if {"$fieldName" == "_key"} { 165 return 1 166 } 167 168 # Otherwise go by type 169 upvar ::ctable::fields::$fieldName field 170 if {[info exists field(type)] && "$field(type)" == "key"} { 171 return 1 172 } 173 174 return 0 175} 176 177# 178# is_hidden - hidden fields are not returned in arrays or lists by default 179# 180proc is_hidden {fieldName} { 181 return [string match {[._]*} $fieldName] 182} 183 184# 185# field_to_enum - return a field mapped to the name we'll use when 186# creating or referencing an enumerated list of field names. 187# 188# for example, creating table fa_position and field longitude, this 189# routine will return FIELD_FA_POSITION_LONGITUDE 190# 191proc field_to_enum {fieldName} { 192 variable table 193 194 if {[regexp {^[._](.*)$} $fieldName _ pseudoName]} { 195 return "SPECIAL_[string toupper $table]_[string toupper $pseudoName]" 196 } 197 return "FIELD_[string toupper $table]_[string toupper $fieldName]" 198} 199 200# 201# field_to_var - generate a unique variable name 202# 203proc field_to_var {table fieldName varName} { 204 if [regexp {^[._](.*)} $fieldName _ pseudoName] { 205 return "_${table}_${pseudoName}_$varName" 206 } 207 return "${table}_${fieldName}_$varName" 208} 209# 210# field_to_nameObj - return a field mapped to the Tcl name object we'll 211# use to expose the name to Tcl 212# 213proc field_to_nameObj {table fieldName} { 214 return [field_to_var $table $fieldName nameObj] 215} 216 217# 218# gen_allocate - return the code to allocate memory 219# 220proc gen_allocate_private {ctable size} { 221 return "(char *) ckalloc($size)" 222} 223 224proc gen_allocate {ctable size {private 0} {may_fail 0}} { 225 variable withSharedTables 226 variable table 227 set priv [gen_allocate_private $ctable $size] 228 229 if {$may_fail} { 230 set pub "${table}_allocate_may_fail($ctable, $size)" 231 } else { 232 set pub "${table}_allocate($ctable, $size)" 233 } 234 235 if {!$withSharedTables || "$private" == "1" || "$private" == "TRUE"} { 236 return $priv 237 } 238 239 if {"$private" == "0" || "$private" == "FALSE"} { 240 return $pub 241 } 242 243 return "(($private) ? $priv : $pub)" 244} 245 246proc gen_allocate_may_fail {ctable size {private 0}} { 247 return [gen_allocate $ctable $size $private 1] 248} 249 250# 251# Oposite function for free 252# 253proc gen_deallocate_private {ctable pointer} { 254 return "ckfree((char *)($pointer))" 255} 256 257proc gen_deallocate {ctable pointer {private 0}} { 258 variable withSharedTables 259 set priv [gen_deallocate_private $ctable $pointer] 260 261 set pub "shmfree(($ctable)->share, (void *)($pointer))" 262 263 if {!$withSharedTables || "$private" == "1" || "$private" == "TRUE"} { 264 return $priv 265 } 266 267 if {"$private" == "0" || "$private" == "FALSE"} { 268 return "(($ctable)->share_type == CTABLE_SHARED_MASTER ? $pub : $priv)" 269 } 270 271 return "( (($ctable)->share_type != CTABLE_SHARED_MASTER || ($private)) ? $priv : $pub)" 272} 273 274variable allocateSource { 275void ${table}_shmpanic(CTable *ctable) 276{ 277 Tcl_Panic ( 278 "Out of shared memory for \"%s\".", ctable->share_file 279 ); 280} 281 282void *${table}_allocate(CTable *ctable, size_t amount) 283{ 284 if(ctable->share_type == CTABLE_SHARED_MASTER) { 285 void *memory = shmalloc(ctable->share, amount); 286 if(!memory) 287 ${table}_shmpanic(ctable); 288 return memory; 289 } 290 return (void *)ckalloc(amount); 291} 292 293void *${table}_allocate_may_fail(CTable *ctable, size_t amount) 294{ 295 void *memory; 296 297 if(ctable->share_type == CTABLE_SHARED_MASTER) { 298 memory = shmalloc(ctable->share, amount); 299 } else { 300 memory = (void *)ckalloc(amount); 301 } 302 303 return memory; 304} 305 306} 307 308proc gen_allocate_function {table} { 309 variable withSharedTables 310 variable allocateSource 311 if {$withSharedTables} { 312 emit [string range [subst -nobackslashes -nocommands $allocateSource] 1 end-1] 313 } 314} 315 316variable sanitySource { 317void ${table}_sanity_check_pointer(CTable *ctable, void *ptr, int indexCtl, CONST char *where) 318{ 319#ifdef WITH_SHARED_TABLES 320 if(indexCtl != CTABLE_INDEX_NEW) { 321 if(ctable->share_type == CTABLE_SHARED_MASTER || ctable->share_type == CTABLE_SHARED_READER) { 322 if(ctable->share == NULL) 323 Tcl_Panic("%s: ctable->share_type = %d but ctable->share = NULL", where, ctable->share_type); 324 if((char *)ptr < (char *)ctable->share->map) 325 Tcl_Panic("%s: ctable->share->map = 0x%lX but ptr == 0x%lX", where, (long)ctable->share->map, (long)ptr); 326 if((size_t)((char *)ptr - (char *)ctable->share->map) > ctable->share->size) 327 Tcl_Panic("%s: ctable->share->size = %ld but ptr is at %ld offset from map", where, (long)ctable->share->size, (long)((char *)ptr - (char *)ctable->share->map)); 328 } 329 } 330#endif 331} 332} 333 334proc gen_sanity_checks {table} { 335 variable sanityChecks 336 variable sanitySource 337 if {$sanityChecks} { 338 emit [string range [subst -nobackslashes -nocommands $sanitySource] 1 end-1] 339 } 340} 341 342variable reinsertRowSource { 343int ${table}_reinsert_row(Tcl_Interp *interp, CTable *ctable, char *value, struct ${table} *row, int indexCtl) 344{ 345 ctable_HashEntry *newrow, *oldrow; 346 int isNew = 0; 347 int flags = KEY_VOLATILE; 348 char *key = value; 349#ifdef WITH_SHARED_TABLES 350 // shmallocated copy of key, if needed 351 char *mem = NULL; 352#endif 353 354 // Check for duplicates 355 oldrow = ctable_FindHashEntry(ctable->keyTablePtr, value); 356 if(oldrow) { 357 Tcl_AppendResult (interp, "Duplicate key '", value, "' when setting key field", (char *)NULL); 358 return TCL_ERROR; 359 } 360 361 if(indexCtl == CTABLE_INDEX_NORMAL) { 362#ifdef WITH_SHARED_TABLES 363 // Make a new copy of the key 364 if(ctable->share_type == CTABLE_SHARED_MASTER) { 365 mem = (char*)shmalloc(ctable->share, strlen(value)+1); 366 if(!mem) { 367 if(ctable->share_panic) ${table}_shmpanic(ctable); 368 Tcl_AppendResult (interp, "out of shared memory when setting key field", (char *)NULL); 369 return TCL_ERROR; 370 } 371 372 // Good to go 373 key = mem; 374 strcpy(mem, value); 375 flags = KEY_STATIC; 376 } 377#endif 378 379 // Remove old key. 380 ${table}_deleteHashEntry (ctable, row); 381 } else { 382 // This shouldn't be possible, but just in case 383 ckfree(row->hashEntry.key); 384 row->hashEntry.key = ctable->nullKeyValue; 385 } 386 387 // Insert existing row with new key 388 newrow = ctable_StoreHashEntry(ctable->keyTablePtr, key, &row->hashEntry, flags, &isNew); 389 390#ifdef SANITY_CHECKS 391 ${table}_sanity_check_pointer(ctable, (void *)newrow, CTABLE_INDEX_NORMAL, "${table}_reinsert_row"); 392#endif 393 394 if(!isNew) { 395 Tcl_AppendResult (interp, "Duplicate key '", value, "' after setting key field!", (char *)NULL); 396#ifdef WITH_SHARED_TABLES 397 if(mem) { 398 /* Don't need to "shmfree" because the key was never made 399 * visible to any readers. 400 */ 401 shmdealloc(ctable->share, mem); 402 } 403#endif 404 return TCL_ERROR; 405 } 406 407 if(indexCtl == CTABLE_INDEX_NEW) { 408 int field; 409 // Add to indexes. 410 for(field = 0; field < ${TABLE}_NFIELDS; field++) { 411 if (ctable_InsertIntoIndex (interp, ctable, row, field) == TCL_ERROR) { 412 return TCL_ERROR; 413 } 414 } 415 } 416 417 return TCL_OK; 418} 419} 420 421proc gen_reinsert_row_function {table} { 422 set TABLE [string toupper $table] 423 variable reinsertRowSource 424 emit [string range [subst -nobackslashes -nocommands $reinsertRowSource] 1 end-1] 425} 426 427# 428# preambleCannedSource -- stuff that goes at the start of the file we generate 429# 430variable preambleCannedSource { 431#include "ctable.h" 432 433#ifndef HAVE_ETHERS 434#include "ethers.c" 435#endif 436} 437 438variable nullIndexDuringSetSource { 439 if (ctable->skipLists[field] != NULL) { 440 if (indexCtl == CTABLE_INDEX_NORMAL) { 441 indexCtl = CTABLE_INDEX_NEW; // inhibit a second removal 442 ctable_RemoveFromIndex (ctable, row, field); 443 } 444 } 445} 446 447# 448# nullCheckDuringSetSource - standard stuff for handling nulls during set 449# 450variable nullCheckDuringSetSource { 451 int obj_is_null = ${table}_obj_is_null (obj); 452 if (obj_is_null) { 453 if (!row->_${fieldName}IsNull) { 454$handleNullIndex 455 // field wasn't null but now is 456 row->_${fieldName}IsNull = 1; 457 } else { 458 // No change, don't do anything 459 return TCL_OK; 460 } 461 } 462} 463 464# 465# gen_null_check_during_set_source - generate standard null checking 466# for a set 467# 468proc gen_null_check_during_set_source {table fieldName {elseCase ""}} { 469 variable nullCheckDuringSetSource 470 variable nullIndexDuringSetSource 471 variable fields 472 473 upvar ::ctable::fields::$fieldName field 474 475 if {[info exists field(notnull)] && $field(notnull)} { 476 return $elseCase 477 } 478 479 if {[info exists field(indexed)] && $field(indexed)} { 480 set handleNullIndex $nullIndexDuringSetSource 481 } else { 482 set handleNullIndex "" 483 } 484 485 if {"$elseCase" != ""} { 486 set elseCase " else { if(!obj_is_null) { $elseCase } }" 487 } 488 489 return [string range [subst -nobackslashes -nocommands $nullCheckDuringSetSource] 1 end-1]$elseCase 490} 491 492variable unsetNullDuringSetSource { 493 if (!obj_is_null && row->_${fieldName}IsNull) { 494 if ((indexCtl == CTABLE_INDEX_NORMAL) && (ctable->skipLists[field] != NULL)) { 495 indexCtl = CTABLE_INDEX_NEW; // inhibit a second removal 496 ctable_RemoveFromIndex (ctable, row, field); 497 } 498 499 row->_${fieldName}IsNull = 0; 500 } 501} 502 503variable unsetNullDuringSetSource_unindexed { 504 if (!obj_is_null && row->_${fieldName}IsNull) { 505 row->_${fieldName}IsNull = 0; 506 } 507} 508 509# gen_if_equal - generate code if $v1 == $v2 510proc gen_if_equal {v1 v2 code} { 511 if {"$v1" == "$v2"} {return $code} 512 return "" 513} 514 515# 516# gen_unset_null_during_set_source - generate standard null unsetting 517# for a set 518# 519proc gen_unset_null_during_set_source {table fieldName {elsecode {}}} { 520 variable unsetNullDuringSetSource 521 variable unsetNullDuringSetSource_unindexed 522 variable fields 523 524 upvar ::ctable::fields::$fieldName field 525 526 if {[info exists field(notnull)] && $field(notnull)} { 527 if {"$elsecode" == ""} { 528 return "" 529 } else { 530 return " $elsecode" 531 } 532 } else { 533 if {"$elsecode" != ""} { 534 set elsecode " else { if(!obj_is_null) { $elsecode } }" 535 } 536 if {[info exists field(indexed)] && $field(indexed)} { 537 return "[string range [subst -nobackslashes -nocommands $unsetNullDuringSetSource] 1 end-1]$elsecode" 538 } else { 539 return "[string range [subst -nobackslashes -nocommands $unsetNullDuringSetSource_unindexed] 1 end-1]$elsecode" 540 } 541 } 542} 543 544##### 545# 546# Generating Code To Set Values In Rows 547# 548##### 549 550variable removeFromIndexSource { 551 if ((indexCtl == CTABLE_INDEX_NORMAL) && (ctable->skipLists[field] != NULL)) { 552 ctable_RemoveFromIndex (ctable, row, field); 553 } 554} 555 556# 557# gen_ctable_remove_from_index - return code to remove the specified field 558# from an index, or nothing if the field is not indexable -- requires 559# interp, ctable, row and field to be defined and in scope in the C target. 560# 561proc gen_ctable_remove_from_index {fieldName} { 562 variable fields 563 variable removeFromIndexSource 564 565 upvar ::ctable::fields::$fieldName field 566 567 if {[info exists field(indexed)] && $field(indexed)} { 568 return $removeFromIndexSource 569 } else { 570 return "" 571 } 572} 573 574variable insertIntoIndexSource { 575 if ((indexCtl != CTABLE_INDEX_PRIVATE) && (ctable->skipLists[field] != NULL)) { 576 if (ctable_InsertIntoIndex (interp, ctable, row, field) == TCL_ERROR) { 577 return TCL_ERROR; 578 } 579 } 580} 581 582# 583# gen_ctable_insert_into_index - return code to insert the specified field 584# into an index, or nothing if the field is not indexable -- requires 585# interp, ctable, row and field to be defined and in scope in the C target. 586# 587proc gen_ctable_insert_into_index {fieldName} { 588 variable fields 589 variable insertIntoIndexSource 590 591 upvar ::ctable::fields::$fieldName field 592 593 if {[info exists field(indexed)] && $field(indexed)} { 594 return $insertIntoIndexSource 595 } else { 596 return "" 597 } 598} 599 600# 601# boolSetSource - code we run subst over to generate a set of a boolean (bit) 602# 603variable boolSetSource { 604 case $optname: { 605 int boolean = 0; 606 607[gen_null_check_during_set_source $table $fieldName \ 608 "if (Tcl_GetBooleanFromObj (interp, obj, &boolean) == TCL_ERROR) { 609 Tcl_AppendResult (interp, \" while converting $fieldName\", (char *)NULL); 610 return TCL_ERROR; 611 }"] 612[gen_unset_null_during_set_source $table $fieldName \ 613 "if (row->$fieldName == boolean) 614 return TCL_OK;"] 615 616 row->$fieldName = boolean; 617 [gen_if_equal $fieldName _dirty "return TCL_OK; // Don't set dirty for meta-fields"] 618 break; 619 } 620} 621 622# 623# numberSetSource - code we run subst over to generate a set of a standard 624# number such as an integer, long, double, and wide integer. (We have to 625# handle shorts and floats specially due to type coercion requirements.) 626# 627variable numberSetSource { 628 case $optname: { 629 $typeText value; 630[gen_null_check_during_set_source $table $fieldName \ 631 "if ($getObjCmd (interp, obj, &value) == TCL_ERROR) { 632 Tcl_AppendResult (interp, \" while converting $fieldName\", (char *)NULL); 633 return TCL_ERROR; 634 }"] 635[gen_unset_null_during_set_source $table $fieldName \ 636 "if (row->$fieldName == value) 637 return TCL_OK;"] 638[gen_ctable_remove_from_index $fieldName] 639 row->$fieldName = value; 640[gen_ctable_insert_into_index $fieldName] 641 break; 642 } 643} 644variable keySetSource { 645 case $optname: { 646 char *value = Tcl_GetString(obj); 647 648 if (row->hashEntry.key != ctable->nullKeyValue && *value == *row->hashEntry.key && strcmp(value, row->hashEntry.key) == 0) 649 return TCL_OK; 650 651 switch (indexCtl) { 652 case CTABLE_INDEX_PRIVATE: { 653 // fake hash entry for search 654 if(row->hashEntry.key != ctable->nullKeyValue) [gen_deallocate_private ctable row->hashEntry.key]; 655 row->hashEntry.key = (char *)[gen_allocate_private ctable "strlen(value)+1"]; 656 strcpy(row->hashEntry.key, value); 657 break; 658 } 659 case CTABLE_INDEX_NORMAL: 660 case CTABLE_INDEX_NEW: { 661 662#ifdef SANITY_CHECKS 663 ${table}_sanity_check_pointer(ctable, (void *)row, CTABLE_INDEX_NORMAL, "${table}::keySetSource"); 664#endif 665 if (${table}_reinsert_row(interp, ctable, value, row, indexCtl) == TCL_ERROR) 666 return TCL_ERROR; 667 break; 668 } 669 } 670 break; 671 } 672} 673 674# 675# varstringSetSource - code we run subst over to generate a set of a string. 676# 677# strings are char *'s that we manage automagically. 678# 679# If the string isn't changed, return immediately. 680# 681# Remove from index if we're indexed. 682# 683# If the new string doesn't fit in the allocated space: 684# 685# If space has been allocated for the existing string (not null and not initialized to the 686# static default string), free the old string. 687# 688# Allocate space for the new string (TODO: allocate strings to fixed size blocks to reduse fragmentation) 689# 690# Copy the string in and set the new length. 691# 692# Add back to index. 693# 694variable varstringSetSource { 695 case $optname: { 696 const char *stringPtr = NULL; 697 int length; 698[gen_null_check_during_set_source $table $fieldName] 699 700 stringPtr = Tcl_GetStringFromObj (obj, &length); 701[gen_unset_null_during_set_source $table $fieldName " 702 if(length == row->_${fieldName}Length && *stringPtr == *row->$fieldName && strcmp(stringPtr, row->$fieldName) == 0) 703 return TCL_OK;"] 704 705 // previous field isn't null, new field isn't null, and 706 // isn't the same as the previous field 707 [gen_ctable_remove_from_index $fieldName] 708 709 // new string value 710 // if the allocated length is less than what we need, get more, 711 // else reuse the previously allocated space 712 if (row->$fieldName == NULL || row->_${fieldName}AllocatedLength <= length) { 713 // Allocating shmem may fail, so allocate mem ahead of time 714 char *mem = (char*)[ 715 gen_allocate_may_fail ctable \ 716 "length + 1" \ 717 "indexCtl == CTABLE_INDEX_PRIVATE" 718 ]; 719 if (!mem) { 720#ifdef WITH_SHARED_TABLES 721 if(ctable->share_panic) ${table}_shmpanic(ctable); 722#endif 723 Tcl_AppendResult (interp, \" out of memory allocating space for $fieldName\", (char *)NULL); 724 return TCL_ERROR; 725 } 726 727 if (row->_${fieldName}AllocatedLength > 0) { 728 [gen_deallocate ctable "row->$fieldName" "indexCtl == CTABLE_INDEX_PRIVATE"]; 729 } 730 row->$fieldName = mem; 731 row->_${fieldName}AllocatedLength = length + 1; 732 } 733 strncpy (row->$fieldName, stringPtr, length + 1); 734 row->_${fieldName}Length = length; 735 736 // if we got here and this field has an index, we've removed 737 // the old index either by removing a null index or by 738 // removing the prior index, now insert the new index 739[gen_ctable_insert_into_index $fieldName] 740 break; 741 } 742} 743 744# 745# charSetSource - code we run subst over to generate a set of a single char. 746# 747variable charSetSource { 748 case $optname: { 749 const char *stringPtr; 750[gen_null_check_during_set_source $table $fieldName] 751 stringPtr = Tcl_GetString (obj); 752[gen_unset_null_during_set_source $table $fieldName \ 753 "if(row->$fieldName != stringPtr\[0]) 754 return TCL_OK;"] 755[gen_ctable_remove_from_index $fieldName] 756 row->$fieldName = stringPtr\[0\]; 757[gen_ctable_insert_into_index $fieldName] 758 break; 759 } 760} 761 762# 763# fixedstringSetSource - code we run subst over to generate a set of a 764# fixed-length string. 765# 766variable fixedstringSetSource { 767 case $optname: { 768 const char *stringPtr; 769 int len; 770[gen_null_check_during_set_source $table $fieldName] 771 stringPtr = Tcl_GetStringFromObj (obj, &len); 772[gen_unset_null_during_set_source $table $fieldName " 773 if (len == 0 && [expr [string length $default] > 0]) stringPtr = \"[cquote $default]\"; 774 if (*stringPtr == *row->$fieldName && strncmp(row->$fieldName, stringPtr, $length) == 0) 775 return TCL_OK;"] 776[gen_ctable_remove_from_index $fieldName] 777 if(len < $length) { 778 strncpy (row->$fieldName, "[cquote $default]", $length); 779 strncpy (row->$fieldName, stringPtr, len); 780 } else 781 strncpy (row->$fieldName, stringPtr, $length); 782[gen_ctable_insert_into_index $fieldName] 783 break; 784 } 785} 786 787# 788# inetSetSource - code we run subst over to generate a set of an IPv4 789# internet address. 790# 791variable inetSetSource { 792 case $optname: { 793 struct in_addr value = {INADDR_ANY}; 794[gen_null_check_during_set_source $table $fieldName \ 795 "if (!inet_aton (Tcl_GetString (obj), &value)) { 796 Tcl_AppendResult (interp, \"expected IP address but got \\\"\", Tcl_GetString (obj), \"\\\" parsing field \\\"$fieldName\\\"\", (char *)NULL); 797 return TCL_ERROR; 798 }"] 799[gen_unset_null_during_set_source $table $fieldName \ 800 "if (memcmp (&row->$fieldName, &value, sizeof (struct in_addr)) == 0) 801 return TCL_OK;"] 802 803[gen_ctable_remove_from_index $fieldName] 804 row->$fieldName = value; 805[gen_ctable_insert_into_index $fieldName] 806 break; 807 } 808} 809 810# 811# macSetSource - code we run subst over to generate a set of an ethernet 812# MAC address. 813# 814variable macSetSource { 815 case $optname: { 816 struct ether_addr *mac = (struct ether_addr *) NULL; 817[gen_null_check_during_set_source $table $fieldName \ 818 "{ 819 mac = ether_aton (Tcl_GetString (obj)); 820 if (mac == (struct ether_addr *) NULL) { 821 Tcl_AppendResult (interp, \"expected MAC address but got \\\"\", Tcl_GetString (obj), \"\\\" parsing field \\\"$fieldName\\\"\", (char *)NULL); 822 return TCL_ERROR; 823 } 824 }"] 825 826[gen_unset_null_during_set_source $table $fieldName \ 827 "if (memcmp (&row->$fieldName, mac, sizeof (struct ether_addr)) == 0) 828 return TCL_OK;"] 829[gen_ctable_remove_from_index $fieldName] 830 row->$fieldName = *mac; 831[gen_ctable_insert_into_index $fieldName] 832 break; 833 } 834} 835 836# 837# tclobjSetSource - code we run subst over to generate a set of a tclobj. 838# 839# tclobjs are Tcl_Obj *'s that we manage automagically. 840# 841variable tclobjSetSource { 842 case $optname: { 843 844 if (row->$fieldName != (Tcl_Obj *) NULL) { 845 Tcl_DecrRefCount (row->$fieldName); 846 row->$fieldName = NULL; 847 } 848[gen_null_check_during_set_source $table $fieldName \ 849 "{ 850 row->$fieldName = obj; 851 Tcl_IncrRefCount (obj); 852 }"] 853[gen_unset_null_during_set_source $table $fieldName] 854 break; 855 } 856} 857 858##### 859# 860# Generating Code For Sort Comparisons 861# 862##### 863 864# 865# nullSortSource - code to be inserted when null values are permitted for the 866# field. 867# 868variable nullSortSource { 869 if (row1->_${fieldName}IsNull) { 870 if (row2->_${fieldName}IsNull) { 871 result = 0; 872 break; 873 } 874 875 return direction; 876 } else if (row2->_${fieldName}IsNull) { 877 return -direction; 878 } 879} 880 881# 882# gen_null_check_during_sort_comp - 883# emit null checking as part of field 884# comparing in a sort 885# 886proc gen_null_check_during_sort_comp {table fieldName} { 887 variable nullSortSource 888 variable varstringSortCompareNullSource 889 890 upvar ::ctable::fields::$fieldName field 891 892 if {"$field(type)" == "varstring"} { 893 set source $varstringSortCompareNullSource 894 } elseif {[info exists field(notnull)] && $field(notnull)} { 895 set source "" 896 } else { 897 set source $nullSortSource 898 } 899 900 return [string range [subst -nobackslashes -nocommands $source] 1 end-1] 901} 902 903variable nullExcludeSource { 904 if (row->_${fieldName}IsNull) { 905 exclude = 1; 906 break; 907 } 908} 909 910proc gen_null_exclude_during_sort_comp {table fieldName} { 911 variable nullExcludeSource 912 913 upvar ::ctable::fields::$fieldName field 914 915 if {[info exists field(notnull)] && $field(notnull)} { 916 return "" 917 } else { 918 return [string range [subst -nobackslashes -nocommands $nullExcludeSource] 1 end-1] 919 } 920} 921 922# 923# boolSortSource - code we run subst over to generate a compare of a 924# boolean (bit) for use in a sort. 925# 926variable boolSortSource { 927 case $fieldEnum: { 928[gen_null_check_during_sort_comp $table $fieldName] 929 if (row1->$fieldName && !row2->$fieldName) { 930 result = -direction; 931 break; 932 } 933 934 if (!row1->$fieldName && row2->$fieldName) { 935 result = direction; 936 break; 937 } 938 939 result = 0; 940 break; 941 } 942} 943 944# 945# numberSortSource - code we run subst over to generate a compare of a standard 946# number such as an integer, long, double, and wide integer for use in a sort. 947# 948variable numberSortSource { 949 case $fieldEnum: { 950[gen_null_check_during_sort_comp $table $fieldName] 951 if (row1->$fieldName < row2->$fieldName) { 952 result = -direction; 953 break; 954 } 955 956 if (row1->$fieldName > row2->$fieldName) { 957 result = direction; 958 break; 959 } 960 961 result = 0; 962 break; 963 } 964} 965 966# 967# varstringSortCompareNullSource - compare against default empty string 968# for sorting 969# 970# note there's also a varstringCompareNullSource that's pretty close to this 971# but returns everything instead of just returning on non-match. 972# 973variable varstringSortCompareNullSource { 974 if (!row1->$fieldName) { 975 if(!row2->$fieldName) { 976 result = 0; 977 break; 978 } else { 979 return direction * -1; 980 } 981 } else { 982 if(!row2->$fieldName) { 983 return direction; 984 } 985 } 986} 987 988# 989# varstringSortSource - code we run subst over to generate a compare of 990# a string for use in a sort. 991# 992variable varstringSortSource { 993 case $fieldEnum: { 994[gen_null_check_during_sort_comp $table $fieldName] 995 996 result = direction * strcmp (row1->$fieldName, row2->$fieldName); 997 break; 998 } 999} 1000 1001# 1002# fixedstringSortSource - code we run subst over to generate a comapre of a 1003# fixed-length string for use in a sort. 1004# 1005variable fixedstringSortSource { 1006 case $fieldEnum: { 1007[gen_null_check_during_sort_comp $table $fieldName] 1008 result = direction * strncmp (row1->$fieldName, row2->$fieldName, $length); 1009 break; 1010 } 1011} 1012 1013# 1014# binaryDataSortSource - code we run subst over to generate a comapre of a 1015# inline binary arrays (inets and mac addrs) for use in a sort. 1016# 1017variable binaryDataSortSource { 1018 case $fieldEnum: { 1019[gen_null_check_during_sort_comp $table $fieldName] 1020 result = direction * memcmp (&row1->$fieldName, &row2->$fieldName, $length); 1021 break; 1022 } 1023} 1024 1025# 1026# tclobjSortSource - code we run subst over to generate a compare of 1027# a tclobj for use in a sort. 1028# 1029variable tclobjSortSource { 1030 case $fieldEnum: { 1031 result = direction * strcmp (Tcl_GetString (row1->$fieldName), Tcl_GetString (row2->$fieldName)); 1032 break; 1033 } 1034} 1035 1036# 1037# keySortSource - code we run subst over to generate a compare of 1038# a key for use in a sort. 1039# 1040variable keySortSource { 1041 case $fieldEnum: { 1042 if(*row1->hashEntry.key > *row2->hashEntry.key) 1043 result = direction; 1044 else if(*row1->hashEntry.key < *row2->hashEntry.key) 1045 result = -direction; 1046 else 1047 result = direction * strcmp (row1->hashEntry.key, row2->hashEntry.key); 1048 break; 1049 } 1050} 1051 1052##### 1053# 1054# Generating Code For Search Comparisons 1055# 1056##### 1057 1058# 1059# standardCompNullCheckSource - variable to substitute to do null 1060# handling in all comparison types 1061# 1062variable standardCompNullCheckSource { 1063 if (row->_${fieldName}IsNull) { 1064 if (compType == CTABLE_COMP_NULL) { 1065 break; 1066 } 1067 exclude = 1; 1068 break; 1069 } 1070 1071 if (compType == CTABLE_COMP_NULL) { 1072 exclude = 1; 1073 break; 1074 } 1075 1076 if (compType == CTABLE_COMP_NOTNULL) { 1077 break; 1078 } 1079} 1080 1081# 1082# standardCompNotNullCheckSource - variable to substitute to do null 1083# comparison handling for fields defined notnull. 1084# 1085variable standardCompNotNullCheckSource { 1086 if (compType == CTABLE_COMP_NULL) { 1087 exclude = 1; 1088 break; 1089 } else if (compType == CTABLE_COMP_NOTNULL) { 1090 break; 1091 } 1092} 1093 1094# 1095# gen_standard_comp_null_check_source - gen code to check null stuff 1096# when generating search comparison routines 1097# 1098proc gen_standard_comp_null_check_source {table fieldName} { 1099 variable standardCompNullCheckSource 1100 variable standardCompNotNullCheckSource 1101 upvar ::ctable::fields::$fieldName field 1102 1103 if {[info exists field(notnull)] && $field(notnull)} { 1104 return [string range $standardCompNotNullCheckSource 1 end-1] 1105 } else { 1106 return [string range [subst -nobackslashes -nocommands $standardCompNullCheckSource] 1 end-1] 1107 } 1108} 1109 1110# 1111# standardCompSwitchSource -stuff that gets emitted in a number of compare 1112# routines we generate 1113# 1114variable standardCompSwitchSource { 1115 switch (compType) { 1116 case CTABLE_COMP_LT: 1117 exclude = !(strcmpResult < 0); 1118 break; 1119 1120 case CTABLE_COMP_LE: 1121 exclude = !(strcmpResult <= 0); 1122 break; 1123 1124 case CTABLE_COMP_EQ: 1125 exclude = !(strcmpResult == 0); 1126 break; 1127 1128 case CTABLE_COMP_NE: 1129 exclude = !(strcmpResult != 0); 1130 break; 1131 1132 case CTABLE_COMP_GE: 1133 exclude = !(strcmpResult >= 0); 1134 break; 1135 1136 case CTABLE_COMP_GT: 1137 exclude = !(strcmpResult > 0); 1138 break; 1139 1140 default: 1141 Tcl_Panic ("compare type %d not implemented for field \"${fieldName}\"", compType); 1142 } 1143 break; 1144} 1145 1146# 1147# gen_standard_comp_switch_source - emit the standard compare source 1148# 1149proc gen_standard_comp_switch_source {fieldName} { 1150 variable standardCompSwitchSource 1151 1152 return [string range [subst -nobackslashes -nocommands $standardCompSwitchSource] 1 end-1] 1153} 1154 1155# 1156# boolCompSource - code we run subst over to generate a compare of a 1157# boolean (bit) 1158# 1159variable boolCompSource { 1160 case $fieldEnum: { 1161[gen_standard_comp_null_check_source $table $fieldName] 1162 switch (compType) { 1163 case CTABLE_COMP_TRUE: 1164 exclude = (!row->$fieldName); 1165 break; 1166 1167 case CTABLE_COMP_FALSE: 1168 exclude = row->$fieldName; 1169 break; 1170 } 1171 break; 1172 } 1173} 1174 1175# 1176# numberCompSource - code we run subst over to generate a compare of a standard 1177# number such as an integer, long, double, and wide integer. (We have to 1178# handle shorts and floats specially due to type coercion requirements.) 1179# 1180variable numberCompSource { 1181 case $fieldEnum: { 1182[gen_standard_comp_null_check_source $table $fieldName] 1183 switch (compType) { 1184 case CTABLE_COMP_LT: 1185 exclude = !(row->$fieldName < row1->$fieldName); 1186 break; 1187 1188 case CTABLE_COMP_LE: 1189 exclude = !(row->$fieldName <= row1->$fieldName); 1190 break; 1191 1192 case CTABLE_COMP_EQ: 1193 exclude = !(row->$fieldName == row1->$fieldName); 1194 break; 1195 1196 case CTABLE_COMP_NE: 1197 exclude = !(row->$fieldName != row1->$fieldName); 1198 break; 1199 1200 case CTABLE_COMP_GE: 1201 exclude = !(row->$fieldName >= row1->$fieldName); 1202 break; 1203 1204 case CTABLE_COMP_GT: 1205 exclude = !(row->$fieldName > row1->$fieldName); 1206 break; 1207 1208 case CTABLE_COMP_TRUE: 1209 exclude = (!row->$fieldName); 1210 break; 1211 1212 case CTABLE_COMP_FALSE: 1213 exclude = row->$fieldName; 1214 break; 1215 1216 default: 1217 Tcl_Panic ("compare type %d not implemented for field \"${fieldName}\"", compType); 1218 } 1219 break; 1220 } 1221} 1222 1223# 1224# varstringCompSource - code we run subst over to generate a compare of 1225# a string. 1226# 1227variable varstringCompSource { 1228 case $fieldEnum: { 1229 int strcmpResult; 1230 1231[gen_standard_comp_null_check_source $table $fieldName] 1232 1233 if ((compType == CTABLE_COMP_MATCH) || (compType == CTABLE_COMP_NOTMATCH) || (compType == CTABLE_COMP_MATCH_CASE) || (compType == CTABLE_COMP_NOTMATCH_CASE)) { 1234[gen_null_exclude_during_sort_comp $table $fieldName] 1235 // matchMeansKeep will be 1 if matching means keep, 1236 // 0 if it means discard 1237 int matchMeansKeep = ((compType == CTABLE_COMP_MATCH) || (compType == CTABLE_COMP_MATCH_CASE)); 1238 struct ctableSearchMatchStruct *sm = (struct ctableSearchMatchStruct *)component->clientData; 1239 1240 if (sm->type == CTABLE_STRING_MATCH_ANCHORED) { 1241 CONST char *field; 1242 CONST char *match; 1243 1244 exclude = !matchMeansKeep; 1245 for (field = row->$fieldName, match = row1->$fieldName; *match != '*' && *match != '\0'; match++, field++) { 1246 // printf("comparing '%c' and '%c'\n", *field, *match); 1247 if (sm->nocase) { 1248 if (tolower (*field) != tolower (*match)) { 1249 exclude = matchMeansKeep; 1250 break; 1251 } 1252 } else { 1253 if (*field != *match) { 1254 exclude = matchMeansKeep; 1255 break; 1256 } 1257 } 1258 } 1259 // if we got here it was anchored and we now know the score 1260 break; 1261 } else if (sm->type == CTABLE_STRING_MATCH_UNANCHORED) { 1262 exclude = (boyer_moore_search (sm, (unsigned char *)row->$fieldName, row->_${fieldName}Length, sm->nocase) == NULL); 1263 if (!matchMeansKeep) exclude = !exclude; 1264 break; 1265 } else if (sm->type == CTABLE_STRING_MATCH_PATTERN) { 1266 exclude = !(Tcl_StringCaseMatch (row->$fieldName, row1->$fieldName, ((compType == CTABLE_COMP_MATCH) || (compType == CTABLE_COMP_NOTMATCH)))); 1267 if (!matchMeansKeep) exclude = !exclude; 1268 break; 1269 } else { 1270 Tcl_Panic ("software bug, sm->type unknown match type"); 1271 } 1272 } 1273 1274 strcmpResult = strcmp (row->$fieldName, row1->$fieldName); 1275[gen_standard_comp_switch_source $fieldName] 1276 } 1277} 1278 1279# 1280# fixedstringCompSource - code we run subst over to generate a comapre of a 1281# fixed-length string. 1282# 1283variable fixedstringCompSource { 1284 case $fieldEnum: { 1285 int strcmpResult; 1286 1287[gen_standard_comp_null_check_source $table $fieldName] 1288 strcmpResult = strncmp (row->$fieldName, row1->$fieldName, $length); 1289[gen_standard_comp_switch_source $fieldName] 1290 } 1291} 1292 1293# 1294# binaryDataCompSource - code we run subst over to generate a comapre of a 1295# binary data. 1296# 1297variable binaryDataCompSource { 1298 case $fieldEnum: { 1299 int strcmpResult; 1300 1301[gen_standard_comp_null_check_source $table $fieldName] 1302 strcmpResult = memcmp ((void *)&row->$fieldName, (void *)&row1->$fieldName, $length); 1303[gen_standard_comp_switch_source $fieldName] 1304 } 1305} 1306 1307# 1308# tclobjCompSource - code we run subst over to generate a compare of 1309# a tclobj for use in a search. 1310# 1311# this could be so wrong - there may be a way to keep it from generating 1312# the text -- right now we are doing a Tcl_GetStringFromObj in the 1313# routine that sets this up, maybe don't do that and figure out some 1314# way to compare objects (?) 1315# 1316variable tclobjCompSource { 1317 case $fieldEnum: { 1318 int strcmpResult; 1319 1320[gen_standard_comp_null_check_source $table $fieldName] 1321 strcmpResult = strcmp (Tcl_GetString (row->$fieldName), Tcl_GetString (row1->$fieldName)); 1322[gen_standard_comp_switch_source $fieldName] 1323 } 1324} 1325 1326# 1327# keyCompSource - code we run subst over to generate a compare of 1328# a string. 1329# 1330variable keyCompSource { 1331 case $fieldEnum: { 1332 int strcmpResult; 1333 1334[gen_standard_comp_null_check_source $table $fieldName] 1335 if ((compType == CTABLE_COMP_MATCH) || (compType == CTABLE_COMP_NOTMATCH) || (compType == CTABLE_COMP_MATCH_CASE) || (compType == CTABLE_COMP_NOTMATCH_CASE)) { 1336[gen_null_exclude_during_sort_comp $table $fieldName] 1337 // matchMeansKeep will be 1 if matching means keep, 1338 // 0 if it means discard 1339 int matchMeansKeep = ((compType == CTABLE_COMP_MATCH) || (compType == CTABLE_COMP_MATCH_CASE)); 1340 struct ctableSearchMatchStruct *sm = (struct ctableSearchMatchStruct *)component->clientData; 1341 1342 if (sm->type == CTABLE_STRING_MATCH_ANCHORED) { 1343 char *field; 1344 char *match; 1345 1346 exclude = !matchMeansKeep; 1347 for (field = row->hashEntry.key, match = row1->hashEntry.key; *match != '*' && *match != '\0'; match++, field++) { 1348 // printf("comparing '%c' and '%c'\n", *field, *match); 1349 if (sm->nocase) { 1350 if (tolower (*field) != tolower (*match)) { 1351 exclude = matchMeansKeep; 1352 break; 1353 } 1354 } else { 1355 if (*field != *match) { 1356 exclude = matchMeansKeep; 1357 break; 1358 } 1359 } 1360 } 1361 // if we got here it was anchored and we now know the score 1362 break; 1363 } else if (sm->type == CTABLE_STRING_MATCH_UNANCHORED) { 1364 exclude = (boyer_moore_search (sm, (unsigned char *)row->hashEntry.key, strlen(row->hashEntry.key), sm->nocase) == NULL); 1365 if (!matchMeansKeep) exclude = !exclude; 1366 break; 1367 } else if (sm->type == CTABLE_STRING_MATCH_PATTERN) { 1368 exclude = !(Tcl_StringCaseMatch (row->hashEntry.key, row1->hashEntry.key, ((compType == CTABLE_COMP_MATCH) || (compType == CTABLE_COMP_NOTMATCH)))); 1369 if (!matchMeansKeep) exclude = !exclude; 1370 break; 1371 } else { 1372 Tcl_Panic ("software bug, sm->type unknown match type"); 1373 } 1374 } 1375 1376 strcmpResult = strcmp (row->hashEntry.key, row1->hashEntry.key); 1377[gen_standard_comp_switch_source $fieldName] 1378 } 1379} 1380 1381 1382##### 1383# 1384# Generating Code To Set Fields In Rows 1385# 1386##### 1387 1388variable fieldObjSetSource { 1389ctable_BaseRow *${table}_make_empty_row (CTable *ctable) { 1390 struct $table *row; 1391 1392 row = (struct $table *)ckalloc (sizeof (struct $table)); 1393 ${table}_init (ctable, row); 1394 1395 return (ctable_BaseRow*) row; 1396} 1397 1398// 1399// Wrapper for hash search. 1400// 1401// Always succeeds unless using shared memory and we run out of shared memory 1402// 1403// Must handle this in caller beacuse we're not passing an interpreter in 1404// 1405struct $table *${table}_find_or_create (Tcl_Interp *interp, CTable *ctable, const char *key, int *indexCtlPtr) { 1406 int flags = KEY_VOLATILE; 1407 const char *key_value = key; 1408 struct $table *row = NULL; 1409 1410 static struct $table *savedRow = NULL; 1411#ifdef WITH_SHARED_TABLES 1412 static struct $table *savedSharedRow = NULL; 1413 int isShared = ctable->share_type == CTABLE_SHARED_MASTER; 1414 struct $table *nextRow = isShared ? savedSharedRow : savedRow; 1415#else 1416 struct $table *nextRow = savedRow; 1417#endif 1418 1419 // Make sure the preallocated row is prepared 1420 if(!nextRow) { 1421#ifdef WITH_SHARED_TABLES 1422 if(isShared) { 1423 nextRow = (struct $table *)shmalloc(ctable->share, sizeof(struct $table)); 1424 if(!nextRow) { 1425 if(ctable->share_panic) ${table}_shmpanic(ctable); 1426 TclShmError(interp, key); 1427 return NULL; 1428 } 1429 } else 1430#endif 1431 nextRow = (struct $table *)ckalloc(sizeof(struct $table)); 1432 1433 ${table}_init (ctable, nextRow); 1434 } 1435 1436#ifdef WITH_SHARED_TABLES 1437 if(isShared) { 1438 char *new_key_value = (char *)shmalloc(ctable->share, strlen(key)+1); 1439 if(!new_key_value) { 1440 if(ctable->share_panic) ${table}_shmpanic(ctable); 1441 TclShmError(interp, key); 1442 return NULL; 1443 } 1444 strcpy(new_key_value, key); 1445 key_value = new_key_value; 1446 flags = KEY_STATIC; 1447 } 1448#endif 1449 1450 row = (struct $table *)ctable_StoreHashEntry (ctable->keyTablePtr, key_value, &nextRow->hashEntry, flags, indexCtlPtr); 1451 1452 // If we actually added a row, add it to the hash 1453 if (*indexCtlPtr) { 1454 ctable_ListInsertHead (&ctable->ll_head, (ctable_BaseRow *)row, 0); 1455 ctable->count++; 1456 // printf ("created new entry for '%s'\n", key); 1457 1458 // Discard the row we used 1459 nextRow = NULL; 1460 } else { 1461 // printf ("found existing entry for '%s'\n", key); 1462 1463#ifdef WITH_SHARED_TABLES 1464 // Discard the copy of the key we used 1465 if(flags == KEY_STATIC) { 1466 // Don't need to "shmfree" because the key was never made visible to 1467 // any readers. 1468 shmdealloc(ctable->share, (char*)key_value); 1469 } 1470#endif 1471 } 1472 1473 // Remember what we allocated (or didn't). 1474#ifdef WITH_SHARED_TABLES 1475 if(isShared) 1476 savedSharedRow = nextRow; 1477 else 1478#endif 1479 savedRow = nextRow; 1480 1481 return row; 1482} 1483 1484int 1485${table}_set_fieldobj (Tcl_Interp *interp, CTable *ctable, Tcl_Obj *obj, struct $table *row, Tcl_Obj *fieldObj, int indexCtl, int nocomplain) 1486{ 1487 int field; 1488 1489 if (Tcl_GetIndexFromObj (interp, fieldObj, ${table}_fields, "field", TCL_EXACT, &field) != TCL_OK) { 1490 if (nocomplain) { 1491 Tcl_ResetResult(interp); 1492 return TCL_OK; 1493 } 1494 return TCL_ERROR; 1495 } 1496 1497 return ${table}_set (interp, ctable, obj, row, field, indexCtl); 1498} 1499} 1500 1501variable fieldSetSource { 1502int 1503${table}_set (Tcl_Interp *interp, CTable *ctable, Tcl_Obj *obj, ctable_BaseRow *vRow, int field, int indexCtl) $leftCurly 1504 ${table} *row = (${table}*) vRow; 1505} 1506 1507variable fieldSetSwitchSource { 1508 switch ((enum ${table}_fields) field) $leftCurly 1509} 1510 1511variable fieldObjGetSource { 1512ctable_BaseRow *${table}_find (CTable *ctable, CONST char *key) { 1513 ctable_HashEntry *hashEntry; 1514 1515 hashEntry = ctable_FindHashEntry (ctable->keyTablePtr, key); 1516 if (hashEntry == (ctable_HashEntry *) NULL) { 1517 return (ctable_BaseRow *) NULL; 1518 } 1519 1520 return (ctable_BaseRow *)((char*)hashEntry - offsetof(ctable_BaseRow, hashEntry)); 1521} 1522 1523Tcl_Obj * 1524${table}_get_fieldobj (Tcl_Interp *interp, struct $table *row, Tcl_Obj *fieldObj) 1525{ 1526 int field; 1527 1528 if (Tcl_GetIndexFromObj (interp, fieldObj, ${table}_fields, "field", TCL_EXACT, &field) != TCL_OK) { 1529 return (Tcl_Obj *)NULL; 1530 } 1531 1532 return ${table}_get (interp, row, field); 1533} 1534 1535int 1536${table}_lappend_field (Tcl_Interp *interp, Tcl_Obj *destListObj, ctable_BaseRow *vPointer, int field) 1537{ 1538 struct $table *row = (struct $table *) vPointer; 1539 1540 Tcl_Obj *obj = ${table}_get (interp, row, field); 1541 1542 if (Tcl_ListObjAppendElement (interp, destListObj, obj) == TCL_ERROR) { 1543 return TCL_ERROR; 1544 } 1545 1546 return TCL_OK; 1547} 1548 1549int 1550${table}_lappend_fieldobj (Tcl_Interp *interp, ctable_BaseRow *vPointer, Tcl_Obj *fieldObj) 1551{ 1552 struct $table *row = (struct $table*) vPointer; 1553 Tcl_Obj *obj = ${table}_get_fieldobj (interp, row, fieldObj); 1554 1555 if (obj == NULL) { 1556 return TCL_ERROR; 1557 } 1558 1559 if (Tcl_ListObjAppendElement (interp, Tcl_GetObjResult(interp), obj) == TCL_ERROR) { 1560 return TCL_ERROR; 1561 } 1562 1563 return TCL_OK; 1564} 1565} 1566 1567variable lappendFieldAndNameObjSource { 1568int 1569${table}_lappend_field_and_name (Tcl_Interp *interp, Tcl_Obj *destListObj, ctable_BaseRow *vPointer, int field) 1570{ 1571 struct $table *row = (struct $table *) vPointer; 1572 Tcl_Obj *obj; 1573 1574 if (Tcl_ListObjAppendElement (interp, destListObj, ${table}_NameObjList[field]) == TCL_ERROR) { 1575 return TCL_ERROR; 1576 } 1577 1578 obj = ${table}_get (interp, row, field); 1579 if (Tcl_ListObjAppendElement (interp, destListObj, obj) == TCL_ERROR) { 1580 return TCL_ERROR; 1581 } 1582 1583 return TCL_OK; 1584} 1585 1586int 1587${table}_lappend_field_and_nameobj (Tcl_Interp *interp, ctable_BaseRow *vPointer, Tcl_Obj *fieldObj) 1588{ 1589 int field; 1590 1591 if (Tcl_GetIndexFromObj (interp, fieldObj, ${table}_fields, "field", TCL_EXACT, &field) != TCL_OK) { 1592 return TCL_ERROR; 1593 } 1594 1595 return ${table}_lappend_field_and_name (interp, Tcl_GetObjResult(interp), vPointer, field); 1596} 1597 1598} 1599 1600variable lappendNonnullFieldAndNameObjSource { 1601int 1602${table}_lappend_nonnull_field_and_name (Tcl_Interp *interp, Tcl_Obj *destListObj, ctable_BaseRow *vPointer, int field) 1603{ 1604 struct $table *row = (struct $table *)vPointer; 1605 Tcl_Obj *obj; 1606 1607 obj = ${table}_get (interp, row, field); 1608 if (obj == ${table}_NullValueObj) { 1609 return TCL_OK; 1610 } 1611 1612 if (Tcl_ListObjAppendElement (interp, destListObj, ${table}_NameObjList[field]) == TCL_ERROR) { 1613 return TCL_ERROR; 1614 } 1615 1616 if (Tcl_ListObjAppendElement (interp, destListObj, obj) == TCL_ERROR) { 1617 return TCL_ERROR; 1618 } 1619 1620 return TCL_OK; 1621} 1622 1623int 1624${table}_lappend_nonnull_field_and_nameobj (Tcl_Interp *interp, ctable_BaseRow *vPointer, Tcl_Obj *fieldObj) 1625{ 1626 int field; 1627 1628 if (Tcl_GetIndexFromObj (interp, fieldObj, ${table}_fields, "field", TCL_EXACT, &field) != TCL_OK) { 1629 return TCL_ERROR; 1630 } 1631 1632 return ${table}_lappend_nonnull_field_and_name (interp, Tcl_GetObjResult(interp), vPointer, field); 1633} 1634 1635} 1636 1637variable arraySetFromFieldSource { 1638int 1639${table}_array_set (Tcl_Interp *interp, Tcl_Obj *arrayNameObj, ctable_BaseRow *vPointer, int field) 1640{ 1641 struct $table *row = (struct $table *)vPointer; 1642 Tcl_Obj *obj; 1643 1644 obj = ${table}_get (interp, row, field); 1645 if (obj == ${table}_NullValueObj) { 1646 // it's null? unset it from the array, might not be there, ignore error 1647 Tcl_UnsetVar2 (interp, Tcl_GetString (arrayNameObj), ${table}_fields[field], 0); 1648 return TCL_OK; 1649 } 1650 1651 if (Tcl_ObjSetVar2 (interp, arrayNameObj, ${table}_NameObjList[field], obj, TCL_LEAVE_ERR_MSG) == (Tcl_Obj *)NULL) { 1652 return TCL_ERROR; 1653 } 1654 1655 return TCL_OK; 1656} 1657 1658int 1659${table}_array_set_with_nulls (Tcl_Interp *interp, Tcl_Obj *arrayNameObj, ctable_BaseRow *vPointer, int field) 1660{ 1661 struct $table *row = (struct $table*)vPointer; 1662 Tcl_Obj *obj; 1663 1664 obj = ${table}_get (interp, row, field); 1665 if (Tcl_ObjSetVar2 (interp, arrayNameObj, ${table}_NameObjList[field], obj, TCL_LEAVE_ERR_MSG) == (Tcl_Obj *)NULL) { 1666 return TCL_ERROR; 1667 } 1668 1669 return TCL_OK; 1670} 1671 1672} 1673 1674##### 1675# 1676# Generating Code To Get Fields From A Rows 1677# 1678##### 1679 1680variable fieldGetSource { 1681Tcl_Obj * 1682${table}_get (Tcl_Interp *interp, ctable_BaseRow *vPointer, int field) $leftCurly 1683 struct $table *row = (struct $table*) vPointer; 1684 1685 switch ((enum ${table}_fields) field) $leftCurly 1686} 1687 1688variable fieldGetStringSource { 1689CONST char * 1690${table}_get_string (const ctable_BaseRow *vPointer, int field, int *lengthPtr, Tcl_Obj *utilityObj) $leftCurly 1691 int length; 1692 const struct $table *row = (const struct $table*) vPointer; 1693 1694 if (lengthPtr == (int *) NULL) { 1695 lengthPtr = &length; 1696 } 1697 1698 switch ((enum ${table}_fields) field) $leftCurly 1699} 1700 1701##### 1702# 1703# Generating Code To Read And Write Tab-Separated Rows 1704# 1705##### 1706 1707variable tabSepFunctionsSource { 1708 1709void ${table}_dumpFieldNums(int *fieldNums, int nFields, CONST char *msg) 1710{ 1711 int i; 1712 1713 fprintf(stderr, "%s, %d fields: ", msg, nFields); 1714 1715 for(i = 0; i < nFields; i++) { 1716 int num = fieldNums[i]; 1717 if(num == -1) fprintf(stderr, "* "); 1718 else fprintf(stderr, "%d=%s ", num, ${table}_fields[num]); 1719 } 1720 1721 fprintf(stderr, "\n"); 1722} 1723 1724void 1725${table}_dstring_append_get_tabsep (CONST char *key, ctable_BaseRow *vPointer, int *fieldNums, int nFields, Tcl_DString *dsPtr, int noKeys, CONST char *sepstr, int quoteType, CONST char *nullString) { 1726 int i; 1727 CONST char *string; 1728 int nChars; 1729 Tcl_Obj *utilityObj = Tcl_NewObj(); 1730 struct $table *row = (struct $table *) vPointer; 1731 1732 if (!noKeys) { 1733 int copy = 0; 1734 if(quoteType) { 1735 copy = ctable_quoteString(&key, NULL, quoteType, sepstr); 1736 } 1737 Tcl_DStringAppend (dsPtr, key, -1); 1738 if(copy) { 1739 ckfree((char *)key); 1740 key = NULL; 1741 } 1742 } 1743 1744 for (i = 0; i < nFields; i++) { 1745 if (!noKeys || (i > 0)) { 1746 Tcl_DStringAppend (dsPtr, sepstr, -1); 1747 } 1748 1749 if(nullString && ${table}_is_null(row, fieldNums[i])) { 1750 Tcl_DStringAppend (dsPtr, nullString, -1); 1751 continue; 1752 } 1753 1754 string = ${table}_get_string (row, fieldNums[i], &nChars, utilityObj); 1755 if (nChars != 0) { 1756 int copy = 0; 1757 if (quoteType && ${table}_needs_quoting[fieldNums[i]]) { 1758 copy = ctable_quoteString(&string, &nChars, quoteType, sepstr); 1759 } 1760 Tcl_DStringAppend (dsPtr, string, nChars); 1761 if(copy) { 1762 ckfree((char *)string); 1763 string = NULL; 1764 } 1765 } 1766 } 1767 Tcl_DStringAppend (dsPtr, "\n", 1); 1768 Tcl_DecrRefCount (utilityObj); 1769} 1770 1771void 1772${table}_dstring_append_fieldnames (int *fieldNums, int nFields, Tcl_DString *dsPtr, int noKeys, CONST char *sepstr) 1773{ 1774 int i; 1775 1776 if(!noKeys) { 1777 Tcl_DStringAppend(dsPtr, "_key", 4); 1778 } 1779 1780 for (i = 0; i < nFields; i++) { 1781 if (!noKeys || (i > 0)) { 1782 Tcl_DStringAppend (dsPtr, sepstr, -1); 1783 } 1784 1785 Tcl_DStringAppend(dsPtr, ${table}_fields[fieldNums[i]], -1); 1786 } 1787 Tcl_DStringAppend (dsPtr, "\n", 1); 1788} 1789 1790// TODO: stringPtr argument should probably be CONST and not modified. 1791int 1792${table}_get_fields_from_tabsep (Tcl_Interp *interp, char *stringPtr, int *nFieldsPtr, int **fieldNumsPtr, int *noKeysPtr, CONST char *sepstr, int nocomplain) 1793{ 1794 int i; 1795 int field; 1796 char *tab; 1797 char save = '\0'; 1798 int seplen = strlen(sepstr); 1799 int *fieldNums = NULL; 1800 char *s; 1801 int nColumns; 1802 int keyCol = -1; 1803 1804 *noKeysPtr = 1; 1805 1806 // find the number of fields and allocate space 1807 nColumns = 2; 1808 s = stringPtr; 1809 while((s = strstr(s, sepstr))) { 1810 nColumns++; 1811 s += strlen(sepstr); 1812 } 1813 fieldNums = (int *)ckalloc(nColumns * sizeof(*fieldNums)); 1814 1815 field = 0; 1816 while(stringPtr) { 1817 if ( (tab = strstr(stringPtr, sepstr)) ) { 1818 save = *tab; 1819 *tab = 0; 1820 } 1821 1822 if(*noKeysPtr && field == 0 && strcmp(stringPtr, "_key") == 0) { 1823 *noKeysPtr = 0; 1824 keyCol = 0; 1825 } else { 1826 int num = -1; 1827 for(i = 0; ${table}_fields[i]; i++) { 1828 if(strcmp(stringPtr, ${table}_fields[i]) == 0) { 1829 num = i; 1830 break; 1831 } 1832 } 1833 1834 if(!nocomplain && num == -1) { 1835 Tcl_AppendResult (interp, "Unknown field \"", stringPtr, "\" in ${table}", (char *)NULL); 1836 ckfree((char *)fieldNums); 1837 return TCL_ERROR; 1838 } 1839 1840 if(num == ${table}_keyField) { 1841 if(keyCol>= 0) 1842 num = -1; 1843 else 1844 keyCol = num; 1845 } 1846 1847 fieldNums[field++] = num; 1848 } 1849 1850 if(tab) { 1851 *tab = save; 1852 tab += seplen; 1853 } 1854 1855 stringPtr = tab; 1856 } 1857 1858 *nFieldsPtr = field; 1859 *fieldNumsPtr = fieldNums; 1860 1861 return TCL_OK; 1862} 1863 1864int 1865${table}_export_tabsep (Tcl_Interp *interp, CTable *ctable, CONST char *channelName, int *fieldNums, int nFields, char *pattern, int noKeys, int withFieldNames, CONST char *sepstr, CONST char *term, int quoteType, CONST char *nullString) { 1866 Tcl_Channel channel; 1867 int mode; 1868 Tcl_DString dString; 1869 char *key; 1870 ctable_BaseRow *row; 1871 1872 if ((channel = Tcl_GetChannel (interp, channelName, &mode)) == NULL) { 1873 return TCL_ERROR; 1874 } 1875 1876 if ((mode & TCL_WRITABLE) == 0) { 1877 Tcl_AppendResult (interp, "channel \"", channelName, "\" not writable", (char *)NULL); 1878 return TCL_ERROR; 1879 } 1880 1881 Tcl_DStringInit (&dString); 1882 1883 if (withFieldNames) { 1884 1885 Tcl_DStringSetLength (&dString, 0); 1886 1887 ${table}_dstring_append_fieldnames (fieldNums, nFields, &dString, noKeys, sepstr); 1888 1889 if (Tcl_WriteChars (channel, Tcl_DStringValue (&dString), Tcl_DStringLength (&dString)) < 0) { 1890 Tcl_AppendResult (interp, "write error on channel \"", channelName, "\"", (char *)NULL); 1891 Tcl_DStringFree (&dString); 1892 return TCL_ERROR; 1893 } 1894 1895 } 1896 1897 CTABLE_LIST_FOREACH (ctable->ll_head, row, 0) { 1898 // if there's no pattern and no keys has been set, no need to 1899 // get the key 1900 if ((pattern == NULL) && noKeys) { 1901 key = NULL; 1902 } else { 1903 // key is needed and if there's a pattern, check it 1904 key = row->hashEntry.key; 1905 if ((pattern != NULL) && (!Tcl_StringCaseMatch (key, pattern, 1))) continue; 1906 } 1907 1908 Tcl_DStringSetLength (&dString, 0); 1909 1910 ${table}_dstring_append_get_tabsep (key, (struct ${table} *)row, fieldNums, nFields, &dString, noKeys, sepstr, quoteType, nullString); 1911 1912 if (Tcl_WriteChars (channel, Tcl_DStringValue (&dString), Tcl_DStringLength (&dString)) < 0) { 1913 Tcl_AppendResult (interp, "write error on channel \"", channelName, "\"", (char *)NULL); 1914 Tcl_DStringFree (&dString); 1915 return TCL_ERROR; 1916 } 1917 } 1918 1919 Tcl_DStringFree (&dString); 1920 1921 if(term) { 1922 if (Tcl_WriteChars (channel, term, strlen(term)) < 0 || Tcl_WriteChars(channel, "\n", 1) < 0) { 1923 Tcl_AppendResult (interp, "write error on channel \"", channelName, "\"", (char *)NULL); 1924 return TCL_ERROR; 1925 } 1926 } 1927 1928 return TCL_OK; 1929} 1930 1931int 1932${table}_set_from_tabsep (Tcl_Interp *interp, CTable *ctable, CONST char *stringPtr, int *fieldIds, int nFields, int keyColumn, CONST char *sepstr, CONST char *nullString, int quoteType, int dirty) { 1933 struct $table *row; 1934 const char *key; 1935 int indexCtl; 1936 int i; 1937 int col; 1938 Tcl_Obj *utilityObj = Tcl_NewObj (); 1939 char keyNumberString[32]; 1940 char blankString[] = { '\0' }; 1941 char *keyCopy = NULL; 1942 int seplen = strlen(sepstr); 1943 1944 if (keyColumn == -1) { 1945 sprintf (keyNumberString, "%d", ctable->autoRowNumber++); 1946 key = keyNumberString; 1947 } else { 1948 // find the beginning of the "keyColumn"th column in the string. 1949 for (key = stringPtr, i = 0; key && i < keyColumn; i++) { 1950 key = strstr(key, sepstr); 1951 if(key) key += seplen; 1952 } 1953 if (key) { 1954 int keyLength; 1955 CONST char *keyEnd = strstr(key, sepstr); 1956 if(keyEnd) { 1957 keyLength = keyEnd - key; 1958 } else { 1959 keyLength = strlen(key); 1960 } 1961 keyCopy = (char *) ckalloc(keyLength+1); 1962 if(quoteType) { 1963 ctable_copyDequoted(keyCopy, key, keyLength, quoteType); 1964 } else { 1965 strncpy(keyCopy, key, keyLength); 1966 keyCopy[keyLength] = '\0'; 1967 } 1968 key = keyCopy; 1969 } 1970 if(!key) { 1971 keyNumberString[0] = '\0'; 1972 key = keyNumberString; 1973 } 1974 } 1975 1976 row = ${table}_find_or_create (interp, ctable, key, &indexCtl); 1977 if(keyCopy) { 1978 // done with keyCopy so free it now before checking the row result. 1979 ckfree(keyCopy); 1980 key = keyCopy = NULL; 1981 } 1982 if(!row) { 1983 return TCL_ERROR; 1984 } 1985 1986 for (col = i = 0; col < nFields; i++) { 1987 const char *field; 1988 int fieldLength; 1989 1990 if(stringPtr) { 1991 field = stringPtr; 1992 stringPtr = strstr (stringPtr, sepstr); 1993 if(stringPtr) { 1994 fieldLength = (stringPtr - field); 1995 stringPtr += seplen; 1996 } else { 1997 fieldLength = -1; 1998 } 1999 } else { 2000 field = (nullString != NULL ? nullString : ""); 2001 fieldLength = -1; 2002 } 2003 2004 if(i == keyColumn) { 2005 continue; 2006 } 2007 if(fieldIds[col] == -1) { 2008 col++; 2009 continue; 2010 } 2011 2012 if(nullString == NULL || 2013 ${table}_nullable_fields[fieldIds[col]] == 0 || 2014 (field != nullString && 2015 field[0] != nullString[0] && 2016 strcmp(field, nullString) != 0) 2017 ) { 2018 Tcl_SetStringObj (utilityObj, field, fieldLength); 2019 2020 if (${table}_needs_quoting[fieldIds[col]] && quoteType && field != blankString) { 2021 fieldLength = ctable_dequoteString(Tcl_GetString(utilityObj), -1, quoteType); 2022 Tcl_SetObjLength(utilityObj, fieldLength); 2023 } 2024 2025 if (${table}_set (interp, ctable, utilityObj, row, fieldIds[col], indexCtl) == TCL_ERROR) { 2026 Tcl_DecrRefCount (utilityObj); 2027 return TCL_ERROR; 2028 } 2029 } 2030 2031 col++; 2032 } 2033 2034 if(dirty) { 2035 if (${table}_dirty (interp, ctable, row) == TCL_ERROR) { 2036 Tcl_DecrRefCount (utilityObj); 2037 return TCL_ERROR; 2038 } 2039 } 2040 2041 2042 if (indexCtl == CTABLE_INDEX_NEW) { 2043 if(${table}_index_defaults(interp, ctable, row) == TCL_ERROR) { 2044 Tcl_DecrRefCount (utilityObj); 2045 return TCL_ERROR; 2046 } 2047 } 2048 2049 Tcl_DecrRefCount (utilityObj); 2050 return TCL_OK; 2051} 2052 2053int 2054${table}_import_tabsep (Tcl_Interp *interp, CTable *ctable, CONST char *channelName, int *fieldNums, int nFields, CONST char *pattern, int noKeys, int withFieldNames, CONST char *sepstr, CONST char *skip, CONST char *term, int nocomplain, int withNulls, int quoteType, CONST char *nullString, int poll_interval, Tcl_Obj *poll_code, int poll_foreground, int dirty) { 2055 Tcl_Channel channel; 2056 int mode; 2057 Tcl_Obj *lineObj = NULL; 2058 char *stringPtr; // TODO: should probably be CONST and not modified. 2059 int recordNumber = 0; 2060 char keyNumberString[32]; 2061 int keyColumn; 2062 int i; 2063 int seplen = strlen(sepstr); 2064 int col; 2065 int *newFieldNums = NULL; 2066 int status = TCL_OK; 2067 int poll_counter = 0; 2068 2069 if ((channel = Tcl_GetChannel (interp, channelName, &mode)) == NULL) { 2070 return TCL_ERROR; 2071 } 2072 2073 if ((mode & TCL_READABLE) == 0) { 2074 Tcl_AppendResult (interp, "channel \"", channelName, "\" not readable", (char *)NULL); 2075 return TCL_ERROR; 2076 } 2077 2078 /* Don't allocate this until necessary */ 2079 lineObj = Tcl_NewObj(); 2080 2081 /* If no fields, read field names from first row */ 2082 if(withFieldNames) { 2083 do { 2084 Tcl_SetStringObj (lineObj, "", 0); 2085 if (Tcl_GetsObj (channel, lineObj) <= 0) { 2086 Tcl_DecrRefCount (lineObj); 2087 return TCL_OK; 2088 } 2089 stringPtr = Tcl_GetString (lineObj); 2090 } while(skip && Tcl_StringMatch(stringPtr, skip)); 2091 2092 if (${table}_get_fields_from_tabsep(interp, stringPtr, &nFields, &newFieldNums, &noKeys, sepstr, nocomplain) == TCL_ERROR) { 2093 status = TCL_ERROR; 2094 goto cleanup; 2095 } 2096 fieldNums = newFieldNums; 2097 } 2098 2099 if(noKeys) { 2100 keyColumn = -1; 2101 } else { 2102 keyColumn = 0; 2103 } 2104 2105 for(col = i = 0; i < nFields; i++) { 2106 if(fieldNums[i] == ${table}_keyField) { 2107 keyColumn = i; 2108 } else { 2109 if(col != i) { 2110 fieldNums[col] = fieldNums[i]; 2111 } 2112 col++; 2113 } 2114 } 2115 if(col != i) 2116 nFields--; 2117 2118//${table}_dumpFieldNums(fieldNums, nFields, "after key check"); 2119 if(withNulls && !nullString) { 2120 int nullLen; 2121 2122 nullString = Tcl_GetStringFromObj (${table}_NullValueObj, &nullLen); 2123 } 2124 2125 while (1) { 2126 2127 if (poll_interval) { 2128 if (++poll_counter >= poll_interval) { 2129 poll_counter = 0; 2130 if (poll_code) { 2131 int result = Tcl_EvalObjEx (interp, poll_code, 0); 2132 if (poll_foreground) { 2133 switch (result) { 2134 case TCL_ERROR: { 2135 Tcl_AppendResult (interp, " in -poll_code", (char *)NULL); 2136 status = TCL_ERROR; 2137 goto cleanup; 2138 } 2139 case TCL_BREAK: { 2140 status = TCL_OK; 2141 goto cleanup; 2142 } 2143 case TCL_RETURN: { 2144 status = TCL_RETURN; 2145 goto cleanup; 2146 } 2147 } 2148 } else if(result == TCL_ERROR) { 2149 Tcl_BackgroundError(interp); 2150 Tcl_ResetResult(interp); 2151 // Stop polling if the poll command fails 2152 poll_interval = 0; 2153 } 2154 } else { 2155 Tcl_DoOneEvent(0); 2156 } 2157 } 2158 } 2159 2160 do { 2161 Tcl_SetStringObj (lineObj, "", 0); 2162 if (Tcl_GetsObj (channel, lineObj) <= 0) { 2163 goto done; 2164 } 2165 2166 stringPtr = Tcl_GetString (lineObj); 2167 2168 if(term && term[0] && Tcl_StringCaseMatch (stringPtr, term, 1)) goto done; 2169 } while(skip && Tcl_StringMatch(stringPtr, skip)); 2170 2171 // if pattern exists, see if it does not match key and if so, skip 2172 if (pattern != NULL) { 2173 char *key; 2174 for (key = stringPtr, i = 0; key && i < keyColumn; i++) { 2175 key = strstr(key, sepstr); 2176 if(key) key += seplen; 2177 } 2178 if (key) { 2179 char *keyEnd = strstr(key, sepstr); 2180 if(keyEnd) { 2181 char save = *keyEnd; // modifying read-only strings is gross. 2182 *keyEnd = '\0'; 2183 if (!Tcl_StringCaseMatch (stringPtr, pattern, 1)) { 2184 *keyEnd = save; 2185 continue; 2186 } 2187 *keyEnd = save; 2188 } else { 2189 if (!Tcl_StringCaseMatch (stringPtr, pattern, 1)) continue; 2190 } 2191 2192 2193 } 2194 } 2195 2196 if (${table}_set_from_tabsep (interp, ctable, stringPtr, fieldNums, nFields, keyColumn, sepstr, nullString, quoteType, dirty) == TCL_ERROR) { 2197 char lineNumberString[32]; 2198 2199 sprintf (lineNumberString, "%d", recordNumber + 1); 2200 Tcl_AppendResult (interp, " while reading line ", lineNumberString, " of input", (char *)NULL); 2201 status = TCL_ERROR; 2202 goto cleanup; 2203 } 2204 2205 recordNumber++; 2206 } 2207done: 2208 2209 if(noKeys) 2210 { 2211 sprintf (keyNumberString, "%d", ctable->autoRowNumber - 1); 2212 Tcl_SetObjResult (interp, Tcl_NewStringObj (keyNumberString, -1)); 2213 } 2214 2215cleanup: 2216 if(lineObj) { 2217 Tcl_DecrRefCount (lineObj); 2218 } 2219 2220 if(newFieldNums) { 2221 ckfree((char *)newFieldNums); 2222 } 2223 2224 return status; 2225} 2226} 2227 2228# 2229# new_table - the proc that starts defining a table, really, a meta table, and 2230# also following it will be the definition of the structure itself. Clears 2231# all per-table variables in ::ctable:: 2232# 2233proc new_table {name} { 2234 variable table 2235 variable booleans 2236 variable nonBooleans 2237 variable fields 2238 variable fieldList 2239 variable keyField 2240 variable keyFieldName 2241 variable filters 2242 variable rawCode 2243 2244 set table $name 2245 2246 set booleans "" 2247 set nonBooleans "" 2248 unset -nocomplain fields 2249 set fieldList "" 2250 unset -nocomplain keyField 2251 unset -nocomplain keyFieldName 2252 unset -nocomplain filters 2253 unset -nocomplain rawCode 2254 2255 foreach var [info vars ::ctable::fields::*] { 2256 unset -nocomplain $var 2257 } 2258} 2259 2260# 2261# end_table - proc that declares the end of defining a table - currently does 2262# nothing 2263# 2264proc end_table {} { 2265} 2266 2267# 2268# filters 2269# 2270 2271# 2272# Defining word for a C filter in a CTable - mostly error checking 2273# 2274proc cfilter {filterName args} { 2275 variable filters 2276 variable reservedWords 2277 2278 if {[lsearch -exact $reservedWords $filterName] >= 0} { 2279 error "illegal filter name \"$filterName\" -- it's a reserved word" 2280 } 2281 2282 if {![is_legal $filterName]} { 2283 error "filter name \"$filterName\" must start with a letter and can only contain letters, numbers, and underscores" 2284 } 2285 2286 if {[llength $args] % 2 != 0} { 2287 error "number of values in filter '$filterName' definition arguments ('$args') must be even" 2288 } 2289 2290 if {[info exists filters($filterName)]} { 2291 error "duplicate definition of filter '$filterName'" 2292 } 2293 2294 array set filter $args 2295 2296 if {![info exists filter(code)]} { 2297 error "no code provided for cfilter '$filterName'" 2298 } 2299 2300 set filters($filterName) $args 2301} 2302 2303# 2304# Generate code to declare a memoized argument from a list of arguments 2305# 2306proc gen_decl_filter_arg {type name} { 2307 # Note spacing 2308 array set type_map { 2309 boolean "int " 2310 varstring "char *" 2311 fixedstring "char *" 2312 short "long " 2313 int "long " 2314 long "long " 2315 float "double " 2316 double "double " 2317 key "char *" 2318 } 2319 array set init_map { 2320 boolean " = 0" 2321 varstring " = NULL" 2322 fixedstring " = NULL" 2323 short " = 0" 2324 int " = 0" 2325 long " = 0" 2326 float " = 0.0" 2327 double " = 0.0" 2328 key " = NULL" 2329 } 2330 2331 if [info exists type_map($type)] { 2332 emit " static $type_map($type)$name$init_map($type);" 2333 } else { 2334 error "Type '$type' not supported in cfilter auto_generated code" 2335 } 2336} 2337 2338# 2339# Generate code to extract one argument from a list of arguments 2340# 2341proc gen_get_filter_arg {type name source} { 2342 if {"$type" == "varstring" || "$type" == "fixedstring" || "$type" == "key"} { 2343 emit " $name = Tcl_GetString ($source);" 2344 } elseif {"$type" == "float" || "$type" == "double"} { 2345 emit " if(Tcl_GetDoubleFromObj (interp, $source, &$name) != TCL_OK)" 2346 emit " return TCL_ERROR;" 2347 } elseif {"$type" == "boolean"} { 2348 emit " if(Tcl_GetBooleanFromObj (interp, $source, &$name) != TCL_OK)" 2349 emit " return TCL_ERROR;" 2350 } else { # it's an integer 2351 emit " if(Tcl_GetLongFromObj (interp, $source, &$name) != TCL_OK)" 2352 emit " return TCL_ERROR;" 2353 } 2354} 2355 2356# 2357# make sure an argument name is valid 2358# 2359proc validate_arg_name {name} { 2360 variable reservedWords 2361 2362 if {[lsearch -exact $reservedWords $name] >= 0} { 2363 error "illegal argument name \"$name\" -- it's a reserved word" 2364 } 2365 2366 if {![is_legal $name]} { 2367 error "argument name \"$name\" must start with a letter and can only contain letters, numbers, and underscores" 2368 } 2369} 2370 2371# 2372# Generate filter structs and procs 2373# 2374proc gen_filters {} { 2375 variable table 2376 variable filters 2377 variable leftCurly 2378 variable rightCurly 2379 2380 set filterList [lsort [array names filters]] 2381 2382 emit "#define [string toupper $table]_NFILTERS [llength $filterList]" 2383 2384 # Define filter functions 2385 foreach name $filterList { 2386 catch {array unset filter} 2387 array set filter $filters($name) 2388 emit "int ${table}_filter_${name} (Tcl_Interp *interp, struct CTable *ctable, ctable_BaseRow *vRow, Tcl_Obj *filter, int sequence)" 2389 emit "$leftCurly" 2390 emit " struct ${table} *row = (struct ${table}*)vRow;" 2391 if [info exists filter(args)] { 2392 if {[llength $filter(args)] == 3 && "[lindex $filter(args) 0]" == "list"} { 2393 2394 set listCount [lindex $filter(args) 1] 2395 set listName [lindex $filter(args) 2] 2396 validate_arg_name $listCount 2397 validate_arg_name $listName 2398 2399 emit " Tcl_Obj **$listName;" 2400 emit " int $listCount;\n" 2401 emit " if(Tcl_ListObjGetElements(interp, filter, &$listCount, &$listName) != TCL_OK)" 2402 emit " return TCL_ERROR;" 2403 2404 } else { 2405 2406 emit " static int lastSequence = 0;" 2407 foreach {type name} $filter(args) { 2408 validate_arg_name $name 2409 gen_decl_filter_arg $type $name 2410 } 2411 2412 emit "\n if (sequence != lastSequence) $leftCurly" 2413 emit " lastSequence = sequence;" 2414 2415 if {[llength $filter(args)] == 2} { 2416 gen_get_filter_arg [lindex $filter(args) 0] [lindex $filter(args) 1] filter 2417 } else { 2418 emit " Tcl_Obj **filterList;" 2419 emit " int filterCount;\n" 2420 emit " if (Tcl_ListObjGetElements(interp, filter, &filterCount, &filterList) != TCL_OK)" 2421 emit " return TCL_ERROR;\n" 2422 set argNames "" 2423 set nArguments [expr {[llength $filter(args)] / 2}] 2424 foreach {type name} $filter(args) { 2425 append argNames "$name, " 2426 } 2427 emit " if (filterCount != $nArguments) {" 2428 emit " Tcl_WrongNumArgs (interp, 0, NULL, \"filter requires $nArguments arguments: [string range $argNames 0 end-2]\");" 2429 emit " return TCL_ERROR;" 2430 emit " }\n" 2431 2432 set index 0 2433 foreach {type name} $filter(args) { 2434 gen_get_filter_arg $type $name "filterList\[$index]" 2435 incr index 2436 } 2437 } 2438 2439 emit " $rightCurly" 2440 } 2441 } 2442 2443 emit $filter(code) 2444 emit "$rightCurly\n" 2445 } 2446 2447 # Define filter lookup table 2448 emit "static CONST char *${table}_filterNames\[] = $leftCurly" 2449 foreach name $filterList { 2450 emit " \"$name\"," 2451 } 2452 emit " (char *) NULL" 2453 emit "$rightCurly;\n" 2454 2455 emit "static CONST filterFunction_t ${table}_filterFunctions\[] = $leftCurly" 2456 foreach name $filterList { 2457 emit " ${table}_filter_${name}," 2458 } 2459 emit " (filterFunction_t) NULL" 2460 emit "$rightCurly;\n" 2461} 2462 2463# 2464# Is this a legal field name. 2465# 2466# Special fields are automatically legal. 2467# 2468proc is_legal {fieldName} { 2469 variable specialFieldNames 2470 if {[lsearch $specialFieldNames $fieldName] != -1} { 2471 return 1 2472 } 2473 return [regexp {^[a-zA-Z][_a-zA-Z0-9]*$} $fieldName] 2474} 2475 2476# 2477# deffield - helper for defining fields. 2478# 2479proc deffield {fieldName argList {listName nonBooleans}} { 2480 variable fields 2481 variable fieldList 2482 variable $listName 2483 variable ctableTypes 2484 variable reservedWords 2485 2486 if {[lsearch -exact $reservedWords $fieldName] >= 0} { 2487 error "illegal field name \"$fieldName\" -- it's a reserved word" 2488 } 2489 2490 if {![is_legal $fieldName]} { 2491 error "field name \"$fieldName\" must start with a letter and can only contain letters, numbers, and underscores" 2492 } 2493 2494 if {[llength $argList] % 2 != 0} { 2495 error "number of values in field '$fieldName' definition arguments ('$argList') must be even" 2496 } 2497 2498 array set argHash $argList 2499 2500 # If "key" is still in the option list, then it's not on the right type 2501 if {[info exists argHash(key)] && $argHash(key)} { 2502 error "field '$fieldName' is the wrong type for a key" 2503 } 2504 2505 # If it's got a default value, then it must be notnull 2506 if {[info exists argHash(default)]} { 2507 if {[info exists argHash(notnull)]} { 2508 if {!$argHash(notnull)} { 2509 error "field '$fieldName' must not be null" 2510 } 2511 } else { 2512 set argHash(notnull) 1 2513 lappend argList notnull 1 2514 } 2515 } 2516 2517 set fields($fieldName) [linsert $argList 0 name $fieldName] 2518 array set ::ctable::fields::$fieldName $fields($fieldName) 2519 2520 lappend fieldList $fieldName 2521 lappend $listName $fieldName 2522} 2523 2524# 2525# boolean - define a boolean field 2526# 2527proc boolean {fieldName args} { 2528 deffield $fieldName [linsert $args 0 type boolean] booleans 2529} 2530 2531# 2532# fixedstring - define a fixed-length string field 2533# 2534proc fixedstring {fieldName length args} { 2535 array set field $args 2536 2537 # if it's defined notnull, it must have a default string 2538 if {[info exists field(notnull)] && $field(notnull)} { 2539 if {![info exists field(default)]} { 2540 error "fixedstring \"$fieldName\" is defined notnull but has no default string, which is required" 2541 } 2542 } 2543 2544 # if there's a default string, it must be the correct width 2545 if {[info exists field(default)]} { 2546 if {[string length $field(default)] != $length} { 2547 error "fixedstring \"$fieldName\" default string \"[cquote $field(default)]\" must match length \"$length\"" 2548 } 2549 } 2550 2551 deffield $fieldName [linsert $args 0 type fixedstring length $length needsQuoting 1] 2552} 2553 2554# 2555# varstring - define a variable-length string field 2556# 2557# If "key 1" is in the argument list, make it a "key" instead 2558# 2559proc varstring {fieldName args} { 2560 if {[set i [lsearch -exact $args "key"]] % 2 == 0} { 2561 incr i 2562 if {[lindex $args $i]} { 2563 return [eval [list key $fieldName] $args] 2564 } 2565 } 2566 deffield $fieldName [linsert $args 0 type varstring needsQuoting 1] 2567} 2568 2569# 2570# char - define a single character field -- this should probably just be 2571# fixedstring[1] but it's simpler. shrug. 2572# 2573proc char {fieldName args} { 2574 deffield $fieldName [linsert $args 0 type char needsQuoting 1] 2575} 2576 2577# 2578# mac - define a mac address field 2579# 2580proc mac {fieldName args} { 2581 deffield $fieldName [linsert $args 0 type mac] 2582} 2583 2584# 2585# short - define a short integer field 2586# 2587proc short {fieldName args} { 2588 deffield $fieldName [linsert $args 0 type short] 2589} 2590 2591# 2592# int - define an integer field 2593# 2594proc int {fieldName args} { 2595 deffield $fieldName [linsert $args 0 type int] 2596} 2597 2598# 2599# long - define a long integer field 2600# 2601proc long {fieldName args} { 2602 deffield $fieldName [linsert $args 0 type long] 2603} 2604 2605# 2606# wide - define a wide integer field -- should always be at least 64 bits 2607# 2608proc wide {fieldName args} { 2609 deffield $fieldName [linsert $args 0 type wide] 2610} 2611 2612# 2613# float - define a floating point field 2614# 2615proc float {fieldName args} { 2616 deffield $fieldName [linsert $args 0 type float] 2617} 2618 2619# 2620# double - define a double-precision floating point field 2621# 2622proc double {fieldName args} { 2623 deffield $fieldName [linsert $args 0 type double] 2624} 2625 2626# 2627# inet - define an IPv4 address field 2628# 2629proc inet {fieldName args} { 2630 deffield $fieldName [linsert $args 0 type inet] 2631} 2632 2633# 2634# tclobj - define an straight-through Tcl_Obj 2635# 2636proc tclobj {fieldName args} { 2637 deffield $fieldName [linsert $args 0 type tclobj needsQuoting 1] 2638} 2639 2640# 2641# key - define a pseudofield for the key 2642# 2643proc key {name args} { 2644 # Sanitize arguments 2645 if {[set i [lsearch -exact $args key]] % 2 == 0} { 2646 set args [lreplace $args $i [expr {$i + 1}]] 2647 } 2648 2649 # Only allow one key field 2650 if [info exists ::ctable::keyFieldName] { 2651 # But only complain if it's not an internal "special" field 2652 if {[lsearch $::ctable::specialFieldNames $name] == -1} { 2653 error "Duplicate key field" 2654 } 2655 return 2656 } 2657 2658 deffield $name [linsert $args 0 type key needsQuoting 1 notnull 1] 2659 set ::ctable::keyField [lsearch $::ctable::fieldList $name] 2660 if {$::ctable::keyField == -1} { 2661 unset ::ctable::keyField 2662 } else { 2663 set ::ctable::keyFieldName $name 2664 } 2665} 2666 2667# 2668# putfield - write out a field definition when emitting a C struct 2669# 2670proc putfield {type fieldName {comment ""}} { 2671 if {[string index $fieldName 0] != "*"} { 2672 set fieldName " $fieldName" 2673 } 2674 2675 if {$comment != ""} { 2676 set comment " /* $comment */" 2677 } 2678 emit [format " %-20s %s;%s" $type $fieldName $comment] 2679} 2680 2681# 2682# ctable_type_to_enum - return a type mapped to the name we use when 2683# creating or referencing an enumerated list of ctable types. 2684# 2685proc ctable_type_to_enum {type} { 2686 return "CTABLE_TYPE_[string toupper $type]" 2687} 2688 2689# 2690# gen_ctable_type_stuff - # generate an array of char pointers to the type names 2691# 2692proc gen_ctable_type_stuff {} { 2693 variable ctableTypes 2694 variable leftCurly 2695 variable rightCurly 2696 2697 emit "static CONST char *ctableTypes\[\] = $leftCurly" 2698 foreach type $ctableTypes { 2699 emit " \"$type\"," 2700 } 2701 emit " (char *) NULL" 2702 emit "$rightCurly;" 2703 emit "" 2704} 2705 2706# 2707# gen_defaults_subr - gen code to set a row to default values 2708# 2709proc gen_defaults_subr {struct} { 2710 variable table 2711 variable fields 2712 variable withSharedTables 2713 variable withDirty 2714 variable fieldList 2715 variable leftCurly 2716 variable rightCurly 2717 2718 set baseCopy ${struct}_basecopy 2719 2720 emit "void ${struct}_init(CTable *ctable, struct $struct *row) $leftCurly" 2721 emit " static int firstPass = 1;" 2722 emit " static struct $struct $baseCopy;" 2723 emit "" 2724 emit " if (firstPass) $leftCurly" 2725 emit " int i;" 2726 emit " firstPass = 0;" 2727 emit "" 2728 emit " $baseCopy.hashEntry.key = ctable->nullKeyValue;" 2729 2730 if {$withSharedTables} { 2731 emit " $baseCopy._row_cycle = LOST_HORIZON;" 2732 } 2733 2734 emit "" 2735 emit " for(i = 0; i < [string toupper $table]_NLINKED_LISTS; i++) $leftCurly" 2736 emit " $baseCopy._ll_nodes\[i].next = NULL;" 2737 emit " $baseCopy._ll_nodes\[i].prev = NULL;" 2738 emit " $baseCopy._ll_nodes\[i].head = NULL;" 2739 emit " $rightCurly" 2740 emit "" 2741 2742 set fieldNum 0 2743 foreach fieldName $fieldList { 2744 upvar ::ctable::fields::$fieldName field 2745 2746 switch $field(type) { 2747 key { 2748 # No work to do 2749 } 2750 2751 varstring { 2752 set initLength 0 2753 if {[info exists field(default)]} { 2754 set initValue "${table}_defaultStrings\[$fieldNum]" 2755 set initLength [string length $field(default)] 2756 } elseif {[info exists field(notnull)] && $field(notnull)} { 2757 set initValue "${table}_defaultStrings\[$fieldNum]" 2758 } else { 2759 set initValue "NULL" 2760 } 2761 emit " $baseCopy.$fieldName = (char *) $initValue;" 2762 emit " $baseCopy._${fieldName}Length = $initLength;" 2763 emit " $baseCopy._${fieldName}AllocatedLength = 0;" 2764 2765 if {![info exists field(notnull)] || !$field(notnull)} { 2766 if {[info exists field(default)]} { 2767 emit " $baseCopy._${fieldName}IsNull = 0;" 2768 } else { 2769 emit " $baseCopy._${fieldName}IsNull = 1;" 2770 } 2771 } 2772 } 2773 2774 fixedstring { 2775 if {[info exists field(default)]} { 2776 emit " strncpy ($baseCopy.$fieldName, \"[cquote $field(default)]\", $field(length));" 2777 if {![info exists field(notnull)] || !$field(notnull)} { 2778 emit " $baseCopy._${fieldName}IsNull = 0;" 2779 } 2780 } else { 2781 if {![info exists field(notnull)] || !$field(notnull)} { 2782 emit " $baseCopy._${fieldName}IsNull = 1;" 2783 } 2784 } 2785 } 2786 2787 mac { 2788 if {[info exists field(default)]} { 2789 emit " $baseCopy.$fieldName = *ether_aton (\"$field(default)\");" 2790 if {![info exists field(notnull)] || !$field(notnull)} { 2791 emit " $baseCopy._${fieldName}IsNull = 0;" 2792 } 2793 } else { 2794 if {![info exists field(notnull)] || !$field(notnull)} { 2795 emit " $baseCopy._${fieldName}IsNull = 1;" 2796 } 2797 } 2798 } 2799 2800 inet { 2801 if {[info exists field(default)]} { 2802 emit " inet_aton (\"$field(default)\", &$baseCopy.$fieldName);" 2803 if {![info exists field(notnull)] || !$field(notnull)} { 2804 emit " $baseCopy._${fieldName}IsNull = 0;" 2805 } 2806 } else { 2807 if {![info exists field(notnull)] || !$field(notnull)} { 2808 emit " $baseCopy._${fieldName}IsNull = 1;" 2809 } 2810 } 2811 } 2812 2813 char { 2814 if {[info exists field(default)]} { 2815 emit " $baseCopy.$fieldName = '[cquote [string index $field(default) 0] {'}]';" 2816 if {![info exists field(notnull)] || !$field(notnull)} { 2817 emit " $baseCopy._${fieldName}IsNull = 0;" 2818 } 2819 } else { 2820 if {![info exists field(notnull)] || !$field(notnull)} { 2821 emit " $baseCopy._${fieldName}IsNull = 1;" 2822 } 2823 } 2824 } 2825 2826 tclobj { 2827 emit " $baseCopy.$fieldName = (Tcl_Obj *) NULL;" 2828 if {![info exists field(notnull)] || !$field(notnull)} { 2829 emit " $baseCopy._${fieldName}IsNull = 1;" 2830 } 2831 } 2832 2833 default { 2834 if {[info exists field(default)]} { 2835 emit " $baseCopy.$fieldName = $field(default);" 2836 if {![info exists field(notnull)] || !$field(notnull)} { 2837 emit " $baseCopy._${fieldName}IsNull = 0;" 2838 } 2839 } else { 2840 if {![info exists field(notnull)] || !$field(notnull)} { 2841 emit " $baseCopy._${fieldName}IsNull = 1;" 2842 } 2843 } 2844 } 2845 } 2846 incr fieldNum; 2847 } 2848 2849 emit " $rightCurly" 2850 emit "" 2851 emit " *row = $baseCopy;" 2852 2853 # Poke in shared default strings where needed. 2854 if {$withSharedTables} { 2855 set fieldNum 0 2856 foreach fieldName $fieldList { 2857 upvar ::ctable::fields::$fieldName field 2858 2859 if {$field(type) == "varstring"} { 2860 if {[info exists field(default)] || ([info exists field(notnull)] && $field(notnull))} { 2861 emit " row->$fieldName = (char *) ctable->defaultStrings\[$fieldNum];" 2862 } 2863 } 2864 incr fieldNum 2865 } 2866 } 2867 2868 emit "$rightCurly" 2869 emit "" 2870 2871 emit "int ${struct}_index_defaults(Tcl_Interp *interp, CTable *ctable, struct $struct *row) $leftCurly" 2872 2873if 0 { 2874emit "printf(\"${struct}_index_defaults(...);\\n\");" 2875} 2876 set fieldnum 0 ; # postincremented 0 .. fields 2877 set listnum 0 ; # preincrementd 1 .. lists+1 2878 foreach fieldName $fieldList { 2879 upvar ::ctable::fields::$fieldName field 2880 2881 # Index everything null or otherwise 2882 if {[info exists field(indexed)] && $field(indexed)} { 2883 incr listnum 2884 if {[info exists field(default)]} { 2885 set def $field(default) 2886 } else { 2887 set def "" 2888 } 2889 emit "// Field \"$fieldName\" ($fieldnum) index $listnum:" 2890if 0 { 2891emit "printf(\"ctable->skipLists\[$fieldnum] == %08lx\\n\", (long)ctable->skipLists\[$fieldnum]);" 2892emit "printf(\"row->_ll_nodes\[$listnum].head == %08lx\\n\", (long)row->_ll_nodes\[$listnum].head);" 2893} 2894 2895 emit " if(ctable->skipLists\[$fieldnum] && row->_ll_nodes\[$listnum].prev == NULL) $leftCurly" 2896if 0 { 2897emit "fprintf(stderr, \"row->_ll_nodes\[$listnum] = { 0x%lx 0x%lx 0x%lx }\"," 2898emit " (long)row->_ll_nodes\[$listnum].head," 2899emit " (long)row->_ll_nodes\[$listnum].prev," 2900emit " (long)row->_ll_nodes\[$listnum].next);" 2901emit "fprintf(stderr, \"Inserting $fieldName into new row for $struct\\n\");" 2902} 2903 emit " if (ctable_InsertIntoIndex (interp, ctable, row, $fieldnum) == TCL_ERROR)" 2904 emit " return TCL_ERROR;" 2905 emit " $rightCurly" 2906 } else { 2907 emit "// Field \"$fieldName\" ($fieldnum) not indexed" 2908 } 2909 incr fieldnum 2910 } 2911 2912 emit " return TCL_OK;" 2913 2914 emit "$rightCurly" 2915 emit "" 2916} 2917 2918variable deleteRowHelperSource { 2919void ${table}_deleteKey(CTable *ctable, struct ${table} *row, int free_shared) 2920{ 2921 if(row->hashEntry.key == ctable->nullKeyValue) 2922 return; 2923 2924#ifdef WITH_SHARED_TABLES 2925 if(ctable->share_type == CTABLE_SHARED_MASTER) { 2926 if(free_shared) 2927 shmfree(ctable->share, (void *)row->hashEntry.key); 2928 } else 2929#endif 2930 ckfree(row->hashEntry.key); 2931 row->hashEntry.key = ctable->nullKeyValue; 2932} 2933 2934void ${table}_deleteHashEntry(CTable *ctable, struct ${table} *row) 2935{ 2936#ifdef WITH_SHARED_TABLES 2937 if(row->hashEntry.key != ctable->nullKeyValue && ctable->share_type == CTABLE_SHARED_MASTER) { 2938 shmfree(ctable->share, (void *)row->hashEntry.key); 2939 row->hashEntry.key = ctable->nullKeyValue; 2940 } 2941#endif 2942 ctable_DeleteHashEntry (ctable->keyTablePtr, &row->hashEntry, ctable->nullKeyValue); 2943} 2944} 2945 2946# 2947# gen_delete_subr - gen code to delete (free) a row 2948# 2949proc gen_delete_subr {subr struct} { 2950 variable table 2951 variable fields 2952 variable fieldList 2953 variable leftCurly 2954 variable rightCurly 2955 variable withSharedTables 2956 variable deleteRowHelperSource 2957 2958 emit [string range [subst -nobackslashes -nocommands $deleteRowHelperSource] 1 end-1] 2959 2960 emit "void ${subr}(CTable *ctable, ctable_BaseRow *vRow, int indexCtl) {" 2961 emit " struct $struct *row = (struct $struct *)vRow;" 2962 if {$withSharedTables} { 2963 emit " // 'final' means 'shared memory will be deleted anyway, just zero out'" 2964 emit " int final = indexCtl == CTABLE_INDEX_DESTROY_SHARED;" 2965 emit " int is_master = ctable->share_type == CTABLE_SHARED_MASTER;" 2966 emit " int is_shared = ctable->share_type != CTABLE_SHARED_NONE;" 2967 } 2968 emit "" 2969 emit " switch (indexCtl) $leftCurly" 2970 emit " case CTABLE_INDEX_NORMAL:" 2971 emit " // If there's an index, AND we're not deleting all indices" 2972 emit " ctable_RemoveFromAllIndexes (ctable, row);" 2973 if {$withSharedTables} { 2974 emit " ${table}_deleteKey(ctable, row, TRUE);" 2975 } 2976 emit " ctable_DeleteHashEntry (ctable->keyTablePtr, &row->hashEntry, ctable->nullKeyValue);" 2977 emit " break;" 2978 emit " case CTABLE_INDEX_FASTDELETE: // Key has already been deleted" 2979 emit " break;" 2980 emit " case CTABLE_INDEX_DESTROY_SHARED: // Row is in dead pool" 2981 emit " case CTABLE_INDEX_PRIVATE: // Key is never indexed" 2982 emit " ${table}_deleteKey(ctable, row, FALSE);" 2983 emit " break;" 2984 emit " default: // Not in hash, shared (should not happen)" 2985 emit " ${table}_deleteKey(ctable, row, TRUE);" 2986 emit " break;" 2987 emit " $rightCurly;" 2988 2989 emit "" 2990 2991 foreach fieldName $fieldList { 2992 upvar ::ctable::fields::$fieldName field 2993 2994 switch $field(type) { 2995 varstring { 2996 if {$withSharedTables} { 2997 emit " if (row->_${fieldName}AllocatedLength > 0) {" 2998 emit " if(!is_shared || indexCtl == CTABLE_INDEX_PRIVATE)" 2999 emit " ckfree((char *)row->$fieldName);" 3000 emit " else if(is_master && !final)" 3001 emit " shmfree(ctable->share, (char *)row->$fieldName);" 3002 emit " }" 3003 } else { 3004 emit " if (row->_${fieldName}AllocatedLength > 0) ckfree((char *)row->$fieldName);" 3005 } 3006 } 3007 } 3008 } 3009 if {$withSharedTables} { 3010 emit " if(!is_shared || indexCtl == CTABLE_INDEX_PRIVATE)" 3011 emit " ckfree((char *)row);" 3012 emit " else if(is_master && !final)" 3013 emit " shmfree(ctable->share, (char *)row);" 3014 } else { 3015 emit " ckfree((char *)row);" 3016 } 3017 3018 emit "}" 3019 emit "" 3020} 3021 3022 3023variable isNullSubrSource { 3024int ${table}_obj_is_null(Tcl_Obj *obj) { 3025 char *objString; 3026 int objStringLength; 3027 3028 objString = Tcl_GetStringFromObj (obj, &objStringLength); 3029 3030 if (objStringLength == ${table}_NullValueSize) { 3031 if (objStringLength == 0) { 3032 // strings are both zero length, a match on empty strings 3033 return 1; 3034 } 3035 3036 return (strncmp (${table}_NullValueString, objString, ${table}_NullValueSize) == 0); 3037 } 3038 3039 // string lengths didn't match so strings don't match 3040 return 0; 3041} 3042 3043} 3044 3045# 3046# gen_is_null_subr - gen code to determine if an object contains the null value 3047# 3048proc gen_obj_is_null_subr {} { 3049 variable table 3050 variable isNullSubrSource 3051 3052 emit [string range [subst -nobackslashes -nocommands $isNullSubrSource] 1 end-1] 3053} 3054 3055# 3056# sanity_check - prior to generating everything, make sure what we're being 3057# asked to do is reasonable 3058# 3059proc sanity_check {} { 3060 variable fieldList 3061 variable table 3062 3063 if {[llength $fieldList] == 0} { 3064 error "no fields defined in table \"$table\" -- at least one field must be defined in a table" 3065 } 3066} 3067 3068# 3069# determine_how_many_linked_lists - count up the number of indexed 3070# nodes and any other stuff we want linked lists in the row for 3071# 3072# currently one defined for every row for a master linked list and one 3073# defined for each field that is defined indexed and not unique 3074# for use with skip lists to have indexes on fields of rows that have 3075# duplicate entries like, for instance, latitude and/or longitude. 3076# 3077proc determine_how_many_linked_lists_and_gen_field_index_table {} { 3078 variable nonBooleans 3079 variable fields 3080 variable fieldList 3081 variable booleans 3082 variable table 3083 variable leftCurly 3084 variable rightCurly 3085 3086 set result "int ${table}_index_numbers\[\] = $leftCurly" 3087 set nLinkedLists 1 3088 foreach fieldName $fieldList { 3089 upvar ::ctable::fields::$fieldName field 3090 3091 # if the "indexed" field doesn't exist or is 0, skip it 3092 if {![info exists field(indexed)] || !$field(indexed)} { 3093 append result "\n -1," 3094 continue 3095 } 3096 3097 # we're going to use linked lists even if it's not unique 3098if 0 { 3099 # if the "unique" field doesn't exist or isn't set to 0 3100 if {![info exists field(unique)] || $field(unique)} { 3101 append result "\n -1," 3102 continue 3103 } 3104} 3105 3106 # if we got here it's indexed and not unique, 3107 # i.e. field args include "indexed 1 unique 0" 3108 # generate them a list entry 3109 3110 append result "\n[format "%6d" $nLinkedLists]," 3111 3112 incr nLinkedLists 3113 } 3114 3115 emit "[string range $result 0 end-1]\n$rightCurly;" 3116 3117 return $nLinkedLists 3118} 3119 3120# 3121# gen_struct - gen the C structure of the table being defined 3122# 3123proc gen_struct {} { 3124 variable table 3125 variable booleans 3126 variable nonBooleans 3127 variable fields 3128 variable fieldList 3129 variable leftCurly 3130 variable rightCurly 3131 3132 set nLinkedLists [determine_how_many_linked_lists_and_gen_field_index_table] 3133 set NLINKED_LISTS [string toupper $table]_NLINKED_LISTS 3134 emit "#define $NLINKED_LISTS $nLinkedLists" 3135 emit "" 3136 3137 emit "struct $table : public ctable_BaseRow $leftCurly" 3138 3139 # this array must be first, since it will overlap with the empty list defined at the end of ctable_BaseRow. 3140 putfield "ctable_LinkedListNode" "_ll_nodes\[$NLINKED_LISTS\]" 3141 3142 foreach fieldName $nonBooleans { 3143 upvar ::ctable::fields::$fieldName field 3144 3145 switch $field(type) { 3146 varstring { 3147 putfield char "*$field(name)" 3148 putfield int "_$field(name)Length" 3149 putfield int "_$field(name)AllocatedLength" 3150 } 3151 3152 fixedstring { 3153 putfield char "$field(name)\[$field(length)]" 3154 } 3155 3156 wide { 3157 putfield "Tcl_WideInt" $field(name) 3158 } 3159 3160 mac { 3161 putfield "struct ether_addr" $field(name) 3162 } 3163 3164 inet { 3165 putfield "struct in_addr" $field(name) 3166 } 3167 3168 tclobj { 3169 putfield "struct Tcl_Obj" "*$field(name)" 3170 } 3171 3172 key { 3173 # Do nothing, it's in the hashEntry 3174 } 3175 3176 default { 3177 putfield $field(type) $field(name) 3178 } 3179 } 3180 } 3181 3182 foreach fieldName $booleans { 3183 putfield "unsigned int" "$fieldName:1" 3184 } 3185 3186 foreach fieldName $fieldList { 3187 upvar ::ctable::fields::$fieldName field 3188 3189 if {![info exists field(notnull)] || !$field(notnull)} { 3190 putfield "unsigned int" _${fieldName}IsNull:1 3191 } 3192 } 3193 3194 emit "$rightCurly;" 3195 emit "" 3196} 3197 3198# 3199# emit_set_num_field - emit code to set a numeric field 3200# 3201proc emit_set_num_field {fieldName type} { 3202 variable numberSetSource 3203 variable table 3204 variable withSharedTables 3205 3206 set typeText $type 3207 3208 switch $type { 3209 short { 3210 set newObjCmd Tcl_NewIntObj 3211 set getObjCmd Tcl_GetIntFromObj 3212 set typeText "int" 3213 } 3214 3215 int { 3216 set newObjCmd Tcl_NewIntObj 3217 set getObjCmd Tcl_GetIntFromObj 3218 } 3219 3220 long { 3221 set newObjCmd Tcl_NewLongObj 3222 set getObjCmd Tcl_GetLongFromObj 3223 3224 } 3225 3226 wide { 3227 set type "Tcl_WideInt" 3228 set newObjCmd Tcl_NewWideIntObj 3229 set getObjCmd Tcl_GetWideIntFromObj 3230 set typeText "Tcl_WideInt" 3231 } 3232 3233 float { 3234 set newObjCmd Tcl_NewDoubleObj 3235 set getObjCmd Tcl_GetDoubleFromObj 3236 set typeText "double" 3237 } 3238 3239 double { 3240 set newObjCmd Tcl_NewDoubleObj 3241 set getObjCmd Tcl_GetDoubleFromObj 3242 } 3243 3244 default { 3245 error "unknown numeric field type: $type" 3246 } 3247 } 3248 3249 set optname [field_to_enum $fieldName] 3250 3251 emit [string range [subst $numberSetSource] 1 end-1] 3252} 3253 3254# 3255# emit_set_standard_field - emit code to set a field that has a 3256# "set source" string to go with it and gets managed in a standard 3257# way 3258# 3259proc emit_set_standard_field {fieldName setSourceVarName} { 3260 variable $setSourceVarName 3261 variable table 3262 3263 set optname [field_to_enum $fieldName] 3264 emit [string range [subst [set $setSourceVarName]] 1 end-1] 3265} 3266 3267# 3268# emit_set_varstring_field - emit code to set a varstring field 3269# 3270proc emit_set_varstring_field {table fieldName} { 3271 variable varstringSetSource 3272 3273 set optname [field_to_enum $fieldName] 3274 3275 emit [string range [subst $varstringSetSource] 1 end-1] 3276} 3277 3278# 3279# emit_set_fixedstring_field - emit code to set a fixedstring field 3280# 3281proc emit_set_fixedstring_field {fieldName length} { 3282 variable fixedstringSetSource 3283 variable table 3284 3285 upvar ::ctable::fields::$fieldName field 3286 3287 if {[info exists field(default)]} { 3288 set default $field(default) 3289 } else { 3290 set default "" 3291 } 3292 3293 set optname [field_to_enum $fieldName] 3294 3295 emit [string range [subst $fixedstringSetSource] 1 end-1] 3296} 3297 3298variable fieldIncrSource { 3299int 3300${table}_incr (Tcl_Interp *interp, CTable *ctable, Tcl_Obj *obj, struct $table *row, int field, int indexCtl) $leftCurly 3301 3302 switch ((enum ${table}_fields) field) $leftCurly 3303} 3304 3305variable numberIncrNullCheckSource { 3306 if (row->_${fieldName}IsNull) { 3307 // incr of a null field, default to 0 3308 if ((indexCtl == CTABLE_INDEX_NORMAL) && ctable->skipLists[field] != NULL) { 3309 ctable_RemoveFromIndex (ctable, row, field); 3310 } 3311 row->_${fieldName}IsNull = 0; 3312 row->$fieldName = incrAmount; 3313 3314 if ((indexCtl != CTABLE_INDEX_PRIVATE) && (ctable->skipLists[field] != NULL)) { 3315 if (ctable_InsertIntoIndex (interp, ctable, row, field) == TCL_ERROR) { 3316 return TCL_ERROR; 3317 } 3318 } 3319 break; 3320 } 3321} 3322 3323# 3324# gen_number_incr_null_check_code - return code to check for null stuff 3325# inside incr code, if the field doesn't prevent it by having notnull set, 3326# in which case return nothing. 3327# 3328proc gen_number_incr_null_check_code {table fieldName} { 3329 variable numberIncrNullCheckSource 3330 upvar ::ctable::fields::$fieldName field 3331 3332 if {[info exists field(notnull)] && $field(notnull)} { 3333 return "" 3334 } else { 3335 return [string range [subst -nobackslashes -nocommands $numberIncrNullCheckSource] 1 end-1] 3336 } 3337} 3338 3339# 3340# gen_set_notnull_if_notnull - if the field has not been defined "not null", 3341# return code to set that it isn't null 3342# 3343proc gen_set_notnull_if_notnull {table fieldName} { 3344 upvar ::ctable::fields::$fieldName field 3345 3346 if {[info exists field(notnull)] && $field(notnull)} { 3347 return "" 3348 } else { 3349 return "row->_${fieldName}IsNull = 0;" 3350 } 3351} 3352 3353# 3354# numberIncrSource - code we run subst over to generate a set of a standard 3355# number such as an integer, long, double, and wide integer. (We have to 3356# handle shorts and floats specially due to type coercion requirements.) 3357# 3358variable numberIncrSource { 3359 case $optname: { 3360 int incrAmount; 3361 3362 if (Tcl_GetIntFromObj (interp, obj, &incrAmount) == TCL_ERROR) { 3363 Tcl_AppendResult (interp, " while converting $fieldName increment amount", (char *)NULL); 3364 return TCL_ERROR; 3365 } 3366[gen_number_incr_null_check_code $table $fieldName] 3367 3368 if ((indexCtl == CTABLE_INDEX_NORMAL) && ctable->skipLists\[field] != NULL) { 3369 ctable_RemoveFromIndex (ctable, row, field); 3370 } 3371 3372 row->$fieldName += incrAmount; 3373[gen_set_notnull_if_notnull $table $fieldName] 3374 if ((indexCtl != CTABLE_INDEX_PRIVATE) && (ctable->skipLists\[field] != NULL)) { 3375 if (ctable_InsertIntoIndex (interp, ctable, row, field) == TCL_ERROR) { 3376 return TCL_ERROR; 3377 } 3378 } 3379 break; 3380 } 3381} 3382 3383variable illegalIncrSource { 3384 case $optname: { 3385 Tcl_ResetResult (interp); 3386 Tcl_AppendResult (interp, "can't incr non-numeric field '$fieldName'", (char *)NULL); 3387 return TCL_ERROR; 3388 } 3389} 3390 3391variable incrFieldObjSource { 3392int 3393${table}_incr_fieldobj (Tcl_Interp *interp, CTable *ctable, Tcl_Obj *obj, struct $table *row, Tcl_Obj *fieldObj, int indexCtl) 3394{ 3395 int field; 3396 3397 if (Tcl_GetIndexFromObj (interp, fieldObj, ${table}_fields, "field", TCL_EXACT, &field) != TCL_OK) { 3398 return TCL_ERROR; 3399 } 3400 3401 return ${table}_incr (interp, ctable, obj, row, field, indexCtl); 3402} 3403} 3404 3405# 3406# emit_incr_num_field - emit code to incr a numeric field 3407# 3408proc emit_incr_num_field {fieldName} { 3409 variable numberIncrSource 3410 variable table 3411 3412 set optname [field_to_enum $fieldName] 3413 3414 emit [string range [subst $numberIncrSource] 1 end-1] 3415} 3416 3417# 3418# emit_incr_illegal_field - we run this to generate code that will cause 3419# an error on attempts to incr the field that's being processed -- for 3420# when incr is not a reasonable thing 3421# 3422proc emit_incr_illegal_field {fieldName} { 3423 variable illegalIncrSource 3424 3425 set optname [field_to_enum $fieldName] 3426 emit [string range [subst -nobackslashes -nocommands $illegalIncrSource] 1 end-1] 3427} 3428 3429# 3430# gen_incrs - emit code to incr all of the incr'able fields of the table being 3431# defined 3432# 3433proc gen_incrs {} { 3434 variable table 3435 variable booleans 3436 variable fields 3437 variable fieldList 3438 variable leftCurly 3439 variable rightCurly 3440 3441 foreach fieldName $fieldList { 3442 upvar ::ctable::fields::$fieldName field 3443 3444 switch $field(type) { 3445 int { 3446 emit_incr_num_field $fieldName 3447 } 3448 3449 long { 3450 emit_incr_num_field $fieldName 3451 } 3452 3453 wide { 3454 emit_incr_num_field $fieldName 3455 } 3456 3457 double { 3458 emit_incr_num_field $fieldName 3459 } 3460 3461 short { 3462 emit_incr_num_field $fieldName 3463 } 3464 3465 float { 3466 emit_incr_num_field $fieldName 3467 } 3468 3469 default { 3470 emit_incr_illegal_field $fieldName 3471 } 3472 } 3473 } 3474} 3475 3476# 3477# gen_incr_function - create a *_incr routine that takes a pointer to the 3478# tcl interp, an object, a pointer to a table row and a field number, 3479# and incrs that field in that row by the the value extracted from the obj 3480# 3481proc gen_incr_function {table} { 3482 variable fieldIncrSource 3483 variable incrFieldObjSource 3484 variable leftCurly 3485 variable rightCurly 3486 3487 emit [string range [subst -nobackslashes -nocommands $fieldIncrSource] 1 end-1] 3488 3489 gen_incrs 3490 3491 emit " $rightCurly" 3492 emit " return TCL_OK;" 3493 emit "$rightCurly" 3494 3495 emit [string range [subst -nobackslashes -nocommands $incrFieldObjSource] 1 end-1] 3496} 3497 3498# 3499# gen_sets - emit code to set all of the fields of the table being defined 3500# 3501proc gen_sets {} { 3502 variable table 3503 variable booleans 3504 variable fields 3505 variable fieldList 3506 variable leftCurly 3507 variable rightCurly 3508 3509 foreach fieldName $fieldList { 3510 upvar ::ctable::fields::$fieldName field 3511 3512 switch $field(type) { 3513 key { 3514 emit_set_standard_field $fieldName keySetSource 3515 } 3516 3517 int { 3518 emit_set_num_field $fieldName int 3519 } 3520 3521 long { 3522 emit_set_num_field $fieldName long 3523 } 3524 3525 wide { 3526 emit_set_num_field $fieldName wide 3527 } 3528 3529 double { 3530 emit_set_num_field $fieldName double 3531 } 3532 3533 short { 3534 emit_set_num_field $fieldName int 3535 } 3536 3537 float { 3538 emit_set_num_field $fieldName float 3539 } 3540 3541 fixedstring { 3542 emit_set_fixedstring_field $fieldName $field(length) 3543 } 3544 3545 varstring { 3546 emit_set_varstring_field $table $fieldName 3547 } 3548 3549 boolean { 3550 emit_set_standard_field $fieldName boolSetSource 3551 } 3552 3553 char { 3554 emit_set_standard_field $fieldName charSetSource 3555 } 3556 3557 inet { 3558 emit_set_standard_field $fieldName inetSetSource 3559 } 3560 3561 mac { 3562 emit_set_standard_field $fieldName macSetSource 3563 } 3564 3565 tclobj { 3566 emit_set_standard_field $fieldName tclobjSetSource 3567 } 3568 3569 default { 3570 error "attempt to emit set field of unknown type $field(type)" 3571 } 3572 } 3573 } 3574} 3575 3576# 3577# setNullSource - code that gets substituted for nonnull fields for set_null 3578# 3579variable setNullSource { 3580 case $optname: { 3581 ${table} *row = (${table} *)vRow; 3582 if (row->_${myField}IsNull) { 3583 break; 3584 } 3585 3586 if ((indexCtl == CTABLE_INDEX_NORMAL) && (ctable->skipLists[field] != NULL)) { 3587 ctable_RemoveFromIndex (ctable, row, field); 3588 } 3589 row->_${myField}IsNull = 1; 3590 if ((indexCtl != CTABLE_INDEX_PRIVATE) && (ctable->skipLists[field] != NULL)) { 3591 if (ctable_InsertIntoIndex (interp, ctable, row, field) == TCL_ERROR) { 3592 return TCL_ERROR; 3593 } 3594 } 3595 break; 3596 } 3597} 3598 3599variable setNullNotNullSource { 3600 case $optname: 3601 Tcl_AppendResult (interp, "can't set non-null field \"${myField}\" to be null", (char *)NULL); 3602 return TCL_ERROR; 3603} 3604 3605# 3606# gen_set_null_function - emit C routine to set a specific field to null 3607# in a given table and row 3608# 3609proc gen_set_null_function {table} { 3610 variable fieldList 3611 variable leftCurly 3612 variable rightCurly 3613 variable setNullSource 3614 variable setNullNotNullSource 3615 3616 emit "int" 3617 emit "${table}_set_null (Tcl_Interp *interp, CTable *ctable, ctable_BaseRow *vRow, int field, int indexCtl) $leftCurly" 3618 emit " switch ((enum ${table}_fields) field) $leftCurly" 3619 3620 foreach myField $fieldList { 3621 upvar ::ctable::fields::$myField field 3622 3623 set optname [field_to_enum $myField] 3624 3625 if {[info exists field(notnull)] && $field(notnull)} { 3626 emit [subst -nobackslashes -nocommands $setNullNotNullSource] 3627 } else { 3628 emit [subst -nobackslashes -nocommands $setNullSource] 3629 } 3630 } 3631 3632 emit " $rightCurly" 3633 emit " return TCL_OK;" 3634 emit "$rightCurly" 3635 emit "" 3636} 3637 3638# 3639# gen_is_null_function - emit C routine to test if a specific field is null 3640# in a given table and row 3641# 3642proc gen_is_null_function {table} { 3643 variable fieldList 3644 variable leftCurly 3645 variable rightCurly 3646 3647 emit "int" 3648 emit "${table}_is_null (struct $table *row, int field) $leftCurly" 3649 3650 emit " switch ((enum ${table}_fields) field) $leftCurly" 3651 3652 foreach myField $fieldList { 3653 upvar ::ctable::fields::$myField field 3654 3655 set optname [field_to_enum $myField] 3656 3657 if {!([info exists field(notnull)] && $field(notnull))} { 3658 emit " case [field_to_enum $myField]:" 3659 emit " return row->_${myField}IsNull;" 3660 } 3661 } 3662 3663 emit " default:" 3664 emit " return 0;" 3665 emit " $rightCurly" 3666 emit "$rightCurly" 3667 emit "" 3668} 3669 3670# 3671# put_metatable_source - emit the code to define the meta table (table-defining 3672# command) 3673# 3674proc put_metatable_source {table} { 3675 variable metaTableSource 3676 3677 set Id {CTable template Id} 3678 3679 emit [subst -nobackslashes -nocommands $metaTableSource] 3680} 3681 3682# 3683# put_init_command_source - emit the code to initialize create within Tcl 3684# the command that will invoke the C command defined by 3685# put_metatable_source 3686# 3687proc put_init_command_source {table} { 3688 variable extensionFragmentSource 3689 3690 set Id {init extension Id} 3691 set NFIELDS [string toupper $table]_NFIELDS 3692 set NLINKED_LISTS [string toupper $table]_NLINKED_LISTS 3693 set NFILTERS [string toupper $table]_NFILTERS 3694 3695 emit [subst -nobackslashes -nocommands $extensionFragmentSource] 3696} 3697 3698# 3699# put_init_extension_source - emit the code to create the C functions that 3700# Tcl will expect to find when loading the shared library. 3701# 3702proc put_init_extension_source {extension extensionVersion} { 3703 variable initExtensionSource 3704 variable tables 3705 3706 set Id {init extension Id} 3707 emit [subst -nobackslashes -nocommands $initExtensionSource] 3708} 3709 3710variable noCleanDirtyTableSource { 3711CONST int 3712${table}_clean(Tcl_Interp *interp, CTable *ctable) 3713{ 3714 Tcl_AppendResult(interp, "Dirty bits not implemented.", NULL); 3715 return TCL_ERROR; 3716} 3717 3718CONST int 3719${table}_dirty(Tcl_Interp *interp, CTable *ctable) 3720{ 3721 Tcl_AppendResult(interp, "Dirty bits not implemented.", NULL); 3722 return TCL_ERROR; 3723} 3724} 3725 3726variable cleanDirtyTableSource { 3727CONST int 3728${table}_clean(Tcl_Interp *interp, CTable *ctable) 3729{ 3730 ctable_BaseRow *row = NULL; 3731 3732#ifdef WITH_SHARED_TABLES 3733 if(ctable->share_type == CTABLE_SHARED_READER) { 3734 Tcl_AppendResult(interp, "Clean not possible in a shared reader.", NULL); 3735 Tcl_SetErrorCode (interp, "speedtables", "read_only", NULL); 3736 return TCL_ERROR; 3737 } 3738#endif 3739 3740 CTABLE_LIST_FOREACH (ctable->ll_head, row, 0) { 3741 ((${table} *)row)->_dirty = 0; 3742 } 3743 3744 return TCL_OK; 3745} 3746 3747CONST int 3748${table}_dirty(Tcl_Interp *interp, CTable *ctable, ctable_BaseRow *row) 3749{ 3750#ifdef WITH_SHARED_TABLES 3751 if(ctable->share_type == CTABLE_SHARED_READER) { 3752 Tcl_AppendResult(interp, "Dirty not possible in a shared reader.", NULL); 3753 Tcl_SetErrorCode (interp, "speedtables", "read_only", NULL); 3754 return TCL_ERROR; 3755 } 3756#endif 3757 3758 if (row) { 3759 ((${table} *)row)->_dirty = 1; 3760 } else { 3761 CTABLE_LIST_FOREACH (ctable->ll_head, row, 0) { 3762 ((${table} *)row)->_dirty = 1; 3763 } 3764 } 3765 3766 return TCL_OK; 3767} 3768} 3769 3770# 3771# gen_clean_function - create a *_clean function to clean the dirty bits in the table 3772# 3773proc gen_clean_function {table} { 3774 variable withDirty 3775 variable cleanDirtyTableSource 3776 variable noCleanDirtyTableSource 3777 3778 if {!$withDirty} { 3779 emit [subst -nobackslashes -nocommands $noCleanDirtyTableSource] 3780 } else { 3781 set _dirty "SPECIAL_[string toupper $table]_DIRTY" 3782 emit [subst -nobackslashes -nocommands $cleanDirtyTableSource] 3783 } 3784} 3785 3786# 3787# gen_set_function - create a *_set routine that takes a pointer to the 3788# tcl interp, an object, a pointer to a table row and a field number, 3789# and sets the value extracted from the obj into the field of the row 3790# 3791proc gen_set_function {table} { 3792 variable withDirty 3793 variable withSharedTables 3794 variable sanityChecks 3795 variable fieldObjSetSource 3796 variable fieldSetSource 3797 variable fieldSetSwitchSource 3798 variable leftCurly 3799 variable rightCurly 3800 3801 emit [string range [subst -nobackslashes -nocommands $fieldSetSource] 1 end-1] 3802 3803 if {$withSharedTables} { 3804 emit " if (ctable->share_type == CTABLE_SHARED_MASTER) $leftCurly" 3805 if {$sanityChecks} { 3806 emit " if(ctable->share->map->cycle == LOST_HORIZON)" 3807 emit " Tcl_Panic(\"map->cycle not updated?\");" 3808 } 3809 emit " row->_row_cycle = ctable->share->map->cycle;" 3810 emit " $rightCurly" 3811 } 3812 3813 emit [string range [subst -nobackslashes -nocommands $fieldSetSwitchSource] 1 end-1] 3814 gen_sets 3815 3816 emit " $rightCurly" 3817 3818 if {$withDirty} { 3819 emit " row->_dirty = 1;" 3820 } 3821 3822 emit " return TCL_OK;" 3823 emit "$rightCurly" 3824 3825 emit [string range [subst -nobackslashes -nocommands $fieldObjSetSource] 1 end-1] 3826 3827} 3828 3829# 3830# gen_get_function - create a *_get routine that takes a pointer to the 3831# tcl interp, an object pointer, a pointer to a table row and a field number, 3832# and gets the value from the field of the row and store it into the 3833# object. 3834# 3835# Also create a *_get_fieldobj function that takes pointers to the same 3836# tcl interpreter, object, and table row but takes an object containg 3837# a string identifying the field, which is then looked up to identify 3838# the field number and used in a call to the *_get function. 3839# 3840proc gen_get_function {table} { 3841 variable fieldObjGetSource 3842 variable lappendFieldAndNameObjSource 3843 variable lappendNonnullFieldAndNameObjSource 3844 variable arraySetFromFieldSource 3845 variable tabSepFunctionsSource 3846 variable fieldGetSource 3847 variable fieldGetStringSource 3848 variable leftCurly 3849 variable rightCurly 3850 3851 emit [string range [subst -nobackslashes -nocommands $fieldGetSource] 1 end-1] 3852 gen_gets_cases 3853 emit " $rightCurly" 3854 emit " return TCL_OK;" 3855 emit "$rightCurly" 3856 3857 emit [string range [subst -nobackslashes -nocommands $fieldObjGetSource] 1 end-1] 3858 3859 emit [string range [subst -nobackslashes -nocommands $lappendFieldAndNameObjSource] 1 end-1] 3860 3861 emit [string range [subst -nobackslashes -nocommands $lappendNonnullFieldAndNameObjSource] 1 end-1] 3862 3863 emit [string range [subst -nobackslashes -nocommands $fieldGetStringSource] 1 end-1] 3864 gen_gets_string_cases 3865 emit " $rightCurly" 3866 emit " return TCL_OK;" 3867 emit "$rightCurly" 3868 3869 emit [string range [subst -nobackslashes -nocommands $tabSepFunctionsSource] 1 end-1] 3870 3871 emit [string range [subst -nobackslashes -nocommands $arraySetFromFieldSource] 1 end-1] 3872} 3873 3874# 3875# gen_setup_routine - emit code to be run for this table type at shared 3876# libary load time 3877# 3878proc gen_setup_routine {table} { 3879 variable fieldList 3880 variable fields 3881 variable leftCurly 3882 variable rightCurly 3883 3884 emit "void ${table}_setup (void) $leftCurly" 3885 3886 # create and initialize all of the NameObj objects containing field 3887 # names as Tcl objects and increment their reference counts so 3888 # (hopefully, heh) they'll never be deleted. 3889 # 3890 # also populate the *_NameObjList table 3891 # 3892 set position 0 3893 foreach fieldName $fieldList { 3894 upvar ::ctable::fields::$fieldName field 3895 3896 set nameObj [field_to_nameObj $table $fieldName] 3897 emit " ${table}_NameObjList\[$position\] = $nameObj = Tcl_NewStringObj (\"$fieldName\", -1);" 3898 emit " Tcl_IncrRefCount ($nameObj);" 3899 emit "" 3900 incr position 3901 } 3902 emit " ${table}_NameObjList\[$position\] = (Tcl_Obj *) NULL;" 3903 emit "" 3904 3905 set emptyObj ${table}_DefaultEmptyStringObj 3906 emit " $emptyObj = Tcl_NewObj ();" 3907 emit " Tcl_IncrRefCount ($emptyObj);" 3908 emit "" 3909 3910 emit " // initialize the null string object to the default (empty) value" 3911 emit " ${table}_NullValueObj = Tcl_NewObj ();" 3912 emit " ${table}_NullValueString = Tcl_GetStringFromObj (${table}_NullValueObj, &${table}_NullValueSize);" 3913 emit " Tcl_IncrRefCount (${table}_NullValueObj);" 3914 3915 emit "$rightCurly" 3916 emit "" 3917} 3918 3919# Generate allocator for shared ctables 3920proc gen_shared_string_allocator {} { 3921 variable withSharedTables 3922 variable table 3923 variable leftCurly 3924 variable rightCurly 3925 variable fieldList 3926 3927 if {!$withSharedTables} { 3928 return 3929 } 3930 3931 emit "int ${table}_setupDefaultStrings(CTable *ctable) $leftCurly" 3932 emit " volatile char **defaultList;" 3933 emit " volatile char *bundle;" 3934 emit "" 3935 3936 emit " // If it's not a shared table, just use constants" 3937 emit " if(ctable->share_type == CTABLE_SHARED_NONE) $leftCurly" 3938 emit " ctable->emptyString = \"\";" 3939 emit " ctable->defaultStrings = ${table}_defaultStrings;" 3940 emit " return TRUE;" 3941 emit " $rightCurly" 3942 emit "" 3943 3944 emit " // reader table, use the master table" 3945 emit " if(ctable->share_type == CTABLE_SHARED_READER) $leftCurly" 3946 emit " ctable->emptyString = ctable->share_ctable->emptyString;" 3947 emit " ctable->defaultStrings = ctable->share_ctable->defaultStrings;" 3948 emit " return TRUE;" 3949 emit " $rightCurly" 3950 emit "" 3951 3952 # Generate and save the assignments 3953 # to the shared bundle, and set up bundle 3954 set bundle {\0} 3955 set bundleLen 1 3956 set fieldNum 0 3957 foreach fieldName $fieldList { 3958 upvar ::ctable::fields::$fieldName field 3959 3960 if {$field(type) != "varstring"} { 3961 lappend sets " defaultList\[$fieldNum] = NULL;" 3962 } elseif {![info exists field(default)]} { 3963 lappend sets " defaultList\[$fieldNum] = &bundle\[0];" 3964 } else { 3965 set def [cquote $field(default)] 3966 lappend sets " defaultList\[$fieldNum] = &bundle\[$bundleLen];" 3967 3968 append bundle $def 3969 incr bundleLen [string length $field(default)] 3970 append bundle {\0} 3971 incr bundleLen 3972 } 3973 incr fieldNum 3974 } 3975 3976 emit " // Allocate array and the strings themselves in one chunk" 3977 3978 set totalSize "$fieldNum * sizeof (char *) + $bundleLen" 3979 emit " defaultList = (volatile char **)shmalloc(ctable->share, $totalSize);" 3980 emit " if (!defaultList) {" 3981 emit " if(ctable->share_panic) ${table}_shmpanic(ctable);" 3982 emit " return FALSE;" 3983 emit " }" 3984 emit "" 3985 3986 emit " bundle = (char *)&defaultList\[$fieldNum];" 3987 emit "" 3988 3989 emit " memcpy((char *)bundle, \"$bundle\", $bundleLen);" 3990 emit "" 3991 3992 emit " ctable->emptyString = (char *)&bundle\[0];" 3993 emit " ctable->defaultStrings = (const char **)defaultList;" 3994 emit "" 3995 3996 emit [join $sets "\n"] 3997 emit "" 3998 emit " return TRUE;" 3999 emit "$rightCurly" 4000} 4001 4002# 4003# gen_code - generate all of the code for the underlying methods for 4004# managing a created table 4005# 4006proc gen_code {} { 4007 variable table 4008 variable booleans 4009 variable fields 4010 variable fieldList 4011 variable withSharedTables 4012 variable leftCurly 4013 variable rightCurly 4014 variable cmdBodySource 4015 4016 # Used in cmdBodySubst: 4017 variable extension 4018 variable keyFieldName 4019 4020 #set pointer "${table}_ptr" 4021 set pointer p 4022 4023 set Id {CTable template Id} 4024 4025 set nFields [string toupper $table]_NFIELDS 4026 4027 set rowStruct $table 4028 4029 gen_sanity_checks $table 4030 4031 gen_allocate_function $table 4032 4033 gen_reinsert_row_function $table 4034 4035 gen_clean_function $table 4036 4037 gen_set_function $table 4038 4039 gen_set_null_function $table 4040 4041 gen_is_null_function $table 4042 4043 gen_get_function $table 4044 4045 gen_incr_function $table 4046 4047 gen_field_compare_functions 4048 4049 gen_sort_compare_function 4050 4051 gen_search_compare_function 4052 4053 gen_make_key_functions 4054 4055 gen_shared_string_allocator 4056 4057 emit [subst -nobackslashes -nocommands $cmdBodySource] 4058} 4059 4060# 4061# gen_new_obj - given a data type, pointer name and field name, return 4062# the C code to generate a Tcl object containing that element from the 4063# pointer pointing to the named field. 4064# 4065proc gen_new_obj {type fieldName} { 4066 variable table 4067 upvar ::ctable::fields::$fieldName field 4068 4069 switch $type { 4070 key { 4071 return "Tcl_NewStringObj (row->hashEntry.key, -1)" 4072 } 4073 4074 short { 4075 if {![info exists field(notnull)] || !$field(notnull)} { 4076 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewIntObj (row->$fieldName)" 4077 } else { 4078 return "Tcl_NewIntObj (row->$fieldName)" 4079 } 4080 } 4081 4082 int { 4083 if {![info exists field(notnull)] || !$field(notnull)} { 4084 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewIntObj (row->$fieldName)" 4085 } else { 4086 return "Tcl_NewIntObj (row->$fieldName)" 4087 } 4088 } 4089 4090 long { 4091 if {![info exists field(notnull)] || !$field(notnull)} { 4092 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewLongObj (row->$fieldName)" 4093 } else { 4094 return "Tcl_NewLongObj (row->$fieldName)" 4095 } 4096 } 4097 4098 wide { 4099 if {![info exists field(notnull)] || !$field(notnull)} { 4100 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewWideIntObj (row->$fieldName)" 4101 } else { 4102 return "Tcl_NewWideIntObj (row->$fieldName)" 4103 } 4104 } 4105 4106 double { 4107 if {![info exists field(notnull)] || !$field(notnull)} { 4108 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewDoubleObj (row->$fieldName)" 4109 } else { 4110 return "Tcl_NewDoubleObj (row->$fieldName)" 4111 } 4112 } 4113 4114 float { 4115 if {![info exists field(notnull)] || !$field(notnull)} { 4116 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewDoubleObj (row->$fieldName)" 4117 } else { 4118 return "Tcl_NewDoubleObj (row->$fieldName)" 4119 } 4120 } 4121 4122 boolean { 4123 if {![info exists field(notnull)] || !$field(notnull)} { 4124 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewBooleanObj (row->$fieldName)" 4125 } else { 4126 return "Tcl_NewBooleanObj (row->$fieldName)" 4127 } 4128 } 4129 4130 varstring { 4131 if {![info exists field(notnull)] || !$field(notnull)} { 4132 return "(row->_${fieldName}IsNull || !row->$fieldName) ? ${table}_NullValueObj : Tcl_NewStringObj (row->$fieldName, row->_${fieldName}Length)" 4133 } else { 4134 return "Tcl_NewStringObj (row->$fieldName, row->_${fieldName}Length)" 4135 } 4136 } 4137 4138 char { 4139 if {![info exists field(notnull)] || !$field(notnull)} { 4140 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewStringObj (&row->$fieldName, 1)" 4141 } else { 4142 return "Tcl_NewStringObj (&row->$fieldName, 1)" 4143 } 4144 } 4145 4146 fixedstring { 4147 if {![info exists field(notnull)] || !$field(notnull)} { 4148 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewStringObj (row->$fieldName, $field(length))" 4149 } else { 4150 return "Tcl_NewStringObj (row->$fieldName, $field(length))" 4151 } 4152 } 4153 4154 inet { 4155 if {![info exists field(notnull)] || !$field(notnull)} { 4156 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewStringObj (inet_ntoa (row->$fieldName), -1)" 4157 } else { 4158 return "Tcl_NewStringObj (inet_ntoa (row->$fieldName), -1)" 4159 } 4160 } 4161 4162 mac { 4163 if {![info exists field(notnull)] || !$field(notnull)} { 4164 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : Tcl_NewStringObj (ether_ntoa (&row->$fieldName), -1)" 4165 } else { 4166 return "Tcl_NewStringObj (ether_ntoa (&row->$fieldName), -1)" 4167 } 4168 } 4169 4170 tclobj { 4171 if {![info exists field(notnull)] || !$field(notnull)} { 4172 return "row->_${fieldName}IsNull ? ${table}_NullValueObj : ((row->$fieldName == (Tcl_Obj *) NULL) ? Tcl_NewObj () : row->$fieldName)" 4173 } else { 4174 return "((row->$fieldName == (Tcl_Obj *) NULL) ? Tcl_NewObj () : row->$fieldName)" 4175 } 4176 } 4177 4178 default { 4179 error "no code to gen obj for type $type" 4180 } 4181 } 4182} 4183 4184# 4185# gen_get_set_obj - given an object, a data type, pointer name and field name, 4186# return the C code to set a Tcl object to contain that element from the 4187# pointer pointing to the named field. 4188# 4189# note: this is an inefficient way to get the value of varstrings, 4190# fixedstrings and chars, and can't even do tclobjs. 4191# 4192# do what gen_get_string_cases does, or call its parent anyway *_get_string, 4193# to get string representations of those efficiently. 4194# 4195proc gen_get_set_obj {obj type fieldName} { 4196 variable fields 4197 variable table 4198 4199 switch $type { 4200 short { 4201 return "Tcl_SetIntObj ($obj, row->$fieldName)" 4202 } 4203 4204 int { 4205 return "Tcl_SetIntObj ($obj, row->$fieldName)" 4206 } 4207 4208 long { 4209 return "Tcl_SetLongObj ($obj, row->$fieldName)" 4210 } 4211 4212 wide { 4213 return "Tcl_SetWideIntObj ($obj, row->$fieldName)" 4214 } 4215 4216 double { 4217 return "Tcl_SetDoubleObj ($obj, row->$fieldName)" 4218 } 4219 4220 float { 4221 return "Tcl_SetDoubleObj ($obj, row->$fieldName)" 4222 } 4223 4224 boolean { 4225 return "Tcl_SetBooleanObj ($obj, row->$fieldName)" 4226 } 4227 4228 varstring { 4229 return "Tcl_SetStringObj ($obj, row->$fieldName, row->_${fieldName}Length)" 4230 } 4231 4232 char { 4233 return "Tcl_SetStringObj ($obj, &row->$fieldName, 1)" 4234 } 4235 4236 fixedstring { 4237 upvar ::ctable::fields::$fieldName field 4238 4239 return "Tcl_SetStringObj ($obj, row->$fieldName, $field(length))" 4240 } 4241 4242 inet { 4243 return "Tcl_SetStringObj ($obj, inet_ntoa (row->$fieldName), -1)" 4244 } 4245 4246 mac { 4247 return "Tcl_SetStringObj ($obj, ether_ntoa (&row->$fieldName), -1)" 4248 } 4249 4250 tclobj { 4251 error "can't set a string to a tclobj (field \"$fieldName\") -- you have to handle this outside of gen_get_set_obj" 4252 } 4253 4254 default { 4255 error "no code to gen obj for type $type" 4256 } 4257 } 4258} 4259 4260# 4261# set_list_obj - generate C code to emit a Tcl obj containing the named 4262# field into a list that's being cons'ed up 4263# 4264proc set_list_obj {position type fieldName} { 4265 emit " listObjv\[$position] = [gen_new_obj $type $fieldName];" 4266} 4267 4268# 4269# append_list_element - generate C code to append a list element to the 4270# output object. used by code that lets you get one or more named fields. 4271# 4272proc append_list_element {type fieldName} { 4273 return "Tcl_ListObjAppendElement (interp, Tcl_GetObjResult(interp), [gen_new_obj $type $fieldName])" 4274} 4275 4276# 4277# gen_list - generate C code to emit an entire row into a Tcl list 4278# 4279proc gen_list {} { 4280 variable table 4281 variable booleans 4282 variable fields 4283 variable fieldList 4284 variable leftCurly 4285 variable rightCurly 4286 4287 set lengthDef [string toupper $table]_NFIELDS 4288 4289 emit "Tcl_Obj *${table}_genlist (Tcl_Interp *interp, ctable_BaseRow *vRow) $leftCurly" 4290 emit " struct $table *row = (struct $table *)vRow;" 4291 4292 emit " Tcl_Obj *listObjv\[$lengthDef];" 4293 emit "" 4294 4295 set position 0 4296 foreach fieldName $fieldList { 4297 if {[is_hidden $fieldName]} { 4298 continue 4299 } 4300 4301 upvar ::ctable::fields::$fieldName field 4302 4303 set_list_obj $position $field(type) $fieldName 4304 4305 incr position 4306 } 4307 4308 emit " return Tcl_NewListObj ($position, listObjv);" 4309 emit "$rightCurly" 4310 emit "" 4311} 4312 4313# 4314# gen_keyvalue_list - generate C code to emit an entire row into a Tcl list in 4315# "array set" format 4316# 4317proc gen_keyvalue_list {} { 4318 variable table 4319 variable booleans 4320 variable fields 4321 variable fieldList 4322 variable leftCurly 4323 variable rightCurly 4324 4325 set lengthDef [string toupper $table]_NFIELDS 4326 4327 emit "Tcl_Obj *${table}_gen_keyvalue_list (Tcl_Interp *interp, ctable_BaseRow *vRow) $leftCurly" 4328 emit " struct $table *row = (struct $table *)vRow;" 4329 4330 emit " Tcl_Obj *listObjv\[$lengthDef * 2];" 4331 emit "" 4332 4333 set position 0 4334 foreach fieldName $fieldList { 4335 if {[is_hidden $fieldName]} { 4336 continue 4337 } 4338 4339 upvar ::ctable::fields::$fieldName field 4340 4341 emit " listObjv\[$position] = [field_to_nameObj $table $fieldName];" 4342 incr position 4343 4344 set_list_obj $position $field(type) $fieldName 4345 incr position 4346 4347 emit "" 4348 } 4349 4350 emit " return Tcl_NewListObj ($position, listObjv);" 4351 emit "$rightCurly" 4352 emit "" 4353} 4354 4355# 4356# gen_nonnull_keyvalue_list - generate C code to emit all of the nonnull 4357# values in an entire row into a Tcl list in "array set" format 4358# 4359proc gen_nonnull_keyvalue_list {} { 4360 variable table 4361 variable booleans 4362 variable fields 4363 variable fieldList 4364 variable leftCurly 4365 variable rightCurly 4366 4367 set lengthDef [string toupper $table]_NFIELDS 4368 4369 emit "Tcl_Obj *${table}_gen_nonnull_keyvalue_list (Tcl_Interp *interp, struct $table *row) $leftCurly" 4370 4371 emit " Tcl_Obj *listObjv\[$lengthDef * 2];" 4372 emit " int position = 0;" 4373 emit " Tcl_Obj *obj;" 4374 emit "" 4375 4376 foreach fieldName $fieldList { 4377 if {[is_hidden $fieldName]} { 4378 continue 4379 } 4380 4381 upvar ::ctable::fields::$fieldName field 4382 4383 if {[is_key $fieldName]} { 4384 emit " listObjv\[position++] = [field_to_nameObj $table $fieldName];" 4385 emit " listObjv\[position++] = [gen_new_obj $field(type) $fieldName];" 4386 } else { 4387 emit " obj = [gen_new_obj $field(type) $fieldName];" 4388 emit " if (obj != ${table}_NullValueObj) $leftCurly" 4389 emit " listObjv\[position++] = [field_to_nameObj $table $fieldName];" 4390 emit " listObjv\[position++] = obj;" 4391 emit " $rightCurly" 4392 } 4393 } 4394 4395 emit " return Tcl_NewListObj (position, listObjv);" 4396 emit "$rightCurly" 4397 emit "" 4398} 4399 4400# 4401# gen_make_key_functions - Generate C code to return the key fields as a list 4402# 4403proc gen_make_key_functions {} { 4404 gen_make_key_from_keylist 4405} 4406 4407proc gen_make_key_from_keylist {} { 4408 variable table 4409 variable fields 4410 variable keyFieldName 4411 variable fieldList 4412 variable leftCurly 4413 variable rightCurly 4414 4415 emit "Tcl_Obj *${table}_key_from_keylist (Tcl_Interp *interp, Tcl_Obj **objv, int objc) $leftCurly" 4416 4417 if {"$keyFieldName" != ""} { 4418 emit " int i;" 4419 emit "" 4420 4421 emit " for(i = 0; i < objc; i+=2)" 4422 emit " if(strcmp(Tcl_GetString(objv\[i]), \"$keyFieldName\") == 0)" 4423 emit " return objv\[i+1];" 4424 emit "" 4425 } 4426 emit " return (Tcl_Obj *)NULL;" 4427 4428 emit "$rightCurly" 4429 emit "" 4430} 4431 4432# 4433# gen_field_names - generate C code containing an array of pointers to strings 4434# comprising the names of all of the fields in a row of the table being 4435# defined. Also generate an enumerated type of all of the field names 4436# mapped to uppercase and prepended with FIELD_ for use with 4437# Tcl_GetIndexFromObj in figuring out what fields are wanted 4438# 4439proc gen_field_names {} { 4440 variable table 4441 variable booleans 4442 variable fields 4443 variable fieldList 4444 variable leftCurly 4445 variable rightCurly 4446 variable keyField 4447 variable withSharedTables 4448 4449 emit "#define [string toupper $table]_NFIELDS [llength $fieldList]" 4450 emit "" 4451 4452 emit "int ${table}_keyField = $keyField;" 4453 4454 emit "static CONST char *${table}_fields\[] = $leftCurly" 4455 foreach fieldName $fieldList { 4456 emit " \"$fieldName\"," 4457 } 4458 emit " (char *) NULL" 4459 emit "$rightCurly;\n" 4460 4461 set fieldenum "enum ${table}_fields $leftCurly" 4462 foreach myField $fieldList { 4463 append fieldenum "\n [field_to_enum $myField]," 4464 } 4465 set fieldenum "[string range $fieldenum 0 end-1]\n$rightCurly;\n" 4466 emit $fieldenum 4467 4468 set typeList "enum ctable_types ${table}_types\[\] = $leftCurly" 4469 foreach myField $fieldList { 4470 upvar ::ctable::fields::$myField field 4471 4472 append typeList "\n [ctable_type_to_enum $field(type)]," 4473 } 4474 emit "[string range $typeList 0 end-1]\n$rightCurly;\n" 4475 4476 emit "// define per-field array for ${table} saying what fields need quoting" 4477 set needsQuoting "static int ${table}_needs_quoting\[\] = $leftCurly" 4478 foreach myField $fieldList { 4479 upvar ::ctable::fields::$myField field 4480 4481 if {[info exists field(needsQuoting)] && $field(needsQuoting)} { 4482 set quoting 1 4483 } else { 4484 set quoting 0 4485 } 4486 append needsQuoting "\n $quoting," 4487 } 4488 emit "[string range $needsQuoting 0 end-1]\n$rightCurly;\n" 4489 4490 emit "// define per-field array for ${table} saying what fields are unique" 4491 set unique "int ${table}_unique\[\] = $leftCurly" 4492 foreach myField $fieldList { 4493 upvar ::ctable::fields::$myField field 4494 4495 if {[info exists field(unique)] && $field(unique)} { 4496 set uniqueVal 1 4497 } else { 4498 set uniqueVal 0 4499 } 4500 append unique "\n $uniqueVal," 4501 } 4502 emit "[string range $unique 0 end-1]\n$rightCurly;\n" 4503 4504 emit "// define objects that will be filled with the corresponding field names" 4505 foreach fieldName $fieldList { 4506 emit "Tcl_Obj *[field_to_nameObj $table $fieldName];" 4507 } 4508 emit "" 4509 4510 emit "// define field property list keys and values to allow introspection" 4511 4512 # do keys 4513 foreach fieldName $fieldList { 4514 upvar ::ctable::fields::$fieldName field 4515 4516 set propstring "static CONST char *[field_to_var $table $fieldName propkeys]\[] = $leftCurly" 4517 4518 foreach fieldName [lsort [array names field]] { 4519 append propstring "\"$fieldName\", " 4520 } 4521 emit "${propstring}(CONST char *)NULL$rightCurly;" 4522 } 4523 emit "" 4524 4525 set propstring "static CONST char **${table}_propKeys\[] = $leftCurly" 4526 foreach fieldName $fieldList { 4527 append propstring "[field_to_var $table $fieldName propkeys]," 4528 } 4529 emit "[string range $propstring 0 end-1]$rightCurly;" 4530 emit "" 4531 # end of keys 4532 4533 # do values, replica of keys, needs to be collapsed 4534 foreach fieldName $fieldList { 4535 upvar ::ctable::fields::$fieldName field 4536 4537 set propstring "static CONST char *[field_to_var $table $fieldName propvalues]\[] = $leftCurly" 4538 4539 foreach fieldName [lsort [array names field]] { 4540 append propstring "\"$field($fieldName)\", " 4541 } 4542 emit "${propstring}(CONST char *)NULL$rightCurly;" 4543 } 4544 emit "" 4545 4546 set propstring "static CONST char **${table}_propValues\[] = $leftCurly" 4547 foreach fieldName $fieldList { 4548 append propstring "[field_to_var $table $fieldName propvalues]," 4549 } 4550 emit "[string range $propstring 0 end-1]$rightCurly;" 4551 emit "" 4552 # end of values 4553 4554 emit "static Tcl_Obj *${table}_NameObjList\[[string toupper $table]_NFIELDS + 1\];" 4555 emit "" 4556 4557 emit "static Tcl_Obj *${table}_DefaultEmptyStringObj;" 4558 emit "" 4559 4560 emit "// define the null value object" 4561 emit "static Tcl_Obj *${table}_NullValueObj;" 4562 emit "static char *${table}_NullValueString;" 4563 emit "static int ${table}_NullValueSize;" 4564 emit "" 4565 4566 set nullableList {} 4567 4568 foreach myField $fieldList { 4569 upvar ::ctable::fields::$myField field 4570 4571 set value 1 4572 if {[info exists field(notnull)] && $field(notnull)} { 4573 set value 0 4574 } 4575 4576 lappend nullableList $value 4577 } 4578 4579 emit "// define fields that may be null" 4580 emit "static int ${table}_nullable_fields\[] = { [join $nullableList ", "] };" 4581 emit "" 4582 4583 if {$withSharedTables} { 4584 set defaultStrings {} 4585 4586 foreach myField $fieldList { 4587 upvar ::ctable::fields::$myField field 4588 4589 if {$field(type) == "varstring" && [info exists field(default)]} { 4590 lappend defaultStrings [cquote $field(default)] 4591 } else { 4592 lappend defaultStrings "" 4593 } 4594 } 4595 4596 emit "// define default string list" 4597 emit "static CONST char *${table}_defaultStrings\[] = $leftCurly" 4598 emit " \"[join $defaultStrings {", "}]\"" 4599 emit "$rightCurly;" 4600 emit "" 4601 } 4602} 4603 4604# 4605# gen_gets_cases - generate case statements for each field, each case fetches 4606# field from row and returns a new Tcl_Obj set with that field's value 4607# 4608proc gen_gets_cases {} { 4609 variable table 4610 variable booleans 4611 variable fields 4612 variable fieldList 4613 variable leftCurly 4614 variable rightCurly 4615 4616 foreach myField $fieldList { 4617 upvar ::ctable::fields::$myField field 4618 4619 emit " case [field_to_enum $myField]:" 4620 emit " return [gen_new_obj $field(type) $myField];" 4621 emit "" 4622 } 4623} 4624 4625# 4626# gen_gets_string_cases - generate case statements for each field, each case 4627# generates a return of a char * to a string representing that field's 4628# value and sets a passed-in int * to the length returned. 4629# 4630proc gen_gets_string_cases {} { 4631 variable table 4632 variable booleans 4633 variable fields 4634 variable fieldList 4635 variable leftCurly 4636 variable rightCurly 4637 4638 foreach myField $fieldList { 4639 upvar ::ctable::fields::$myField field 4640 4641 emit " case [field_to_enum $myField]:" 4642 4643 if {![info exists field(notnull)] || !$field(notnull)} { 4644 emit " if (row->_${myField}IsNull) $leftCurly" 4645 emit " return Tcl_GetStringFromObj (${table}_NullValueObj, lengthPtr);" 4646 emit " $rightCurly" 4647 } 4648 4649 switch $field(type) { 4650 "key" { 4651 emit " *lengthPtr = strlen(row->hashEntry.key);" 4652 emit " return row->hashEntry.key;" 4653 } 4654 4655 "varstring" { 4656 if {![info exists field(notnull)] || $field(notnull) == 0} { 4657 emit " if (row->${myField} == NULL) $leftCurly" 4658 emit " return Tcl_GetStringFromObj (${table}_DefaultEmptyStringObj, lengthPtr);" 4659 emit " $rightCurly" 4660 } 4661 emit " *lengthPtr = row->_${myField}Length;" 4662 emit " return row->$myField;" 4663 } 4664 4665 "fixedstring" { 4666 emit " *lengthPtr = $field(length);" 4667 emit " return row->$myField;" 4668 } 4669 4670 "char" { 4671 emit " *lengthPtr = 1;" 4672 emit " return &row->$myField;" 4673 } 4674 4675 "tclobj" { 4676 emit " if (row->$myField == NULL) $leftCurly" 4677 emit " return Tcl_GetStringFromObj (${table}_DefaultEmptyStringObj, lengthPtr);" 4678 emit " $rightCurly" 4679 emit " return Tcl_GetStringFromObj (row->$myField, lengthPtr);" 4680 } 4681 4682 default { 4683 emit " [gen_get_set_obj utilityObj $field(type) $myField];" 4684 emit " return Tcl_GetStringFromObj (utilityObj, lengthPtr);" 4685 } 4686 } 4687 emit "" 4688 } 4689} 4690 4691# Static utility routines for command body that aren't ctable specific 4692 4693variable sharedStaticSource { 4694 4695// Call write-lock at least once during command. Cursors do their own locking and unlocking. 4696static INLINE void begin_write(CTable *ctable) 4697{ 4698 if(ctable->share_type == CTABLE_SHARED_MASTER && !ctable->cursors) { 4699 write_lock(ctable->share); 4700 ctable->was_locked = 1; 4701 } 4702} 4703 4704// Call write-unlock once at the end of the command, IFF it was locked 4705static INLINE void end_write(CTable *ctable) 4706{ 4707 if(ctable->was_locked && ctable->share_type == CTABLE_SHARED_MASTER && !ctable->cursors) { 4708 write_unlock(ctable->share); 4709 ctable->was_locked = 0; 4710 } 4711} 4712 4713} 4714 4715variable unsharedStaticSource { 4716// Dummy lock/unlock functions, since we're not doing shared ctables. 4717# define begin_write(ct) 4718# define end_write(ct) 4719} 4720 4721# 4722# gen_preamble - generate stuff that goes at the head of the C file 4723# we're generating 4724# 4725proc gen_preamble {} { 4726 variable fullInline 4727 variable fullStatic 4728 variable withPgtcl 4729 variable withCasstcl 4730 variable withSharedTables 4731 variable withSharedTclExtension 4732 variable sanityChecks 4733 variable sharedTraceFile 4734 variable sharedBase 4735 variable sharedGuard 4736 variable sharedLog 4737 variable poolRatio 4738 variable preambleCannedSource 4739 variable sharedStaticSource 4740 variable unsharedStaticSource 4741 variable localDefines 4742 variable localCode 4743 4744 emit "/* -*- mode: c++; buffer-read-only: 1; -*- */" 4745 emit "/* autogenerated by ctable table generator [clock format [clock seconds]] */" 4746 emit "/* DO NOT EDIT */" 4747 emit "" 4748 4749 if {$fullInline} { 4750 emit "#define INLINE inline" 4751 } else { 4752 emit "#define INLINE" 4753 } 4754 4755 if {$fullStatic} { 4756 # Make all possible symbols static except for explicitly exported ones. 4757 emit "#define CTABLE_INTERNAL static" 4758 emit "#define CTABLE_EXTERNAL extern \"C\"" 4759 emit "#if defined(__GNUC__) && (__GNUC__ >= 4)" 4760 emit " #define CTABLE_EXTERNAL2 __attribute__ ((visibility (\"default\")))" 4761 emit "#elif defined(_WIN32) || defined(__CYGWIN__)" 4762 emit " #define CTABLE_EXTERNAL2 __declspec(dllexport)" 4763 emit "#else" 4764 emit " #define CTABLE_EXTERNAL2" 4765 emit "#endif" 4766 emit "#define FULLSTATIC" 4767 } else { 4768 # Leave all symbols exported. 4769 emit "#define CTABLE_INTERNAL" 4770 emit "#define CTABLE_EXTERNAL extern \"C\"" 4771 emit "#define CTABLE_EXTERNAL2" 4772 } 4773 4774 emit "" 4775 if {$withPgtcl} { 4776 emit "#define WITH_PGTCL" 4777 emit "" 4778 } 4779 4780 if {$withCasstcl} { 4781 emit "#define WITH_CASSTCL" 4782 emit "" 4783 } 4784 4785 if {$sanityChecks} { 4786 emit "#define SANITY_CHECKS" 4787 emit "" 4788 } 4789 4790 if {$withSharedTables} { 4791 emit "#define WITH_SHARED_TABLES" 4792 emit "#define WITH_TCL" 4793 4794 if {$withSharedTclExtension} { 4795 emit "#define SHARED_TCL_EXTENSION" 4796 } 4797 4798 emit "" 4799 if {[info exists sharedTraceFile]} { 4800 if {"$sharedTraceFile" != "-none"} { 4801 emit "#define SHM_DEBUG_TRACE" 4802 if {"$sharedTraceFile" != "-stderr"} { 4803 emit "#define SHM_DEBUG_TRACE_FILE \"$sharedTraceFile\"" 4804 } 4805 } 4806 } 4807 4808 if {[info exists sharedBase] && "$sharedBase" != "NULL"} { 4809 emit "#define SHARE_BASE ((char *)$sharedBase)" 4810 } 4811 4812 if {[info exists sharedLog] && "$sharedLog" != "-none"} { 4813 emit "#define SHARED_LOG \"$sharedLog\"" 4814 set sharedGuard 1 4815 } 4816 4817 if {[info exists sharedGuard] && $sharedGuard} { 4818 emit "#define SHARED_GUARD" 4819 } 4820 emit "#define POOL_RATIO $poolRatio" 4821 } 4822 4823 emit $preambleCannedSource 4824 if {$withSharedTables} { 4825 if {[info exists sharedBase] && "$sharedBase" != "NULL"} { 4826 emit "char *set_share_base = NULL;" 4827 } 4828 4829 emit $sharedStaticSource 4830 } else { 4831 emit $unsharedStaticSource 4832 } 4833 4834 if [array exists localDefines] { 4835 foreach {name value} [array get localDefines] { 4836 emit "#define $name $value" 4837 } 4838 } 4839 4840 if [info exists localCode] { 4841 emit $localCode 4842 } 4843} 4844 4845##### 4846# 4847# Field Compare Function Generation 4848# 4849##### 4850 4851# 4852# fieldCompareNullCheckSource - this checks for nulls when comparing a field 4853# 4854variable fieldCompareNullCheckSource { 4855 // nulls sort high 4856 if (row1->_${fieldName}IsNull) { 4857 if (row2->_${fieldName}IsNull) { 4858 return 0; 4859 } 4860 return 1; 4861 } else if (row2->_${fieldName}IsNull) { 4862 return -1; 4863 } 4864} 4865 4866# 4867# gen_field_compare_null_check_source - return code to be emitted into a field 4868# compare, nothing if the field is not null else code to check for null 4869# 4870proc gen_field_compare_null_check_source {table fieldName} { 4871 variable fieldCompareNullCheckSource 4872 variable varstringCompareNullSource 4873 variable varstringCompareEmptySource 4874 upvar ::ctable::fields::$fieldName field 4875 4876 if {[info exists field(notnull)] && $field(notnull)} { 4877 set source "" 4878 } elseif {"$field(type)" == "varstring"} { 4879 set source $varstringCompareNullSource 4880 } else { 4881 set source $fieldCompareNullCheckSource 4882 } 4883 4884 return [string range [subst -nobackslashes -nocommands $source] 1 end-1] 4885} 4886 4887# 4888# fieldCompareHeaderSource - code for defining a field compare function 4889# 4890variable fieldCompareHeaderSource { 4891// field compare function for field '$fieldName' of the '$table' table... 4892int ${table}_field_${fieldName}_compare(const ctable_BaseRow *vPointer1, const ctable_BaseRow *vPointer2) $leftCurly 4893 struct ${table} *row1, *row2; 4894 4895 row1 = (struct $table *) vPointer1; 4896 row2 = (struct $table *) vPointer2; 4897 4898#ifdef SANITY_CHECKS 4899 if(!row1) Tcl_Panic("NULL row1 for ${table}_field_${fieldName}_compare, row2 == 0x%lx", (long)row2); 4900 if(!row2) Tcl_Panic("NULL row2 for ${table}_field_${fieldName}_compare, row1 == 0x%lx", (long)row1); 4901#endif 4902 4903} 4904 4905variable fieldCompareTrailerSource { 4906$rightCurly 4907} 4908 4909# 4910# keyCompareSource - code for defining a key compare function 4911# 4912variable keyCompareSource { 4913// field compare function for key of the '$table' table... 4914int ${table}_key_compare(const ctable_BaseRow *vPointer1, const ctable_BaseRow *vPointer2) $leftCurly 4915 struct ${table} *row1, *row2; 4916 4917 row1 = (struct $table *) vPointer1; 4918 row2 = (struct $table *) vPointer2; 4919 if (*row1->hashEntry.key != *row2->hashEntry.key) { 4920 if (*row1->hashEntry.key < *row2->hashEntry.key) { 4921 return -1; 4922 } else { 4923 return 1; 4924 } 4925 } 4926 return strcmp(row1->hashEntry.key, row2->hashEntry.key); 4927$rightCurly 4928} 4929 4930# 4931# boolFieldCompSource - code we run subst over to generate a compare of a 4932# boolean (bit) for use in a field comparison routine. 4933# 4934variable boolFieldCompSource { 4935 if (row1->$fieldName && !row2->$fieldName) { 4936 return -1; 4937 } 4938 4939 if (!row1->$fieldName && row2->$fieldName) { 4940 return 1; 4941 } 4942 4943 return 0; 4944} 4945 4946# 4947# numberFieldSource - code we run subst over to generate a compare of a standard 4948# number such as an integer, long, double, and wide integer for use in field 4949# compares. 4950# 4951variable numberFieldCompSource { 4952 if (row1->$fieldName < row2->$fieldName) { 4953 return -1; 4954 } 4955 4956 if (row1->$fieldName > row2->$fieldName) { 4957 return 1; 4958 } 4959 4960 return 0; 4961} 4962 4963# 4964# varstringFieldCompSource - code we run subst over to generate a compare of 4965# a string for use in searching, sorting, etc. 4966# 4967# NOTE - this code has NO safety net. This code must NEVER be exposed without the safety net. 4968# 4969variable varstringFieldCompSource { 4970 if (*row1->$fieldName != *row2->$fieldName) { 4971 if (*row1->$fieldName < *row2->$fieldName) { 4972 return -1; 4973 } else { 4974 return 1; 4975 } 4976 } 4977 return strcmp (row1->$fieldName, row2->$fieldName); 4978} 4979 4980 4981# 4982# varstringCompareNullSource - compare against default empty string 4983# 4984# note there's also a varstringSortCompareNullSource that's pretty close to 4985# this but sets a result variable and does a break to get out of a case 4986# statement rather than returning something 4987# 4988variable varstringCompareNullSource { 4989 // NULL sorts high 4990 if (row1->_${fieldName}IsNull || !row1->$fieldName) { 4991 if(row2->_${fieldName}IsNull || !row2->$fieldName) { 4992 return 0; 4993 } else { 4994 return 1; 4995 } 4996 } else { 4997 if(row2->_${fieldName}IsNull || !row2->$fieldName) { 4998 return -1; 4999 } 5000 } 5001} 5002 5003# 5004# fixedstringFieldCompSource - code we run subst over to generate a comapre of a 5005# fixed-length string for use in a searching, sorting, etc. 5006# 5007variable fixedstringFieldCompSource { 5008 if (*row1->$fieldName != *row2->$fieldName) { 5009 if (*row1->$fieldName < *row2->$fieldName) { 5010 return -1; 5011 } else { 5012 return 1; 5013 } 5014 } 5015 return strncmp (row1->$fieldName, row2->$fieldName, $length); 5016} 5017 5018# 5019# binaryDataFieldCompSource - code we run subst over to generate a comapre of a 5020# inline binary arrays (inets and mac addrs) for use in searching and sorting. 5021# 5022variable binaryDataFieldCompSource { 5023 return memcmp (&row1->$fieldName, &row2->$fieldName, $length); 5024} 5025 5026# 5027# tclobjFieldCompSource - code we run subst over to generate a compare of 5028# a tclobj for use in searching and sorting. 5029# 5030variable tclobjFieldCompSource { 5031 return strcmp (Tcl_GetString (row1->$fieldName), Tcl_GetString (row2->$fieldName)); 5032} 5033 5034# 5035# gen_field_comp - emit code to compare a field for a field comparison routine 5036# 5037proc gen_field_comp {fieldName} { 5038 variable table 5039 variable booleans 5040 variable fields 5041 variable fieldList 5042 variable leftCurly 5043 variable rightCurly 5044 5045 variable numberFieldCompSource 5046 variable fixedstringFieldCompSource 5047 variable binaryDataFieldCompSource 5048 variable varstringFieldCompSource 5049 variable boolFieldCompSource 5050 variable keyCompSource 5051 variable tclobjFieldCompSource 5052 5053 upvar ::ctable::fields::$fieldName field 5054 5055 # First, handle nulls 5056 emit [gen_field_compare_null_check_source $table $fieldName] 5057 5058 switch $field(type) { 5059 key { 5060 emit [string range [subst -nobackslashes -nocommands $keyCompSource] 1 end-1] 5061 } 5062 5063 int { 5064 emit [string range [subst -nobackslashes -nocommands $numberFieldCompSource] 1 end-1] 5065 } 5066 5067 long { 5068 emit [string range [subst -nobackslashes -nocommands $numberFieldCompSource] 1 end-1] 5069 } 5070 5071 wide { 5072 emit [string range [subst -nobackslashes -nocommands $numberFieldCompSource] 1 end-1] 5073 } 5074 5075 double { 5076 emit [string range [subst -nobackslashes -nocommands $numberFieldCompSource] 1 end-1] 5077 } 5078 5079 short { 5080 emit [string range [subst -nobackslashes -nocommands $numberFieldCompSource] 1 end-1] 5081 } 5082 5083 float { 5084 emit [string range [subst -nobackslashes -nocommands $numberFieldCompSource] 1 end-1] 5085 } 5086 5087 char { 5088 emit [string range [subst -nobackslashes -nocommands $numberFieldCompSource] 1 end-1] 5089 } 5090 5091 fixedstring { 5092 set length $field(length) 5093 emit [string range [subst -nobackslashes -nocommands $fixedstringFieldCompSource] 1 end-1] 5094 } 5095 5096 varstring { 5097 emit [string range [subst -nobackslashes -nocommands $varstringFieldCompSource] 1 end-1] 5098 } 5099 5100 boolean { 5101 emit [string range [subst -nobackslashes -nocommands $boolFieldCompSource] 1 end-1] 5102 } 5103 5104 inet { 5105 set length "sizeof(struct in_addr)" 5106 emit [string range [subst -nobackslashes -nocommands $binaryDataFieldCompSource] 1 end-1] 5107 } 5108 5109 mac { 5110 set length "sizeof(struct ether_addr)" 5111 emit [string range [subst -nobackslashes -nocommands $binaryDataFieldCompSource] 1 end-1] 5112 } 5113 5114 tclobj { 5115 emit [string range [subst -nobackslashes -nocommands $tclobjFieldCompSource] 1 end-1] 5116 } 5117 5118 default { 5119 error "attempt to emit sort compare source for field of unknown type $field(type)" 5120 } 5121 } 5122} 5123# 5124# gen_field_compare_functions - generate functions for each field that will 5125# compare that field from two row pointers and return -1, 0, or 1. 5126# 5127proc gen_field_compare_functions {} { 5128 variable table 5129 variable leftCurly 5130 variable rightCurly 5131 variable fieldCompareHeaderSource 5132 variable fieldCompareTrailerSource 5133 variable keyCompareSource 5134 variable fieldList 5135 5136 # generate all of the field compare functions 5137 foreach fieldName $fieldList { 5138 if [is_key $fieldName] { 5139 emit [subst -nobackslashes $keyCompareSource] 5140 continue 5141 } 5142 emit [string range [subst -nobackslashes $fieldCompareHeaderSource] 1 end-1] 5143 gen_field_comp $fieldName 5144 emit [string range [subst -nobackslashes -nocommands $fieldCompareTrailerSource] 1 end-1] 5145 } 5146 5147 # generate an array of pointers to field compare functions for this type 5148 emit "// array of table's field compare routines indexed by field number" 5149 emit "fieldCompareFunction_t ${table}_compare_functions\[] = $leftCurly" 5150 set typeList "" 5151 foreach fieldName $fieldList { 5152 if [is_key $fieldName] { 5153 append typeList "\n ${table}_key_compare," 5154 } else { 5155 append typeList "\n ${table}_field_${fieldName}_compare," 5156 } 5157 } 5158 emit "[string range $typeList 0 end-1]\n$rightCurly;\n" 5159} 5160 5161##### 5162# 5163# Sort Comparison Function Generation 5164# 5165##### 5166 5167variable sortCompareHeaderSource { 5168 5169int ${table}_sort_compare(void *clientData, const ctable_BaseRow *vRow1, const ctable_BaseRow *vRow2) $leftCurly 5170 CTableSort *sortControl = (CTableSort *)clientData; 5171 const struct $table *row1 = *(const struct $table **)vRow1; 5172 const struct $table *row2 = *(const struct $table **)vRow2; 5173 int i; 5174 int direction; 5175 int result = 0; 5176 5177//fprintf (stderr, "sort comp p1 %p, p2 %p\n", row1, row2); 5178 5179 for (i = 0; i < sortControl->nFields; i++) $leftCurly 5180 direction = sortControl->directions[i]; 5181 switch (sortControl->fields[i]) $leftCurly 5182} 5183 5184variable sortCompareTrailerSource { 5185 $rightCurly // end of switch 5186 5187 // if they're not equal, we're done. if they are, we may need to 5188 // compare a subordinate sort field (if there is one) 5189 if (result != 0) { 5190 break; 5191 } 5192 $rightCurly // end of for loop on sort fields 5193 return result; 5194$rightCurly 5195} 5196 5197# 5198# gen_sort_compare_function - generate a function that will compare fields 5199# in two ctable structures for use by qsort 5200# 5201proc gen_sort_compare_function {} { 5202 variable table 5203 variable leftCurly 5204 variable rightCurly 5205 variable sortCompareHeaderSource 5206 variable sortCompareTrailerSource 5207 5208 emit [string range [subst -nobackslashes -nocommands $sortCompareHeaderSource] 1 end-1] 5209 5210 gen_sort_comp 5211 5212 emit [string range [subst -nobackslashes -nocommands $sortCompareTrailerSource] 1 end-1] 5213} 5214 5215# 5216# gen_sort_comp - emit code to compare fields for sorting 5217# 5218proc gen_sort_comp {} { 5219 variable table 5220 variable booleans 5221 variable fields 5222 variable fieldList 5223 variable leftCurly 5224 variable rightCurly 5225 5226 variable numberSortSource 5227 variable fixedstringSortSource 5228 variable binaryDataSortSource 5229 variable varstringSortSource 5230 variable boolSortSource 5231 variable keySortSource 5232 variable tclobjSortSource 5233 5234 foreach fieldName $fieldList { 5235 upvar ::ctable::fields::$fieldName field 5236 5237 set fieldEnum [field_to_enum $fieldName] 5238 5239 switch $field(type) { 5240 key { 5241 emit [string range [subst -nobackslashes $keySortSource] 1 end-1] 5242 } 5243 5244 int { 5245 emit [string range [subst -nobackslashes $numberSortSource] 1 end-1] 5246 } 5247 5248 long { 5249 emit [string range [subst -nobackslashes $numberSortSource] 1 end-1] 5250 } 5251 5252 wide { 5253 emit [string range [subst -nobackslashes $numberSortSource] 1 end-1] 5254 } 5255 5256 double { 5257 emit [string range [subst -nobackslashes $numberSortSource] 1 end-1] 5258 } 5259 5260 short { 5261 emit [string range [subst -nobackslashes $numberSortSource] 1 end-1] 5262 } 5263 5264 float { 5265 emit [string range [subst -nobackslashes $numberSortSource] 1 end-1] 5266 } 5267 5268 char { 5269 emit [string range [subst -nobackslashes $numberSortSource] 1 end-1] 5270 } 5271 5272 fixedstring { 5273 set length $field(length) 5274 emit [string range [subst -nobackslashes $fixedstringSortSource] 1 end-1] 5275 } 5276 5277 varstring { 5278 emit [string range [subst $varstringSortSource] 1 end-1] 5279 } 5280 5281 boolean { 5282 emit [string range [subst -nobackslashes $boolSortSource] 1 end-1] 5283 } 5284 5285 inet { 5286 set length "sizeof(struct in_addr)" 5287 emit [string range [subst -nobackslashes $binaryDataSortSource] 1 end-1] 5288 } 5289 5290 mac { 5291 set length "sizeof(struct ether_addr)" 5292 emit [string range [subst -nobackslashes $binaryDataSortSource] 1 end-1] 5293 } 5294 5295 tclobj { 5296 emit [string range [subst -nobackslashes $tclobjSortSource] 1 end-1] 5297 } 5298 5299 default { 5300 error "attempt to emit sort compare source for field $fieldName of unknown type $field(type)" 5301 } 5302 } 5303 } 5304} 5305 5306##### 5307# 5308# Search Comparison Function Generation 5309# 5310##### 5311 5312variable searchCompareHeaderSource { 5313 5314// compare a row to a block of search components and see if it matches 5315int ${table}_search_compare(Tcl_Interp *interp, CTableSearch *searchControl, ctable_BaseRow *vPointer) $leftCurly 5316 struct $table *row = (struct $table *)vPointer; 5317 struct $table *row1; 5318 5319 int i; 5320 int exclude = 0; 5321 int compType; 5322 CTableSearchComponent *component; 5323 int inIndex; 5324 5325 5326#ifdef SANITY_CHECKS 5327 ${table}_sanity_check_pointer(searchControl->ctable, vPointer, CTABLE_INDEX_NORMAL, "${table}_search_compare"); 5328#endif 5329 5330 for (i = 0; i < searchControl->nComponents; i++) $leftCurly 5331 if (i == searchControl->alreadySearched) 5332 continue; 5333 5334 component = &searchControl->components[i]; 5335 5336 row1 = (struct $table *)component->row1; 5337 compType = component->comparisonType; 5338 5339 // Take care of the common code first 5340 switch (compType) { 5341 case CTABLE_COMP_IN: 5342 if(component->inListRows == NULL && ctable_CreateInRows(interp, searchControl->ctable, component) == TCL_ERROR) { 5343 return TCL_ERROR; 5344 } 5345 5346 for(inIndex = 0; inIndex < component->inCount; inIndex++) { 5347 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)component->inListRows[inIndex]) == 0) { 5348 break; 5349 } 5350 } 5351 5352 if(inIndex >= component->inCount) { 5353 return TCL_CONTINUE; 5354 } 5355 continue; 5356 5357 case CTABLE_COMP_LT: 5358 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row1) < 0) { 5359 continue; 5360 } 5361 return TCL_CONTINUE; 5362 5363 case CTABLE_COMP_LE: 5364 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row1) <= 0) { 5365 continue; 5366 } 5367 return TCL_CONTINUE; 5368 5369 case CTABLE_COMP_EQ: 5370 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row1) == 0) { 5371 continue; 5372 } 5373 return TCL_CONTINUE; 5374 5375 case CTABLE_COMP_NE: 5376 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row1) != 0) { 5377 continue; 5378 } 5379 return TCL_CONTINUE; 5380 5381 case CTABLE_COMP_GE: 5382 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row1) >= 0) { 5383 continue; 5384 } 5385 return TCL_CONTINUE; 5386 5387 case CTABLE_COMP_GT: 5388 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row1) > 0) { 5389 continue; 5390 } 5391 return TCL_CONTINUE; 5392 5393 case CTABLE_COMP_RANGE: { 5394 struct $table *row2; 5395 5396 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row1) < 0) { 5397 return TCL_CONTINUE; 5398 } 5399 5400 row2 = (struct $table *)component->row2; 5401 5402 if (component->compareFunction ((ctable_BaseRow *)row, (ctable_BaseRow *)row2) >= 0) { 5403 return TCL_CONTINUE; 5404 } 5405 continue; 5406 } 5407 } 5408 5409 switch (component->fieldID) $leftCurly 5410} 5411 5412variable searchCompareTrailerSource { 5413 $rightCurly // end of switch on field ID 5414 5415 // if exclude got set, we're done. 5416 if (exclude) { 5417 return TCL_CONTINUE; 5418 } 5419 $rightCurly // end of for loop on search fields 5420 return TCL_OK; 5421$rightCurly 5422} 5423 5424# 5425# gen_search_compare_function - generate a function that see if a row in 5426# a ctable matches the search criteria 5427# 5428proc gen_search_compare_function {} { 5429 variable table 5430 variable leftCurly 5431 variable rightCurly 5432 variable searchCompareHeaderSource 5433 variable searchCompareTrailerSource 5434 5435 emit [string range [subst -nobackslashes -nocommands $searchCompareHeaderSource] 1 end-1] 5436 5437 gen_search_comp 5438 5439 emit [string range [subst -nobackslashes -nocommands $searchCompareTrailerSource] 1 end-1] 5440} 5441 5442# 5443# gen_search_comp - emit code to compare fields for searching 5444# 5445proc gen_search_comp {} { 5446 variable table 5447 variable booleans 5448 variable fields 5449 variable fieldList 5450 variable leftCurly 5451 variable rightCurly 5452 5453 variable numberCompSource 5454 variable fixedstringCompSource 5455 variable binaryDataCompSource 5456 variable varstringCompSource 5457 variable boolCompSource 5458 variable keyCompSource 5459 variable tclobjCompSource 5460 5461 variable standardCompSwitchSource 5462 variable standardCompNullCheckSource 5463 5464 set value sandbag 5465 5466 foreach fieldName $fieldList { 5467 upvar ::ctable::fields::$fieldName field 5468 5469 set fieldEnum [field_to_enum $fieldName] 5470 set type $field(type) 5471 set typeText $field(type) 5472 5473 switch $type { 5474 int { 5475 set getObjCmd Tcl_GetIntFromObj 5476 emit [string range [subst -nobackslashes $numberCompSource] 1 end-1] 5477 } 5478 5479 long { 5480 set getObjCmd Tcl_GetLongFromObj 5481 emit [string range [subst -nobackslashes $numberCompSource] 1 end-1] 5482 } 5483 5484 wide { 5485 set getObjCmd Tcl_GetWideIntFromObj 5486 set typeText "Tcl_WideInt" 5487 set type "Tcl_WideInt" 5488 emit [string range [subst -nobackslashes $numberCompSource] 1 end-1] 5489 } 5490 5491 double { 5492 set getObjCmd Tcl_GetDoubleFromObj 5493 emit [string range [subst -nobackslashes $numberCompSource] 1 end-1] 5494 } 5495 5496 short { 5497 set typeText "int" 5498 set getObjCmd Tcl_GetIntFromObj 5499 emit [string range [subst -nobackslashes $numberCompSource] 1 end-1] 5500 } 5501 5502 float { 5503 set typeText "double" 5504 set getObjCmd Tcl_GetDoubleFromObj 5505 emit [string range [subst -nobackslashes $numberCompSource] 1 end-1] 5506 } 5507 5508 char { 5509 set typeText "int" 5510 set getObjCmd Tcl_GetIntFromObj 5511 emit [string range [subst -nobackslashes $numberCompSource] 1 end-1] 5512 } 5513 5514 fixedstring { 5515 set getObjCmd Tcl_GetString 5516 set length $field(length) 5517 emit [string range [subst -nobackslashes $fixedstringCompSource] 1 end-1] 5518 } 5519 5520 varstring { 5521 set getObjCmd Tcl_GetString 5522 emit [string range [subst -nobackslashes $varstringCompSource] 1 end-1] 5523 } 5524 5525 boolean { 5526 set getObjCmd Tcl_GetBooleanFromObj 5527 emit [string range [subst -nobackslashes $boolCompSource] 1 end-1] 5528 } 5529 5530 inet { 5531 set getObjCmd Tcl_GetStringFromObj 5532 set length "sizeof(struct in_addr)" 5533 emit [string range [subst -nobackslashes $binaryDataCompSource] 1 end-1] 5534 } 5535 5536 mac { 5537 set getObjCmd Tcl_GetStringFromObj 5538 set length "sizeof(struct ether_addr)" 5539 emit [string range [subst -nobackslashes $binaryDataCompSource] 1 end-1] 5540 } 5541 5542 tclobj { 5543 set getObjCmd Tcl_GetStringFromObj 5544 emit [string range [subst -nobackslashes $tclobjCompSource] 1 end-1] 5545 } 5546 5547 key { 5548 set getObjCmd Tcl_GetString 5549 set length "strlen(row->hashEntry.key)" 5550 emit [string range [subst -nobackslashes $keyCompSource] 1 end-1] 5551 } 5552 5553 default { 5554 error "attempt to emit search compare source for field of unknown type $field(type)" 5555 } 5556 } 5557 } 5558} 5559 5560##### 5561# 5562# Invoking the Compiler 5563# 5564##### 5565 5566proc myexec {command} { 5567 variable showCompilerCommands 5568 5569 if {$showCompilerCommands} { 5570 puts $command; flush stdout 5571 } 5572 5573 eval exec $command 5574} 5575 5576# 5577# Generate the fully qualified path to a file 5578# 5579proc target_name {name version {ext .cpp}} { 5580 return [file join [target_path $name] $name-$version$ext] 5581} 5582 5583# 5584# Generate the path to the target files 5585# 5586# Either buildPath or buildPath/$name 5587# 5588# And make sure it exists! 5589# 5590proc target_path {name} { 5591 variable buildPath 5592 variable withSubdir 5593 variable dirStatus 5594 5595 set path $buildPath 5596 5597 if {$withSubdir} { 5598 set path [file join $path $name] 5599 } 5600 5601 if {![info exists dirStatus($path)]} { 5602 set dirStatus($path) [file isdirectory $path] 5603 if {!$dirStatus($path)} { 5604 file mkdir $path 5605 } 5606 } 5607 5608 return $path 5609} 5610 5611# 5612# compile - compile and link the shared library 5613# 5614proc compile {fileFragName version} { 5615 global tcl_platform 5616 variable buildPath 5617 variable sysFlags 5618 variable withPgtcl 5619 variable withCasstcl 5620 variable genCompilerDebug 5621 variable memDebug 5622 variable withPipe 5623 variable withSubdir 5624 5625 variable sysconfig 5626 5627 set include [target_path include] 5628 set targetPath [target_path $fileFragName] 5629 set sourceFile [target_name $fileFragName $version] 5630 set objFile [target_name $fileFragName $version .o] 5631 set buildFile [target_name $fileFragName $version .sh] 5632 5633 if {$withPipe} { 5634 set pipeFlag "-pipe" 5635 } else { 5636 set pipeFlag "" 5637 } 5638 5639 set stubs [info exists sysconfig(stub)] 5640 5641 if {$genCompilerDebug} { 5642 set optflag "-Os" 5643 set dbgflag $sysconfig(dbg) 5644 5645 if {$memDebug} { 5646 set memSuffix m 5647 } else { 5648 set memSuffix "" 5649 } 5650 5651 if {$stubs} { 5652 set stub "$sysconfig(stubg)$memSuffix" 5653 } 5654 set lib "$sysconfig(libg)$memSuffix" 5655 5656 } else { 5657 set optflag $sysconfig(opt) 5658 set dbgflag "" 5659 5660 if {$stubs} { 5661 set stub " $sysconfig(stub)" 5662 } 5663 set lib $sysconfig(lib) 5664 } 5665 5666 if {$stubs} { 5667 set stubString "-DUSE_TCL_STUBS=$stubs" 5668 } else { 5669 set stubString "" 5670 } 5671 5672 # put -DTCL_MEM_DEBUG in there if you're building with 5673 # memory debugging (see Tcl docs) 5674 if {$memDebug} { 5675 set memDebugString "-DTCL_MEM_DEBUG=1" 5676 } else { 5677 set memDebugString "" 5678 } 5679 5680 if {[info exists sysFlags($tcl_platform(os))]} { 5681 set sysString $sysFlags($tcl_platform(os)) 5682 } else { 5683 set sysString "" 5684 } 5685 5686 if {$withPgtcl} { 5687 set pgString -I$sysconfig(pgtclprefix)/include 5688 if [info exists sysconfig(pqinclude)] { 5689 if {"$sysconfig(pqinclude)" != "$sysconfig(pgtclprefix)/include"} { 5690 append pgString " -I$sysconfig(pqinclude)" 5691 } 5692 } elseif [info exists sysconfig(pqprefix)] { 5693 if {"$sysconfig(pqprefix)" != "$sysconfig(pgtclprefix)"} { 5694 append pgString " -I$sysconfig(pqprefix)/include" 5695 } 5696 } 5697 } else { 5698 set pgString "" 5699 } 5700 5701 if {$withCasstcl} { 5702 set cassString -I$sysconfig(cassprefix)/include 5703 } else { 5704 set cassString "" 5705 } 5706 5707 # Keep sysconfig(ccflags) from overriding optimization level 5708 regsub -all { -O[0-9] } " $sysconfig(ccflags) " { } sysconfig(ccflags) 5709 5710 set cc_cmd "$sysconfig(cxx) $sysString $optflag $dbgflag $sysconfig(ldflags) $sysconfig(ccflags) -I$include $sysconfig(warn) $pgString $cassString $stubString $memDebugString -c $sourceFile -o $objFile 2>@stderr" 5711 myexec $cc_cmd 5712 5713 set ld_cmd "$sysconfig(cxxld) $dbgflag -o $targetPath/lib${fileFragName}$sysconfig(shlib) $objFile" 5714 5715 if {$withPgtcl} { 5716 set pgtcl_libdir $sysconfig(pgtclprefix) 5717 set pgtcl_ver $sysconfig(pgtclver) 5718 set pgtcl_lib pgtcl$pgtcl_ver 5719 set pq_libdir /usr/local/lib 5720 if {[info exists sysconfig(pqlibdir)]} { 5721 set pq_libdir $sysconfig(pqlibdir) 5722 } 5723 5724 append ld_cmd " -Wl,-rpath,$pgtcl_libdir" 5725 append ld_cmd " -L$pgtcl_libdir -l$pgtcl_lib" 5726 append ld_cmd " -L$pq_libdir -lpq" 5727 } 5728 5729 if {$withCasstcl} { 5730 set casstcl_libdir $sysconfig(casstclprefix) 5731 set casstcl_ver $sysconfig(casstclver) 5732 #set casstcl_lib casstcl$casstcl_ver 5733 set casstcl_lib casstcl 5734 5735 append ld_cmd " -Wl,-rpath,$casstcl_libdir" 5736 append ld_cmd " -L$casstcl_libdir" 5737 append ld_cmd " -l$casstcl_lib -L/usr/local/lib -lcassandra" 5738 } 5739 5740 append ld_cmd " $sysconfig(ldflags) $stub" 5741 myexec "$ld_cmd 2>@stderr" 5742 5743 set fp [open $buildFile w] 5744 puts $fp "# Rebuild $fileFragName $version" 5745 puts $fp $cc_cmd 5746 puts $fp $ld_cmd 5747 close $fp 5748 5749 if {$withSubdir} { 5750 set pkg_args [list $buildPath */*.tcl */*[info sharedlibextension]] 5751 } else { 5752 set pkg_args [list $buildPath] 5753 } 5754 5755 variable showCompilerCommands 5756 if {$showCompilerCommands} { 5757 puts [concat + pkg_mkIndex -verbose $pkg_args] 5758 eval pkg_mkIndex -verbose $pkg_args 5759 } else { 5760 eval pkg_mkIndex $pkg_args 5761 } 5762} 5763 5764proc EndExtension {} { 5765 variable tables 5766 variable extension 5767 variable withSharedTables 5768 variable extensionVersion 5769 variable rightCurly 5770 variable ofp 5771 variable memDebug 5772 5773 put_init_extension_source [string totitle $extension] $extensionVersion 5774 5775 if {$withSharedTables} { 5776 emit " Shared_Init(interp);" 5777 } 5778 5779 foreach name $tables { 5780 put_init_command_source $name 5781 } 5782 5783 emit " return TCL_OK;" 5784 emit $rightCurly 5785 5786 close $ofp 5787 unset ofp 5788 5789 compile $extension $::ctable::extensionVersion 5790} 5791 5792# 5793# extension_already_built - see if the extension already exists unchanged 5794# from what's being asked for 5795# 5796proc extension_already_built {name version code} { 5797 # if open of the stash file fails, it ain't built 5798 if {[catch {open [target_name $name $version .ct]} fp] == 1} { 5799 #puts ".ct file not there, build required" 5800 return 0 5801 } 5802 5803 # read the first line for the prior CVS ID, if failed, report not built 5804 if {[gets $fp controlLine] < 0} { 5805 #puts "first line read of .ct file failed, build required" 5806 close $fp 5807 return 0 5808 } 5809 5810 # See if this file's control line matches the line in the .ct file. 5811 # If not, rebuild not built. 5812 if {$controlLine != [control_line]} { 5813 #puts "control line does not match, build required" 5814 close $fp 5815 return 0 5816 } 5817 5818 set priorCode [read -nonewline $fp] 5819 close $fp 5820 5821 # if the prior code and current code aren't identical, report not built 5822 if {$priorCode != $code} { 5823 #puts "extension code changed, build required" 5824 return 0 5825 } 5826 5827 #puts "prior code and generator cvs match, build not required" 5828 return 1 5829} 5830 5831# This is a unique ID that should change whenever anything significant 5832# changes in ctables 5833proc control_line {} { 5834 variable srcDir 5835 variable cvsID 5836 variable keyCompileVariables 5837 5838 foreach v $keyCompileVariables { 5839 variable $v 5840 if [info exists $v] { 5841 lappend compileSettings "$v=[set $v]" 5842 } else { 5843 lappend compileSettings "$v?" 5844 } 5845 } 5846 set compileSettings [join $compileSettings ":"] 5847 5848 return "$cvsID $compileSettings [file mtime $srcDir] [info patchlevel]" 5849} 5850 5851# 5852# save_extension_code - after a successful build, cache the extension 5853# definition so extension_already_built can see if it's necessary to 5854# generate, compile and link the shared library next time we're run 5855# 5856proc save_extension_code {name version code} { 5857 set filename [target_name $name $version .ct] 5858 set fp [open $filename w] 5859 5860 puts $fp [control_line] 5861 puts $fp $code 5862 5863 close $fp 5864} 5865 5866# 5867# install_ch_files - install .h in the target dir if something like it 5868# isn't there already 5869# 5870proc install_ch_files {includeDir} { 5871 variable srcDir 5872 variable withSharedTables 5873 5874 lappend subdirs skiplists hash 5875 5876 set copyFiles { 5877 ctable.h ctable_search.c ctable_lists.c ctable_batch.c 5878 boyer_moore.c jsw_rand.c jsw_rand.h jsw_slib.c jsw_slib.h 5879 speedtables.h speedtableHash.c ctable_io.c ctable_qsort.c 5880 ethers.c 5881 } 5882 5883 if {$withSharedTables} { 5884 lappend copyFiles shared.c shared.h 5885 lappend subdirs shared 5886 } 5887 5888 emit "// Importing .c and .h files to '$includeDir'\n//" 5889 foreach file $copyFiles { 5890 set fullName [file join $srcDir $file] 5891 5892 if {![file exists $fullName]} { 5893 unset fullName 5894 5895 foreach dir $subdirs { 5896 set fullName [file join $srcDir $dir $file] 5897 5898 if {![file exists $fullName]} { 5899 unset fullName 5900 } else { 5901 break 5902 } 5903 } 5904 } 5905 5906 if [info exists fullName] { 5907 file copy -force $fullName $includeDir 5908 emit "// Imported '$fullName'" 5909 } else { 5910 return -code error "Can't find $file in $srcDir" 5911 } 5912 } 5913 emit "// Import complete\n" 5914} 5915 5916# 5917# get_error_info - to keep tracebacks from containing lots of internals 5918# of ctable stuff, we scarf errorInfo into ctableErrorInfo if we get 5919# an error interpreting a CExtension/CTable definition. This allows 5920# one to get the error info if debugging is required, etc. 5921# 5922proc get_error_info {} { 5923 variable ctableErrorInfo 5924 5925 return $ctableErrorInfo 5926} 5927 5928} 5929 5930# 5931# CExtension - define a C extension 5932# 5933proc CExtension {name version code} { 5934 uplevel 1 [list _speedtables $name $version $code] 5935} 5936 5937# 5938# speedtables - define a Speedtable package 5939# 5940proc speedtables {name version code} { 5941 if {![string is upper [string index $name 0]]} { 5942 error "Speed Tables package name must start with an uppercase letter" 5943 } 5944 foreach char [split $name ""] { 5945 if [string is digit $char] { 5946 error "Speed Tables package name can not include any digits" 5947 } 5948 } 5949 uplevel 1 [list _speedtables $name $version $code] 5950} 5951 5952# 5953# _speedtables - Common code to define a package 5954# 5955proc _speedtables {name version code} { 5956 global tcl_platform errorInfo errorCode 5957 5958 # clear the error info placeholder 5959 set ctableErrorInfo "" 5960 5961 if {![info exists ::ctable::buildPath]} { 5962 CTableBuildPath stobj 5963 } 5964 5965 set path [file normalize $::ctable::buildPath] 5966 file mkdir $path 5967 5968 if {[::ctable::extension_already_built $name $version $code]} { 5969 #puts stdout "extension $name $version unchanged" 5970 return 5971 } 5972 5973 set ::ctable::sourceCode $code 5974 set ::ctable::sourceFile [::ctable::target_name $name $version] 5975 set ::ctable::extension $name 5976 set ::ctable::extensionVersion $version 5977 set ::ctable::tables "" 5978 5979 if {[catch {namespace eval ::ctable $code} result] == 1} { 5980 set ::ctable::ctableErrorInfo $errorInfo 5981 5982 if $::ctable::errorDebug { 5983 return -code error -errorcode $errorCode -errorinfo $errorInfo 5984 } else { 5985 return -code error -errorcode $errorCode "$result\n(run ::ctable::get_error_info to see ctable's internal errorInfo)" 5986 } 5987 } 5988 5989 ::ctable::EndExtension 5990 5991 ::ctable::save_extension_code $name $version $code 5992} 5993 5994## 5995## start_ctable_codegen - can't be run until the ctable is loaded 5996## 5997proc start_codegen {} { 5998 if [info exists ::ctable::ofp] { 5999 return 6000 } 6001 6002 set ::ctable::ofp [open $::ctable::sourceFile w] 6003 6004 ::ctable::gen_preamble 6005 6006 ::ctable::gen_ctable_type_stuff 6007 6008 # This runs here so we have the log of where we got files from in 6009 # the right place 6010 ::ctable::install_ch_files [::ctable::target_path include] 6011 6012 ::ctable::emit "#include \"ctable_io.c\"" 6013 6014 ::ctable::emit "#include \"ctable_search.c\"" 6015 6016 ::ctable::emit "static CONST char *sourceCode = \"[::ctable::cquote "CExtension $::ctable::extension $::ctable::extensionVersion { $::ctable::sourceCode }"]\";" 6017 ::ctable::emit "" 6018 6019 ::ctable::emit "static CONST char *ctablePackageVersion = \"$::ctable::ctablePackageVersion\";" 6020 6021 if [info exists ::ctable::rawCode] { 6022 ::ctable::emit "// BEGIN imported C Code" 6023 foreach block $::ctable::rawCode { 6024 ::ctable::emit "$block\n" 6025 } 6026 ::ctable::emit "// END imported C Code" 6027 } 6028} 6029 6030# 6031# ccode - pass C code (such as helper functions) through 6032# 6033proc ccode {block} { 6034 lappend ::ctable::rawCode $block 6035} 6036 6037# 6038# CTable - define a C meta table 6039# 6040proc CTable {name data} { 6041 uplevel 1 [list table $name $data] 6042} 6043 6044# 6045# table - define a Speed Tables table 6046# 6047proc table {name data} { 6048 ::ctable::new_table $name 6049 lappend ::ctable::tables $name 6050 6051 namespace eval ::ctable $data 6052 6053 ::ctable::sanity_check 6054 6055 start_codegen 6056 6057 # Create a key field if there isn't already one 6058 ::ctable::key _key 6059 6060 if {$::ctable::withDirty} { 6061 # Create a 'dirty' field 6062 ::ctable::boolean _dirty notnull 1 default 0 6063 } 6064 6065 ::ctable::gen_struct 6066 6067 ::ctable::gen_field_names 6068 6069 ::ctable::gen_filters 6070 6071 ::ctable::gen_setup_routine $name 6072 6073 ::ctable::gen_defaults_subr $name 6074 6075 ::ctable::gen_delete_subr ${name}_delete $name 6076 6077 ::ctable::gen_obj_is_null_subr 6078 6079 ::ctable::gen_list 6080 6081 ::ctable::gen_keyvalue_list 6082 6083 ::ctable::gen_nonnull_keyvalue_list 6084 6085 ::ctable::gen_code 6086 6087 ::ctable::put_metatable_source $name 6088 6089} 6090 6091# 6092# CTableBuildPath - set the path for where we're building CTable stuff 6093# 6094proc CTableBuildPath {{dir ""}} { 6095 if {$dir == ""} { 6096 if {![info exists ::ctable::buildPath]} { 6097 CTableBuildPath stobj 6098 } 6099 return $::ctable::buildPath 6100 } 6101 6102 set ::ctable::buildPath $dir 6103 6104 if {[lsearch -exact $::auto_path $dir] < 0} { 6105 lappend ::auto_path $dir 6106 } 6107} 6108 6109package provide ctable $::ctable::ctablePackageVersion 6110package provide speedtable $::ctable::ctablePackageVersion 6111 6112# vim: set ts=8 sw=4 sts=4 noet : 6113