os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/tools/genStubs.tcl
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
# genStubs.tcl --
sl@0
     2
#
sl@0
     3
#	This script generates a set of stub files for a given
sl@0
     4
#	interface.  
sl@0
     5
#	
sl@0
     6
#
sl@0
     7
# Copyright (c) 1998-1999 by Scriptics Corporation.
sl@0
     8
# See the file "license.terms" for information on usage and redistribution
sl@0
     9
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    10
# 
sl@0
    11
# RCS: @(#) $Id: genStubs.tcl,v 1.13 2002/10/04 08:25:14 dkf Exp $
sl@0
    12
sl@0
    13
package require Tcl 8
sl@0
    14
sl@0
    15
namespace eval genStubs {
sl@0
    16
    # libraryName --
sl@0
    17
    #
sl@0
    18
    #	The name of the entire library.  This value is used to compute
sl@0
    19
    #	the USE_*_STUB_PROCS macro and the name of the init file.
sl@0
    20
sl@0
    21
    variable libraryName "UNKNOWN"
sl@0
    22
sl@0
    23
    # interfaces --
sl@0
    24
    #
sl@0
    25
    #	An array indexed by interface name that is used to maintain
sl@0
    26
    #   the set of valid interfaces.  The value is empty.
sl@0
    27
sl@0
    28
    array set interfaces {}
sl@0
    29
sl@0
    30
    # curName --
sl@0
    31
    #
sl@0
    32
    #	The name of the interface currently being defined.
sl@0
    33
sl@0
    34
    variable curName "UNKNOWN"
sl@0
    35
sl@0
    36
    # hooks --
sl@0
    37
    #
sl@0
    38
    #	An array indexed by interface name that contains the set of
sl@0
    39
    #	subinterfaces that should be defined for a given interface.
sl@0
    40
sl@0
    41
    array set hooks {}
sl@0
    42
sl@0
    43
    # stubs --
sl@0
    44
    #
sl@0
    45
    #	This three dimensional array is indexed first by interface name,
sl@0
    46
    #	second by platform name, and third by a numeric offset or the
sl@0
    47
    #	constant "lastNum".  The lastNum entry contains the largest
sl@0
    48
    #	numeric offset used for a given interface/platform combo.  Each
sl@0
    49
    #	numeric offset contains the C function specification that
sl@0
    50
    #	should be used for the given entry in the stub table.  The spec
sl@0
    51
    #	consists of a list in the form returned by parseDecl.
sl@0
    52
sl@0
    53
    array set stubs {}
sl@0
    54
sl@0
    55
    # outDir --
sl@0
    56
    #
sl@0
    57
    #	The directory where the generated files should be placed.
sl@0
    58
sl@0
    59
    variable outDir .
sl@0
    60
}
sl@0
    61
sl@0
    62
# genStubs::library --
sl@0
    63
#
sl@0
    64
#	This function is used in the declarations file to set the name
sl@0
    65
#	of the library that the interfaces are associated with (e.g. "tcl").
sl@0
    66
#	This value will be used to define the inline conditional macro.
sl@0
    67
#
sl@0
    68
# Arguments:
sl@0
    69
#	name	The library name.
sl@0
    70
#
sl@0
    71
# Results:
sl@0
    72
#	None.
sl@0
    73
sl@0
    74
proc genStubs::library {name} {
sl@0
    75
    variable libraryName $name
sl@0
    76
}
sl@0
    77
sl@0
    78
# genStubs::interface --
sl@0
    79
#
sl@0
    80
#	This function is used in the declarations file to set the name
sl@0
    81
#	of the interface currently being defined.
sl@0
    82
#
sl@0
    83
# Arguments:
sl@0
    84
#	name	The name of the interface.
sl@0
    85
#
sl@0
    86
# Results:
sl@0
    87
#	None.
sl@0
    88
sl@0
    89
proc genStubs::interface {name} {
sl@0
    90
    variable curName $name
sl@0
    91
    variable interfaces
sl@0
    92
sl@0
    93
    set interfaces($name) {}
sl@0
    94
    return
sl@0
    95
}
sl@0
    96
sl@0
    97
# genStubs::hooks --
sl@0
    98
#
sl@0
    99
#	This function defines the subinterface hooks for the current
sl@0
   100
#	interface.
sl@0
   101
#
sl@0
   102
# Arguments:
sl@0
   103
#	names	The ordered list of interfaces that are reachable through the
sl@0
   104
#		hook vector.
sl@0
   105
#
sl@0
   106
# Results:
sl@0
   107
#	None.
sl@0
   108
sl@0
   109
proc genStubs::hooks {names} {
sl@0
   110
    variable curName
sl@0
   111
    variable hooks
sl@0
   112
sl@0
   113
    set hooks($curName) $names
sl@0
   114
    return
sl@0
   115
}
sl@0
   116
sl@0
   117
# genStubs::declare --
sl@0
   118
#
sl@0
   119
#	This function is used in the declarations file to declare a new
sl@0
   120
#	interface entry.
sl@0
   121
#
sl@0
   122
# Arguments:
sl@0
   123
#	index		The index number of the interface.
sl@0
   124
#	platform	The platform the interface belongs to.  Should be one
sl@0
   125
#			of generic, win, unix, or mac, or macosx or aqua or x11.
sl@0
   126
#	decl		The C function declaration, or {} for an undefined
sl@0
   127
#			entry.
sl@0
   128
#
sl@0
   129
# Results:
sl@0
   130
#	None.
sl@0
   131
sl@0
   132
