00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 namespace genStubs {
00014
00015
00016
00017
00018
00019 variable libraryName "UNKNOWN"
00020
00021
00022
00023
00024
00025
00026 array interfaces = {}
00027
00028
00029
00030
00031
00032 variable curName "UNKNOWN"
00033
00034
00035
00036
00037
00038
00039 array hooks = {}
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051 array stubs = {}
00052
00053
00054
00055
00056
00057 variable outDir .
00058 }
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 ret genStubs::library (type name) {
00073 variable libraryName $name
00074 }
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 ret genStubs::interface (type name) {
00088 variable curName $name
00089 variable interfaces
00090
00091 set interfaces($name) {}
00092 return
00093 }
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107 ret genStubs::hooks (type names) {
00108 variable curName
00109 variable hooks
00110
00111 set hooks($curName) $names
00112 return
00113 }
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130 ret genStubs::declare (type args) {
00131 variable stubs
00132 variable curName
00133
00134 if {[llength $args] != 3} {
00135 puts stderr "wrong # args: declare $args"
00136 }
00137 lassign $args index platformList decl
00138
00139 # Check for duplicate declarations, then add the declaration and
00140 # bump the lastNum counter if necessary.
00141
00142 foreach platform $platformList {
00143 if {[info exists stubs($curName,$platform,$index)]} {
00144 puts stderr "Duplicate entry: declare $args"
00145 }
00146 }
00147 regsub -all "\[ \t\n\]+" [string trim $decl] " " decl
00148 set decl [parseDecl $decl]
00149
00150 foreach platform $platformList {
00151 if {$decl != ""} {
00152 set stubs($curName,$platform,$index) $decl
00153 if {![info exists stubs($curName,$platform,lastNum)] \
00154 || ($index > $stubs($curName,$platform,lastNum))} {
00155 set stubs($curName,$platform,lastNum) $index
00156 }
00157 }
00158 }
00159 return
00160 }
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175 ret genStubs::rewriteFile (type file , type text) {
00176 if {![file exist $file]} {
00177 puts stderr "Cannot find file: $file"
00178 return
00179 }
00180 set in [open ${file} r]
00181 set out [open ${file}.new w]
00182
00183 # Always write out the file with LF termination
00184 fconfigure $out -translation lf
00185
00186 while {![eof $in]} {
00187 set line [gets $in]
00188 if {[regexp {!BEGIN!} $line]} {
00189 break
00190 }
00191 puts $out $line
00192 }
00193 puts $out "/* !BEGIN!: Do not edit below this line. */"
00194 puts $out $text
00195 while {![eof $in]} {
00196 set line [gets $in]
00197 if {[regexp {!END!} $line]} {
00198 break
00199 }
00200 }
00201 puts $out "/* !END!: Do not edit above this line. */"
00202 puts -nonewline $out [read $in]
00203 close $in
00204 close $out
00205 file rename -force ${file}.new ${file}
00206 return
00207 }
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219 ret genStubs::addPlatformGuard (type plat , type text) {
00220 switch $plat {
00221 win {
00222 return "#ifdef __WIN32__\n${text}#endif /* __WIN32__ */\n"
00223 }
00224 unix {
00225 return "#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */\n${text}#endif /* UNIX */\n"
00226 }
00227 mac {
00228 return "#ifdef MAC_TCL\n${text}#endif /* MAC_TCL */\n"
00229 }
00230 }
00231 return "$text"
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 ret genStubs::emitSlots (type name , type textVar) {
00248 variable stubs
00249 upvar $textVar text
00250
00251 forAllStubs $name makeSlot 1 text {" void *reserved$i;\n"}
00252 return
00253 }
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 ret genStubs::parseDecl (type decl) {
00269 if {![regexp {^(.*)\((.*)\)$} $decl all prefix args]} {
00270 puts stderr "Malformed declaration: $decl"
00271 return
00272 }
00273 set prefix [string trim $prefix]
00274 if {![regexp {^(.+[ ][*]*)([^ *]+)$} $prefix all rtype fname]} {
00275 puts stderr "Bad return type: $decl"
00276 return
00277 }
00278 set rtype [string trim $rtype]
00279 foreach arg [split $args ,] {
00280 lappend argList [string trim $arg]
00281 }
00282 if {![string compare [lindex $argList end] "..."]} {
00283 if {[llength $argList] != 2} {
00284 puts stderr "Only one argument is allowed in varargs form: $decl"
00285 }
00286 set arg [parseArg [lindex $argList 0]]
00287 if {$arg == "" || ([llength $arg] != 2)} {
00288 puts stderr "Bad argument: '[lindex $argList 0]' in '$decl'"
00289 return
00290 }
00291 set args [list TCL_VARARGS $arg]
00292 } else {
00293 set args {}
00294 foreach arg $argList {
00295 set argInfo [parseArg $arg]
00296 if {![string compare $argInfo "void"]} {
00297 lappend args "void"
00298 break
00299 } elseif {[llength $argInfo] == 2 || [llength $argInfo] == 3} {
00300 lappend args $argInfo
00301 } else {
00302 puts stderr "Bad argument: '$arg' in '$decl'"
00303 return
00304 }
00305 }
00306 }
00307 return [list $rtype $fname $args]
00308 }
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321 ret genStubs::parseArg (type arg) {
00322 if {![regexp {^(.+[ ][*]*)([^][ *]+)(\[\])?$} $arg all type name array]} {
00323 if {$arg == "void"} {
00324 return $arg
00325 } else {
00326 return
00327 }
00328 }
00329 set result [list [string trim $type] $name]
00330 if {$array != ""} {
00331 lappend result $array
00332 }
00333 return $result
00334 }
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348 ret genStubs::makeDecl (type name , type decl , type index) {
00349 lassign $decl rtype fname args
00350
00351 append text "/* $index */\n"
00352 set line "EXTERN $rtype"
00353 set count [expr {2 - ([string length $line] / 8)}]
00354 append line [string range "\t\t\t" 0 $count]
00355 set pad [expr {24 - [string length $line]}]
00356 if {$pad <= 0} {
00357 append line " "
00358 set pad 0
00359 }
00360 append line "$fname _ANSI_ARGS_("
00361
00362 set arg1 [lindex $args 0]
00363 switch -exact $arg1 {
00364 void {
00365 append line "(void)"
00366 }
00367 TCL_VARARGS {
00368 set arg [lindex $args 1]
00369 append line "TCL_VARARGS([lindex $arg 0],[lindex $arg 1])"
00370 }
00371 default {
00372 set sep "("
00373 foreach arg $args {
00374 append line $sep
00375 set next {}
00376 append next [lindex $arg 0] " " [lindex $arg 1] \
00377 [lindex $arg 2]
00378 if {[string length $line] + [string length $next] \
00379 + $pad > 76} {
00380 append text $line \n
00381 set line "\t\t\t\t"
00382 set pad 28
00383 }
00384 append line $next
00385 set sep ", "
00386 }
00387 append line ")"
00388 }
00389 }
00390 append text $line
00391
00392 append text ");\n"
00393 return $text
00394 }
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408 ret genStubs::makeMacro (type name , type decl , type index) {
00409 lassign $decl rtype fname args
00410
00411 set lfname [string tolower [string index $fname 0]]
00412 append lfname [string range $fname 1 end]
00413
00414 set text "#ifndef $fname\n#define $fname"
00415 set arg1 [lindex $args 0]
00416 set argList ""
00417 switch -exact $arg1 {
00418 void {
00419 set argList "()"
00420 }
00421 TCL_VARARGS {
00422 }
00423 default {
00424 set sep "("
00425 foreach arg $args {
00426 append argList $sep [lindex $arg 1]
00427 set sep ", "
00428 }
00429 append argList ")"
00430 }
00431 }
00432 append text " \\\n\t(${name}StubsPtr->$lfname)"
00433 append text " /* $index */\n#endif\n"
00434 return $text
00435 }
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449 ret genStubs::makeStub (type name , type decl , type index) {
00450 lassign $decl rtype fname args
00451
00452 set lfname [string tolower [string index $fname 0]]
00453 append lfname [string range $fname 1 end]
00454
00455 append text "/* Slot $index */\n" $rtype "\n" $fname
00456
00457 set arg1 [lindex $args 0]
00458
00459 if {![string compare $arg1 "TCL_VARARGS"]} {
00460 lassign [lindex $args 1] type argName
00461 append text " TCL_VARARGS_DEF($type,$argName)\n\{\n"
00462 append text " " $type " var;\n va_list argList;\n"
00463 if {[string compare $rtype "void"]} {
00464 append text " " $rtype " resultValue;\n"
00465 }
00466 append text "\n var = (" $type ") TCL_VARARGS_START(" \
00467 $type "," $argName ",argList);\n\n "
00468 if {[string compare $rtype "void"]} {
00469 append text "resultValue = "
00470 }
00471 append text "(" $name "StubsPtr->" $lfname "VA)(var, argList);\n"
00472 append text " va_end(argList);\n"
00473 if {[string compare $rtype "void"]} {
00474 append text "return resultValue;\n"
00475 }
00476 append text "\}\n\n"
00477 return $text
00478 }
00479
00480 if {![string compare $arg1 "void"]} {
00481 set argList "()"
00482 set argDecls ""
00483 } else {
00484 set argList ""
00485 set sep "("
00486 foreach arg $args {
00487 append argList $sep [lindex $arg 1]
00488 append argDecls " " [lindex $arg 0] " " \
00489 [lindex $arg 1] [lindex $arg 2] ";\n"
00490 set sep ", "
00491 }
00492 append argList ")"
00493 }
00494 append text $argList "\n" $argDecls "{\n "
00495 if {[string compare $rtype "void"]} {
00496 append text "return "
00497 }
00498 append text "(" $name "StubsPtr->" $lfname ")" $argList ";\n}\n\n"
00499 return $text
00500 }
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514 ret genStubs::makeSlot (type name , type decl , type index) {
00515 lassign $decl rtype fname args
00516
00517 set lfname [string tolower [string index $fname 0]]
00518 append lfname [string range $fname 1 end]
00519
00520 set text " "
00521 append text $rtype " (*" $lfname ") _ANSI_ARGS_("
00522
00523 set arg1 [lindex $args 0]
00524 switch -exact $arg1 {
00525 void {
00526 append text "(void)"
00527 }
00528 TCL_VARARGS {
00529 set arg [lindex $args 1]
00530 append text "TCL_VARARGS([lindex $arg 0],[lindex $arg 1])"
00531 }
00532 default {
00533 set sep "("
00534 foreach arg $args {
00535 append text $sep [lindex $arg 0] " " [lindex $arg 1] \
00536 [lindex $arg 2]
00537 set sep ", "
00538 }
00539 append text ")"
00540 }
00541 }
00542
00543 append text "); /* $index */\n"
00544 return $text
00545 }
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559 ret genStubs::makeInit (type name , type decl , type index) {
00560 append text " " [lindex $decl 1] ", /* " $index " */\n"
00561 return $text
00562 }
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585 ret genStubs::forAllStubs (type name , type slotProc , type onAll , type textVar \
00586 , optional skipString ={"/* Slot =$i is =reserved */\n")} {
00587 variable stubs
00588 upvar $textVar text
00589
00590 set plats [array names stubs $name,*,lastNum]
00591 if {[info exists stubs($name,generic,lastNum)]} {
00592
00593 lastNum = -1
00594 foreach plat [array names stubs $name,*,lastNum] {
00595 if {$stubs($plat) > $lastNum} {
00596 lastNum = $stubs($plat)
00597 }
00598 }
00599 for { i = 0} {$i <= $lastNum} {incr i} {
00600 slots = [array names stubs $name,*,$i]
00601 emit = 0
00602 if {[info exists stubs($name,generic,$i)]} {
00603 if {[llength $slots] > 1} {
00604 puts stderr "platform entry duplicates generic entry: $i"
00605 }
00606 append text [$slotProc $name $stubs($name,generic,$i) $i]
00607 emit = 1
00608 } elseif {[llength $slots] > 0} {
00609 foreach plat {unix win mac} {
00610 if {[info exists stubs($name,$plat,$i)]} {
00611 append text [addPlatformGuard $plat \
00612 [$slotProc $name $stubs($name,$plat,$i) $i]]
00613 emit = 1
00614 } elseif {$onAll} {
00615 append text [eval {addPlatformGuard $plat} $skipString]
00616 emit = 1
00617 }
00618 }
00619 }
00620 if {$emit == 0} {
00621 eval {append text} $skipString
00622 }
00623 }
00624
00625 } else {
00626
00627 foreach plat {unix win mac} {
00628 if {[info exists stubs($name,$plat,lastNum)]} {
00629 lastNum = $stubs($name,$plat,lastNum)
00630 temp = {}
00631 for { i = 0} {$i <= $lastNum} {incr i} {
00632 if {![info exists stubs($name,$plat,$i)]} {
00633 eval {append temp} $skipString
00634 } else {
00635 append temp [$slotProc $name $stubs($name,$plat,$i) $i]
00636 }
00637 }
00638 append text [addPlatformGuard $plat $temp]
00639 }
00640 }
00641 }
00642
00643 }
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656 ret genStubs::emitDeclarations (type name , type textVar) {
00657 variable stubs
00658 upvar $textVar text
00659
00660 append text "\n/*\n * Exported function declarations:\n */\n\n"
00661 forAllStubs $name makeDecl 0 text
00662 return
00663 }
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676 ret genStubs::emitMacros (type name , type textVar) {
00677 variable stubs
00678 variable libraryName
00679 upvar $textVar text
00680
00681 set upName [string toupper $libraryName]
00682 append text "\n#if defined(USE_${upName}_STUBS) && !defined(USE_${upName}_STUB_PROCS)\n"
00683 append text "\n/*\n * Inline function declarations:\n */\n\n"
00684
00685 forAllStubs $name makeMacro 0 text
00686
00687 append text "\n#endif /* defined(USE_${upName}_STUBS) && !defined(USE_${upName}_STUB_PROCS) */\n"
00688 return
00689 }
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702 ret genStubs::emitHeader (type name) {
00703 variable outDir
00704 variable hooks
00705
00706 set capName [string toupper [string index $name 0]]
00707 append capName [string range $name 1 end]
00708
00709 emitDeclarations $name text
00710
00711 if {[info exists hooks($name)]} {
00712 append text "\ntypedef struct ${capName}StubHooks {\n"
00713 foreach hook $hooks($name) {
00714 set capHook [string toupper [string index $hook 0]]
00715 append capHook [string range $hook 1 end]
00716 append text " struct ${capHook}Stubs *${hook}Stubs;\n"
00717 }
00718 append text "} ${capName}StubHooks;\n"
00719 }
00720 append text "\ntypedef struct ${capName}Stubs {\n"
00721 append text " int magic;\n"
00722 append text " struct ${capName}StubHooks *hooks;\n\n"
00723
00724 emitSlots $name text
00725
00726 append text "} ${capName}Stubs;\n"
00727
00728 append text "\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n"
00729 append text "extern ${capName}Stubs *${name}StubsPtr;\n"
00730 append text "#ifdef __cplusplus\n}\n#endif\n"
00731
00732 emitMacros $name text
00733
00734 rewriteFile [file join $outDir ${name}Decls.h] $text
00735 return
00736 }
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749 ret genStubs::emitStubs (type name) {
00750 variable outDir
00751
00752 append text "\n/*\n * Exported stub functions:\n */\n\n"
00753 forAllStubs $name makeStub 0 text
00754
00755 rewriteFile [file join $outDir ${name}Stubs.c] $text
00756 return
00757 }
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770 ret genStubs::emitInit (type name , type textVar) {
00771 variable stubs
00772 variable hooks
00773 upvar $textVar text
00774
00775 set capName [string toupper [string index $name 0]]
00776 append capName [string range $name 1 end]
00777
00778 if {[info exists hooks($name)]} {
00779 append text "\nstatic ${capName}StubHooks ${name}StubHooks = \{\n"
00780 set sep " "
00781 foreach sub $hooks($name) {
00782 append text $sep "&${sub}Stubs"
00783 set sep ",\n "
00784 }
00785 append text "\n\};\n"
00786 }
00787 append text "\n${capName}Stubs ${name}Stubs = \{\n"
00788 append text " TCL_STUB_MAGIC,\n"
00789 if {[info exists hooks($name)]} {
00790 append text " &${name}StubHooks,\n"
00791 } else {
00792 append text " NULL,\n"
00793 }
00794
00795 forAllStubs $name makeInit 1 text {" NULL, /* $i */\n"}
00796
00797 append text "\};\n"
00798 return
00799 }
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812 ret genStubs::emitInits () {
00813 variable hooks
00814 variable outDir
00815 variable libraryName
00816 variable interfaces
00817
00818 # Assuming that dependencies only go one level deep, we need to emit
00819 # all of the leaves first to avoid needing forward declarations.
00820
00821 set leaves {}
00822 set roots {}
00823 foreach name [lsort [array names interfaces]] {
00824 if {[info exists hooks($name)]} {
00825 lappend roots $name
00826 } else {
00827 lappend leaves $name
00828 }
00829 }
00830 foreach name $leaves {
00831 emitInit $name text
00832 }
00833 foreach name $roots {
00834 emitInit $name text
00835 }
00836
00837 rewriteFile [file join $outDir ${libraryName}StubInit.c] $text
00838 }
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850 ret genStubs::init () {
00851 global argv argv0
00852 variable outDir
00853 variable interfaces
00854
00855 if {[llength $argv] < 2} {
00856 puts stderr "usage: $argv0 outDir declFile ?declFile...?"
00857 exit 1
00858 }
00859
00860 set outDir [lindex $argv 0]
00861
00862 foreach file [lrange $argv 1 end] {
00863 source $file
00864 }
00865
00866 foreach name [lsort [array names interfaces]] {
00867 puts "Emitting $name"
00868 emitHeader $name
00869 }
00870
00871 emitInits
00872 }
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885 ret lassign (type valueList , type args) {
00886 if {[llength $args] == 0} {
00887 error "wrong # args: lassign list varname ?varname..?"
00888 }
00889
00890 uplevel [list foreach $args $valueList {break}]
00891 return [lrange $valueList [llength $args] end]
00892 }
00893
00894 genStubs::init
00895