1<html><body bgcolor="white"> 2<hr> 3<h1 align="center">Embedding Tcl in C/C++ Applications</h1> 4 5 <table width="100%"> 6 <tr><td valign="top" align="left" width="46%"> 7 <b>Presented At:</b> 8 <blockquote> 9 The Tcl2K Conference<br> 10 Austin, Texas<br> 11 <nobr>9:00am, February 15, 2000</nobr><br> 12 </blockquote> 13 </td> 14 <td width="5%"> </td> 15 <td valign="top" align="left" width="46%"> 16 <b>Instructor:</b> 17 <blockquote> 18 D. Richard Hipp<br> 19 drh@hwaci.com<br> 20 http://www.hwaci.com/drh/<br> 21 704.948.4565 22 </blockquote> 23 </td></tr> 24 </table><p> 25 <center><table border="2"> 26 <tr><td> 27 <p align="center"> 28 Copies of these notes, example source code,<br>and other 29 resources related to this tutorial<br>are available online at 30 <a href="http://www.hwaci.com/tcl2k/"> 31 http://www.hwaci.com/tcl2k/</a></p> 32 <p align="center"><small>$Id: index.html 31689 2011-05-22 09:26:02Z nobu $</small></p></td></tr> 33 </table> 34 </center> 35</p> 36 37<br clear="both"><p><hr></p> 38<h2 align="center">Tutorial Outline</h2> 39<p><ul><li>Introduction</li> 40<li>Building It Yourself</li> 41<ul><li>"Hello, World!" using Tcl</li> 42<li>Tcl scripts as C strings</li> 43<li>Adding new Tcl commands</li> 44<li>A tour of the Tcl API</li> 45<li>Tcl initialization scripts</li> 46<li>Adding Tk</li> 47</ul><li>Tools Survey</li> 48<li>Mktclapp</li> 49<ul><li>"Hello World" using mktclapp</li> 50<li>Adding C code</li> 51<li>Other Features</li> 52<li>Invoking Tcl from C</li> 53<li>Running mktclapp directly</li> 54<li>Real-world examples</li> 55</ul><li>Summary</li> 56</ul></p> 57<br clear="both"><p><hr></p> 58<h2 align="center">Embedding Tcl in C/C++ Applications</h2> 59<p><ul><li>You know how to program in Tcl/Tk</li></ul><ul><li>You know how to program in C/C++</li></ul><ul><li>This tutorial is about how to do both at the same time.</li></ul></p> 60<br clear="both"><p><hr></p> 61<h2 align="center">Why Mix C With Tcl/Tk?</h2> 62<p><ul><li>Use C for the things C is good at and Tcl for the things 63 Tcl is good at.</li></ul><ul><li>Generate standalone executables. 64 <ul><li>Eliminate the need to install Tcl/Tk.</li> 65 <li>Prevent problems when the wrong version of Tcl/Tk is installed.</li> 66 </ul></li></ul><ul><li>Prevent end users from changing the source code. 67 <ul><li>Keeps users from creating new bugs.</li> 68 <li>Protects proprietary code.</li> 69 </ul></li></ul><ul><li>Office politics</li></ul><ul><li>Use Tcl/Tk as a portability layer for a large C program</li></ul><ul><li>Use Tcl as a testing interface</li></ul></p> 70<br clear="both"><p><hr></p> 71<h2 align="center">Why Mix C With Tcl/Tk?</h2> 72<p><blockquote><big><b> 73 "Use C for the things C is good at and use Tcl/Tk for the things 74 Tcl/Tk is good at." 75 </b></blockquote></p><p> 76 77 <table width="100%"> 78 <tr><td valign="top" align="left" width="46%"> 79 <b>C is good at:</b> 80 <ul> 81 <li>Speed</li> 82 <li>Complex data structures</li> 83 <li>Computation</li> 84 <li>Interacting with hardware</li> 85 <li>Byte-by-byte data analysis</li> 86 </ul> 87 </td> 88 <td width="5%"> </td> 89 <td valign="top" align="left" width="46%"> 90 <b>Tcl/Tk is good at:</b> 91 <ul> 92 <li>Building a user interface</li> 93 <li>Manipulation of strings</li> 94 <li>Portability</li> 95 <li>Opening sockets</li> 96 <li>Handling events</li> 97 </ul> 98 </td></tr> 99 </table> 100<br clear="both"><p><hr></p> 101<h2 align="center">Programming Models</h2> 102<table width="100%"> 103<tr><td valign="top" width="49%"> 104 105 <p><b>Mainstream Tcl Programming Model:</b></p> 106</td> 107<td width="2%"> </td> 108<td valign="top" width="49%"> 109 110 <p><b>Embedded Tcl Programming Model: </b></p> 111</td></tr> 112<tr><td valign="top" width="49%"> 113 114 <ul><li>Add bits of C code to a large Tcl program</li></ul> 115</td> 116<td width="2%"> </td> 117<td valign="top" width="49%"> 118 119 <ul><li>Add bits of Tcl code to a large C program</li></ul> 120</td></tr> 121<tr><td valign="top" width="49%"> 122 123 <ul><li>Main Tcl script loads extensions written in C</li></ul> 124</td> 125<td width="2%"> </td> 126<td valign="top" width="49%"> 127 128 <ul><li>Main C procedure invokes the Tcl interpreter</li></ul> 129</td></tr> 130<tr><td valign="top" width="49%"> 131 132 <ul><li>Tcl/Tk is a programming language</li></ul> 133</td> 134<td width="2%"> </td> 135<td valign="top" width="49%"> 136 137 <ul><li>Tcl/Tk is a C library</li></ul> 138</td></tr> 139<tr><td valign="top" width="49%"> 140 141 <center><img src="image1"><br> 142 Most of the Tcl2K conference is about</center> 143</td> 144<td width="2%"> </td> 145<td valign="top" width="49%"> 146 147 <center><img src="image1"><br> 148 This tutorial is about</center> 149</td></tr> 150</table> 151 152<br clear="both"><p><hr></p> 153<h2 align="center">"Hello, World!" Using The Tcl Library</h2> 154<table cellspacing="0" cellpadding="0" border="0"> 155<tr><td valign="center"> 156<small><tt>#include <tcl.h></tt></small></td> 157<td> </td> 158<td valign="center"><img src="image2"></td> 159<td> </td> 160<td valign="center">Always include <tcl.h></td> 161</tr> 162<tr><td valign="center"> 163<small><tt>int main(int argc, char **argv){<br> 164 Tcl_Interp *interp;</tt></small></td> 165<td></td><td></td><td></td><td></td> 166</tr> 167<tr><td valign="center"> 168<small><tt> interp = Tcl_CreateInterp();</tt></small></td> 169<td> </td> 170<td valign="center"><img src="image2"></td> 171<td> </td> 172<td valign="center">Create a new Tcl interpreter</td> 173</tr> 174<tr><td valign="center"> 175<small><tt> Tcl_Eval(interp, "puts {Hello, World!}");</tt></small></td> 176<td> </td> 177<td valign="center"><img src="image2"></td> 178<td> </td> 179<td valign="center">Execute a Tcl command.</td> 180</tr> 181<tr><td valign="center"> 182<small><tt> return 0;<br> 183}</tt></small></td> 184<td></td><td></td><td></td><td></td> 185</tr> 186</table> 187 188<br clear="both"><p><hr></p> 189<h2 align="center">Compiling "Hello, World!"</h2> 190<p><p><b>Unix:</b></p> 191 <blockquote><tt> 192 $ gcc hello.c -ltcl -lm -ldl<br> 193 $ /a.out<br> 194 Hello, World!</tt></blockquote> 195 196 <p><b>Windows using Cygwin:</b></p> 197 <blockquote><tt> 198 C:> gcc hello.c -ltcl80 -lm<br> 199 C:> a.exe<br> 200 Hello, World!</tt></blockquote> 201 202 <p><b>Windows using Mingw32:</b></p> 203 <blockquote><tt> 204 C:> gcc -mno-cygwin hello.c -ltcl82 -lm<br> 205 </tt></blockquote> 206<table><tr><td valign="top"><img src="image3"></td> 207<td valign="top"><b>Also works with VC++</b></td></tr></table></p> 208<br clear="both"><p><hr></p> 209<h2 align="center">Where Does <tt>-ltcl</tt> Come From On Unix?</h2> 210<p><p>Build it yourself using these steps:</p></p><p> 211<p><ul><li>Get tcl8.2.2.tar.gz from Scriptics</li></ul><ul><li><tt>zcat tcl8.2.2.tar.gz | tar vx </tt></li></ul><ul><li><tt>cd tcl8.2.2/unix</tt></li></ul><ul><li><tt>/configure --disable-shared</tt></li></ul><ul><li><tt>make</tt></li></ul><ul><li>Move <b>libtcl8.2.a</b> to your lib directory.</li></ul><ul><li>Copy <b>/generic/tcl.h</b> into /usr/include.</li></ul></p> 212<br clear="both"><p><hr></p> 213<h2 align="center">What Other Libraries Are Required For Unix?</h2> 214<p><ul><li>The sequence of <b>-l</b> options after <b>-ltcl</b> 215 varies from system to system</li></ul><ul><li>Observe what libraries the TCL makefile inserts when 216 it is building <b>tclsh</b></li></ul><ul><li>Examples in this talk are for RedHat Linux 6.0 for Intel</li></ul></p> 217<br clear="both"><p><hr></p> 218<h2 align="center">How To Compile Under Unix Without Installing Tcl</h2> 219<p><p>Specify the *.a file directly:</p> 220 <blockquote><pre> 221 $ gcc -I../tcl8.2.2/generic hello.c \ 222 /tcl8.2.2/unix/libtcl8.2.a -lm -ldl 223 $ strip a.out 224 $ /a.out 225 Hello, World!</pre></blockquote> 226 227 <p>Or, tell the C compiler where to look for *.a files:</p> 228 <blockquote><pre> 229 $ gcc -I../tcl8.2.2/generic hello.c \ 230 -L../tcl8.2.2/unix -ltcl -lm -ldl 231 $ strip a.out 232 $ /a.out 233 Hello, World!</pre></blockquote> 234<table><tr><td valign="top"><img src="image3"></td> 235<td valign="top"><b>The <tt>-I../tcl8.2.2</tt> argument 236 tells the compiler where to 237 find <tt><tcl.h></tt>.</p></b></td></tr></table></p> 238<br clear="both"><p><hr></p> 239<h2 align="center">What's "Cygwin"?</h2> 240<p><ul><li>An implementation of GCC/G++ and all development tools 241 for Windows95/98/NT/2000</li></ul><ul><li>Available for free download at 242 <blockquote> 243 <tt>http://sourceware.cygnus.com/cygwin/</tt> 244 </blockquote></li></ul><ul><li>Also available shrink-wrapped at your local software retailer or 245 online at 246 <blockquote> 247 <tt>http://www.cygnus.com/cygwin/index.html</tt> 248 </blockquote></li></ul><ul><li>Programs compiled using Cygwin require a special 249 DLL (<b>cygwin1.dll</b>) that provides a POSIX system API</li></ul><ul><li>Cygwin1.dll cannot be shipped with proprietary programs 250 without purchasing a license from Cygnus.</li></ul><ul><li>Mingw32 is the same compiler as Cygwin, but generates 251 binaries that do not use cygwin1.dll</li></ul></p> 252<br clear="both"><p><hr></p> 253<h2 align="center">Where Does <tt>-ltcl82</tt> Come From On Windows?</h2> 254<p><p>Build it like this:</p></p><p> 255<p><ul><li>Get <b>tcl82.lib</b> and <b>tcl82.dll</b> from Scriptics.</li></ul><ul><li><tt>echo EXPORTS >tcl82.def</tt></li></ul><ul><li><tt>nm tcl82.lib | grep 'T _' | sed 's/.* T _//' >>tcl82.def</tt></li></ul><ul><li><tt>dlltool --def tcl82.def --dllname tcl82.dll --output-lib libtcl82.a</tt></li></ul><ul><li>Move <b>libtcl82.a</b> to the lib directory and <b>tcl82.dll</b> 256 to the bin directory.</li></ul></p> 257<br clear="both"><p><hr></p> 258<h2 align="center">Where Does Your Code Go?</h2> 259<table cellspacing="0" cellpadding="0" border="0"> 260<tr><td valign="center"> 261<small><tt>#include <tcl.h><br> 262 <br> 263int main(int argc, char **argv){<br> 264 Tcl_Interp *interp;<br> 265 interp = Tcl_CreateInterp();</tt></small></td> 266<td></td><td></td><td></td><td></td> 267</tr> 268<tr><td valign="center"> 269<small><tt> /* Your application code goes here */</tt></small></td> 270<td> </td> 271<td valign="center"><img src="image2"></td> 272<td> </td> 273<td valign="center">Insert C code here to do whatever it is your program is 274 suppose to do</td> 275</tr> 276<tr><td valign="center"> 277<small><tt> return 0;<br> 278}</tt></small></td> 279<td></td><td></td><td></td><td></td> 280</tr> 281</table> 282 283<br clear="both"><p><hr></p> 284<h2 align="center">Building A Simple TCLSH</h2> 285<table cellspacing="0" cellpadding="0" border="0"> 286<tr><td valign="center"> 287<small><tt>#include <tcl.h><br> 288 <br> 289int main(int argc, char **argv){<br> 290 Tcl_Interp *interp;<br> 291 char *z;<br> 292 char zLine[2000];<br> 293 interp = Tcl_CreateInterp();</tt></small></td> 294<td></td><td></td><td></td><td></td> 295</tr> 296<tr><td valign="center"> 297<small><tt> while( fgets(zLine,sizeof(zLine),stdin) ){</tt></small></td> 298<td> </td> 299<td valign="center"><img src="image2"></td> 300<td> </td> 301<td valign="center">Get one line of input</td> 302</tr> 303<tr><td valign="center"> 304<small><tt> Tcl_Eval(interp, zLine);</tt></small></td> 305<td> </td> 306<td valign="center"><img src="image2"></td> 307<td> </td> 308<td valign="center">Execute the input as Tcl.</td> 309</tr> 310<tr><td valign="center"> 311<small><tt> z = Tcl_GetStringResult(interp);<br> 312 if( z[0] ){<br> 313 printf("����P�X�\n", z);<br> 314 }</tt></small></td> 315<td> </td> 316<td valign="center"><img src="image2"></td> 317<td> </td> 318<td valign="center">Print result if not empty</td> 319</tr> 320<tr><td valign="center"> 321<small><tt> }<br> 322 return 0;<br> 323}</tt></small></td> 324<td></td><td></td><td></td><td></td> 325</tr> 326</table> 327<p><table><tr><td valign="top"><img src="image3"></td> 328<td valign="top"><b>What if user types more than 2000 characters?</b></td></tr></table> 329</p> 330 331<br clear="both"><p><hr></p> 332<h2 align="center">Building A Simple TCLSH</h2> 333<p>Use TCL to handle input. Allows input lines of unlimited length.</p><p> 334<table cellspacing="0" cellpadding="0" border="0"> 335<tr><td valign="center"> 336<small><tt>#include <tcl.h><br> 337 <br> 338/* Tcl code to implement the<br> 339** input loop */<br> 340static char zLoop[] = <br> 341 "while {![eof stdin]} {\n"</tt></small></td> 342<td></td><td></td><td></td><td></td> 343</tr> 344<tr><td valign="center"> 345<small><tt> " set line [gets stdin]\n"</tt></small></td> 346<td> </td> 347<td valign="center"><img src="image2"></td> 348<td> </td> 349<td valign="center">Get one line of input</td> 350</tr> 351<tr><td valign="center"> 352<small><tt> " set result [eval $line]\n"</tt></small></td> 353<td> </td> 354<td valign="center"><img src="image2"></td> 355<td> </td> 356<td valign="center">Execute input as Tcl</td> 357</tr> 358<tr><td valign="center"> 359<small><tt> " if {$result!=\"\"} {puts $result}\n"</tt></small></td> 360<td> </td> 361<td valign="center"><img src="image2"></td> 362<td> </td> 363<td valign="center">Print result</td> 364</tr> 365<tr><td valign="center"> 366<small><tt> "}\n"<br> 367;<br> 368 <br> 369<br> 370int main(int argc, char **argv){<br> 371 Tcl_Interp *interp;<br> 372 interp = Tcl_CreateInterp();</tt></small></td> 373<td></td><td></td><td></td><td></td> 374</tr> 375<tr><td valign="center"> 376<small><tt> Tcl_Eval(interp, zLoop);</tt></small></td> 377<td> </td> 378<td valign="center"><img src="image2"></td> 379<td> </td> 380<td valign="center">Run the Tcl input loop</td> 381</tr> 382<tr><td valign="center"> 383<small><tt> return 0;<br> 384}</tt></small></td> 385<td></td><td></td><td></td><td></td> 386</tr> 387</table> 388<p><table><tr><td valign="top"><img src="image3"></td> 389<td valign="top"><b>But what about commands that span multiple lines of input?</b></td></tr></table> 390</p> 391 392<br clear="both"><p><hr></p> 393<h2 align="center">Better Handling Of Command-Line Input</h2> 394<p>The file "input.tcl"</p><p> 395<table cellspacing="0" cellpadding="0" border="0"> 396<tr><td valign="center"> 397<small><tt>set line {}<br> 398while {![eof stdin]} {</tt></small></td> 399<td></td><td></td><td></td><td></td> 400</tr> 401<tr><td valign="center"> 402<small><tt> if {$line!=""} {<br> 403 puts -nonewline "> "<br> 404 } else {<br> 405 puts -nonewline "% "<br> 406 }<br> 407 flush stdout</tt></small></td> 408<td> </td> 409<td valign="center"><img src="image2"></td> 410<td> </td> 411<td valign="center">Prompt for user input. The prompt is normally "%" 412 but changes to ">" if the current line is a continuation.</td> 413</tr> 414<tr><td valign="center"> 415<small><tt> append line [gets stdin]<br> 416 if {[info complete $line]} {</tt></small></td> 417<td></td><td></td><td></td><td></td> 418</tr> 419<tr><td valign="center"> 420<small><tt> if {[catch {uplevel #0 $line} result]} {</tt></small></td> 421<td> </td> 422<td valign="center"><img src="image2"></td> 423<td> </td> 424<td valign="center">If the command is complete, execute it.</td> 425</tr> 426<tr><td valign="center"> 427<small><tt> puts stderr "Error: $result"<br> 428 } elseif {$result!=""} {<br> 429 puts $result<br> 430 }<br> 431 set line {}</tt></small></td> 432<td></td><td></td><td></td><td></td> 433</tr> 434<tr><td valign="center"> 435<small><tt> } else {<br> 436 append line \n<br> 437 }</tt></small></td> 438<td> </td> 439<td valign="center"><img src="image2"></td> 440<td> </td> 441<td valign="center">If the command is incomplete, append a newline and get 442 another line of text.</td> 443</tr> 444<tr><td valign="center"> 445<small><tt>}</tt></small></td> 446<td></td><td></td><td></td><td></td> 447</tr> 448</table> 449 450<br clear="both"><p><hr></p> 451<h2 align="center">Better Handling Of Command-Line Input</h2> 452<p>The file "input.c"</p><p> 453<table cellspacing="0" cellpadding="0" border="0"> 454<tr><td valign="center"> 455<small><tt>#include <tcl.h><br> 456 <br> 457int main(int argc, char **argv){<br> 458 Tcl_Interp *interp;<br> 459 interp = Tcl_CreateInterp();</tt></small></td> 460<td></td><td></td><td></td><td></td> 461</tr> 462<tr><td valign="center"> 463<small><tt> Tcl_Eval(interp, "source input.tcl");</tt></small></td> 464<td> </td> 465<td valign="center"><img src="image2"></td> 466<td> </td> 467<td valign="center">Read and execute the input loop</td> 468</tr> 469<tr><td valign="center"> 470<small><tt> return 0;<br> 471}</tt></small></td> 472<td></td><td></td><td></td><td></td> 473</tr> 474</table> 475<p><table><tr><td valign="top"><img src="image3"></td> 476<td valign="top"><b>But now the program is not standalone!</b></td></tr></table> 477</p> 478 479<br clear="both"><p><hr></p> 480<h2 align="center">Converting Scripts Into C Strings</h2> 481<table cellspacing="0" cellpadding="0" border="0"> 482<tr><td valign="center"> 483<small><tt>static char zInputLoop[] = <br> 484 "set line {}\n"<br> 485 "while {![eof stdin]} {\n"<br> 486 " if {$line!=\"\"} {\n"<br> 487 " puts -nonewline \"> \"\n"<br> 488 " } else {\n"<br> 489 " puts -nonewline \"% \"\n"<br> 490 " }\n"<br> 491 " flush stdout\n"<br> 492 " append line [gets stdin]\n"<br> 493 " if {[info complete $line]} {\n"<br> 494 " if {[catch {uplevel #0 $line} result]} {\n"<br> 495 " puts stderr \"Error: $result\"\n"<br> 496 " } elseif {$result!=\"\"} {\n"<br> 497 " puts $result\n"<br> 498 " }\n"<br> 499 " set line {}\n"<br> 500 " } else {\n"<br> 501 " append line \\n\n"<br> 502 " }\n"<br> 503 "}\n"<br> 504;</tt></small></td> 505<td></td><td></td><td></td><td></td> 506</tr> 507</table> 508 509<br clear="both"><p><hr></p> 510<h2 align="center">Compile Tcl Scripts Into C Programs</h2> 511<table cellspacing="0" cellpadding="0" border="0"> 512<tr><td valign="center"> 513<small><tt>#include <tcl.h><br> 514</tt></small></td> 515<td></td><td></td><td></td><td></td> 516</tr> 517<tr><td valign="center"> 518<small><tt><br> 519static char zInputLoop[] = <br> 520 /* Actual code omitted */<br> 521;</tt></small></td> 522<td> </td> 523<td valign="center"><img src="image2"></td> 524<td> </td> 525<td valign="center">Copy and paste the converted Tcl script here</td> 526</tr> 527<tr><td valign="center"> 528<small><tt><br> 529int main(int argc, char **argv){<br> 530 Tcl_Interp *interp;<br> 531 interp = Tcl_CreateInterp();</tt></small></td> 532<td></td><td></td><td></td><td></td> 533</tr> 534<tr><td valign="center"> 535<small><tt> Tcl_Eval(interp, zInputLoop);</tt></small></td> 536<td> </td> 537<td valign="center"><img src="image2"></td> 538<td> </td> 539<td valign="center">Execute the Tcl code</td> 540</tr> 541<tr><td valign="center"> 542<small><tt> return 0;<br> 543}</tt></small></td> 544<td></td><td></td><td></td><td></td> 545</tr> 546</table> 547 548<br clear="both"><p><hr></p> 549<h2 align="center">Converting Scripts To Strings<br>Using SED Or TCLSH</h2> 550<table cellspacing="0" cellpadding="0" border="0"> 551<tr><td valign="center"> 552<small><tt>sed -e 's/\\/\\\\/g' \ </tt></small></td> 553<td> </td> 554<td valign="center"><img src="image2"></td> 555<td> </td> 556<td valign="center">Convert <b>\</b> into <b>\\</b></td> 557</tr> 558<tr><td valign="center"> 559<small><tt> -e 's/"/\\"/g' \ </tt></small></td> 560<td> </td> 561<td valign="center"><img src="image2"></td> 562<td> </td> 563<td valign="center">Convert <b>"</b> into <b>\"</b></td> 564</tr> 565<tr><td valign="center"> 566<small><tt> -e 's/^/ "/' \ </tt></small></td> 567<td> </td> 568<td valign="center"><img src="image2"></td> 569<td> </td> 570<td valign="center">Add <b>"</b> to start of each line</td> 571</tr> 572<tr><td valign="center"> 573<small><tt> -e 's/$/\\n"/' input.tcl</tt></small></td> 574<td> </td> 575<td valign="center"><img src="image2"></td> 576<td> </td> 577<td valign="center">Add <b>\n"</b> to end of each line</td> 578</tr> 579<tr><td valign="center"> 580<small><tt><br> 581 <br> 582<br> 583 <br> 584<br> 585while {![eof stdin]} {<br> 586 set line [gets stdin]</tt></small></td> 587<td></td><td></td><td></td><td></td> 588</tr> 589<tr><td valign="center"> 590<small><tt> regsub -all {\} $line {&&} line</tt></small></td> 591<td> </td> 592<td valign="center"><img src="image2"></td> 593<td> </td> 594<td valign="center">Convert <b>\</b> into <b>\\</b></td> 595</tr> 596<tr><td valign="center"> 597<small><tt> regsub -all {"} $line {\"} line</tt></small></td> 598<td> </td> 599<td valign="center"><img src="image2"></td> 600<td> </td> 601<td valign="center">Convert <b>"</b> into <b>\"</b></td> 602</tr> 603<tr><td valign="center"> 604<small><tt> puts "\"$line\\n\""</tt></small></td> 605<td> </td> 606<td valign="center"><img src="image2"></td> 607<td> </td> 608<td valign="center">Add <b>"</b> in front and <b>\n"</b> at the end</td> 609</tr> 610<tr><td valign="center"> 611<small><tt>}</tt></small></td> 612<td></td><td></td><td></td><td></td> 613</tr> 614</table> 615 616<br clear="both"><p><hr></p> 617<h2 align="center">Converting Scripts Into C Strings</h2> 618<p>You may want to save space by removing comments and extra whitespace 619 from scripts.</p><p> 620<table cellspacing="0" cellpadding="0" border="0"> 621<tr><td valign="center"> 622<small><tt>static char zInputLoop[] = <br> 623 "set line {}\n"<br> 624 "while {![eof stdin]} {\n"<br> 625 "if {$line!=\"\"} {\n"<br> 626 "puts -nonewline \"> \"\n"<br> 627 "} else {\n"<br> 628 "puts -nonewline \"% \"\n"<br> 629 "}\n"<br> 630 "flush stdout\n"<br> 631 "append line [gets stdin]\n"<br> 632 "if {[info complete $line]} {\n"<br> 633 "if {[catch {uplevel #0 $line} result]} {\n"<br> 634 "puts stderr \"Error: $result\"\n"<br> 635 "} elseif {$result!=\"\"} {\n"<br> 636 "puts $result\n"<br> 637 "}\n"<br> 638 "set line {}\n"<br> 639 "} else {\n"<br> 640 "append line \\n\n"<br> 641 "}\n"<br> 642 "}\n"<br> 643;</tt></small></td> 644<td></td><td></td><td></td><td></td> 645</tr> 646</table> 647 648<br clear="both"><p><hr></p> 649<h2 align="center">Converting Scripts To Strings</h2> 650<table cellspacing="0" cellpadding="0" border="0"> 651<tr><td valign="center"> 652<small><tt>sed -e 's/\\/\\\\/g' \ <br> 653 -e 's/"/\\"/g' \ </tt></small></td> 654<td></td><td></td><td></td><td></td> 655</tr> 656<tr><td valign="center"> 657<small><tt> -e '/^ *#/d' \ </tt></small></td> 658<td> </td> 659<td valign="center"><img src="image2"></td> 660<td> </td> 661<td valign="center">Delete lines that begin with #</td> 662</tr> 663<tr><td valign="center"> 664<small><tt> -e '/^ *$/d' \ </tt></small></td> 665<td> </td> 666<td valign="center"><img src="image2"></td> 667<td> </td> 668<td valign="center">Delete blank lines</td> 669</tr> 670<tr><td valign="center"> 671<small><tt> -e 's/^ */ "/' \ </tt></small></td> 672<td> </td> 673<td valign="center"><img src="image2"></td> 674<td> </td> 675<td valign="center">Delete leading spaces</td> 676</tr> 677<tr><td valign="center"> 678<small><tt> -e 's/$/\\n"/' input.tcl<br> 679 <br> 680<br> 681 <br> 682<br> 683 <br> 684while {![eof stdin]} {<br> 685 set line [gets stdin]</tt></small></td> 686<td></td><td></td><td></td><td></td> 687</tr> 688<tr><td valign="center"> 689<small><tt> set line [string trimleft $line]</tt></small></td> 690<td> </td> 691<td valign="center"><img src="image2"></td> 692<td> </td> 693<td valign="center">Remove leading space</td> 694</tr> 695<tr><td valign="center"> 696<small><tt> if {$line==""} continue</tt></small></td> 697<td> </td> 698<td valign="center"><img src="image2"></td> 699<td> </td> 700<td valign="center">Delete blank lines</td> 701</tr> 702<tr><td valign="center"> 703<small><tt> if {[string index $line 0]=="#"} {<br> 704 continue<br> 705 }</tt></small></td> 706<td> </td> 707<td valign="center"><img src="image2"></td> 708<td> </td> 709<td valign="center">Delete lines starting with #</td> 710</tr> 711<tr><td valign="center"> 712<small><tt> regsub -all {\} $line {&&} line<br> 713 regsub -all {"} $line {\"} line<br> 714 puts "\"$line\\n\""<br> 715}</tt></small></td> 716<td></td><td></td><td></td><td></td> 717</tr> 718</table> 719 720<br clear="both"><p><hr></p> 721<h2 align="center">Removing Comments Or Leading Space<br>Will Break Some Tcl Scripts!</h2> 722<table cellspacing="0" cellpadding="0" border="0"> 723<tr><td valign="center"> 724<small><tt>image create bitmap smiley -data {</tt></small></td> 725<td></td><td></td><td></td><td></td> 726</tr> 727<tr><td valign="center"> 728<small><tt>#define smile_width 15<br> 729#define smile_height 15</tt></small></td> 730<td> </td> 731<td valign="center"><img src="image2"></td> 732<td> </td> 733<td valign="center">These lines begin with # but are not comment</td> 734</tr> 735<tr><td valign="center"> 736<small><tt>static unsigned char smile_bits[] = {<br> 737 0xc0, 0x01, 0x30, 0x06, 0x0c, 0x18,<br> 738 0x04, 0x10, 0x22, 0x22, 0x52, 0x25,<br> 739 0x01, 0x40, 0x01, 0x40, 0x01, 0x40,<br> 740 0x12, 0x24, 0xe2, 0x23, 0x04, 0x10,<br> 741 0x0c, 0x18, 0x30, 0x06, 0xc0, 0x01};<br> 742}<br> 743 <br> 744<br> 745 <br> 746text .t<br> 747pack .t<br> 748.t insert end [string trim {</tt></small></td> 749<td></td><td></td><td></td><td></td> 750</tr> 751<tr><td valign="center"> 752<small><tt>She walks in beauty, like the night<br> 753 Of cloudless climes and starry skies;<br> 754And all that's best of dark and bright<br> 755 Meet in her aspect and her eyes;</tt></small></td> 756<td> </td> 757<td valign="center"><img src="image2"></td> 758<td> </td> 759<td valign="center">Indentation is deleted on lines 2 760 and 4</td> 761</tr> 762<tr><td valign="center"> 763<small><tt>}] <br> 764 <br> 765</tt></small></td> 766<td></td><td></td><td></td><td></td> 767</tr> 768</table> 769<p><table><tr><td valign="top"><img src="image3"></td> 770<td valign="top"><b>Problems like these are rare</b></td></tr></table> 771</p> 772 773<br clear="both"><p><hr></p> 774<h2 align="center">Adding A "continue" Command</h2> 775<table cellspacing="0" cellpadding="0" border="0"> 776<tr><td valign="center"> 777<small><tt>set line {}<br> 778while {![eof stdin]} {<br> 779 if {$line!=""} {<br> 780 puts -nonewline "> "<br> 781 } else {<br> 782 puts -nonewline "% "<br> 783 }<br> 784 flush stdout<br> 785 append line [gets stdin]<br> 786 if {[info complete $line]} {</tt></small></td> 787<td></td><td></td><td></td><td></td> 788</tr> 789<tr><td valign="center"> 790<small><tt> if {[lindex $line 0]=="continue"} {<br> 791 break;</tt></small></td> 792<td> </td> 793<td valign="center"><img src="image2"></td> 794<td> </td> 795<td valign="center">Break out of the loop if the command 796 is "continue"</td> 797</tr> 798<tr><td valign="center"> 799<small><tt> } elseif {[catch {uplevel #0 $line} result]} {<br> 800 puts stderr "Error: $result"<br> 801 } elseif {$result!=""} {<br> 802 puts $result<br> 803 }<br> 804 set line {}<br> 805 } else {<br> 806 append line \n<br> 807 }<br> 808}</tt></small></td> 809<td></td><td></td><td></td><td></td> 810</tr> 811</table> 812 813<br clear="both"><p><hr></p> 814<h2 align="center">Stop For Tcl Input At Various Points<br>In A C Program</h2> 815<table cellspacing="0" cellpadding="0" border="0"> 816<tr><td valign="center"> 817<small><tt>#include <tcl.h><br> 818 <br> 819static char zInputLoop[] = <br> 820 /* Tcl Input loop as a C string */<br> 821;<br> 822 <br> 823int main(int argc, char **argv){<br> 824 Tcl_Interp *interp;<br> 825 interp = Tcl_CreateInterp();</tt></small></td> 826<td></td><td></td><td></td><td></td> 827</tr> 828<tr><td valign="center"> 829<small><tt> /* Application C code */</tt></small></td> 830<td> </td> 831<td valign="center"><img src="image2"></td> 832<td> </td> 833<td valign="center">Do some computation</td> 834</tr> 835<tr><td valign="center"> 836<small><tt> Tcl_Eval(interp, zInputLoop);</tt></small></td> 837<td> </td> 838<td valign="center"><img src="image2"></td> 839<td> </td> 840<td valign="center">Stop for some Tcl input</td> 841</tr> 842<tr><td valign="center"> 843<small><tt> /* More application C code */</tt></small></td> 844<td> </td> 845<td valign="center"><img src="image2"></td> 846<td> </td> 847<td valign="center">Do more computation</td> 848</tr> 849<tr><td valign="center"> 850<small><tt> Tcl_Eval(interp, zInputLoop);</tt></small></td> 851<td> </td> 852<td valign="center"><img src="image2"></td> 853<td> </td> 854<td valign="center">Stop for more Tcl input</td> 855</tr> 856<tr><td valign="center"> 857<small><tt> /* Finish up the application */</tt></small></td> 858<td> </td> 859<td valign="center"><img src="image2"></td> 860<td> </td> 861<td valign="center">Finish the computation</td> 862</tr> 863<tr><td valign="center"> 864<small><tt> return 0;<br> 865}</tt></small></td> 866<td></td><td></td><td></td><td></td> 867</tr> 868</table> 869 870<br clear="both"><p><hr></p> 871<h2 align="center">Using Tcl For Testing</h2> 872<table cellspacing="0" cellpadding="0" border="0"> 873<tr><td valign="center"> 874<small><tt>#include <tcl.h><br> 875 <br> 876static char zInputLoop[] = <br> 877 /* Tcl Input loop as a C string */<br> 878;<br> 879 <br> 880</tt></small></td> 881<td></td><td></td><td></td><td></td> 882</tr> 883<tr><td valign="center"> 884<small><tt>int main(int argc, char **argv){<br> 885#ifdef TESTING<br> 886 Tcl_Interp *interp;</tt></small></td> 887<td> </td> 888<td valign="center"><img src="image2"></td> 889<td> </td> 890<td valign="center">Create interpreter only if TESTING 891 is defined</td> 892</tr> 893<tr><td valign="center"> 894<small><tt> interp = Tcl_CreateInterp();<br> 895#endif<br> 896 /* Application C code */</tt></small></td> 897<td></td><td></td><td></td><td></td> 898</tr> 899<tr><td valign="center"> 900<small><tt>#ifdef TESTING<br> 901 Tcl_Eval(interp, zInputLoop);<br> 902#endif</tt></small></td> 903<td> </td> 904<td valign="center"><img src="image2"></td> 905<td> </td> 906<td valign="center">Accept command-line input only if TESTING 907 is defined</td> 908</tr> 909<tr><td valign="center"> 910<small><tt> /* More application C code */<br> 911#ifdef TESTING<br> 912 Tcl_Eval(interp, zInputLoop);<br> 913#endif<br> 914 /* Finish up the application */<br> 915 return 0;<br> 916}</tt></small></td> 917<td></td><td></td><td></td><td></td> 918</tr> 919</table> 920 921<br clear="both"><p><hr></p> 922<h2 align="center">Creating A New Tcl Command In C</h2> 923<table cellspacing="0" cellpadding="0" border="0"> 924<tr><td valign="center"> 925<small><tt>#include <tcl.h><br> 926 <br> 927int NewCmd(</tt></small></td> 928<td></td><td></td><td></td><td></td> 929</tr> 930<tr><td valign="center"> 931<small><tt> void *clientData,<br> 932 Tcl_Interp *interp,<br> 933 int argc,<br> 934 char **argv</tt></small></td> 935<td> </td> 936<td valign="center"><img src="image2"></td> 937<td> </td> 938<td valign="center">The Tcl command is implemented as 939 a C function with four arguments.</td> 940</tr> 941<tr><td valign="center"> 942<small><tt>){<br> 943 printf("Hello, World!\n");</tt></small></td> 944<td></td><td></td><td></td><td></td> 945</tr> 946<tr><td valign="center"> 947<small><tt> return TCL_OK;</tt></small></td> 948<td> </td> 949<td valign="center"><img src="image2"></td> 950<td> </td> 951<td valign="center">Returns TCL_OK or TCL_ERROR</td> 952</tr> 953<tr><td valign="center"> 954<small><tt>}<br> 955 <br> 956static char zInputLoop[] = <br> 957 /* Tcl code omitted... */<br> 958;<br> 959 <br> 960int main(int argc, char **argv){<br> 961 Tcl_Interp *interp;<br> 962 interp = Tcl_CreateInterp();</tt></small></td> 963<td></td><td></td><td></td><td></td> 964</tr> 965<tr><td valign="center"> 966<small><tt> Tcl_CreateCommand(interp, "helloworld",<br> 967 NewCmd, 0, 0);</tt></small></td> 968<td> </td> 969<td valign="center"><img src="image2"></td> 970<td> </td> 971<td valign="center">Tell the interpreter which C function to call when the 972 "helloworld" Tcl command is executed</td> 973</tr> 974<tr><td valign="center"> 975<small><tt> Tcl_Eval(interp, zInputLoop);<br> 976 return 0;<br> 977}</tt></small></td> 978<td></td><td></td><td></td><td></td> 979</tr> 980</table> 981 982<br clear="both"><p><hr></p> 983<h2 align="center">Linkage From Tcl To C</h2> 984<p><p align="center"><img src="image4"></p></p><p><ul><li>3rd parameter of Tcl_CreateCommand() is a pointer to the C subroutine 985 that implements the command.</li></ul><ul><li>4th parameter to Tcl_CreateCommand() becomes the 1st parameter to 986 the C routine whenever the Tcl command is executed.</li></ul><ul><li>1st parameter to Tcl_CreateCommand() must be a valid Tcl interpreter. 987 The same pointer appears as the second parameter to the C routine 988 whenever the Tcl command is executed.</li></ul></p> 989 990<br clear="both"><p><hr></p> 991<h2 align="center">Linkage From Tcl To C</h2> 992<p><p align="center"><img src="image5"></p></p><p><ul><li>5th parameter of Tcl_CreateCommand() is a pointer to the C subroutine 993 that is called when the Tcl command is deleted.</li></ul><ul><li>4th parameter to Tcl_CreateCommand() becomes the 1st parameter to 994 the C routine.</li></ul></p> 995 996<br clear="both"><p><hr></p> 997<h2 align="center">When To Use A Delete Proc</h2> 998<p>Examples of where the delete proc is used in standard Tcl/Tk:</p><p> 999<table cellspacing="0" cellpadding="0" border="0"> 1000<tr><td valign="center"> 1001<small><tt>button .b -text Hello<br> 1002pack .b</tt></small></td> 1003<td></td><td></td><td></td><td></td> 1004</tr> 1005<tr><td valign="center"> 1006<small><tt>rename .b {}</tt></small></td> 1007<td> </td> 1008<td valign="center"><img src="image2"></td> 1009<td> </td> 1010<td valign="center">Deleting the <b>.b</b> command causes the button to be destroyed</td> 1011</tr> 1012<tr><td valign="center"> 1013<small><tt><br> 1014 <br> 1015</tt></small></td> 1016<td></td><td></td><td></td><td></td> 1017</tr> 1018<tr><td valign="center"> 1019<small><tt>image create photo smiley \ <br> 1020 -file smiley.gif</tt></small></td> 1021<td></td><td></td><td></td><td></td> 1022</tr> 1023<tr><td valign="center"> 1024<small><tt>rename smiley {}</tt></small></td> 1025<td> </td> 1026<td valign="center"><img src="image2"></td> 1027<td> </td> 1028<td valign="center">Deleting the <b>smiley</b> command destroys the image and reclaims the 1029 memory used to hold the image</td> 1030</tr> 1031</table> 1032<p><ul><li>Always use a delete proc if the clientData is a pointer to 1033 malloced memory or some other resource that needs freeing</li></ul><ul><li>Delete procs are never used in the Tcl core but are used 1034 extensively in Tk</li></ul></p> 1035 1036<br clear="both"><p><hr></p> 1037<h2 align="center">Linkage From Tcl To C</h2> 1038<p>The <tt>argc</tt> and <tt>argv</tt> parameters work just like in 1039 <tt>main()</tt></p><p> 1040<table cellspacing="0" cellpadding="0" border="0"> 1041<tr><td valign="center"> 1042<small><tt>helloworld one {two three} four</tt></small></td> 1043<td> </td> 1044<td valign="center"><img src="image2"></td> 1045<td> </td> 1046<td valign="center"><tt>argc = 4<br> 1047 argv[0] = "helloworld"<br> 1048 argv[1] = "one"<br> 1049 argv[2] = "two three"<br> 1050 argv[3] = "four"<br> 1051 argv[4] = NULL</tt></td> 1052</tr> 1053</table> 1054 1055<br clear="both"><p><hr></p> 1056<h2 align="center">A Short-Cut</h2> 1057<p>In a program with many new Tcl commands implemented in C, it becomes 1058 tedious to type the same four parameters over and over again. So 1059 we define a short-cut.</p><p> 1060<table cellspacing="0" cellpadding="0" border="0"> 1061<tr><td valign="center"> 1062<small><tt>#define TCLARGS \ <br> 1063 void *clientData, \ <br> 1064 Tcl_Interp *interp, \ <br> 1065 int argc, \ <br> 1066 char *argv</tt></small></td> 1067<td> </td> 1068<td valign="center"><img src="image2"></td> 1069<td> </td> 1070<td valign="center">Define TCLARGS once in a header file</td> 1071</tr> 1072<tr><td valign="center"> 1073<small><tt> <br> 1074 <br> 1075 </tt></small></td> 1076<td></td><td></td><td></td><td></td> 1077</tr> 1078<tr><td valign="center"> 1079<small><tt>int NewCmd(TCLARGS){</tt></small></td> 1080<td> </td> 1081<td valign="center"><img src="image2"></td> 1082<td> </td> 1083<td valign="center">Use the TCLARGS macro to define new C functions 1084 that implement Tcl commands.</td> 1085</tr> 1086<tr><td valign="center"> 1087<small><tt> /* implementation... */<br> 1088}</tt></small></td> 1089<td></td><td></td><td></td><td></td> 1090</tr> 1091</table> 1092<p><table><tr><td valign="top"><img src="image3"></td> 1093<td valign="top"><b>For brevity, we will use the TCLARGS macro during the 1094 rest of this talk.</b></td></tr></table> 1095</p> 1096 1097<br clear="both"><p><hr></p> 1098<h2 align="center">Returning A Value From C Back To Tcl</h2> 1099<table cellspacing="0" cellpadding="0" border="0"> 1100<tr><td valign="center"> 1101<small><tt>int NewCmd(TCLARGS){</tt></small></td> 1102<td> </td> 1103<td valign="center"><img src="image2"></td> 1104<td> </td> 1105<td valign="center">Note that the C function returns an "int"</td> 1106</tr> 1107<tr><td valign="center"> 1108<small><tt> return TCL_OK;</tt></small></td> 1109<td> </td> 1110<td valign="center"><img src="image2"></td> 1111<td> </td> 1112<td valign="center">Return value is TCL_OK or TCL_ERROR</td> 1113</tr> 1114<tr><td valign="center"> 1115<small><tt>}</tt></small></td> 1116<td></td><td></td><td></td><td></td> 1117</tr> 1118</table> 1119<p><ul><li>TCL_OK and TCL_ERROR are defined in <tcl.h></li></ul><ul><li>Other valid return values TCL_RETURN, TCL_BREAK and TCL_CONTINUE 1120 are rarely used</li></ul><ul><li>Common mistake: forgetting to return TCL_OK</li></ul></p> 1121 1122<br clear="both"><p><hr></p> 1123<h2 align="center">Returning A Value From C Back To Tcl</h2> 1124<table cellspacing="0" cellpadding="0" border="0"> 1125<tr><td valign="center"> 1126<small><tt>int NewCmd(TCLARGS){</tt></small></td> 1127<td></td><td></td><td></td><td></td> 1128</tr> 1129<tr><td valign="center"> 1130<small><tt> Tcl_SetResult(interp,"Hello!",TCL_STATIC);</tt></small></td> 1131<td> </td> 1132<td valign="center"><img src="image2"></td> 1133<td> </td> 1134<td valign="center">Set the result to "Hello!"</td> 1135</tr> 1136<tr><td valign="center"> 1137<small><tt> return TCL_OK;<br> 1138}</tt></small></td> 1139<td></td><td></td><td></td><td></td> 1140</tr> 1141</table> 1142<p><ul><li>Result should be the text of an error message if you 1143 return TCL_ERROR.</li></ul><ul><li>3rd argument to Tcl_SetResult() can be TCL_STATIC, 1144 TCL_DYNAMIC, TCL_VOLATILE, or a function pointer.</li></ul><ul><li>Also consider using Tcl_AppendResult().</li></ul><ul><li>Direct access to <tt>interp->result</tt> is deprecated.</li></ul><ul><li>See the man pages for details.</li></ul></p> 1145 1146<br clear="both"><p><hr></p> 1147<h2 align="center">The Tcl_Obj Interface</h2> 1148<p><ul><li>A new way to write Tcl commands in C code</li></ul><ul><li>First introduced in Tcl8.0</li></ul><ul><li>Can be much faster, especially for lists or numeric values.</li></ul><ul><li>Able to handle arbitrary binary data.</li></ul><ul><li>More difficult to program.</li></ul></p> 1149<br clear="both"><p><hr></p> 1150<h2 align="center">The Tcl_Obj Interface</h2> 1151<table cellspacing="0" cellpadding="0" border="0"> 1152<tr><td valign="center"> 1153<small><tt>int NewObjCmd(<br> 1154 void *clientData,<br> 1155 Tcl_Interp *interp,<br> 1156 int objc,</tt></small></td> 1157<td></td><td></td><td></td><td></td> 1158</tr> 1159<tr><td valign="center"> 1160<small><tt> Tcl_Obj *const* objv</tt></small></td> 1161<td> </td> 1162<td valign="center"><img src="image2"></td> 1163<td> </td> 1164<td valign="center">4th parameter is an array Tcl_Objs, not an array of strings</td> 1165</tr> 1166<tr><td valign="center"> 1167<small><tt>){<br> 1168 /* Implementation... */<br> 1169 return TCL_OK;<br> 1170}<br> 1171 <br> 1172static char zInputLoop[] = <br> 1173 /* Tcl code omitted... */<br> 1174;<br> 1175 <br> 1176int main(int argc, char **argv){<br> 1177 Tcl_Interp *interp;<br> 1178 interp = Tcl_CreateInterp();</tt></small></td> 1179<td></td><td></td><td></td><td></td> 1180</tr> 1181<tr><td valign="center"> 1182<small><tt> Tcl_CreateObjCommand(interp, "newcmd",<br> 1183 NewObjCmd, 0, 0);</tt></small></td> 1184<td> </td> 1185<td valign="center"><img src="image2"></td> 1186<td> </td> 1187<td valign="center">Use a different function to register the command</td> 1188</tr> 1189<tr><td valign="center"> 1190<small><tt> Tcl_Eval(interp, zInputLoop);<br> 1191 return 0;<br> 1192}</tt></small></td> 1193<td></td><td></td><td></td><td></td> 1194</tr> 1195</table> 1196 1197<br clear="both"><p><hr></p> 1198<h2 align="center">The Tcl_Obj Interface</h2> 1199<p><ul><li>There are countless access methods for reading information from and 1200 placing information in Tcl_Objs. Always use the access methods.</li></ul><ul><li>Details provided at Lee Bernhard's talk this afternoon.</li></ul><ul><li>Definitely use Tcl_Objs if you are writing a new Tcl extension.</li></ul><ul><li>Tcl_Objs address some of the weaknesses of Tcl relative to C/C++. 1201 <ul> 1202 <li> Tcl_Objs are faster </li> 1203 <li> Tcl_Objs work with binary data </li> 1204 </ul> 1205 But C/C++ is faster still and better for working with binary data.</li></ul><ul><li>When mixing C/C++ with Tcl/Tk the benefits of Tcl_Objs are 1206 less important. Using Tcl_Objs in this context may not be 1207 worth the extra trouble.</li></ul><ul><li>This talk will focus on the string interface.</li></ul></p> 1208<br clear="both"><p><hr></p> 1209<h2 align="center">Nickel Tour Of The Tcl API</h2> 1210<p><p><b>Memory allocation functions</b></p> 1211<center><table width="90%"><tr> 1212<td width="32%" valign="top"><small><tt> 1213 Tcl_Alloc<br> 1214</tt></small></td> 1215<td width="32%" valign="top"><small><tt> 1216 Tcl_Free<br> 1217</tt></small></td> 1218<td width="32%" valign="top"><small><tt> 1219 Tcl_Realloc<br> 1220</tt></small></td> 1221</table></center><p><b>Functions useful in the implementation of new Tcl commands</b></p> 1222<center><table width="90%"><tr> 1223<td width="32%" valign="top"><small><tt> 1224 Tcl_AppendElement<br> 1225 Tcl_AppendResult<br> 1226 Tcl_GetBoolean<br> 1227</tt></small></td> 1228<td width="32%" valign="top"><small><tt> 1229 Tcl_GetDouble<br> 1230 Tcl_GetInt<br> 1231 Tcl_GetStringResult<br> 1232</tt></small></td> 1233<td width="32%" valign="top"><small><tt> 1234 Tcl_ResetResult<br> 1235 Tcl_SetResult<br> 1236</tt></small></td> 1237</table></center><p><b>Functions for controlling the Tcl interpreter</b></p> 1238<center><table width="90%"><tr> 1239<td width="32%" valign="top"><small><tt> 1240 Tcl_CreateCommand<br> 1241 Tcl_CreateInterp<br> 1242</tt></small></td> 1243<td width="32%" valign="top"><small><tt> 1244 Tcl_CreateObjCommand<br> 1245 Tcl_DeleteCommand<br> 1246</tt></small></td> 1247<td width="32%" valign="top"><small><tt> 1248 Tcl_DeleteInterp<br> 1249 Tcl_Exit<br> 1250</tt></small></td> 1251</table></center></p> 1252<br clear="both"><p><hr></p> 1253<h2 align="center">Nickel Tour Of The Tcl API</h2> 1254<p><p><b>I/O functions</b></p> 1255<center><table width="90%"><tr> 1256<td width="32%" valign="top"><small><tt> 1257 Tcl_Close<br> 1258 Tcl_Eof<br> 1259 Tcl_Flush<br> 1260 Tcl_GetChannel<br> 1261 Tcl_GetChannelMode<br> 1262 Tcl_GetChannelName<br> 1263</tt></small></td> 1264<td width="32%" valign="top"><small><tt> 1265 Tcl_Gets<br> 1266 Tcl_OpenCommandChannel<br> 1267 Tcl_OpenFileChannel<br> 1268 Tcl_OpenTcpClient<br> 1269 Tcl_OpenTcpServer<br> 1270 Tcl_Read<br> 1271</tt></small></td> 1272<td width="32%" valign="top"><small><tt> 1273 Tcl_Seek<br> 1274 Tcl_Tell<br> 1275 Tcl_Ungets<br> 1276 Tcl_Write<br> 1277 Tcl_WriteChars<br> 1278</tt></small></td> 1279</table></center><p><b>Names and meanings of system error codes</b></p> 1280<center><table width="90%"><tr> 1281<td width="32%" valign="top"><small><tt> 1282 Tcl_ErrnoId<br> 1283 Tcl_ErrnoMsg<br> 1284</tt></small></td> 1285<td width="32%" valign="top"><small><tt> 1286 Tcl_GetErrno<br> 1287 Tcl_SetErrno<br> 1288</tt></small></td> 1289<td width="32%" valign="top"><small><tt> 1290 Tcl_SignalId<br> 1291 Tcl_SignalMsg<br> 1292</tt></small></td> 1293</table></center></p> 1294<br clear="both"><p><hr></p> 1295<h2 align="center">Nickel Tour Of The Tcl API</h2> 1296<p><p><b>General Operating System Calls</b></p> 1297<center><table width="90%"><tr> 1298<td width="32%" valign="top"><small><tt> 1299 Tcl_Access<br> 1300 Tcl_Chdir<br> 1301 Tcl_GetCwd<br> 1302</tt></small></td> 1303<td width="32%" valign="top"><small><tt> 1304 Tcl_GetHostName<br> 1305 Tcl_GetNameOfExecutable<br> 1306 Tcl_Sleep<br> 1307</tt></small></td> 1308<td width="32%" valign="top"><small><tt> 1309 Tcl_Stat<br> 1310</tt></small></td> 1311</table></center><p><b>String Manipulation And Comparison</b></p> 1312<center><table width="90%"><tr> 1313<td width="32%" valign="top"><small><tt> 1314 Tcl_Concat<br> 1315 Tcl_Merge<br> 1316</tt></small></td> 1317<td width="32%" valign="top"><small><tt> 1318 Tcl_SplitList<br> 1319 Tcl_StringCaseMatch<br> 1320</tt></small></td> 1321<td width="32%" valign="top"><small><tt> 1322 Tcl_StringMatch<br> 1323</tt></small></td> 1324</table></center><p><b>Dynamically Resizable Strings</b></p> 1325<center><table width="90%"><tr> 1326<td width="49%" valign="top"><small><tt> 1327 Tcl_DStringAppend<br> 1328 Tcl_DStringAppendElement<br> 1329 Tcl_DStringEndSublist<br> 1330 Tcl_DStringInit<br> 1331 Tcl_DStringLength<br> 1332</tt></small></td> 1333<td width="49%" valign="top"><small><tt> 1334 Tcl_DStringResult<br> 1335 Tcl_DStringSetLength<br> 1336 Tcl_DStringStartSublist<br> 1337 Tcl_DStringValue<br> 1338</tt></small></td> 1339</table></center></p> 1340<br clear="both"><p><hr></p> 1341<h2 align="center">Nickel Tour Of The Tcl API</h2> 1342<p><p><b>Event Handlers</b></p> 1343<center><table width="90%"><tr> 1344<td width="49%" valign="top"><small><tt> 1345 Tcl_CancelIdleCall<br> 1346 Tcl_CreateChannelHandler<br> 1347 Tcl_CreateTimerHandler<br> 1348 Tcl_DeleteChannelHandler<br> 1349</tt></small></td> 1350<td width="49%" valign="top"><small><tt> 1351 Tcl_DeleteTimerHandler<br> 1352 Tcl_DoOneEvent<br> 1353 Tcl_DoWhenIdle<br> 1354</tt></small></td> 1355</table></center><p><b>Functions For Reading And Writing Tcl Variables</b></p> 1356<center><table width="90%"><tr> 1357<td width="32%" valign="top"><small><tt> 1358 Tcl_GetVar<br> 1359 Tcl_GetVar2<br> 1360 Tcl_LinkVar<br> 1361 Tcl_SetVar<br> 1362 Tcl_SetVar2<br> 1363</tt></small></td> 1364<td width="32%" valign="top"><small><tt> 1365 Tcl_TraceVar<br> 1366 Tcl_TraceVar2<br> 1367 Tcl_UnlinkVar<br> 1368 Tcl_UnsetVar<br> 1369 Tcl_UnsetVar2<br> 1370</tt></small></td> 1371<td width="32%" valign="top"><small><tt> 1372 Tcl_UntraceVar<br> 1373 Tcl_UntraceVar2<br> 1374 Tcl_UpdateLinkedVar<br> 1375</tt></small></td> 1376</table></center><p><b>Functions For Executing Tcl Code</b></p> 1377<center><table width="90%"><tr> 1378<td width="32%" valign="top"><small><tt> 1379 Tcl_Eval<br> 1380 Tcl_EvalFile<br> 1381</tt></small></td> 1382<td width="32%" valign="top"><small><tt> 1383 Tcl_EvalObj<br> 1384 Tcl_GlobalEval<br> 1385</tt></small></td> 1386<td width="32%" valign="top"><small><tt> 1387 Tcl_GlobalEvalObj<br> 1388 Tcl_VarEval<br> 1389</tt></small></td> 1390</table></center></p> 1391<br clear="both"><p><hr></p> 1392<h2 align="center">Nickel Tour Of The Tcl API</h2> 1393<p><p><b>Functions For Dealing With Unicode</b></p> 1394<center><table width="90%"><tr> 1395<td width="49%" valign="top"><small><tt> 1396 Tcl_NumUtfChars<br> 1397 Tcl_UniCharAtIndex<br> 1398 Tcl_UniCharIsAlnum<br> 1399 Tcl_UniCharIsAlpha<br> 1400 Tcl_UniCharIsControl<br> 1401 Tcl_UniCharIsDigit<br> 1402 Tcl_UniCharIsGraph<br> 1403 Tcl_UniCharIsLower<br> 1404 Tcl_UniCharIsPrint<br> 1405 Tcl_UniCharIsPunct<br> 1406 Tcl_UniCharIsSpace<br> 1407 Tcl_UniCharIsUpper<br> 1408 Tcl_UniCharIsWordChar<br> 1409 Tcl_UniCharLen<br> 1410 Tcl_UniCharNcmp<br> 1411 Tcl_UniCharToLower<br> 1412 Tcl_UniCharToTitle<br> 1413</tt></small></td> 1414<td width="49%" valign="top"><small><tt> 1415 Tcl_UniCharToUpper<br> 1416 Tcl_UniCharToUtf<br> 1417 Tcl_UniCharToUtfDString<br> 1418 Tcl_UtfAtIndex<br> 1419 Tcl_UtfBackslash<br> 1420 Tcl_UtfCharComplete<br> 1421 Tcl_UtfFindFirst<br> 1422 Tcl_UtfFindLast<br> 1423 Tcl_UtfNcasecmp<br> 1424 Tcl_UtfNcmp<br> 1425 Tcl_UtfNext<br> 1426 Tcl_UtfPrev<br> 1427 Tcl_UtfToLower<br> 1428 Tcl_UtfToTitle<br> 1429 Tcl_UtfToUniChar<br> 1430 Tcl_UtfToUniCharDString<br> 1431 Tcl_UtfToUpper<br> 1432</tt></small></td> 1433</table></center> 1434 <p><b>Functions For Dealing With Tcl_Objs</b></p> 1435 <blockquote><i>Too numerous to list...</i></blockquote></p> 1436<br clear="both"><p><hr></p> 1437<h2 align="center">Documentation Of The Tcl API</h2> 1438<p><ul><li>Tcl comes with excellent man pages</li></ul><ul><li>"Use the source, Luke"</li></ul><ul><li>See <tt>tclDecl.h</tt> for a list of API functions</li></ul><ul><li>The header comments on the implementation of API functions usually 1439 gives a good description of what the function does and how it should 1440 be used.</li></ul><ul><li>Most API functions are used within Tcl and Tk. Use grep to locate 1441 examples.</li></ul></p> 1442<br clear="both"><p><hr></p> 1443<h2 align="center">Initialization Scripts</h2> 1444<p><ul><li>Run the mini TCLSH implemented above and execute the <tt>parray</tt> command</li></ul><ul><li>It doesn't work! What's wrong? </p></li></li></ul><ul><li><tt>parray</tt> is really a Tcl proc that is read in when the 1445 interpreter is initialized. </p></li></li></ul><ul><li><tt>parray</tt> (and several other commands) are stored in a 1446 handful of "Initialization Scripts" </p></li></li></ul><ul><li>All the initialization scripts are stored in the 1447 "Tcl Library" - a directory on the host 1448 computer. </p></li></li></ul><table><tr><td valign="top"><img src="image3"></td> 1449<td valign="top"><b>Invoke the Tcl_Init() function to locate and read the 1450 Tcl initialization scripts.</b></td></tr></table></p> 1451<br clear="both"><p><hr></p> 1452<h2 align="center">The <tt>Tcl_Init()</tt> Function</h2> 1453<table cellspacing="0" cellpadding="0" border="0"> 1454<tr><td valign="center"> 1455<small><tt>#include <tcl.h><br> 1456 <br> 1457static char zInputLoop[] = <br> 1458 /* Tcl code omitted... */<br> 1459;<br> 1460 <br> 1461int main(int argc, char **argv){<br> 1462 Tcl_Interp *interp;<br> 1463 interp = Tcl_CreateInterp();</tt></small></td> 1464<td></td><td></td><td></td><td></td> 1465</tr> 1466<tr><td valign="center"> 1467<small><tt> Tcl_Init(interp);</tt></small></td> 1468<td> </td> 1469<td valign="center"><img src="image2"></td> 1470<td> </td> 1471<td valign="center">Locate and read the initialization scripts</td> 1472</tr> 1473<tr><td valign="center"> 1474<small><tt> /* Call Tcl_CreateCommand()? */<br> 1475 Tcl_Eval(interp, zInputLoop);<br> 1476 return 0;<br> 1477}</tt></small></td> 1478<td></td><td></td><td></td><td></td> 1479</tr> 1480</table> 1481<p><table><tr><td valign="top"><img src="image3"></td> 1482<td valign="top"><b>But Tcl_Init() can fail. We need to check its return value...</b></td></tr></table> 1483</p> 1484 1485<br clear="both"><p><hr></p> 1486<h2 align="center">The <tt>Tcl_Init()</tt> Function</h2> 1487<table cellspacing="0" cellpadding="0" border="0"> 1488<tr><td valign="center"> 1489<small><tt>#include <tcl.h><br> 1490 <br> 1491static char zInputLoop[] = <br> 1492 /* Tcl code omitted... */<br> 1493;<br> 1494 <br> 1495int main(int argc, char **argv){<br> 1496 Tcl_Interp *interp;<br> 1497 interp = Tcl_CreateInterp();</tt></small></td> 1498<td></td><td></td><td></td><td></td> 1499</tr> 1500<tr><td valign="center"> 1501<small><tt> if( Tcl_Init(interp)!=TCL_OK ){<br> 1502 fprintf(stderr,"Tcl_Init() failed: ����P�X�",<br> 1503 Tcl_GetStringResult(interp));<br> 1504 }</tt></small></td> 1505<td> </td> 1506<td valign="center"><img src="image2"></td> 1507<td> </td> 1508<td valign="center">Print error message if Tcl_Init() fails</td> 1509</tr> 1510<tr><td valign="center"> 1511<small><tt> /* Call Tcl_CreateCommand()? */<br> 1512 Tcl_Eval(interp, zInputLoop);<br> 1513 return 0;<br> 1514}</tt></small></td> 1515<td></td><td></td><td></td><td></td> 1516</tr> 1517</table> 1518<p><table><tr><td valign="top"><img src="image3"></td> 1519<td valign="top"><b>But now the program is not standalone.</b></td></tr></table> 1520</p> 1521 1522<br clear="both"><p><hr></p> 1523<h2 align="center">How <tt>Tcl_Init()</tt> Works</h2> 1524<p><ul><li>Computes the value of variable <tt>tcl_libPath</tt>.</li></ul><ul><li>Invokes the procedure named "<tt>tclInit</tt>"</li></ul><ul><li>A default <tt>tclInit</tt> procedure is built into Tcl. 1525 You can define an alternative <tt>tclInit</tt> procedure 1526 prior to calling <tt>Tcl_Init()</tt>.</li></ul></p> 1527<br clear="both"><p><hr></p> 1528<h2 align="center">The Default <tt>initTcl</tt> Procedure</h2> 1529<table cellspacing="0" cellpadding="0" border="0"> 1530<tr><td valign="center"> 1531<small><tt>set errors {}<br> 1532set dirs {}<br> 1533if {[info exists tcl_library]} {<br> 1534 lappend dirs $tcl_library<br> 1535} else {<br> 1536 if {[info exists env(TCL_LIBRARY)]} {<br> 1537 lappend dirs $env(TCL_LIBRARY)<br> 1538 }<br> 1539 lappend dirs $tclDefaultLibrary<br> 1540 unset tclDefaultLibrary<br> 1541 set dirs [concat $dirs $tcl_libPath]<br> 1542}<br> 1543foreach i $dirs {<br> 1544 set tcl_library $i<br> 1545 set tclfile [file join $i init.tcl]<br> 1546 if {[file exists $tclfile]} {<br> 1547 if {![catch {uplevel #0 [list source $tclfile]} msg]} {<br> 1548 return<br> 1549 } else {<br> 1550 append errors "$tclfile: $msg\n$errorInfo\n"<br> 1551 }<br> 1552 }<br> 1553}<br> 1554error "Can't find a usable init.tcl ..."</tt></small></td> 1555<td></td><td></td><td></td><td></td> 1556</tr> 1557</table> 1558 1559<br clear="both"><p><hr></p> 1560<h2 align="center">The Default Initialization Sequence</h2> 1561<p><ul><li>The <tt>tclInit</tt> procedure locates and sources the <tt>init.tcl</tt> 1562 script. The directory that contains <tt>init.tcl</tt> is stored in 1563 the <tt>tcl_library</tt> variable.</li></ul><ul><li>The <tt>init.tcl</tt> script creates an <tt>unknown</tt> procedure. 1564 The <tt>unknown</tt> procedure will run whenever Tcl encounters an 1565 unknown command.</li></ul><ul><li>The <tt>unknown</tt> procedure consults the file <tt>tclIndex</tt> in the 1566 <tt>tcl_library</tt> directory to see if the command is defined by one of 1567 the initialization scripts.</li></ul><ul><li>The <tt>unknown</tt> procedure sources any needed initialization scripts 1568 and retries the command.</li></ul><table><tr><td valign="top"><img src="image3"></td> 1569<td valign="top"><b>Commands defined in the initialization scripts are loaded 1570 on demand.</b></td></tr></table></p> 1571<br clear="both"><p><hr></p> 1572<h2 align="center">Standalone Initialization Techniques</h2> 1573<p><p><b>Manually execute all initialization scripts</b></p> 1574<ul><li>Convert all initialization scripts into C strings and 1575 put them in the executable.</li></ul><ul><li>Call <tt>Tcl_Eval()</tt> on each initialization script and omit the 1576 call to <tt>Tcl_Init()</tt></li></ul><ul><li>Or, redefine <tt>tclInit</tt> so that it does not attempt to source 1577 <tt>init.tcl</tt> then call <tt>Tcl_Eval()</tt> on each initialization 1578 script after <tt>Tcl_Init()</tt> returns.</li></ul><table><tr><td valign="top"><img src="image3"></td> 1579<td valign="top"><b>This approach is not recommended</b></td></tr></table></p> 1580<br clear="both"><p><hr></p> 1581<h2 align="center">Standalone Initialization Techniques</h2> 1582<p><p><b>Redefining the builtin <tt>source</tt> command</b></p> 1583<ul><li>Convert all initialization scripts into C strings and 1584 put them in the executable.</li></ul><ul><li>Create a new <tt>source</tt> command that 1585 calls <tt>Tcl_Eval()</tt> on the appropriate built-in string 1586 instead of reading from the disk.</li></ul><ul><li>Read from disk if the named file is not one that is built in.</li></ul></p> 1587<br clear="both"><p><hr></p> 1588<h2 align="center">Redefining <tt>source</tt></h2> 1589<table cellspacing="0" cellpadding="0" border="0"> 1590<tr><td valign="center"> 1591<small><tt>static char zInitTcl[] = "...";<br> 1592static char zParrayTcl[] = "...";</tt></small></td> 1593<td> </td> 1594<td valign="center"><img src="image2"></td> 1595<td> </td> 1596<td valign="center">Scripts <tt>init.tcl</tt> and <tt>parray.tcl</tt></td> 1597</tr> 1598<tr><td valign="center"> 1599<small><tt><br> 1600int NewSourceCmd(TCLARGS){</tt></small></td> 1601<td></td><td></td><td></td><td></td> 1602</tr> 1603<tr><td valign="center"> 1604<small><tt> if( !strcmp(argv[1],"/builtin/init.tcl") )<br> 1605 return Tcl_Eval(interp, zInitTcl);<br> 1606 if( !strcmp(argv[1],"/builtin/parray.tcl") )<br> 1607 return Tcl_Eval(interp, zParrayTcl);</tt></small></td> 1608<td> </td> 1609<td valign="center"><img src="image2"></td> 1610<td> </td> 1611<td valign="center">Call <tt>Tcl_Eval()</tt> on builtin strings if the names match</td> 1612</tr> 1613<tr><td valign="center"> 1614<small><tt> return Tcl_EvalFile(interp, argv[1]);</tt></small></td> 1615<td> </td> 1616<td valign="center"><img src="image2"></td> 1617<td> </td> 1618<td valign="center">Call <tt>Tcl_EvalFile()</tt> if no match</td> 1619</tr> 1620<tr><td valign="center"> 1621<small><tt>}<br> 1622 <br> 1623int main(int argc, char **argv){<br> 1624 Tcl_Interp *interp;</tt></small></td> 1625<td></td><td></td><td></td><td></td> 1626</tr> 1627<tr><td valign="center"> 1628<small><tt> setenv("TCL_LIBRARY","/builtin");</tt></small></td> 1629<td> </td> 1630<td valign="center"><img src="image2"></td> 1631<td> </td> 1632<td valign="center">Causes <tt>tclInit</tt> to look for <tt>init.tcl</tt> in <tt>/builtin</tt></td> 1633</tr> 1634<tr><td valign="center"> 1635<small><tt> interp = Tcl_CreateInterp();</tt></small></td> 1636<td></td><td></td><td></td><td></td> 1637</tr> 1638<tr><td valign="center"> 1639<small><tt> Tcl_CreateCommand(interp, "source",<br> 1640 NewSourceCmd, 0, 0);</tt></small></td> 1641<td> </td> 1642<td valign="center"><img src="image2"></td> 1643<td> </td> 1644<td valign="center">Redefine <tt>source</tt></td> 1645</tr> 1646<tr><td valign="center"> 1647<small><tt> Tcl_Init(interp);<br> 1648 Tcl_Eval(interp, zInputLoop);<br> 1649 return 0;<br> 1650}</tt></small></td> 1651<td></td><td></td><td></td><td></td> 1652</tr> 1653</table> 1654 1655<br clear="both"><p><hr></p> 1656<h2 align="center">Redefining <tt>source</tt></h2> 1657<p><ul><li>This approach works for all versions of Tcl and Tk.</li></ul><ul><li>Also need to redefine the "<tt>file exists</tt>" Tcl command since it 1658 too is used by <tt>tclInit</tt>.</li></ul><ul><li>To verify that the program is really standalone, remove the call 1659 to <tt>Tcl_EvalFile()</tt>.</li></ul></p> 1660<br clear="both"><p><hr></p> 1661<h2 align="center">Standalone Initialization Techniques</h2> 1662<p><p><b>Use the <tt>Tcl</tt>*<tt>InsertProc()</tt> functions</b></p> 1663<ul><li>Three routines that overload basic file I/O operations: 1664 <ul> 1665 <li> <tt>TclStatInsertProc()</tt> </li> 1666 <li> <tt>TclAccessInsertProc()</tt> </li> 1667 <li> <tt>TclOpenFileChannelInsertProc()</tt> </li> 1668 </ul></li></ul><ul><li>Allows us to implement a virtual filesystem that overlays the 1669 real filesystem.</li></ul><ul><li>The virtual filesystem contains all the initialization scripts 1670 as compiled-in strings. The initialization scripts look like 1671 they are resident on disk even though they are built in.</li></ul><ul><li>These functions first appeared in Tcl8.0.3. 1672 Presumably to support TclPro Wrapper.</li></ul><ul><li>The only documentation is comments on the code. 1673 See the Tcl source file <tt>generic/tclIOUtil.c</tt></li></ul></p> 1674<br clear="both"><p><hr></p> 1675<h2 align="center">The <tt>TclStatInsertProc()</tt> Function</h2> 1676<p><ul><li>Sole argument is a pointer to a function whose interface is the 1677 same as <tt>stat()</tt></li></ul><ul><li>Functions are stacked. Tcl tries each <tt>stat</tt> function on the 1678 list, beginning with the most recently inserted, until one succeeds.</li></ul></p> 1679<br clear="both"><p><hr></p> 1680<h2 align="center">The <tt>TclStatInsertProc()</tt> Function</h2> 1681<table cellspacing="0" cellpadding="0" border="0"> 1682<tr><td valign="center"> 1683<small><tt>#include <tclInt.h></tt></small></td> 1684<td> </td> 1685<td valign="center"><img src="image2"></td> 1686<td> </td> 1687<td valign="center">Rather than <tt><tcl.h></tt>!</td> 1688</tr> 1689<tr><td valign="center"> 1690<small><tt><br> 1691static int<br> 1692BltinFileStat(char *path,struct stat *buf){<br> 1693 char *zData;<br> 1694 int nData;</tt></small></td> 1695<td></td><td></td><td></td><td></td> 1696</tr> 1697<tr><td valign="center"> 1698<small><tt> zData = FindBuiltinFile(path, 0, &nData);</tt></small></td> 1699<td> </td> 1700<td valign="center"><img src="image2"></td> 1701<td> </td> 1702<td valign="center">Check if <tt>path</tt> is a builtin</td> 1703</tr> 1704<tr><td valign="center"> 1705<small><tt> if( zData==0 ){<br> 1706 return -1;<br> 1707 }</tt></small></td> 1708<td> </td> 1709<td valign="center"><img src="image2"></td> 1710<td> </td> 1711<td valign="center">Fail if <tt>path</tt> is not a builtin</td> 1712</tr> 1713<tr><td valign="center"> 1714<small><tt> memset(buf, 0, sizeof(*buf));<br> 1715 buf->st_mode = 0400;<br> 1716 buf->st_size = nData;</tt></small></td> 1717<td></td><td></td><td></td><td></td> 1718</tr> 1719<tr><td valign="center"> 1720<small><tt> return 0;</tt></small></td> 1721<td> </td> 1722<td valign="center"><img src="image2"></td> 1723<td> </td> 1724<td valign="center">Success if it is builtin</td> 1725</tr> 1726<tr><td valign="center"> 1727<small><tt>}<br> 1728 <br> 1729int main(int argc, char **argv){<br> 1730 Tcl_Interp *interp;</tt></small></td> 1731<td></td><td></td><td></td><td></td> 1732</tr> 1733<tr><td valign="center"> 1734<small><tt> TclStatInsertProc(BltinFileStat);</tt></small></td> 1735<td> </td> 1736<td valign="center"><img src="image2"></td> 1737<td> </td> 1738<td valign="center">Register new <tt>stat</tt> function</td> 1739</tr> 1740<tr><td valign="center"> 1741<small><tt> interp = Tcl_CreateInterp();<br> 1742 Tcl_Init(interp);<br> 1743 Tcl_Eval(interp, zInputLoop);<br> 1744 return 0;<br> 1745}</tt></small></td> 1746<td></td><td></td><td></td><td></td> 1747</tr> 1748</table> 1749 1750<br clear="both"><p><hr></p> 1751<h2 align="center">The <tt>TclAccessInsertProc()</tt> Function</h2> 1752<table cellspacing="0" cellpadding="0" border="0"> 1753<tr><td valign="center"> 1754<small><tt>#include <tclInt.h></tt></small></td> 1755<td> </td> 1756<td valign="center"><img src="image2"></td> 1757<td> </td> 1758<td valign="center">Rather than <tt><tcl.h></tt>!</td> 1759</tr> 1760<tr><td valign="center"> 1761<small><tt><br> 1762/* BltinFileStat() not shown... */<br> 1763 <br> 1764static int<br> 1765BltinFileAccess(char *path, int mode){<br> 1766 char *zData;</tt></small></td> 1767<td></td><td></td><td></td><td></td> 1768</tr> 1769<tr><td valign="center"> 1770<small><tt> if( mode & 3 ) return -1;</tt></small></td> 1771<td> </td> 1772<td valign="center"><img src="image2"></td> 1773<td> </td> 1774<td valign="center">All builtins are read-only</td> 1775</tr> 1776<tr><td valign="center"> 1777<small><tt> zData = FindBuiltinFile(path, 0, &nData);</tt></small></td> 1778<td> </td> 1779<td valign="center"><img src="image2"></td> 1780<td> </td> 1781<td valign="center">Check if <tt>path</tt> is a builtin</td> 1782</tr> 1783<tr><td valign="center"> 1784<small><tt> if( zData==0 ) return -1;</tt></small></td> 1785<td> </td> 1786<td valign="center"><img src="image2"></td> 1787<td> </td> 1788<td valign="center">Fail if <tt>path</tt> is not a builtin</td> 1789</tr> 1790<tr><td valign="center"> 1791<small><tt> return 0;</tt></small></td> 1792<td> </td> 1793<td valign="center"><img src="image2"></td> 1794<td> </td> 1795<td valign="center">Success if it is builtin</td> 1796</tr> 1797<tr><td valign="center"> 1798<small><tt>}<br> 1799 <br> 1800int main(int argc, char **argv){<br> 1801 Tcl_Interp *interp;</tt></small></td> 1802<td></td><td></td><td></td><td></td> 1803</tr> 1804<tr><td valign="center"> 1805<small><tt> TclStatInsertProc(BltinFileStat);<br> 1806 TclAccessInsertProc(BltinFileAccess);</tt></small></td> 1807<td> </td> 1808<td valign="center"><img src="image2"></td> 1809<td> </td> 1810<td valign="center">Register new <tt>stat</tt> and <tt>access</tt> functions</td> 1811</tr> 1812<tr><td valign="center"> 1813<small><tt> interp = Tcl_CreateInterp();<br> 1814 Tcl_Init(interp);<br> 1815 Tcl_Eval(interp, zInputLoop);<br> 1816 return 0;<br> 1817}</tt></small></td> 1818<td></td><td></td><td></td><td></td> 1819</tr> 1820</table> 1821 1822<br clear="both"><p><hr></p> 1823<h2 align="center">The <tt>TclOpenFileChannelInsertProc()</tt> Function</h2> 1824<table cellspacing="0" cellpadding="0" border="0"> 1825<tr><td valign="center"> 1826<small><tt>static Tcl_Channel BuiltinFileOpen(<br> 1827 Tcl_Interp *interp, /* The TCL interpreter doing the open */<br> 1828 char *zFilename, /* Name of the file to open */<br> 1829 char *modeString, /* Mode string for the open (ignored) */<br> 1830 int permissions /* Permissions for a newly created file (ignored) */<br> 1831){<br> 1832 char *zData;<br> 1833 BuiltinFileStruct *p;<br> 1834 int nData;<br> 1835 char zName[50];<br> 1836 Tcl_Channel chan;<br> 1837 static int count = 1;<br> 1838 <br> 1839 zData = FindBuiltinFile(zFilename, 1, &nData);<br> 1840 if( zData==0 ) return NULL;<br> 1841 p = (BuiltinFileStruct*)Tcl_Alloc( sizeof(BuiltinFileStruct) );<br> 1842 if( p==0 ) return NULL;<br> 1843 p->zData = zData;<br> 1844 p->nData = nData;<br> 1845 p->cursor = 0;<br> 1846 sprintf(zName,"etbi_bffffc7c_8049b04",((int)BuiltinFileOpen)>>12,count++);<br> 1847 chan = Tcl_CreateChannel(&builtinChannelType, zName, <br> 1848 (ClientData)p, TCL_READABLE);<br> 1849 return chan;<br> 1850}</tt></small></td> 1851<td></td><td></td><td></td><td></td> 1852</tr> 1853</table> 1854 1855<br clear="both"><p><hr></p> 1856<h2 align="center">The <tt>TclOpenFileChannelInsertProc()</tt> Function</h2> 1857<table cellspacing="0" cellpadding="0" border="0"> 1858<tr><td valign="center"> 1859<small><tt>static Tcl_ChannelType builtinChannelType = {<br> 1860 "builtin", /* Type name. */<br> 1861 NULL, /* Always non-blocking.*/<br> 1862 BuiltinFileClose, /* Close proc. */<br> 1863 BuiltinFileInput, /* Input proc. */<br> 1864 BuiltinFileOutput, /* Output proc. */<br> 1865 BuiltinFileSeek, /* Seek proc. */<br> 1866 NULL, /* Set option proc. */<br> 1867 NULL, /* Get option proc. */<br> 1868 BuiltinFileWatch, /* Watch for events on console. */<br> 1869 BuiltinFileHandle, /* Get a handle from the device. */<br> 1870};</tt></small></td> 1871<td></td><td></td><td></td><td></td> 1872</tr> 1873</table> 1874<p> 1875 <p>For additional information see:</p> 1876 <ul> 1877 <li>The man page for <tt>Tcl_CreateChannel()</tt></li> 1878 <li>Tk source code file <tt>generic/tkConsole.c</tt></li> 1879 </ul> 1880</p> 1881 1882<br clear="both"><p><hr></p> 1883<h2 align="center">Initializing Tk</h2> 1884<p><ul><li>All the same initialization script issues as Tcl</li></ul><ul><li>Tk initialization scripts are in a different directory 1885 than the Tcl initialization scripts - the "Tk Library"</li></ul><ul><li>Call <tt>Tk_Init()</tt> after <tt>Tcl_Init()</tt></li></ul><ul><li>Must have an event loop or Tk will not work!</li></ul></p> 1886<br clear="both"><p><hr></p> 1887<h2 align="center">Implementing An Event Loop</h2> 1888<table cellspacing="0" cellpadding="0" border="0"> 1889<tr><td valign="center"> 1890<small><tt>button .b -text Hello -command exit<br> 1891pack .b</tt></small></td> 1892<td> </td> 1893<td valign="center"><img src="image2"></td> 1894<td> </td> 1895<td valign="center">Create a Tk interface</td> 1896</tr> 1897<tr><td valign="center"> 1898<small><tt><br> 1899</tt></small></td> 1900<td></td><td></td><td></td><td></td> 1901</tr> 1902<tr><td valign="center"> 1903<small><tt>bind . <Destroy> {<br> 1904 if {![winfo exists .]} exit<br> 1905}</tt></small></td> 1906<td> </td> 1907<td valign="center"><img src="image2"></td> 1908<td> </td> 1909<td valign="center">Close the application when the main window 1910 is destroyed</td> 1911</tr> 1912<tr><td valign="center"> 1913<small><tt><br> 1914</tt></small></td> 1915<td></td><td></td><td></td><td></td> 1916</tr> 1917<tr><td valign="center"> 1918<small><tt>while 1 {vwait forever}</tt></small></td> 1919<td> </td> 1920<td valign="center"><img src="image2"></td> 1921<td> </td> 1922<td valign="center">The event loop</td> 1923</tr> 1924</table> 1925 1926<br clear="both"><p><hr></p> 1927<h2 align="center">"Hello, World!" Using Tk</h2> 1928<table cellspacing="0" cellpadding="0" border="0"> 1929<tr><td valign="center"> 1930<small><tt>#include <tk.h><br> 1931 <br> 1932</tt></small></td> 1933<td></td><td></td><td></td><td></td> 1934</tr> 1935<tr><td valign="center"> 1936<small><tt>static char zHello[] = </tt></small></td> 1937<td> </td> 1938<td valign="center"><img src="image2"></td> 1939<td> </td> 1940<td valign="center">The application code</td> 1941</tr> 1942<tr><td valign="center"> 1943<small><tt> "button .b "<br> 1944 "-text {Hello, World} "<br> 1945 "-command exit\n"<br> 1946 "pack .b\n";<br> 1947 <br> 1948</tt></small></td> 1949<td></td><td></td><td></td><td></td> 1950</tr> 1951<tr><td valign="center"> 1952<small><tt>static char zEventLoop[] =</tt></small></td> 1953<td> </td> 1954<td valign="center"><img src="image2"></td> 1955<td> </td> 1956<td valign="center">The event loop</td> 1957</tr> 1958<tr><td valign="center"> 1959<small><tt> "bind . <Destroy> {\n"<br> 1960 " if {![winfo exists .]} exit\n"<br> 1961 "}\n"<br> 1962 "while 1 {vwait forever}\n";<br> 1963 <br> 1964<br> 1965int main(int argc, char **argv){<br> 1966 Tcl_Interp *interp;<br> 1967 interp = Tcl_CreateInterp();</tt></small></td> 1968<td></td><td></td><td></td><td></td> 1969</tr> 1970<tr><td valign="center"> 1971<small><tt> Tcl_Init(interp);<br> 1972 Tk_Init(interp);</tt></small></td> 1973<td> </td> 1974<td valign="center"><img src="image2"></td> 1975<td> </td> 1976<td valign="center">We really should check the return values of the init functions...</td> 1977</tr> 1978<tr><td valign="center"> 1979<small><tt> Tcl_Eval(interp, zHello);</tt></small></td> 1980<td></td><td></td><td></td><td></td> 1981</tr> 1982<tr><td valign="center"> 1983<small><tt> Tcl_Eval(interp, zEventLoop);</tt></small></td> 1984<td> </td> 1985<td valign="center"><img src="image2"></td> 1986<td> </td> 1987<td valign="center">The event loop never returns</td> 1988</tr> 1989<tr><td valign="center"> 1990<small><tt> /*NOTREACHED*/<br> 1991}</tt></small></td> 1992<td></td><td></td><td></td><td></td> 1993</tr> 1994</table> 1995 1996<br clear="both"><p><hr></p> 1997<h2 align="center">Compiling "Hello, World!" For Tk</h2> 1998<p><p><b>Unix:</b></p> 1999 <blockquote><pre> 2000 $ gcc hello.c -ltk -L/usr/X11R6/lib \ 2001 -lX11 -ltcl -lm -ldl 2002 $ /a.out</pre></blockquote> 2003 2004 <p><b>Windows using Cygwin:</b></p> 2005 <blockquote><pre> 2006 C:> gcc hello.c -mwindows -ltk80 -ltcl80 -lm 2007 C:> a.exe</pre></blockquote> 2008 2009 <p><b>Windows using Mingw32:</b></p> 2010 <blockquote><pre> 2011 C:> gcc -mno-cygwin hello.c -mwindows \ 2012 -ltk82 -ltcl82 -lm 2013 C:> a.exe</pre></blockquote></p> 2014<br clear="both"><p><hr></p> 2015<h2 align="center">Making The Program Standalone</h2> 2016<p><p>To make a Tcl application standalone you have to convert the following 2017 initialization scripts to C strings and compile them into the 2018 executable:</p> 2019 <table><tr> 2020 <td valign="top"><tt> 2021 auto.tcl<br> 2022 history.tcl<br> 2023 init.tcl 2024 </tt></td> 2025 <td valign="top"><tt> 2026 ldAout.tcl<br> 2027 package.tcl 2028 </tt></td> 2029 <td valign="top"><tt> 2030 parray.tcl<br> 2031 safe.tcl 2032 </tt></td> 2033 <td valign="top"><tt> 2034 tclIndex<br> 2035 word.tcl 2036 </tt></td> 2037 </tr></table> 2038 2039 <p>To make a Tk application standalone requires these additional 2040 initialization scripts from the Tk Library:</p> 2041 <table><tr> 2042 <td valign="top"><tt> 2043 bgerror.tcl<br> 2044 button.tcl<br> 2045 clrpick.tcl<br> 2046 comdlg.tcl<br> 2047 console.tcl<br> 2048 dialog.tcl 2049 </tt></td> 2050 <td valign="top"><tt> 2051 entry.tcl<br> 2052 focus.tcl<br> 2053 listbox.tcl<br> 2054 menu.tcl<br> 2055 msgbox.tcl<br> 2056 optMenu.tcl 2057 </tt></td> 2058 <td valign="top"><tt> 2059 palette.tcl<br> 2060 safetk.tcl<br> 2061 scale.tcl<br> 2062 scrlbar.tcl<br> 2063 tclIndex<br> 2064 tearoff.tcl 2065 </tt></td> 2066 <td valign="top"><tt> 2067 text.tcl<br> 2068 tk.tcl<br> 2069 tkfbox.tcl<br> 2070 xmfbox.tcl 2071 </tt></td> 2072 </tr></table> 2073 2074 <p>Total of about 13K lines and 400K bytes of text or 9K lines and 2075 250K bytes if you strip comments and leading spaces</p></p> 2076<br clear="both"><p><hr></p> 2077<h2 align="center">A Review Of The Features We Want</h2> 2078<p><ol type="A"> 2079 <li value="1"> 2080 Combine C/C++ with Tcl/Tk into a single executable.</dd> 2081 </li></ol> 2082 2083 <ol type="A"> 2084 <li value="2"> 2085 The executable should be standalone. It must not depend 2086 on files not normally found on the system. 2087 </li></ol> 2088 2089 <ol type="A"> 2090 <li value="3"> 2091 It should be difficult for end users to alter the program 2092 (and introduce bugs). 2093 </li></ol></p> 2094<br clear="both"><p><hr></p> 2095<h2 align="center">Available Programming Aids</h2> 2096<p><p>Several tools are available. The chart below shows which tools 2097 help achieve which objectives.</p> 2098 2099 <center><table border="2"> 2100 <tr> 2101 <td></td> 2102 <td colspan="3" align="center"> 2103 <b>Features The Tool Helps To Achieve</b></td> 2104 </tr> 2105 <tr> 2106 <td align="center"><b>Tool Name</b></td> 2107 <td align="center">Mix C and Tcl</td> 2108 <td align="center">Standalone</td> 2109 <td align="center">Hide Source</td> 2110 </tr> 2111 <tr> 2112 <td>SWIG</td> 2113 <td align="center"><img src="image6"></td> 2114 <td> </td> 2115 <td> </td> 2116 </tr> 2117 <tr> 2118 <td>TclPro Wrapper</td> 2119 <td> </td> 2120 <td align="center"><img src="image6"></td> 2121 <td align="center"><img src="image6"></td> 2122 </tr> 2123 <tr> 2124 <td>FreeWrap</td> 2125 <td> </td> 2126 <td align="center"><img src="image6"></td> 2127 <td align="center"><img src="image6"></td> 2128 </tr> 2129 <tr> 2130 <td>Wrap</td> 2131 <td> </td> 2132 <td align="center"><img src="image6"></td> 2133 <td> </td> 2134 </tr> 2135 <tr> 2136 <td>mktclapp</td> 2137 <td align="center"><img src="image6"></td> 2138 <td align="center"><img src="image6"></td> 2139 <td align="center"><img src="image6"></td> 2140 </tr> 2141 </table></center></p> 2142<br clear="both"><p><hr></p> 2143<h2 align="center">SWIG</h2> 2144<table><tr><td valign="top"><img src="image7"></td> 2145<td valign="top"><p><ul><li>Creates an interface between an existing C/C++ library and a high-level 2146 programming language. Support for: 2147 <ul> 2148 <li> Tcl/Tk </li> 2149 <li> Perl </li> 2150 <li> Python </li> 2151 <li> Java </li> 2152 <li> Eiffel </li> 2153 <li> Guile </li> 2154 </ul></li></ul><ul><li>No changes required to C/C++ code. Can be used with legacy libraries.</li></ul><ul><li>Generates an extension, not a standalone binary</li></ul><ul><li>The tutorial on SWIG was yesterday afternoon.</li></ul><ul><li>http://www.swig.org/</li></ul></p></td></tr></table> 2155 2156<br clear="both"><p><hr></p> 2157<h2 align="center">Wrapper Programs</h2> 2158<table><tr><td valign="top"><img src="image8"></td> 2159<td valign="top"><p><ul><li>Convert a pure Tcl/Tk program into a standalone binary</li></ul><ul><li>Several wrapper programs are available: 2160 <ul> 2161 <li> TclPro Wrapper - http://www.scriptics.com/ </li> 2162 <li> FreeWrap - http://www.albany.net/~dlabelle/freewrap/freewrap.html </li> 2163 <li> Wrap - http://members1.chello.nl/~j.nijtmans/wrap.html </li> 2164 </ul></li></ul><ul><li>No C compiler required!</li></ul><ul><li>TclPro will convert Tcl script into bytecode so that it cannot be 2165 easily read by the end user. FreeWrap encrypts the scripts.</li></ul><ul><li>FreeWrap uses compression on its executable. 2166 Wrap uses compression on both the executable and on the bundled script files.</li></ul><ul><li>Usually include extensions like winico and/or BLT</li></ul></p></td></tr></table> 2167 2168<br clear="both"><p><hr></p> 2169<h2 align="center">mktclapp</h2> 2170<table><tr><td valign="top"><img src="image9"></td> 2171<td valign="top"><p><ul><li>Mix C/C++ with Tcl/Tk into a standalone binary</li></ul> 2172<ul><li><tt>mktclapp</tt> generates an application initialization file 2173 that contains Tcl scripts as strings and makes all necessary calls 2174 to <tt>Tcl_Init</tt>, <tt>Tcl_CreateCommand</tt>, 2175 <tt>Tcl</tt>*<tt>InsertProc</tt>, etc.</li></ul><ul><li>Features to make it easier to write new Tcl command in C</li></ul><ul><li><tt>xmktclapp.tcl</tt> provides a GUI interface to <tt>mktclapp</tt></li></ul><ul><li>http://www.hwaci.com/sw/mktclapp/</li></ul></p></td></tr></table> 2176 2177<br clear="both"><p><hr></p> 2178<h2 align="center">"Hello, World!" Using Mktclapp</h2> 2179<p><ul><li>Download <tt>mktclapp.c</tt> and <tt>xmktclapp.tcl</tt> from 2180 http://www.hwaci.com/sw/mktclapp/</li></ul><ul><li>Compile <tt>mktclapp</tt>: 2181 <blockquote><pre> 2182 cc -o mktclapp mktclapp.c 2183 </pre></blockquote></li></ul><ul><li>Create "Hello, World!" as a Tcl script in file <tt>hw.tcl</tt>: 2184 <blockquote><pre> 2185 button .b -text {Hello, World!} -command exit 2186 pack .b 2187 </pre></blockquote></li></ul><ul><li>Launch xmktclapp: 2188 <blockquote><pre> 2189 wish xmktclapp.tcl 2190 </pre></blockquote></li></ul></p> 2191<br clear="both"><p><hr></p> 2192<h2 align="center">"Hello, World!" Using Mktclapp</h2> 2193<table width="100%"><tr><td valign="top"><p><ul><li>Set "Command Line Input?" to "None"</li></ul><ul><li>Set "Standalone?" to "Yes"</li></ul><ul><li>Enter "<tt>hw.mta</tt>" for the Configuration File</li></ul><ul><li>Enter "<tt>hw.c</tt>" for the Output C File</li></ul></p></td> 2194<td valign="top" align="right"><img src="image10"></td></tr></table> 2195 2196<br clear="both"><p><hr></p> 2197<h2 align="center">"Hello, World!" Using Mktclapp</h2> 2198<table width="100%"><tr><td valign="top"><p><ul><li>Go to the "Tcl Scripts" page</li></ul><ul><li>Press "Insert" and add <tt>hw.tcl</tt> to the list of 2199 Tcl scripts</li></ul><ul><li>Change the "Startup Script" to be <tt>hw.tcl</tt>.</li></ul><ul><li>Select File/Build and File/Exit</li></ul></p></td> 2200<td valign="top" align="right"><img src="image11"></td></tr></table> 2201 2202<br clear="both"><p><hr></p> 2203<h2 align="center">"Hello, World!" Using Mktclapp</h2> 2204<p><ul><li>Mktclapp generates <tt>hw.c</tt>. 2205 Compile it something like this: 2206 <pre> 2207 cc hw.c -ltk -L/usr/X11R6/lib -lX11 -ltcl -lm -ldl 2208 </pre></li></ul><ul><li>Or, if using Cygwin: 2209 <pre> 2210 gcc hw.c -mwindows -ltk80 -ltcl80 -lm 2211 </pre></li></ul><ul><li>Or, if using Mingw32: 2212 <pre> 2213 gcc -mno-cygwin hw.c -mwindows -ltk82 -ltcl82 -lm 2214 </pre></li></ul><ul><li>And you're done!</li></ul></p> 2215<br clear="both"><p><hr></p> 2216<h2 align="center">Adding C Code To Your Program</h2> 2217<p>Put the new C code in a new source file named "<tt>add.c</tt>"</p><p> 2218<table cellspacing="0" cellpadding="0" border="0"> 2219<tr><td valign="center"> 2220<small><tt>#include "hw.h"</tt></small></td> 2221<td> </td> 2222<td valign="center"><img src="image2"></td> 2223<td> </td> 2224<td valign="center">Generated by mktclapp</td> 2225</tr> 2226<tr><td valign="center"> 2227<small><tt></tt></small></td> 2228<td></td><td></td><td></td><td></td> 2229</tr> 2230<tr><td valign="center"> 2231<small><tt>int ET_COMMAND_add(ET_TCLARGS){</tt></small></td> 2232<td> </td> 2233<td valign="center"><img src="image2"></td> 2234<td> </td> 2235<td valign="center"><tt>ET_TCLARGS</tt> is a macro defined in <tt>hw.h</tt></td> 2236</tr> 2237<tr><td valign="center"> 2238<small><tt> int a, b;<br> 2239 char zResult[30];<br> 2240 a = atoi(argv[1]);<br> 2241 b = atoi(argv[2]);<br> 2242 sprintf(zResult, "-1073742724", a+b);<br> 2243 Tcl_SetResult(interp, zResult, TCL_VOLATILE);<br> 2244 return TCL_OK;<br> 2245}</tt></small></td> 2246<td></td><td></td><td></td><td></td> 2247</tr> 2248</table> 2249 2250<br clear="both"><p><hr></p> 2251<h2 align="center">Adding C Code To Your Program</h2> 2252<table width="100%"><tr><td valign="top"><p><ul><li>Go to the "C/C++ Modules" page of xmktclapp.tcl</li></ul> 2253<ul><li>Press "Insert" and add <tt>add.c</tt> to the list of 2254 C/C++ modules</p></li></ul></li></ul><ul><li>Select File/Build and File/Exit</li></ul></p></td> 2255<td valign="top" align="right"><img src="image12"></td></tr></table> 2256 2257<br clear="both"><p><hr></p> 2258<h2 align="center">Adding C Code To Your Program</h2> 2259<p><ul><li>Compile as follows: 2260 <pre> 2261 cc add.c hw.c -ltk -L/usr/X11R6/lib -ltcl -lm -ldl 2262 </pre></li></ul><ul><li>Or construct a Makefile that compiles <tt>add.c</tt> into <tt>add.o</tt> 2263 and <tt>hw.c</tt> into <tt>hw.o</tt> and then links them.</li></ul><ul><li>Compile the same way for Windows except use the usual Windows 2264 libraries and options...</li></ul><table><tr><td valign="top"><img src="image3"></td> 2265<td valign="top"><b>Don't have to worry with <tt>Tcl_CreateCommand()</tt> - Mktclapp takes 2266 care of that automatically.</b></td></tr></table></p> 2267<br clear="both"><p><hr></p> 2268<h2 align="center">Checking Parameters In The <tt>add</tt> Command</h2> 2269<p>Modify <tt>add.c</tt> to insure the <tt>add</tt> command 2270 is called with exactly two integer arguments</p><p> 2271<table cellspacing="0" cellpadding="0" border="0"> 2272<tr><td valign="center"> 2273<small><tt>#include "hw.h"<br> 2274 <br> 2275int ET_COMMAND_add(ET_TCLARGS){<br> 2276 int a, b;<br> 2277 char zResult[30];</tt></small></td> 2278<td></td><td></td><td></td><td></td> 2279</tr> 2280<tr><td valign="center"> 2281<small><tt> if( argc!=3 ){<br> 2282 Tcl_AppendResult(interp,<br> 2283 "wrong # args: should be: \"",<br> 2284 argv[0], " VALUE VALUE\"", 0);<br> 2285 return TCL_ERROR;<br> 2286 }</tt></small></td> 2287<td> </td> 2288<td valign="center"><img src="image2"></td> 2289<td> </td> 2290<td valign="center">Report an error if there are not exactly 2291 2 arguments</td> 2292</tr> 2293<tr><td valign="center"> 2294<small><tt> if( Tcl_GetInt(interp, argv[1], &a)!=TCL_OK ){<br> 2295 return TCL_ERROR;<br> 2296 }</tt></small></td> 2297<td> </td> 2298<td valign="center"><img src="image2"></td> 2299<td> </td> 2300<td valign="center">Report an error if the first argument is 2301 not an integer</td> 2302</tr> 2303<tr><td valign="center"> 2304<small><tt> if( Tcl_GetInt(interp, argv[2], &b)!=TCL_OK ){<br> 2305 return TCL_ERROR;<br> 2306 }</tt></small></td> 2307<td> </td> 2308<td valign="center"><img src="image2"></td> 2309<td> </td> 2310<td valign="center">Do the same for the second argument</td> 2311</tr> 2312<tr><td valign="center"> 2313<small><tt> sprintf(zResult, "-1073742724", a+b);<br> 2314 Tcl_SetResult(interp, zResult, TCL_VOLATILE);<br> 2315 return TCL_OK;<br> 2316}</tt></small></td> 2317<td></td><td></td><td></td><td></td> 2318</tr> 2319</table> 2320 2321<br clear="both"><p><hr></p> 2322<h2 align="center">Using The Tcl_Obj Interface</h2> 2323<p>In the file <tt>objadd.c</tt> put this code:</p><p> 2324<table cellspacing="0" cellpadding="0" border="0"> 2325<tr><td valign="center"> 2326<small><tt>#include "hw.h"</tt></small></td> 2327<td></td><td></td><td></td><td></td> 2328</tr> 2329<tr><td valign="center"> 2330<small><tt><br> 2331int ET_OBJCOMMAND_add2(ET_OBJARGS){<br> 2332 int a, b;</tt></small></td> 2333<td> </td> 2334<td valign="center"><img src="image2"></td> 2335<td> </td> 2336<td valign="center">Use "<tt>ET_OBJCOMMAND</tt>" instead of "<tt>ET_COMMAND</tt>" and 2337 "<tt>ET_OBJARGS</tt>" instead of "<tt>ET_TCLARGS</tt>"</td> 2338</tr> 2339<tr><td valign="center"> 2340<small><tt> if( objc!=3 ){<br> 2341 Tcl_WrongNumArgs(interp, 1, objv,<br> 2342 "number number");<br> 2343 return TCL_ERROR;<br> 2344 }</tt></small></td> 2345<td> </td> 2346<td valign="center"><img src="image2"></td> 2347<td> </td> 2348<td valign="center">A special routine for "wrong # args" error</td> 2349</tr> 2350<tr><td valign="center"> 2351<small><tt> if( Tcl_GetIntFromObj(interp, objv[1], &a) ){</tt></small></td> 2352<td> </td> 2353<td valign="center"><img src="image2"></td> 2354<td> </td> 2355<td valign="center">Instead of <tt>Tcl_GetInt</tt></td> 2356</tr> 2357<tr><td valign="center"> 2358<small><tt> return TCL_ERROR;<br> 2359 }<br> 2360 if( Tcl_GetIntFromObj(interp, objv[2], &b) ){<br> 2361 return TCL_ERROR;<br> 2362 }</tt></small></td> 2363<td></td><td></td><td></td><td></td> 2364</tr> 2365<tr><td valign="center"> 2366<small><tt> Tcl_SetIntObj(Tcl_GetObjResult(interp), a+b);</tt></small></td> 2367<td> </td> 2368<td valign="center"><img src="image2"></td> 2369<td> </td> 2370<td valign="center">Result stored as integer, not a string</td> 2371</tr> 2372<tr><td valign="center"> 2373<small><tt> return TCL_OK;<br> 2374}</tt></small></td> 2375<td></td><td></td><td></td><td></td> 2376</tr> 2377</table> 2378 2379<br clear="both"><p><hr></p> 2380<h2 align="center">Speed Of Tcl_Obj Versus "char*" Interfaces</h2> 2381<p><ul><li>Compile both <tt>add</tt> and <tt>add2</tt> into the same executable.</li></ul><ul><li>Compare their speeds: 2382 <pre> 2383 time {add 123456 654321} 10000 2384 <font color="blue">26 microseconds per iteration</font> 2385 time {add2 123456 654321} 10000 2386 <font color="blue">4 microseconds per iteration</font> 2387 </pre></li></ul><ul><li>The Tcl_Obj version is 650 faster!</li></ul><ul><li>Replace the addition with a "real" computation that takes 2388 10 milliseconds.</li></ul><ul><li>Now the Tcl_Obj version is only 0.2 faster!</li></ul><table><tr><td valign="top"><img src="image3"></td> 2389<td valign="top"><b>In many real-world problems, the Tcl_Obj interface has no noticeable 2390 speed advantage over the string interface.</b></td></tr></table></p> 2391<br clear="both"><p><hr></p> 2392<h2 align="center">More About Built-in Tcl Scripts</h2> 2393<table><tr><td valign="top"><img src="image11"></td> 2394<td valign="top"><p><ul><li>Comments and leading white-space are removed from the 2395 script by default. Use the "Don't Strip Comments" 2396 button to change this.</li></ul><ul><li>The file name must exactly match the name that is 2397 used by the <tt>source</tt> command.</li></ul></p></td></tr></table> 2398 2399<br clear="both"><p><hr></p> 2400<h2 align="center">Locations Of Libraries</h2> 2401<table><tr><td valign="top"><img src="image13"></td> 2402<td valign="top"><p><ul><li>Tells mktclapp where to look for script libraries.</li></ul><ul><li>All Tcl scripts in the indicated directories are 2403 compiled into the <tt>appinit.c</tt> file.</li></ul><ul><li>Comments and extra white-space are removed. 2404 There is no way to turn this off.</li></ul></p></td></tr></table> 2405 2406<br clear="both"><p><hr></p> 2407<h2 align="center">Built-in Binary Data Files</h2> 2408<table><tr><td valign="top"><img src="image14"></td> 2409<td valign="top"><p><ul><li>Arbitrary files become part of the virtual filesystem</li></ul><ul><li>No comment or white-space removal is attempted</li></ul><ul><li>Useful for images or other binary data</li></ul></p></td></tr></table> 2410 2411<br clear="both"><p><hr></p> 2412<h2 align="center">New Commands In Namespaces</h2> 2413<p>Two underscores (__) are replaced by two colons (::) in 2414 command names, thus giving the ability to define new commands 2415 in a namespace</p><p> 2416<table cellspacing="0" cellpadding="0" border="0"> 2417<tr><td valign="center"> 2418<small><tt>#include <hw.h></tt></small></td> 2419<td></td><td></td><td></td><td></td> 2420</tr> 2421<tr><td valign="center"> 2422<small><tt><br> 2423int ET_COMMAND_adder__add(ET_TCLARGS){<br> 2424 int a, b;</tt></small></td> 2425<td> </td> 2426<td valign="center"><img src="image2"></td> 2427<td> </td> 2428<td valign="center">Creates the Tcl command called "<tt>adder::add</tt>"</td> 2429</tr> 2430<tr><td valign="center"> 2431<small><tt> char *zResult[30];<br> 2432 if( argc!=3 ){<br> 2433 Tcl_AppendResult(interp,<br> 2434 "wrong # args: should be: \"",<br> 2435 argv[0], " VALUE VALUE\"", 0);<br> 2436 return TCL_ERROR;<br> 2437 }<br> 2438 if( Tcl_GetInt(interp, argv[1], &a)!=TCL_OK ){<br> 2439 return TCL_ERROR;<br> 2440 }<br> 2441 if( Tcl_GetInt(interp, argv[1], &b)!=TCL_OK ){<br> 2442 return TCL_ERROR;<br> 2443 }<br> 2444 sprintf(zResult, "-1073742724", a+b);<br> 2445 Tcl_SetResult(interp, zResult, TCL_VOLATILE);<br> 2446 return TCL_OK;<br> 2447}</tt></small></td> 2448<td></td><td></td><td></td><td></td> 2449</tr> 2450</table> 2451 2452<br clear="both"><p><hr></p> 2453<h2 align="center">Adding Your Own <tt>main()</tt></h2> 2454<table cellspacing="0" cellpadding="0" border="0"> 2455<tr><td valign="center"> 2456<small><tt>int main(int argc, char **argv){<br> 2457 /* Application specific initialization */</tt></small></td> 2458<td></td><td></td><td></td><td></td> 2459</tr> 2460<tr><td valign="center"> 2461<small><tt> Et_Init(argc, argv);</tt></small></td> 2462<td> </td> 2463<td valign="center"><img src="image2"></td> 2464<td> </td> 2465<td valign="center">Never returns!</td> 2466</tr> 2467<tr><td valign="center"> 2468<small><tt> /*NOTREACHED*/<br> 2469 return 0;<br> 2470}</tt></small></td> 2471<td></td><td></td><td></td><td></td> 2472</tr> 2473</table> 2474<p><table><tr><td valign="top"><img src="image3"></td> 2475<td valign="top"><b>The "Autofork" feature is disabled if you supply your own <tt>main()</tt></b></td></tr></table> 2476</p> 2477 2478<br clear="both"><p><hr></p> 2479<h2 align="center">Initializing The Tcl Interpreter</h2> 2480<table cellspacing="0" cellpadding="0" border="0"> 2481<tr><td valign="center"> 2482<small><tt>#include <tcl.h><br> 2483 <br> 2484int counter = 0;<br> 2485 <br> 2486int main(int argc, char **argv){<br> 2487 Et_Init(argc, argv);<br> 2488 /*NOTREACHED*/<br> 2489 return 0;<br> 2490}<br> 2491 <br> 2492int Et_AppInit(Tcl_Interp *interp){</tt></small></td> 2493<td></td><td></td><td></td><td></td> 2494</tr> 2495<tr><td valign="center"> 2496<small><tt> if( Blt_Init(Interp) ){<br> 2497 return TCL_ERROR;<br> 2498 }</tt></small></td> 2499<td> </td> 2500<td valign="center"><img src="image2"></td> 2501<td> </td> 2502<td valign="center">Example: Initialize an extension</td> 2503</tr> 2504<tr><td valign="center"> 2505<small><tt> Tcl_LinkVar(interp, "counter", &counter,<br> 2506 TCL_LINK_INT);</tt></small></td> 2507<td> </td> 2508<td valign="center"><img src="image2"></td> 2509<td> </td> 2510<td valign="center">Or link a C variable to a Tcl variable</td> 2511</tr> 2512<tr><td valign="center"> 2513<small><tt> return TCL_OK;</tt></small></td> 2514<td> </td> 2515<td valign="center"><img src="image2"></td> 2516<td> </td> 2517<td valign="center">Return TCL_OK if successful</td> 2518</tr> 2519<tr><td valign="center"> 2520<small><tt>}</tt></small></td> 2521<td></td><td></td><td></td><td></td> 2522</tr> 2523</table> 2524 2525<br clear="both"><p><hr></p> 2526<h2 align="center">Writing Your Own Event Loop</h2> 2527<table cellspacing="0" cellpadding="0" border="0"> 2528<tr><td valign="center"> 2529<small><tt>#include <tcl.h><br> 2530</tt></small></td> 2531<td></td><td></td><td></td><td></td> 2532</tr> 2533<tr><td valign="center"> 2534<small><tt>void Et_CustomMainLoop(Tcl_Interp *interp){</tt></small></td> 2535<td> </td> 2536<td valign="center"><img src="image2"></td> 2537<td> </td> 2538<td valign="center">Replaces the default event loop</td> 2539</tr> 2540<tr><td valign="center"> 2541<small><tt> return;</tt></small></td> 2542<td> </td> 2543<td valign="center"><img src="image2"></td> 2544<td> </td> 2545<td valign="center">Ex: Return without handling any events.</td> 2546</tr> 2547<tr><td valign="center"> 2548<small><tt>}<br> 2549 <br> 2550int main(int argc, char **argv){</tt></small></td> 2551<td></td><td></td><td></td><td></td> 2552</tr> 2553<tr><td valign="center"> 2554<small><tt> Et_Init(argc, argv);</tt></small></td> 2555<td> </td> 2556<td valign="center"><img src="image2"></td> 2557<td> </td> 2558<td valign="center">This now returns after initializing Tcl</td> 2559</tr> 2560<tr><td valign="center"> 2561<small><tt> /* Application code here */<br> 2562 return 0;<br> 2563}</tt></small></td> 2564<td></td><td></td><td></td><td></td> 2565</tr> 2566</table> 2567 2568<br clear="both"><p><hr></p> 2569<h2 align="center">Writing Your Own Event Loop</h2> 2570<table cellspacing="0" cellpadding="0" border="0"> 2571<tr><td valign="center"> 2572<small><tt>#include <tcl.h><br> 2573 <br> 2574void Et_CustomMainLoop(Tcl_Interp *interp){</tt></small></td> 2575<td></td><td></td><td></td><td></td> 2576</tr> 2577<tr><td valign="center"> 2578<small><tt> for(;;){<br> 2579 Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);<br> 2580 /* Other processing... */<br> 2581 }</tt></small></td> 2582<td> </td> 2583<td valign="center"><img src="image2"></td> 2584<td> </td> 2585<td valign="center">Intermix processing and event handling</td> 2586</tr> 2587<tr><td valign="center"> 2588<small><tt>}<br> 2589 <br> 2590int main(int argc, char **argv){</tt></small></td> 2591<td></td><td></td><td></td><td></td> 2592</tr> 2593<tr><td valign="center"> 2594<small><tt> Et_Init(argc, argv);</tt></small></td> 2595<td> </td> 2596<td valign="center"><img src="image2"></td> 2597<td> </td> 2598<td valign="center">Never returns</td> 2599</tr> 2600<tr><td valign="center"> 2601<small><tt> /*NOTREACHED*/<br> 2602 return 0;<br> 2603}</tt></small></td> 2604<td></td><td></td><td></td><td></td> 2605</tr> 2606</table> 2607 2608<br clear="both"><p><hr></p> 2609<h2 align="center">Mktclapp Initialization Sequence</h2> 2610<p><ul><li>Initialization starts when the <tt>Et_Init()</tt> 2611 function is called either by client code or by 2612 the <tt>main()</tt> that mktclapp generates</li></ul><ul><li>Create the main Tcl interpreter</li></ul><ul><li>Construct the virtual filesystem overlay by redefining 2613 the <tt>source</tt> command and by using the 2614 <tt>Tcl</tt>*<tt>InsertProc()</tt> functions</li></ul><ul><li>Call <tt>Et_PreInit()</tt> if the client defines it</li></ul><ul><li>Call <tt>Tcl_Init()</tt> and <tt>Tk_Init()</tt></li></ul><ul><li>Call <tt>Tcl_CreateCommand()</tt> and <tt>Tcl_CreateObjCommand()</tt> 2615 for every <tt>ET_COMMAND_</tt>* and <tt>ET_OBJCOMMAND_</tt>* function 2616 in the client code</li></ul><ul><li>Call <tt>Et_AppInit()</tt> if the client defines it</li></ul><ul><li>Run the main Tcl script if there is one</li></ul><ul><li>Call <tt>Et_CustomMainLoop()</tt> if defined by client code or 2617 else run the built-in event loop</li></ul></p> 2618<br clear="both"><p><hr></p> 2619<h2 align="center">Invoking Tcl From C</h2> 2620<p><ul><li>Use one of the built-in evaluation functions: 2621 <center><table width="80%"> 2622 <tr><td valign="top" width="50%"><ul> 2623 <li> Tcl_Eval() </li> 2624 <li> Tcl_VarEval() </li> 2625 <li> Tcl_EvalFile() </li> 2626 <li> Tcl_GlobalEval() </li> 2627 </ul></td> 2628 <td valign="top" width="50%"><ul> 2629 <li> Tcl_EvalObj() </li> 2630 <li> Tcl_GlobalEvalObj() </li> 2631 </ul></td></tr> 2632 </table></center></li></ul><ul><li>Mktclapp provides evaluation functions with variable argument 2633 lists as in <tt>printf()</tt>: 2634 <ul> 2635 <li> Et_EvalF() </li> 2636 <li> Et_GlobalEvalF() </li> 2637 </ul></li></ul><ul><li>Mktclapp provides a global variable <tt>Et_Interp</tt> which is 2638 a pointer to the main interpreter</li></ul></p> 2639<br clear="both"><p><hr></p> 2640<h2 align="center">Invoking Tcl From C</h2> 2641<p>Example: A C function that pops up an error message dialog box</p><p> 2642<table cellspacing="0" cellpadding="0" border="0"> 2643<tr><td valign="center"> 2644<small><tt>#include "appinit.h"<br> 2645 <br> 2646void ErrMsg(char *zMsg){<br> 2647 Tcl_SetVar(Et_Interp, "zMsg", zMsg, TCL_GLOBAL_ONLY);<br> 2648 Tcl_GlobalEval(Et_Interp, <br> 2649 "tk_messageBox -icon error -msg $zMsg -type ok");<br> 2650 Tcl_UnsetVar(Et_Interp, "zMsg", TCL_GLOBAL_ONLY);<br> 2651}</tt></small></td> 2652<td></td><td></td><td></td><td></td> 2653</tr> 2654</table> 2655 2656<br clear="both"><p><hr></p> 2657<h2 align="center">Invoking Tcl From C</h2> 2658<p>The same C function implemented using <tt>Et_EvalF()</tt> instead 2659 of <tt>Tcl_GlobalEval()</tt></p><p> 2660<table cellspacing="0" cellpadding="0" border="0"> 2661<tr><td valign="center"> 2662<small><tt>#include "appinit.h"<br> 2663 <br> 2664void ErrMsg(char *zMsg){<br> 2665 Et_EvalF(Et_Interp, <br> 2666 "tk_messageBox -icon error -msg {����P�X�} -type ok",<br> 2667 zMsg);<br> 2668}</tt></small></td> 2669<td></td><td></td><td></td><td></td> 2670</tr> 2671</table> 2672<p> 2673 <ul><li> 2674 Suppose the function is called as follows: 2675 <blockquote> 2676 <tt>ErrMsg("Syntax error near \"}\"");</tt> 2677 </blockquote> 2678 </li></ul> 2679 2680 <ul><li> 2681 The command that gets executed is: 2682 <pre> 2683 tk_messageBox -icon error -msg \ 2684 {Syntax error near "}"} -type ok 2685 </pre> 2686 </li></ul> 2687 2688 <ul><li> 2689 But this is an ill-formed Tcl command! 2690 </li></ul> 2691</p> 2692 2693<br clear="both"><p><hr></p> 2694<h2 align="center">Invoking Tcl From C</h2> 2695<p>Use the "<tt></tt>" format to generate a quoted string</p><p> 2696<table cellspacing="0" cellpadding="0" border="0"> 2697<tr><td valign="center"> 2698<small><tt>#include "appinit.h"<br> 2699 <br> 2700void ErrMsg(char *zMsg){<br> 2701 Et_EvalF(Et_Interp, <br> 2702 "tk_messageBox -icon error -msg \"%\" -type ok",<br> 2703 zMsg);<br> 2704}</tt></small></td> 2705<td></td><td></td><td></td><td></td> 2706</tr> 2707</table> 2708<p><ul><li>The <tt></tt> puts a backslash before all characters that 2709 are special to Tcl</li></ul><ul><li>The Tcl command becomes: 2710 <pre> 2711 tk_messageBox -icon error -msg \ 2712 "Syntax error near \"\}\"" -type ok 2713 </pre></li></ul></p> 2714 2715<br clear="both"><p><hr></p> 2716<h2 align="center">Other Functions Provided By Mktclapp</h2> 2717<p><ul><li><tt>void Et_ResultF(Tcl_Interp*, ...);</tt></li></ul><ul><li><tt>char *Et_DStringAppendF(Tcl_DString*, ...);</tt></li></ul><ul><li><tt>int Et_AppendObjF(Tcl_Obj*, ...);</tt></li></ul><ul><li><tt>char *mprintf(const char *format, ...);<br> 2718 char *vmprintf(const char *format, va_list);</tt></li></ul><ul><li><tt>void Et_NewBuiltinFile(char *filename, char *data, int amt);</tt></li></ul></p> 2719<br clear="both"><p><hr></p> 2720<h2 align="center">Operating Mktclapp From The Command Line</h2> 2721<p><ul><li>Generate the <tt>appinit.h</tt> header file like this: 2722 <blockquote> 2723 <tt>mktclapp -header >appinit.h</tt> 2724 </blockquote></li></ul><ul><li>Generate the <tt>appinit.c</tt> file like this: 2725 <blockquote> 2726 <tt>mktclapp -f appinit.mta >appinit.c</tt> 2727 </blockquote></li></ul><ul><li>The <tt>*.mta</tt> file is just a list of command-line options</li></ul><ul><li>Enter 2728 <blockquote> 2729 <tt>mktclapp -help</tt> 2730 </blockquote> 2731 to get a list of available options</li></ul><ul><li>Look at MTA files generated by xmktclapp.tcl for examples</li></ul></p> 2732<br clear="both"><p><hr></p> 2733<h2 align="center">Format Of An MTA File</h2> 2734<table cellspacing="0" cellpadding="0" border="0"> 2735<tr><td valign="center"> 2736<small><tt># Configuration file generated by xmktclapp<br> 2737# Hand editing is not recommended<br> 2738#</tt></small></td> 2739<td> </td> 2740<td valign="center"><img src="image2"></td> 2741<td> </td> 2742<td valign="center">Comments begin with one #</td> 2743</tr> 2744<tr><td valign="center"> 2745<small><tt>## Autofork No<br> 2746## CFile:add.c 1<br> 2747## CFile:objadd.c 1<br> 2748## CmdLine Console<br> 2749## ConfigFile hw.mta<br> 2750## Data:check.gif 1<br> 2751## MainScript hw.tcl<br> 2752## Mode Tcl/Tk<br> 2753## NoSource No<br> 2754## OutputFile hw.c<br> 2755## Shroud No<br> 2756## Standalone Yes<br> 2757## TclFile:hw.tcl 1<br> 2758## TclLib /usr/lib/tcl8.0<br> 2759## TkLib /usr/lib/tk8.0</tt></small></td> 2760<td> </td> 2761<td valign="center"><img src="image2"></td> 2762<td> </td> 2763<td valign="center">Lines beginning with two #s are used 2764 by xmktclapp.tcl and ignored by mktclapp</td> 2765</tr> 2766<tr><td valign="center"> 2767<small><tt>-console<br> 2768-main-script "hw.tcl"<br> 2769-tcl-library "/usr/lib/tcl8.0"<br> 2770-tk-library "/usr/lib/tk8.0"<br> 2771"add.c"<br> 2772"objadd.c"<br> 2773-i "check.gif"<br> 2774-strip-tcl "hw.tcl"</tt></small></td> 2775<td> </td> 2776<td valign="center"><img src="image2"></td> 2777<td> </td> 2778<td valign="center">All other lines are read by mktclapp and 2779 ignored by xmktclapp.tcl</td> 2780</tr> 2781</table> 2782 2783<br clear="both"><p><hr></p> 2784<h2 align="center">Summary</h2> 2785<p><ul><li>Use Tcl for the things Tcl is good at and use C/C++ for the things that 2786 C/C++ is good at</li></ul><ul><li>Use wrapper programs to make pure Tcl programs standalone</li></ul><ul><li>Use mktclapp to combine Tcl/Tk with C/C++ into a standalone</li></ul></p> 2787<br clear="both"><p><hr></p> 2788