proc genStubs::declare {args} {
sl@0
   133
    variable stubs
sl@0
   134
    variable curName
sl@0
   135
sl@0
   136
    if {[llength $args] != 3} {
sl@0
   137
	puts stderr "wrong # args: declare $args"
sl@0
   138
    }
sl@0
   139
    lassign $args index platformList decl
sl@0
   140
sl@0
   141
    # Check for duplicate declarations, then add the declaration and
sl@0
   142
    # bump the lastNum counter if necessary.
sl@0
   143
sl@0
   144
    foreach platform $platformList {
sl@0
   145
	if {[info exists stubs($curName,$platform,$index)]} {
sl@0
   146
	    puts stderr "Duplicate entry: declare $args"
sl@0
   147
	}
sl@0
   148
    }
sl@0
   149
    regsub -all "\[ \t\n\]+" [string trim $decl] " " decl
sl@0
   150
    set decl [parseDecl $decl]
sl@0
   151
sl@0
   152
    foreach platform $platformList {
sl@0
   153
	if {$decl != ""} {
sl@0
   154
	    set stubs($curName,$platform,$index) $decl
sl@0
   155
	    if {![info exists stubs($curName,$platform,lastNum)] \
sl@0
   156
		    || ($index > $stubs($curName,$platform,lastNum))} {
sl@0
   157
		set stubs($curName,$platform,lastNum) $index
sl@0
   158
	    }
sl@0
   159
	}
sl@0
   160
    }
sl@0
   161
    return
sl@0
   162
}
sl@0
   163
sl@0
   164
# genStubs::rewriteFile --
sl@0
   165
#
sl@0
   166
#	This function replaces the machine generated portion of the
sl@0
   167
#	specified file with new contents.  It looks for the !BEGIN! and
sl@0
   168
#	!END! comments to determine where to place the new text.
sl@0
   169
#
sl@0
   170
# Arguments:
sl@0
   171
#	file	The name of the file to modify.
sl@0
   172
#	text	The new text to place in the file.
sl@0
   173
#
sl@0
   174
# Results:
sl@0
   175
#	None.
sl@0
   176
sl@0
   177
proc genStubs::rewriteFile {file text} {
sl@0
   178
    if {![file exists $file]} {
sl@0
   179
	puts stderr "Cannot find file: $file"
sl@0
   180
	return
sl@0
   181
    }
sl@0
   182
    set in [open ${file} r]
sl@0
   183
    set out [open ${file}.new w]
sl@0
   184
sl@0
   185
    while {![eof $in]} {
sl@0
   186
	set line [gets $in]
sl@0
   187
	if {[regexp {!BEGIN!} $line]} {
sl@0
   188
	    break
sl@0
   189
	}
sl@0
   190
	puts $out $line
sl@0
   191
    }
sl@0
   192
    puts $out "/* !BEGIN!: Do not edit below this line. */"
sl@0
   193
    puts $out $text
sl@0
   194
    while {![eof $in]} {
sl@0
   195
	set line [gets $in]
sl@0
   196
	if {[regexp {!END!} $line]} {
sl@0
   197
	    break
sl@0
   198
	}
sl@0
   199
    }
sl@0
   200
    puts $out "/* !END!: Do not edit above this line. */"
sl@0
   201
    puts -nonewline $out [read $in]
sl@0
   202
    close $in
sl@0
   203
    close $out
sl@0
   204
    file rename -force ${file}.new ${file}
sl@0
   205
    return
sl@0
   206
}
sl@0
   207
sl@0
   208
# genStubs::addPlatformGuard --
sl@0
   209
#
sl@0
   210
#	Wrap a string inside a platform #ifdef.
sl@0
   211
#
sl@0
   212
# Arguments:
sl@0
   213
#	plat	Platform to test.
sl@0
   214
#
sl@0
   215
# Results:
sl@0
   216
#	Returns the original text inside an appropriate #ifdef.
sl@0
   217
sl@0
   218
proc genStubs::addPlatformGuard {plat text} {
sl@0
   219
    switch $plat {
sl@0
   220
	win {
sl@0
   221
	    return "#ifdef __WIN32__\n${text}#endif /* __WIN32__ */\n"
sl@0
   222
	}
sl@0
   223
	unix {
sl@0
   224
	    return "#if !defined(__WIN32__) && !defined(MAC_TCL) /* UNIX */\n${text}#endif /* UNIX */\n"
sl@0
   225
	}		    
sl@0
   226
	mac {
sl@0
   227
	    return "#ifdef MAC_TCL\n${text}#endif /* MAC_TCL */\n"
sl@0
   228
	}
sl@0
   229
	macosx {
sl@0
   230
	    return "#ifdef MAC_OSX_TCL\n${text}#endif /* MAC_OSX_TCL */\n"
sl@0
   231
	}
sl@0
   232
	aqua {
sl@0
   233
	    return "#ifdef MAC_OSX_TK\n${text}#endif /* MAC_OSX_TK */\n"
sl@0
   234
	}
sl@0
   235
	x11 {
sl@0
   236
	    return "#if !(defined(__WIN32__) || defined(MAC_TCL) || defined(MAC_OSX_TK)) /* X11 */\n${text}#endif /* X11 */\n"
sl@0
   237
	}
sl@0
   238
    }
sl@0
   239
    return "$text"
sl@0
   240
}
sl@0
   241
sl@0
   242
# genStubs::emitSlots --
sl@0
   243
#
sl@0
   244
#	Generate the stub table slots for the given interface.  If there
sl@0
   245
#	are no generic slots, then one table is generated for each
sl@0
   246
#	platform, otherwise one table is generated for all platforms.
sl@0
   247
#
sl@0
   248
# Arguments:
sl@0
   249
#	name	The name of the interface being emitted.
sl@0
   250
#	textVar	The variable to use for output.
sl@0
   251
#
sl@0
   252
# Results:
sl@0
   253
#	None.
sl@0
   254
sl@0
   255
proc genStubs::emitSlots {name textVar} {
sl@0
   256
    variable stubs
sl@0
   257
    upvar $textVar text
sl@0
   258
sl@0
   259
    forAllStubs $name makeSlot 1 text {"    void *reserved$i;\n"}
sl@0
   260
    return
sl@0
   261
}
sl@0
   262
sl@0
   263
# genStubs::parseDecl --
sl@0
   264
#
sl@0
   265
#	Parse a C function declaration into its component parts.
sl@0
   266
#
sl@0
   267
# Arguments:
sl@0
   268
#	decl	The function declaration.
sl@0
   269
#
sl@0
   270
# Results:
sl@0
   271
#	Returns a list of the form {returnType name args}.  The args
sl@0
   272
#	element consists of a list of type/name pairs, or a single
sl@0
   273
#	element "void".  If the function declaration is malformed
sl@0
   274
#	then an error is displayed and the return value is {}.
sl@0
   275
sl@0
   276
proc genStubs::parseDecl {decl} {
sl@0
   277
    if {![regexp {^(.*)\((.*)\)$} $decl all prefix args]} {
sl@0
   278
	puts stderr "Malformed declaration: $decl"
sl@0
   279
	return
sl@0
   280
    }
sl@0
   281
    set prefix [string trim $prefix]
sl@0
   282
    if {![regexp {^(.+[ ][*]*)([^ *]+)$} $prefix all rtype fname]} {
sl@0
   283
	puts stderr "Bad return type: $decl"
sl@0
   284
	return
sl@0
   285
    }
sl@0
   286
    set rtype [string trim $rtype]
sl@0
   287
    foreach arg [split $args ,] {
sl@0
   288
	lappend argList [string trim $arg]
sl@0
   289
    }
sl@0
   290
    if {![string compare [lindex $argList end] "..."]} {
sl@0
   291
	if {[llength $argList] != 2} {
sl@0
   292
	    puts stderr "Only one argument is allowed in varargs form: $decl"
sl@0
   293
	}
sl@0
   294
	set arg [parseArg [lindex $argList 0]]
sl@0
   295
	if {$arg == "" || ([llength $arg] != 2)} {
sl@0
   296
	    puts stderr "Bad argument: '[lindex $argList 0]' in '$decl'"
sl@0
   297
	    return
sl@0
   298
	}
sl@0
   299
	set args [list TCL_VARARGS $arg]
sl@0
   300
    } else {
sl@0
   301
	set args {}
sl@0
   302
	foreach arg $argList {
sl@0
   303
	    set argInfo [parseArg $arg]
sl@0
   304
	    if {![string compare $argInfo "void"]} {
sl@0
   305
		lappend args "void"
sl@0
   306
		break
sl@0
   307
	    } elseif {[llength $argInfo] == 2 || [llength $argInfo] == 3} {
sl@0
   308
		lappend args $argInfo
sl@0
   309
	    } else {
sl@0
   310
		puts stderr "Bad argument: '$arg' in '$decl'"
sl@0
   311
		return
sl@0
   312
	    }
sl@0
   313
	}
sl@0
   314
    }
sl@0
   315
    return [list $rtype $fname $args]
sl@0
   316
}
sl@0
   317
sl@0
   318
# genStubs::parseArg --
sl@0
   319
#
sl@0
   320
#	This function parses a function argument into a type and name.
sl@0
   321
#
sl@0
   322
# Arguments:
sl@0
   323
#	arg	The argument to parse.
sl@0
   324
#
sl@0
   325
# Results:
sl@0
   326
#	Returns a list of type and name with an optional third array
sl@0
   327
#	indicator.  If the argument is malformed, returns "".
sl@0
   328
sl@0
   329
proc genStubs::parseArg {arg} {
sl@0
   330
    if {![regexp {^(.+[ ][*]*)([^][ *]+)(\[\])?$} $arg all type name array]} {
sl@0
   331
	if {$arg == "void"} {
sl@0
   332
	    return $arg
sl@0
   333
	} else {
sl@0
   334
	    return
sl@0
   335
	}
sl@0
   336
    }
sl@0
   337
    set result [list [string trim $type] $name]
sl@0
   338
    if {$array != ""} {
sl@0
   339
	lappend result $array
sl@0
   340
    }
sl@0
   341
    return $result
sl@0
   342
}
sl@0
   343
sl@0
   344
# genStubs::makeDecl --
sl@0
   345
#
sl@0
   346
#	Generate the prototype for a function.
sl@0
   347
#
sl@0
   348
# Arguments:
sl@0
   349
#	name	The interface name.
sl@0
   350
#	decl	The function declaration.
sl@0
   351
#	index	The slot index for this function.
sl@0
   352
#
sl@0
   353
# Results:
sl@0
   354
#	Returns the formatted declaration string.
sl@0
   355
sl@0
   356
proc genStubs::makeDecl {name decl index} {
sl@0
   357
    lassign $decl rtype fname args
sl@0
   358
sl@0
   359
    append text "/* $index */\n"
sl@0
   360
    set line "EXTERN $rtype"
sl@0
   361
    set count [expr {2 - ([string length $line] / 8)}]
sl@0
   362
    append line [string range "\t\t\t" 0 $count]
sl@0
   363
    set pad [expr {24 - [string length $line]}]
sl@0
   364
    if {$pad <= 0} {
sl@0
   365
	append line " "
sl@0
   366
	set pad 0
sl@0
   367
    }
sl@0
   368
    append line "$fname _ANSI_ARGS_("
sl@0
   369
sl@0
   370
    set arg1 [lindex $args 0]
sl@0
   371
    switch -exact $arg1 {
sl@0
   372
	void {
sl@0
   373
	    append line "(void)"
sl@0
   374
	}
sl@0
   375
	TCL_VARARGS {
sl@0
   376
	    set arg [lindex $args 1]
sl@0
   377
	    append line "TCL_VARARGS([lindex $arg 0],[lindex $arg 1])"
sl@0
   378
	}
sl@0
   379
	default {
sl@0
   380
	    set sep "("
sl@0
   381
	    foreach arg $args {
sl@0
   382
		append line $sep
sl@0
   383
		set next {}
sl@0
   384
		append next [lindex $arg 0] " " [lindex $arg 1] \
sl@0
   385
			[lindex $arg 2]
sl@0
   386
		if {[string length $line] + [string length $next] \
sl@0
   387
			+ $pad > 76} {
sl@0
   388
		    append text $line \n
sl@0
   389
		    set line "\t\t\t\t"
sl@0
   390
		    set pad 28
sl@0
   391
		}
sl@0
   392
		append line $next
sl@0
   393
		set sep ", "
sl@0
   394
	    }
sl@0
   395
	    append line ")"
sl@0
   396
	}
sl@0
   397
    }
sl@0
   398
    append text $line
sl@0
   399
    
sl@0
   400
    append text ");\n"
sl@0
   401
    return $text
sl@0
   402
}
sl@0
   403
sl@0
   404
# genStubs::makeMacro --
sl@0
   405
#
sl@0
   406
#	Generate the inline macro for a function.
sl@0
   407
#
sl@0
   408
# Arguments:
sl@0
   409
#	name	The interface name.
sl@0
   410
#	decl	The function declaration.
sl@0
   411
#	index	The slot index for this function.
sl@0
   412
#
sl@0
   413
# Results:
sl@0
   414
#	Returns the formatted macro definition.
sl@0
   415
sl@0
   416
proc genStubs::makeMacro {name decl index} {
sl@0
   417
    lassign $decl rtype fname args
sl@0
   418
sl@0
   419
    set lfname [string tolower [string index $fname 0]]
sl@0
   420
    append lfname [string range $fname 1 end]
sl@0
   421
sl@0
   422
    set text "#ifndef $fname\n#define $fname"
sl@0
   423
    set arg1 [lindex $args 0]
sl@0
   424
    set argList ""
sl@0
   425
    switch -exact $arg1 {
sl@0
   426
	void {
sl@0
   427
	    set argList "()"
sl@0
   428
	}
sl@0
   429
	TCL_VARARGS {
sl@0
   430
	}
sl@0
   431
	default {
sl@0
   432
	    set sep "("
sl@0
   433
	    foreach arg $args {
sl@0
   434
		append argList $sep [lindex $arg 1]
sl@0
   435
		set sep ", "
sl@0
   436
	    }
sl@0
   437
	    append argList ")"
sl@0
   438
	}
sl@0
   439
    }
sl@0
   440
    append text " \\\n\t(${name}StubsPtr->$lfname)"
sl@0
   441
    append text " /* $index */\n#endif\n"
sl@0
   442
    return $text
sl@0
   443
}
sl@0
   444
sl@0
   445
# genStubs::makeStub --
sl@0
   446
#
sl@0
   447
#	Emits a stub function definition.
sl@0
   448
#
sl@0
   449
# Arguments:
sl@0
   450
#	name	The interface name.
sl@0
   451
#	decl	The function declaration.
sl@0
   452
#	index	The slot index for this function.
sl@0
   453
#
sl@0
   454
# Results:
sl@0
   455
#	Returns the formatted stub function definition.
sl@0
   456
sl@0
   457
proc genStubs::makeStub {name decl index} {
sl@0
   458
    lassign $decl rtype fname args
sl@0
   459
sl@0
   460
    set lfname [string tolower [string index $fname 0]]
sl@0
   461
    append lfname [string range $fname 1 end]
sl@0
   462
sl@0
   463
    append text "/* Slot $index */\n" $rtype "\n" $fname
sl@0
   464
sl@0
   465
    set arg1 [lindex $args 0]
sl@0
   466
sl@0
   467
    if {![string compare $arg1 "TCL_VARARGS"]} {
sl@0
   468
	lassign [lindex $args 1] type argName 
sl@0
   469
	append text " TCL_VARARGS_DEF($type,$argName)\n\{\n"
sl@0
   470
	append text "    " $type " var;\n    va_list argList;\n"
sl@0
   471
	if {[string compare $rtype "void"]} {
sl@0
   472
	    append text "    " $rtype " resultValue;\n"
sl@0
   473
	}
sl@0
   474
	append text "\n    var = (" $type ") TCL_VARARGS_START(" \
sl@0
   475
		$type "," $argName ",argList);\n\n    "
sl@0
   476
	if {[string compare $rtype "void"]} {
sl@0
   477
	    append text "resultValue = "
sl@0
   478
	}
sl@0
   479
	append text "(" $name "StubsPtr->" $lfname "VA)(var, argList);\n"
sl@0
   480
	append text "    va_end(argList);\n"
sl@0
   481
	if {[string compare $rtype "void"]} {
sl@0
   482
	    append text "return resultValue;\n"
sl@0
   483
	}
sl@0
   484
	append text "\}\n\n"
sl@0
   485
	return $text
sl@0
   486
    }
sl@0
   487
sl@0
   488
    if {![string compare $arg1 "void"]} {
sl@0
   489
	set argList "()"
sl@0
   490
	set argDecls ""
sl@0
   491
    } else {
sl@0
   492
	set argList ""
sl@0
   493
	set sep "("
sl@0
   494
	foreach arg $args {
sl@0
   495
	    append argList $sep [lindex $arg 1]
sl@0
   496
	    append argDecls "    " [lindex $arg 0] " " \
sl@0
   497
		    [lindex $arg 1] [lindex $arg 2] ";\n"
sl@0
   498
	    set sep ", "
sl@0
   499
	}
sl@0
   500
	append argList ")"
sl@0
   501
    }
sl@0
   502
    append text $argList "\n" $argDecls "{\n    "
sl@0
   503
    if {[string compare $rtype "void"]} {
sl@0
   504
	append text "return "
sl@0
   505
    }
sl@0
   506
    append text "(" $name "StubsPtr->" $lfname ")" $argList ";\n}\n\n"
sl@0
   507
    return $text
sl@0
   508
}
sl@0
   509
sl@0
   510
# genStubs::makeSlot --
sl@0
   511
#
sl@0
   512
#	Generate the stub table entry for a function.
sl@0
   513
#
sl@0
   514
# Arguments:
sl@0
   515
#	name	The interface name.
sl@0
   516
#	decl	The function declaration.
sl@0
   517
#	index	The slot index for this function.
sl@0
   518
#
sl@0
   519
# Results:
sl@0
   520
#	Returns the formatted table entry.
sl@0
   521
sl@0
   522
proc genStubs::makeSlot {name decl index} {
sl@0
   523
    lassign $decl rtype fname args
sl@0
   524
sl@0
   525
    set lfname [string tolower [string index $fname 0]]
sl@0
   526
    append lfname [string range $fname 1 end]
sl@0
   527
sl@0
   528
    set text "    "
sl@0
   529
    append text $rtype " (*" $lfname ") _ANSI_ARGS_("
sl@0
   530
sl@0
   531
    set arg1 [lindex $args 0]
sl@0
   532
    switch -exact $arg1 {
sl@0
   533
	void {
sl@0
   534
	    append text "(void)"
sl@0
   535
	}
sl@0
   536
	TCL_VARARGS {
sl@0
   537
	    set arg [lindex $args 1]
sl@0
   538
	    append text "TCL_VARARGS([lindex $arg 0],[lindex $arg 1])"
sl@0
   539
	}
sl@0
   540
	default {
sl@0
   541
	    set sep "("
sl@0
   542
	    foreach arg $args {
sl@0
   543
		append text $sep [lindex $arg 0] " " [lindex $arg 1] \
sl@0
   544
			[lindex $arg 2]
sl@0
   545
		set sep ", "
sl@0
   546
	    }
sl@0
   547
	    append text ")"
sl@0
   548
	}
sl@0
   549
    }
sl@0
   550
    
sl@0
   551
    append text "); /* $index */\n"
sl@0
   552
    return $text
sl@0
   553
}
sl@0
   554
sl@0
   555
# genStubs::makeInit --
sl@0
   556
#
sl@0
   557
#	Generate the prototype for a function.
sl@0
   558
#
sl@0
   559
# Arguments:
sl@0
   560
#	name	The interface name.
sl@0
   561
#	decl	The function declaration.
sl@0
   562
#	index	The slot index for this function.
sl@0
   563
#
sl@0
   564
# Results:
sl@0
   565
#	Returns the formatted declaration string.
sl@0
   566
sl@0
   567
proc genStubs::makeInit {name decl index} {
sl@0
   568
    append text "    " [lindex $decl 1] ", /* " $index " */\n"
sl@0
   569
    return $text
sl@0
   570
}
sl@0
   571
sl@0
   572
# genStubs::forAllStubs --
sl@0
   573
#
sl@0
   574
#	This function iterates over all of the platforms and invokes
sl@0
   575
#	a callback for each slot.  The result of the callback is then
sl@0
   576
#	placed inside appropriate platform guards.
sl@0
   577
#
sl@0
   578
# Arguments:
sl@0
   579
#	name		The interface name.
sl@0
   580
#	slotProc	The proc to invoke to handle the slot.  It will
sl@0
   581
#			have the interface name, the declaration,  and
sl@0
   582
#			the index appended.
sl@0
   583
#	onAll		If 1, emit the skip string even if there are
sl@0
   584
#			definitions for one or more platforms.
sl@0
   585
#	textVar		The variable to use for output.
sl@0
   586
#	skipString	The string to emit if a slot is skipped.  This
sl@0
   587
#			string will be subst'ed in the loop so "$i" can
sl@0
   588
#			be used to substitute the index value.
sl@0
   589
#
sl@0
   590
# Results:
sl@0
   591
#	None.
sl@0
   592
sl@0
   593
proc genStubs::forAllStubs {name slotProc onAll textVar \
sl@0
   594
	{skipString {"/* Slot $i is reserved */\n"}}} {
sl@0
   595
    variable stubs
sl@0
   596
    upvar $textVar text
sl@0
   597
sl@0
   598
    set plats [array names stubs $name,*,lastNum]
sl@0
   599
    if {[info exists stubs($name,generic,lastNum)]} {
sl@0
   600
	# Emit integrated stubs block
sl@0
   601
	set lastNum -1
sl@0
   602
	foreach plat [array names stubs $name,*,lastNum] {
sl@0
   603
	    if {$stubs($plat) > $lastNum} {
sl@0
   604
		set lastNum $stubs($plat)
sl@0
   605
	    }
sl@0
   606
	}
sl@0
   607
	for {set i 0} {$i <= $lastNum} {incr i} {
sl@0
   608
	    set slots [array names stubs $name,*,$i]
sl@0
   609
	    set emit 0
sl@0
   610
	    if {[info exists stubs($name,generic,$i)]} {
sl@0
   611
		if {[llength $slots] > 1} {
sl@0
   612
		    puts stderr "platform entry duplicates generic entry: $i"
sl@0
   613
		}
sl@0
   614
		append text [$slotProc $name $stubs($name,generic,$i) $i]
sl@0
   615
		set emit 1
sl@0
   616
	    } elseif {[llength $slots] > 0} {
sl@0
   617
		foreach plat {unix win mac} {
sl@0
   618
		    if {[info exists stubs($name,$plat,$i)]} {
sl@0
   619
			append text [addPlatformGuard $plat \
sl@0
   620
				[$slotProc $name $stubs($name,$plat,$i) $i]]
sl@0
   621
			set emit 1
sl@0
   622
		    } elseif {$onAll} {
sl@0
   623
			append text [eval {addPlatformGuard $plat} $skipString]
sl@0
   624
			set emit 1
sl@0
   625
		    }
sl@0
   626
		}
sl@0
   627
                #
sl@0
   628
                # "aqua" and "macosx" and "x11" are special cases, 
sl@0
   629
                # since "macosx" always implies "unix" and "aqua", 
sl@0
   630
                # "macosx", so we need to be careful not to 
sl@0
   631
                # emit duplicate stubs entries for the two.
sl@0
   632
                #
sl@0
   633
		if {[info exists stubs($name,aqua,$i)]
sl@0
   634
                        && ![info exists stubs($name,macosx,$i)]} {
sl@0
   635
		    append text [addPlatformGuard aqua \
sl@0
   636
			    [$slotProc $name $stubs($name,aqua,$i) $i]]
sl@0
   637
		    set emit 1
sl@0
   638
		}
sl@0
   639
		if {[info exists stubs($name,macosx,$i)]
sl@0
   640
                        && ![info exists stubs($name,unix,$i)]} {
sl@0
   641
		    append text [addPlatformGuard macosx \
sl@0
   642
			    [$slotProc $name $stubs($name,macosx,$i) $i]]
sl@0
   643
		    set emit 1
sl@0
   644
		}
sl@0
   645
		if {[info exists stubs($name,x11,$i)]
sl@0
   646
                        && ![info exists stubs($name,unix,$i)]} {
sl@0
   647
		    append text [addPlatformGuard x11 \
sl@0
   648
			    [$slotProc $name $stubs($name,x11,$i) $i]]
sl@0
   649
		    set emit 1
sl@0
   650
		}
sl@0
   651
	    }
sl@0
   652
	    if {$emit == 0} {
sl@0
   653
		eval {append text} $skipString
sl@0
   654
	    }
sl@0
   655
	}
sl@0
   656
	
sl@0
   657
    } else {
sl@0
   658
	# Emit separate stubs blocks per platform
sl@0
   659
	foreach plat {unix win mac} {
sl@0
   660
	    if {[info exists stubs($name,$plat,lastNum)]} {
sl@0
   661
		set lastNum $stubs($name,$plat,lastNum)
sl@0
   662
		set temp {}
sl@0
   663
		for {set i 0} {$i <= $lastNum} {incr i} {
sl@0
   664
		    if {![info exists stubs($name,$plat,$i)]} {
sl@0
   665
			eval {append temp} $skipString
sl@0
   666
		    } else {
sl@0
   667
			append temp [$slotProc $name $stubs($name,$plat,$i) $i]
sl@0
   668
		    }
sl@0
   669
		}
sl@0
   670
		append text [addPlatformGuard $plat $temp]
sl@0
   671
	    }
sl@0
   672
	}
sl@0
   673
        # Again, make sure you don't duplicate entries for macosx & aqua.
sl@0
   674
	if {[info exists stubs($name,aqua,lastNum)]
sl@0
   675
                && ![info exists stubs($name,macosx,lastNum)]} {
sl@0
   676
	    set lastNum $stubs($name,aqua,lastNum)
sl@0
   677
	    set temp {}
sl@0
   678
	    for {set i 0} {$i <= $lastNum} {incr i} {
sl@0
   679
		if {![info exists stubs($name,aqua,$i)]} {
sl@0
   680
		    eval {append temp} $skipString
sl@0
   681
		} else {
sl@0
   682
			append temp [$slotProc $name $stubs($name,aqua,$i) $i]
sl@0
   683
		    }
sl@0
   684
		}
sl@0
   685
		append text [addPlatformGuard aqua $temp]
sl@0
   686
	    }
sl@0
   687
        # Again, make sure you don't duplicate entries for macosx & unix.
sl@0
   688
	if {[info exists stubs($name,macosx,lastNum)]
sl@0
   689
                && ![info exists stubs($name,unix,lastNum)]} {
sl@0
   690
	    set lastNum $stubs($name,macosx,lastNum)
sl@0
   691
	    set temp {}
sl@0
   692
	    for {set i 0} {$i <= $lastNum} {incr i} {
sl@0
   693
		if {![info exists stubs($name,macosx,$i)]} {
sl@0
   694
		    eval {append temp} $skipString
sl@0
   695
		} else {
sl@0
   696
			append temp [$slotProc $name $stubs($name,macosx,$i) $i]
sl@0
   697
		    }
sl@0
   698
		}
sl@0
   699
		append text [addPlatformGuard macosx $temp]
sl@0
   700
	    }
sl@0
   701
        # Again, make sure you don't duplicate entries for x11 & unix.
sl@0
   702
	if {[info exists stubs($name,x11,lastNum)]
sl@0
   703
                && ![info exists stubs($name,unix,lastNum)]} {
sl@0
   704
	    set lastNum $stubs($name,x11,lastNum)
sl@0
   705
	    set temp {}
sl@0
   706
	    for {set i 0} {$i <= $lastNum} {incr i} {
sl@0
   707
		if {![info exists stubs($name,x11,$i)]} {
sl@0
   708
		    eval {append temp} $skipString
sl@0
   709
		} else {
sl@0
   710
			append temp [$slotProc $name $stubs($name,x11,$i) $i]
sl@0
   711
		    }
sl@0
   712
		}
sl@0
   713
		append text [addPlatformGuard x11 $temp]
sl@0
   714
	    }
sl@0
   715
    }
sl@0
   716
}
sl@0
   717
sl@0
   718
# genStubs::emitDeclarations --
sl@0
   719
#
sl@0
   720
#	This function emits the function declarations for this interface.
sl@0
   721
#
sl@0
   722
# Arguments:
sl@0
   723
#	name	The interface name.
sl@0
   724
#	textVar	The variable to use for output.
sl@0
   725
#
sl@0
   726
# Results:
sl@0
   727
#	None.
sl@0
   728
sl@0
   729
proc genStubs::emitDeclarations {name textVar} {
sl@0
   730
    variable stubs
sl@0
   731
    upvar $textVar text
sl@0
   732
sl@0
   733
    append text "\n/*\n * Exported function declarations:\n */\n\n"
sl@0
   734
    forAllStubs $name makeDecl 0 text
sl@0
   735
    return
sl@0
   736
}
sl@0
   737
sl@0
   738
# genStubs::emitMacros --
sl@0
   739
#
sl@0
   740
#	This function emits the inline macros for an interface.
sl@0
   741
#
sl@0
   742
# Arguments:
sl@0
   743
#	name	The name of the interface being emitted.
sl@0
   744
#	textVar	The variable to use for output.
sl@0
   745
#
sl@0
   746
# Results:
sl@0
   747
#	None.
sl@0
   748
sl@0
   749
proc genStubs::emitMacros {name textVar} {
sl@0
   750
    variable stubs
sl@0
   751
    variable libraryName
sl@0
   752
    upvar $textVar text
sl@0
   753
sl@0
   754
    set upName [string toupper $libraryName]
sl@0
   755
    append text "\n#if defined(USE_${upName}_STUBS) && !defined(USE_${upName}_STUB_PROCS)\n"
sl@0
   756
    append text "\n/*\n * Inline function declarations:\n */\n\n"
sl@0
   757
    
sl@0
   758
    forAllStubs $name makeMacro 0 text
sl@0
   759
sl@0
   760
    append text "\n#endif /* defined(USE_${upName}_STUBS) && !defined(USE_${upName}_STUB_PROCS) */\n"
sl@0
   761
    return
sl@0
   762
}
sl@0
   763
sl@0
   764
# genStubs::emitHeader --
sl@0
   765
#
sl@0
   766
#	This function emits the body of the <name>Decls.h file for
sl@0
   767
#	the specified interface.
sl@0
   768
#
sl@0
   769
# Arguments:
sl@0
   770
#	name	The name of the interface being emitted.
sl@0
   771
#
sl@0
   772
# Results:
sl@0
   773
#	None.
sl@0
   774
sl@0
   775
proc genStubs::emitHeader {name} {
sl@0
   776
    variable outDir
sl@0
   777
    variable hooks
sl@0
   778
sl@0
   779
    set capName [string toupper [string index $name 0]]
sl@0
   780
    append capName [string range $name 1 end]
sl@0
   781
sl@0
   782
    emitDeclarations $name text
sl@0
   783
sl@0
   784
    if {[info exists hooks($name)]} {
sl@0
   785
	append text "\ntypedef struct ${capName}StubHooks {\n"
sl@0
   786
	foreach hook $hooks($name) {
sl@0
   787
	    set capHook [string toupper [string index $hook 0]]
sl@0
   788
	    append capHook [string range $hook 1 end]
sl@0
   789
	    append text "    struct ${capHook}Stubs *${hook}Stubs;\n"
sl@0
   790
	}
sl@0
   791
	append text "} ${capName}StubHooks;\n"
sl@0
   792
    }
sl@0
   793
    append text "\ntypedef struct ${capName}Stubs {\n"
sl@0
   794
    append text "    int magic;\n"
sl@0
   795
    append text "    struct ${capName}StubHooks *hooks;\n\n"
sl@0
   796
sl@0
   797
    emitSlots $name text
sl@0
   798
sl@0
   799
    append text "} ${capName}Stubs;\n"
sl@0
   800
sl@0
   801
    append text "\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n"
sl@0
   802
    append text "extern ${capName}Stubs *${name}StubsPtr;\n"
sl@0
   803
    append text "#ifdef __cplusplus\n}\n#endif\n"
sl@0
   804
sl@0
   805
    emitMacros $name text
sl@0
   806
sl@0
   807
    rewriteFile [file join $outDir ${name}Decls.h] $text
sl@0
   808
    return
sl@0
   809
}
sl@0
   810
sl@0
   811
# genStubs::emitStubs --
sl@0
   812
#
sl@0
   813
#	This function emits the body of the <name>Stubs.c file for
sl@0
   814
#	the specified interface.
sl@0
   815
#
sl@0
   816
# Arguments:
sl@0
   817
#	name	The name of the interface being emitted.
sl@0
   818
#
sl@0
   819
# Results:
sl@0
   820
#	None.
sl@0
   821
sl@0
   822
proc genStubs::emitStubs {name} {
sl@0
   823
    variable outDir
sl@0
   824
sl@0
   825
    append text "\n/*\n * Exported stub functions:\n */\n\n"
sl@0
   826
    forAllStubs $name makeStub 0 text
sl@0
   827
sl@0
   828
    rewriteFile [file join $outDir ${name}Stubs.c] $text
sl@0
   829
    return    
sl@0
   830
}
sl@0
   831
sl@0
   832
# genStubs::emitInit --
sl@0
   833
#
sl@0
   834
#	Generate the table initializers for an interface.
sl@0
   835
#
sl@0
   836
# Arguments:
sl@0
   837
#	name		The name of the interface to initialize.
sl@0
   838
#	textVar		The variable to use for output.
sl@0
   839
#
sl@0
   840
# Results:
sl@0
   841
#	Returns the formatted output.
sl@0
   842
sl@0
   843
proc genStubs::emitInit {name textVar} {
sl@0
   844
    variable stubs
sl@0
   845
    variable hooks
sl@0
   846
    upvar $textVar text
sl@0
   847
sl@0
   848
    set capName [string toupper [string index $name 0]]
sl@0
   849
    append capName [string range $name 1 end]
sl@0
   850
sl@0
   851
    if {[info exists hooks($name)]} {
sl@0
   852
 	append text "\nstatic ${capName}StubHooks ${name}StubHooks = \{\n"
sl@0
   853
	set sep "    "
sl@0
   854
	foreach sub $hooks($name) {
sl@0
   855
	    append text $sep "&${sub}Stubs"
sl@0
   856
	    set sep ",\n    "
sl@0
   857
	}
sl@0
   858
	append text "\n\};\n"
sl@0
   859
    }
sl@0
   860
    append text "\n${capName}Stubs ${name}Stubs = \{\n"
sl@0
   861
    append text "    TCL_STUB_MAGIC,\n"
sl@0
   862
    if {[info exists hooks($name)]} {
sl@0
   863
	append text "    &${name}StubHooks,\n"
sl@0
   864
    } else {
sl@0
   865
	append text "    NULL,\n"
sl@0
   866
    }
sl@0
   867
    
sl@0
   868
    forAllStubs $name makeInit 1 text {"    NULL, /* $i */\n"}
sl@0
   869
sl@0
   870
    append text "\};\n"
sl@0
   871
    return
sl@0
   872
}
sl@0
   873
sl@0
   874
# genStubs::emitInits --
sl@0
   875
#
sl@0
   876
#	This function emits the body of the <name>StubInit.c file for
sl@0
   877
#	the specified interface.
sl@0
   878
#
sl@0
   879
# Arguments:
sl@0
   880
#	name	The name of the interface being emitted.
sl@0
   881
#
sl@0
   882
# Results:
sl@0
   883
#	None.
sl@0
   884
sl@0
   885
proc genStubs::emitInits {} {
sl@0
   886
    variable hooks
sl@0
   887
    variable outDir
sl@0
   888
    variable libraryName
sl@0
   889
    variable interfaces
sl@0
   890
sl@0
   891
    # Assuming that dependencies only go one level deep, we need to emit
sl@0
   892
    # all of the leaves first to avoid needing forward declarations.
sl@0
   893
sl@0
   894
    set leaves {}
sl@0
   895
    set roots {}
sl@0
   896
    foreach name [lsort [array names interfaces]] {
sl@0
   897
	if {[info exists hooks($name)]} {
sl@0
   898
	    lappend roots $name
sl@0
   899
	} else {
sl@0
   900
	    lappend leaves $name
sl@0
   901
	}
sl@0
   902
    }
sl@0
   903
    foreach name $leaves {
sl@0
   904
	emitInit $name text
sl@0
   905
    }
sl@0
   906
    foreach name $roots {
sl@0
   907
	emitInit $name text
sl@0
   908
    }
sl@0
   909
sl@0
   910
    rewriteFile [file join $outDir ${libraryName}StubInit.c] $text
sl@0
   911
}
sl@0
   912
sl@0
   913
# genStubs::init --
sl@0
   914
#
sl@0
   915
#	This is the main entry point.
sl@0
   916
#
sl@0
   917
# Arguments:
sl@0
   918
#	None.
sl@0
   919
#
sl@0
   920
# Results:
sl@0
   921
#	None.
sl@0
   922
sl@0
   923
proc genStubs::init {} {
sl@0
   924
    global argv argv0
sl@0
   925
    variable outDir
sl@0
   926
    variable interfaces
sl@0
   927
sl@0
   928
    if {[llength $argv] < 2} {
sl@0
   929
	puts stderr "usage: $argv0 outDir declFile ?declFile...?"
sl@0
   930
	exit 1
sl@0
   931
    }
sl@0
   932
sl@0
   933
    set outDir [lindex $argv 0]
sl@0
   934
sl@0
   935
    foreach file [lrange $argv 1 end] {
sl@0
   936
	source $file
sl@0
   937
    }
sl@0
   938
sl@0
   939
    foreach name [lsort [array names interfaces]] {
sl@0
   940
	puts "Emitting $name"
sl@0
   941
	emitHeader $name
sl@0
   942
    }
sl@0
   943
sl@0
   944
    emitInits
sl@0
   945
}
sl@0
   946
sl@0
   947
# lassign --
sl@0
   948
#
sl@0
   949
#	This function emulates the TclX lassign command.
sl@0
   950
#
sl@0
   951
# Arguments:
sl@0
   952
#	valueList	A list containing the values to be assigned.
sl@0
   953
#	args		The list of variables to be assigned.
sl@0
   954
#
sl@0
   955
# Results:
sl@0
   956
#	Returns any values that were not assigned to variables.
sl@0
   957
sl@0
   958
proc lassign {valueList args} {
sl@0
   959
  if {[llength $args] == 0} {
sl@0
   960
      error "wrong # args: lassign list varname ?varname..?"
sl@0
   961
  }
sl@0
   962
sl@0
   963
  uplevel [list foreach $args $valueList {break}]
sl@0
   964
  return [lrange $valueList [llength $args] end]
sl@0
   965
}
sl@0
   966
sl@0
   967
genStubs::init