1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
|
package Tcl;
$Tcl::VERSION = '1.27';
=head1 NAME
Tcl - Tcl extension module for Perl
=head1 SYNOPSIS
use Tcl;
$interp = Tcl->new;
$interp->Eval('puts "Hello world"');
=head1 DESCRIPTION
The Tcl extension module gives access to the Tcl library with
functionality and interface similar to the C functions of Tcl.
In other words, you can
=over
=item *
create Tcl interpreters
The Tcl interpreters so created are Perl objects whose destructors
delete the interpreters cleanly when appropriate.
=item *
execute Tcl code in an interpreter
The code can come from strings, files or Perl filehandles.
=item *
bind in new Tcl procedures
The new procedures can be either C code (with addresses presumably
obtained using I<dl_open> and I<dl_find_symbol>) or Perl subroutines
(by name, reference or as anonymous subs). The (optional) deleteProc
callback in the latter case is another perl subroutine which is called
when the command is explicitly deleted by name or else when the
destructor for the interpreter object is explicitly or implicitly called.
=item *
Manipulate the result field of a Tcl interpreter
=item *
Set and get values of variables in a Tcl interpreter
=item *
Tie perl variables to variables in a Tcl interpreter
The variables can be either scalars or hashes.
=back
=head2 Methods in class Tcl
To create a new Tcl interpreter, use
$interp = Tcl->new;
The following methods and routines can then be used on the Perl object
returned (the object argument omitted in each case).
=over
=item $interp->Init ()
Invoke I<Tcl_Init> on the interpreter.
=item $interp->CreateSlave (NAME, SAFE)
Invoke I<Tcl_CreateSlave> on the interpreter. Name is arbitrary.
The safe variable, if true, creates a safe sandbox interpreter.
See: http://www.tcl.tk/software/plugin/safetcl.html
http://www.tcl.tk/man/tcl8.4/TclCmd/safe.htm
This command returns a new interpreter.
=item $interp->Eval (STRING, FLAGS)
Evaluate script STRING in the interpreter. If the script returns
successfully (TCL_OK) then the Perl return value corresponds to Tcl
interpreter's result otherwise a I<die> exception is raised with the $@
variable corresponding to Tcl's interpreter result object. In each case,
I<corresponds> means that if the method is called in scalar context then
the string result is returned but if the method is called in list context
then the result is split as a Tcl list and returned as a Perl list.
The FLAGS field is optional and can be a bitwise OR of the constants
Tcl::EVAL_GLOBAL or Tcl::EVAL_DIRECT.
=item $interp->GlobalEval (STRING)
REMOVED. Evalulate script STRING at global level.
Call I<Eval>(STRING, Tcl::EVAL_GLOBAL) instead.
=item $interp->EvalFile (FILENAME)
Evaluate the contents of the file with name FILENAME. Otherwise, the
same as I<Eval>() above.
=item $interp->EvalFileHandle (FILEHANDLE)
Evaluate the contents of the Perl filehandle FILEHANDLE. Otherwise, the
same as I<Eval>() above. Useful when using the filehandle DATA to tack
on a Tcl script following an __END__ token.
=item $interp->call (PROC, ARG, ...)
Looks up procedure PROC in the interpreter and invokes it using Tcl's eval
semantics that does command tracing and will use the ::unknown (AUTOLOAD)
mechanism. The arguments (ARG, ...) are not passed through the Tcl parser.
For example, spaces embedded in any ARG will not cause it to be split into
two Tcl arguments before being passed to PROC.
Before invoking procedure PROC special processing is performed on ARG list:
1. All subroutine references within ARG will be substituted with Tcl name
which is responsible to invoke this subroutine. This Tcl name will be
created using CreateCommand subroutine (see below).
2. All references to scalars will be substituted with names of Tcl variables
transformed appropriately.
These first two items allow one to write and expect it to work properly such
code as:
my $r = 'aaaa';
button(".d", -textvariable => \$r, -command=>sub {$r++});
3. All references to hashes will be substituted with names of Tcl array
variables transformed appropriately.
4. As a special case, there is a mechanism to deal with Tk's special event
variables (they are mentioned as '%x', '%y' and so on throughout Tcl).
When creating a subroutine reference that uses such variables, you must
declare the desired variables using Tcl::Ev as the first argument to the
subroutine. Example:
sub textPaste {
my ($x,$y,$w) = @_;
widget($w)->insert("\@$x,$y", $interp->Eval('selection get'));
}
$widget->bind('<2>', [\&textPaste, Tcl::Ev('%x', '%y'), $widget] );
=item $interp->return_ref (NAME)
returns a reference corresponding to NAME, which was associated during
previously called C<< $interpnt->call(...) >> preprocessing. As a typical
example this could be variable associated with a widget.
=item $interp->delete_ref (NAME)
deletes and returns a reference corresponding to NAME, which was associated
during previously called C<< $interpnt->call(...) >> preprocessing.
this follows _code_dispose about if NAME is a TClNAME or a DESCNAME.
if NAME is a VARNAME then it is just deleted and returned.
=item $interp->icall (PROC, ARG, ...)
Looks up procedure PROC in the interpreter and invokes it using Tcl's eval
semantics that does command tracing and will use the ::unknown (AUTOLOAD)
mechanism. The arguments (ARG, ...) are not passed through the Tcl parser.
For example, spaces embedded in any ARG will not cause it to be split into
two Tcl arguments before being passed to PROC.
This is the lower-level procedure that the 'call' method uses. Arguments
are converted efficiently from Perl SVs to Tcl_Objs. A Perl AV array
becomes a Tcl_ListObj, an SvIV becomes a Tcl_IntObj, etc. The reverse
conversion is done to the result.
=item $interp->invoke (PROC, ARG, ...)
Looks up procedure PROC in the interpreter and invokes it directly with
arguments (ARG, ...) without passing through the Tcl parser. For example,
spaces embedded in any ARG will not cause it to be split into two Tcl
arguments before being passed to PROC. This differs from icall/call in
that it directly invokes the command name without allowing for command
tracing or making use of Tcl's unknown (AUTOLOAD) mechanism. If the
command does not already exist in the interpreter, and error will be
thrown.
Arguments are converted efficiently from Perl SVs to Tcl_Objs. A Perl AV
array becomes a Tcl_ListObj, an SvIV becomes a Tcl_IntObj, etc. The
reverse conversion is done to the result.
=item Tcl::Ev (FIELD, ...)
Used to declare %-substitution variables of interest to a subroutine
callback. FIELD is expected to be of the form "%#" where # is a single
character, and multiple fields may be specified. Returns a blessed object
that the 'call' method will recognize when it is passed as the first
argument to a subroutine in a callback. See description of 'call' method
for details.
=item $interp->result ()
Returns the current Tcl interpreter result. List v. scalar context is
handled as in I<Eval>() above.
=item $interp->CreateCommand (CMDNAME, CMDPROC, CLIENTDATA, DELETEPROC, FLAGS)
Binds a new procedure named CMDNAME into the interpreter. The
CLIENTDATA and DELETEPROC arguments are optional. There are two cases:
(1) CMDPROC is the address of a C function
(presumably obtained using I<dl_open> and I<dl_find_symbol>. In this case
CLIENTDATA and DELETEPROC are taken to be raw data of the ClientData and
deleteProc field presumably obtained in a similar way.
(2) CMDPROC is a Perl subroutine
(either a sub name, a sub reference or an anonymous sub). In this case
CLIENTDATA can be any perl scalar (e.g. a ref to some other data) and
DELETEPROC must be a perl sub too. When CMDNAME is invoked in the Tcl
interpreter, the arguments passed to the Perl sub CMDPROC are
(CLIENTDATA, INTERP, LIST)
where INTERP is a Perl object for the Tcl interpreter which called out
and LIST is a Perl list of the arguments CMDNAME was called with.
If the 1-bit of FLAGS is set then the 3 first arguments on the call
to CMDPROC are suppressed.
As usual in Tcl, the first element of the list is CMDNAME itself.
When CMDNAME is deleted from the interpreter (either explicitly with
I<DeleteCommand> or because the destructor for the interpreter object
is called), it is passed the single argument CLIENTDATA.
=item $interp->DeleteCommand (CMDNAME)
Deletes command CMDNAME from the interpreter. If the command was created
with a DELETEPROC (see I<CreateCommand> above), then it is invoked at
this point. When a Tcl interpreter object is destroyed either explicitly
or implicitly, an implicit I<DeleteCommand> happens on all its currently
registered commands.
=item (TCLNAME,GENCODE) = $interp->create_tcl_sub(CODEREF, EVENTS, TCLNAME, DESCRNAME)
Creates a COMMAND called TCLNAME calling CODEREF in the interpreter $interp,
and adds it to the internal tracking structure.
DESCRNAME and TCLNAME should not begin
with =
or @
or ::perl::
to avoid colisions
If TCLNAME IS blank or undef, it is constructed from the CODEREF address.
GENCODE starts as TCLNAME but gets @$EVENTS which can contain %vars joined to it.
TCLNAME and DESCRNAME get stored in an internal structure,
and can be used to purge things fRom the command table via code_destroy or $interp->delete_ref;
Returns (TCLNAME,GENCODE).
if you are creating code refs with this you can continue to use the same coderef and it will be converted on each call.
but if you save GENCODE, you can replace the anon-coderef call in the tcl command with GENCODE.
for instance
$interp->call('FILEEVENT',$fileref,WRITABLE=>sub {...});
can be replaced by
my ($tclcode,$gencode)=$interp->create_tcl_sub(sub{...}, EVENTS, TCLNAME, DESCRNAME);
$interp->call('FILEEVENT',$gencode,WRITABLE=>$gencode);
or
my $sub=sub{....};
$interp->call('FILEEVENT',$fileref,WRITABLE=>$sub);
can be replaced by
my ($tclcode,$gencode)=$interp->create_tcl_sub($sub, EVENTS, TCLNAME, DESCRNAME);
$interp->call('FILEEVENT',$gencode,WRITABLE=>$gencode);
although
$interp->call('FILEEVENT',$fileref,WRITABLE=>$sub);
will stil work fine too.
Then you later call
$interp->delete_ref($tclname);
when you are finished with that sub to clean it from the internal tracking and command table.
This means no automatic cleanup will occur on the sub{...} or $sub
And after the destroy inside Tcl any triggering writable on $fileref will fail as well.
so it should be replaced first via
$interp->call('FILEEVENT',$fileref,WRITABLE=>'');
=item (CODEREF) = Tcl::_code_dispose(NAME)
Purges the internal table of a NAME
and may initiate destruction of something created thru call or create_tcl_sub
TCLNAME and DESCRNAME get stored in an internal structure,
and can be used to purge things form the command table.
calling _code_dispose on a TCLNAME retruned from create_tcl_sub removes all use instances and purges the command table.
calling _code_dispose on a DESCRNAME passed to create_tcl_sub removes only that instace
Code used in a DESCRNAME may be used in other places as well,
only when the last usage is purged does the entry get purged from the command table
While the internal tracking structure saves the INTERP the code was added to,
it itself does not keep things separated by INTERP,
A TCLNAME or DESCRNAMe can only exist in one INTERP at a time,
using a new INTERP just causes the one in the last INTERP to disappear,
and probably end up with the Tcl code getting deleted
Returns (CODEREF), this is the original coderef
=item $interp->SetResult (STRING)
Sets Tcl interpreter result to STRING.
=item $interp->AppendResult (LIST)
Appends each element of LIST to Tcl's interpreter result object.
=item $interp->AppendElement (STRING)
Appends STRING to Tcl interpreter result object as an extra Tcl list element.
=item $interp->ResetResult ()
Resets Tcl interpreter result.
=item $interp->SplitList (STRING)
Splits STRING as a Tcl list. Returns a Perl list or the empty list if
there was an error (i.e. STRING was not a properly formed Tcl list).
In the latter case, the error message is left in Tcl's interpreter
result object.
=item $interp->SetVar (VARNAME, VALUE, FLAGS)
The FLAGS field is optional. Sets Tcl variable VARNAME in the
interpreter to VALUE. The FLAGS argument is the usual Tcl one and
can be a bitwise OR of the constants Tcl::GLOBAL_ONLY,
Tcl::LEAVE_ERR_MSG, Tcl::APPEND_VALUE, Tcl::LIST_ELEMENT.
=item $interp->SetVar2 (VARNAME1, VARNAME2, VALUE, FLAGS)
Sets the element VARNAME1(VARNAME2) of a Tcl array to VALUE. The optional
argument FLAGS behaves as in I<SetVar> above.
=item $interp->GetVar (VARNAME, FLAGS)
Returns the value of Tcl variable VARNAME. The optional argument FLAGS
behaves as in I<SetVar> above.
=item $interp->GetVar2 (VARNAME1, VARNAME2, FLAGS)
Returns the value of the element VARNAME1(VARNAME2) of a Tcl array.
The optional argument FLAGS behaves as in I<SetVar> above.
=item $interp->UnsetVar (VARNAME, FLAGS)
Unsets Tcl variable VARNAME. The optional argument FLAGS
behaves as in I<SetVar> above.
=item $interp->UnsetVar2 (VARNAME1, VARNAME2, FLAGS)
Unsets the element VARNAME1(VARNAME2) of a Tcl array.
The optional argument FLAGS behaves as in I<SetVar> above.
=back
=head2 Command table cleanup
In V1.03 command table cleanup was intoduced.
This tries to keep the internal structure and command table clean.
In V1.02 and prior heavy use of sub { .. } in Tcl commands could pollute these tables
as they were never cleared. Command table cleanup tries to alieviate this.
if you call create_tcl_sub the internal reference exists until
you delete_ref or _code_dispose it, or you call create_tcl_sub with the same DESCRNAME.
if the internal reference was created internaly by call(...) there are two rules
=over
=item 1)
If the command is an "after" the internal references is keept at least until 1 second after the delay.
If there are still other "users" of the TCLNAME then it is not deleted until the last one goes away.
If another call with the same CODEREF happens before this,
then it will get registered as a "user" without any need to delete/recreate the tcl command first.
=item 2)
otherwise a DESCRNAME is created with the text sections of the command, prefaced by "=".
Like
"=after 1000"
or "=:.m.m add command -command -label Exit"
or "=::button .f3.b8 -text conn -command"
or "=gets sock9ac2b50"
or "=fileevent sock9827430 writable"
the TCLCODES created for that command will be kept at least until a command with
the same DESCRNAME and containing a subroutine reference is run again.
Since many DESCRNAMES can reference the same TCLNAME only when
the last DESCRNAME referencing a TCLNAME is released is the TCLNAME purged.
NOTE:
Since
$interp->call('fileevent','sock9827430','writable');
does not contain a subroutine reference, it will not release/free the TCLNAME/DESCRNAME created by
$interp->call('fileevent','sock9827430','writable',sub{...});
even though that is the way you deactivate a writable/readable callback in Tcl.
=back
Prior to V1.06 there was also a problem with the coderef never getting cleared from sas,
a refcount was kept at the PVCV that prevented it from getting garbage collected,
but that SV itself got "lost" and could never be garbage collected,
thereby also keeping anything in that codes PAD.
To assist in tracking chages to the internal table and the commands table 3 trace subs were added,
set them to non-blank or non-zero to add the tracking output to SYSOUT, like this in your code:
sub Tcl::TRACE_SHOWCODE(){1}
=over
=item Tcl::TRACE_SHOWCODE
Display all generated Tcl code by call().
Be aware: Tkx::MainLoop runs by issuing a lot of "winfo exists ." calls, a LOT.
But this is a nice way to tell what your programs are doing to Tcl.
=item Tcl::TRACE_CREATECOMMAND
Display Tcl subroutine creation by call/create_tcl_sub
=item Tcl::TRACE_DELETECOMMAND
Display Tcl subroutine deletion by cleanup/delete_ref/_code_dispose
=back
=head2 Linking Perl and Tcl variables
You can I<tie> a Perl variable (scalar or hash) into class Tcl::Var
so that changes to a Tcl variable automatically "change" the value
of the Perl variable. In fact, as usual with Perl tied variables,
its current value is just fetched from the Tcl variable when needed
and setting the Perl variable triggers the setting of the Tcl variable.
To tie a Perl scalar I<$scalar> to the Tcl variable I<tclscalar> in
interpreter I<$interp> with optional flags I<$flags> (see I<SetVar>
above), use
tie $scalar, "Tcl::Var", $interp, "tclscalar", $flags;
Omit the I<$flags> argument if not wanted.
To tie a Perl hash I<%hash> to the Tcl array variable I<array> in
interpreter I<$interp> with optional flags I<$flags>
(see I<SetVar> above), use
tie %hash, "Tcl::Var", $interp, "array", $flags;
Omit the I<$flags> argument if not wanted. Any alteration to Perl
variable I<$hash{"key"}> affects the Tcl variable I<array(key)>
and I<vice versa>.
=head2 Accessing Perl from within Tcl
After creation of Tcl interpreter, in addition to evaluation of Tcl/Tk
commands within Perl, other way round also instantiated. Within a special
namespace C< ::perl > following objects are created:
::perl::Eval
So it is possible to use Perl objects from within Tcl.
=head2 Moving Tcl/Tk around with Tcl.pm
NOTE: explanations below is for developers managing Tcl/Tk installations
itself, users should skip this section.
In order to create Tcl/Tk application with this module, you need to make
sure that Tcl/Tk is available within visibility of this module. There are
many ways to achieve this, varying on ease of starting things up and
providing flexible moveable archived files.
Following list enumerates them, in order of increased possibility to change
location.
=over
=item *
First method
Install Tcl/Tk first, then install Perl module Tcl, so installed Tcl/Tk will
be used. This is most normal approach, and no care of Tcl/Tk distribution is
taken on Perl side (this is done on Tcl/Tk side)
=item *
Second method
Copy installed Tcl/Tk binaries to some location, then install Perl module Tcl
with a special action to make Tcl.pm know of this location. This approach
makes sure that only chosen Tcl installation is used.
=item *
Third method
During compiling Tcl Perl module, Tcl/Tk could be statically linked into
module's shared library and all other files zipped into a single archive, so
each file extracted when needed.
To link Tcl/Tk binaries, prepare their libraries and then instruct Makefile.PL
to use these libraries in a link stage.
(TODO provide better detailed description)
=back
=cut
use strict;
sub TRACE_SHOWCODE(){0} # display generated code in call();
sub TRACE_CREATECOMMAND(){0} # display sub creates;
sub TRACE_DELETECOMMAND(){0} # display sub deletes;
sub SAVEALLCODES () {1} # simulate the v1.05 way of saving only last call
sub SAVENOCODE() {1} # simulate the v1.05 way of leaving any existing
# $anon_refs{$descrname} alone if new line has no coderefs
our $DL_PATH;
unless (defined $DL_PATH) {
$DL_PATH = $ENV{PERL_TCL_DL_PATH} || $ENV{PERL_TCL_DLL} || "";
}
=for ignore
sub Tcl::seek_tkkit {
# print STDERR "wohaaa!\n";
unless ($DL_PATH) {
require Config;
for my $inc (@INC) {
my $tkkit = "$inc/auto/Tcl/tkkit.$Config::Config{so}";
if (-f $tkkit) {
$DL_PATH = $tkkit;
last;
}
}
}
}
=cut
seek_tkkit() if defined &seek_tkkit;
my $path;
if ($^O eq 'darwin') {
# Darwin 7.9 (OS X 10.3) requires the path of the executable be prepended
# for #! scripts to operate properly (avoids RegisterProcess error).
require Config;
unless (grep { $_ eq $Config::Config{binexp} } split $Config::Config{path_sep}, $ENV{PATH}) {
$path = join $Config::Config{path_sep}, $Config::Config{binexp}, $ENV{PATH};
}
}
require XSLoader;
{
local $ENV{PATH} = $path if $path;
XSLoader::load('Tcl', $Tcl::VERSION);
}
sub new {
my $int = _new(@_);
return $int;
}
my $pid = $$; # see rt ticket 77522
END {
Tcl::_Finalize()
if $$ == $pid;
}
# %anon_refs keeps track of anonymous subroutines and scalar/array/hash
# references which are created on the fly for tcl/tk interchange
# at a step when 'call' interpreter method prepares its arguments for
# tcl/tk call, which is invoked by 'icall' interpreter method
# (this argument transformation is done with "CreateCommand" method for
# subs and with 'tie' for other)
my %anon_refs;
sub _anon_refs_cheat { return \%anon_refs;}
# Coderef tracking creates 3 types of entries in %anon_refs
# 1) Tcl::Code
# this is added to %anon_refs when a coderef is found in a tcl call
# [\$sub, $interp, $tclname,$descrname]
# It is an array with four elements stored at the key $descrname
# where [0] $sub is the perlcoderef,
# [1] $interp is the tcl interpreter it is in,
# [2] $tclname is the name for the coderef in tcl
# and serves as a pointer to the Tcl::Cmdbase entry
# [3] $descrname is the name passed from call() or the app
# 2) Tcl::Cmdbase
# this is added to %anon_refs when a new code reference is added to the tcl internal tables.
# It is an array with two elements stored at the key $tclname
# The first element[0] is an array [\$sub, $interp, $tclname,0]
# where [0] $sub is the perlcoderef,
# [1] $interp is the tcl interpreter it is in,
# [2] $tclname is the name for the coderef in tcl
# [3] is the number of Tcl::Code elemets that reference it.
# The second element [1] is a hash
# for each Tcl::Code $descrname there is a $descrname key here,
# the value at the key is the number of Tcl::Code of that $descrname pointing to this Tcl::Cmdbase
# when this is >1 it is because there are Tcl::Code elements in $lastcodes that will be destroyed soon
# and we want to be able to retain the $descrname backlink after they are destroyed
# 3) an array of Tcl::Code elements stored at the key $descrname
# (TODO -- find out how to check for refcounting and proper releasing of
# resources)
# Subroutine "call" preprocess the arguments for special cases
# and then calls "icall" (implemented in Tcl.xs), which invokes
# the command in Tcl.
sub call {
my $interp = shift;
my @args = @_;
# add = so as not to interfere with user defined DESCRNAMEs
my $descrname = '='.join ' ', grep {defined} grep {!ref} @args;
my @codes;
my $lastcodes = $anon_refs{$descrname}; # could be an array, want to hold all for now so they can be reused
unless (SAVENOCODE()) {
unless ($#args == 1 and $args[0] eq 'set') {
# this was to clean up things like
# Tkx::fileevent($remote, writable => sub {...});
# when this was run later
# Tkx::fileevent($fh, writable => '');
# but "set foo" doesnt clear its setting, it returns its value
# after busting the test in the Tkx tests for set foo
# i decided to disable this for now
delete $anon_refs{$descrname}; # if old one had a call and new one doesnt make sure it goes away
}
}
# Process arguments looking for special cases
for (my $argcnt=0; $argcnt<=$#args; $argcnt++) {
my $arg = $args[$argcnt];
my $ref = ref($arg);
next unless $ref;
if ($ref eq 'CODE' || $ref eq 'Tcl::Code') {
# We have been passed something like \&subroutine
# Create a proc in Tcl that invokes this subroutine (no args)
$args[$argcnt] = $interp->create_tcl_sub($arg, undef, undef, $descrname);
push @codes, $anon_refs{$descrname}; # push CODE also only to keep it from early disposal
}
elsif ($ref eq 'SCALAR') {
# We have been passed something like \$scalar
# Create a tied variable between Tcl and Perl.
# stringify scalar ref, create in ::perl namespace on Tcl side
# This will be SCALAR(0xXXXXXX) - leave it to become part of a
# Tcl array.
my $nm = "::perl::$arg";
unless (exists $anon_refs{$nm}) {
$anon_refs{$nm} = $arg;
my $s = $$arg;
tie $$arg, 'Tcl::Var', $interp, $nm;
$s = '' unless defined $s;
$$arg = $s;
}
$args[$argcnt] = $nm; # ... and substitute its name
}
elsif ($ref eq 'HASH') {
# We have been passed something like \%hash
# Create a tied variable between Tcl and Perl.
# stringify hash ref, create in ::perl namespace on Tcl side
# This will be HASH(0xXXXXXX) - leave it to become part of a
# Tcl array.
my $nm = $arg;
$nm =~ s/\W/_/g; # remove () from stringified name
$nm = "::perl::$nm";
unless (exists $anon_refs{$nm}) {
$anon_refs{$nm} = $arg;
my %s = %$arg;
tie %$arg, 'Tcl::Var', $interp, $nm;
%$arg = %s;
}
$args[$argcnt] = $nm; # ... and substitute its name
}
elsif ($ref eq 'ARRAY' && ref($arg->[0]) eq 'CODE') {
# We have been passed something like [\&subroutine, $arg1, ...]
# Create a proc in Tcl that invokes this subroutine with args
my $events;
# Look for Tcl::Ev objects as the first arg - these must be
# passed through for Tcl to evaluate. Used primarily for %-subs
# This could check for any arg ref being Tcl::Ev obj, but it
# currently doesn't.
if ($#$arg >= 1 && ref($arg->[1]) eq 'Tcl::Ev') {
$events = splice(@$arg, 1, 1);
}
$args[$argcnt] =
$interp->create_tcl_sub(sub {
$arg->[0]->(@_, @$arg[1..$#$arg]);
}, $events, undef, $descrname);
push @codes, $anon_refs{$descrname};
}
elsif ($ref eq 'REF' and ref($$arg) eq 'SCALAR') {
# this is a very special shortcut: if we see construct like \\"xy"
# then place proper Tcl::Ev(...) for easier access
my $events = [map {"%$_"} split '', $$$arg];
if (ref($args[$argcnt+1]) eq 'ARRAY' &&
ref($args[$argcnt+1]->[0]) eq 'CODE') {
$arg = $args[$argcnt+1];
$args[$argcnt] =
$interp->create_tcl_sub(sub {
$arg->[0]->(@_, @$arg[1..$#$arg]);
}, $events, undef, $descrname);
push @codes, $anon_refs{$descrname};
}
elsif (ref($args[$argcnt+1]) eq 'CODE') {
$args[$argcnt] = $interp->create_tcl_sub($args[$argcnt+1],$events, undef, $descrname);
push @codes, $anon_refs{$descrname};
}
else {
warn "not CODE/ARRAY expected after description of event fields";
}
splice @args, $argcnt+1, 1;
}
}
$lastcodes = []; # now let any from last use be destroyed
showcode(\@args) if TRACE_SHOWCODE();
if ($#codes>-1 and $args[0] eq 'after') {
# AFTERS can clog up the tables real quick, so plan to delete them via Tcl::Code::DESTROY
my $delay='';
if ($args[1] =~ /^\d+$/) { $delay=$args[1]+1000; }
elsif ($args[1] eq 'idle') { $delay='idle'; } # just hope they run in order
if ($delay) {
my $id = $interp->icall(@args);
#print STDERR "rebind for $interp;$id\n";
# in 'after' methods, disposal of CODE REFs based on 'after' id
# i.e based on return value of tcl call
my $newname='@'.$interp.';'.$id;
$anon_refs{$newname} = \@codes;
for my $code (@codes) {
my $ridptr=$anon_refs{$code->[2]}[1];
$ridptr->{$newname}++ ; # save under new name
$ridptr->{$descrname}-- ; # maybe take out this name
unless ($ridptr->{$descrname}>0){
delete $ridptr->{$descrname}; # clean up old
}
}
delete $anon_refs{$descrname}; # and now its clean
for my $code (@codes) { $code->[3]=$newname; } # save under new name
# this should trigger DESTROY unless a newer after or something else still holds a $tclname in the list
$interp->invoke('after',$delay, "perl::Eval {Tcl::_code_dispose('$newname')}");
showcode( ['after',$delay, "perl::Eval {Tcl::_code_dispose('$newname')}"] ) if TRACE_SHOWCODE();
return $id;
} # delay
# if we're here - user does something wrong, but there is nothing we worry about
} # codes and after
# got to keep all of them alive , not just the last
# incase there are lines that have more than one like ===== fileevent readable=>$sub1 writable=>$sub2
# although that isnt legal, but this is ===== if 1 $sub1 $sub2
# the downside is that add/delete processing goes on for $sub1 every call if we only keep the last
if (SAVEALLCODES()) {
if (scalar(@codes)>1) { # no reason to save an array of just 1
delete $anon_refs{$descrname};
$anon_refs{$descrname}=\@codes;
}
}
# Done with special var processing. The only processing that icall
# will do with the args is efficient conversion of SV to Tcl_Obj.
# A SvIV will become a Tcl_IntObj, ARRAY refs will become Tcl_ListObjs,
# and so on. The return result from icall will do the opposite,
# converting a Tcl_Obj to an SV.
# we need just this:
# return $interp->icall(@args);
# a bit of complications only to allow stack trace, i.e. in case of errors
# user will get error pointing to his program and not in this module.
# and also 'after' tcl method makes bit harder
if (wantarray) {
my @res;
eval { @res = $interp->icall(@args); };
if ($@) {
my $errmsg = $@; # 'require Carp' might destroy $@;
require Carp;
Carp::croak ("Tcl error '$errmsg' while invoking array result call:\n" .
"\t\"@args\"");
}
return @res;
} else {
my $res;
eval { $res = $interp->icall(@args); };
if ($@) {
my $errmsg = $@; # 'require Carp' might destroy $@;
require Carp;
Carp::croak ("Tcl error '$errmsg' while invoking scalar result call:\n" .
"\t\"@args\"");
}
return $res;
}
}
sub showcode{
print 'TCL::TRACE_SHOWCODE:'.join(' ',@{$_[0]})."\n";
}
# create_tcl_sub will create TCL sub that will invoke perl CODE ref
# If $events variable is specified then special processing will be
# performed to provide needed '%' variables.
# If $tclname is specified then procedure will have namely that name,
# otherwise it will have machine-readable name.
# Returns tcl script suitable for using in tcl events.
sub create_tcl_sub {
my ($interp,$sub,$events,$tclname, $descrname) = @_;
# rnames and tclnames begining = or @ or ::perl:: are reserved for internal use
unless ($tclname) {
# stringify sub, becomes "CODE(0x######)" in ::perl namespace
$tclname = "::perl::$sub";
}
#print STDERR "...=$descrname\n";
# the following is a bit more tricky than it seems to.
# because the whole intent of the Tcl:Cmdbase entries in %anon_refs hash is to have refcount
# of (possibly) anonymous sub that is happen to be passed,
# and, if passed for the same widget but arguments are same - then
# previous instance will be overwriten, and sub will be destroyed due
# to no reference count, and command table entry will also be destroyed during
# Tcl::Cmdbase::DESTROY
unless (exists $anon_refs{$tclname}) {
$anon_refs{$tclname} = bless (
[[\$sub, $interp, $tclname,0]
,{} # Tcl::Codes register here so they can get deleted
],'Tcl::Cmdbase');
$interp->CreateCommand($tclname, $sub, undef, undef, 1);
print "TCL::TRACE_CREATECOMMAND: $interp -> $descrname ( $tclname => $sub ,undef,udef,1 )\n" if TRACE_CREATECOMMAND();
delete $anon_refs{$descrname};
}
my @newtcl=@{$anon_refs{$tclname}[0]};
pop @newtcl;
push @newtcl,$descrname;
bless( \@newtcl, 'Tcl::Code');
$anon_refs{$descrname} = \@newtcl;
$anon_refs{$tclname}[0][3]++; # push ref ct
$anon_refs{$tclname}[1]{$descrname}++; #register as user
my $gencode = $tclname;
if ($events) {
# Add any %-substitutions to callback
$gencode = "$tclname " . join(' ', @{$events});
}
return $tclname , $gencode; # return both the base and code-call
}
sub _code_dispose {
my $k = shift;
return undef unless(exists $anon_refs{$k});
my $ret = undef;
my $ref = ref($anon_refs{$k});
if ($ref eq 'Tcl::Code') {
$ret = $anon_refs{$k}[0];
delete $anon_refs{$k};
}
elsif ($ref eq 'Tcl::Cmdbase') {
## mainly for calling from exterior program when it is one that calls ($tclname)=create_tcl_sub().. too
my $atcl = $anon_refs{$k};
return undef unless ($atcl);
$ret = $atcl->[0][0];
my @keys=keys(%{$atcl->[1]});
for my $key (@keys) {
_code_dispose($key); # last one deletes me
}
delete $anon_refs{$k}; # but be sure
}
elsif ($ref eq 'ARRAY') {
my $atclarray = $anon_refs{$k};
$ret = $atclarray->[0][0]; # eh, return the first one, prob discarded anyway
delete $anon_refs{$k}; # bunch of Tcl::Code
}
return $$ret; # original delete_ref result, prob just discarded
}
sub delete_ref {
my $interp = shift;
my $name = shift;
my $iam = $anon_refs{$name};
my $ref = ref($iam);
if ($ref eq 'ARRAY') { return _code_dispose ($name);}
elsif ($ref eq 'Tcl::Code') { return _code_dispose ($name);}
elsif ($ref eq 'Tcl::Cmdbase'){ return _code_dispose ($name);}
else { # these are the ties ??
$interp->UnsetVar($name); #TODO: will this delete variable in Tcl?
delete $anon_refs{$name};
untie $$iam;
return $iam;
}
}
sub return_ref {
my $interp = shift;
my $name = shift;
my $iam = $anon_refs{$name};
my $ref = ref($iam);
if ($ref eq 'ARRAY') { return ${$iam->[0][0]};} # gotta pick one
elsif ($ref eq 'Tcl::Code') { return ${$iam->[0]};}
elsif ($ref eq 'Tcl::Cmdbase'){ return ${$iam->[0][0]};}
return $anon_refs{$name};
}
sub _code_clear {
# for testing
my $debug = shift;
print "_code_clear ARRAY\n" if ($debug);
for my $kk (keys %anon_refs) {
if (ref($anon_refs{$kk}) eq 'ARRAY'){ print "ARRAY $kk\n" if ($debug); delete $anon_refs{$kk};}
}
print "_code_clear Tcl::Code list\n" if ($debug);
for my $kk (keys %anon_refs) {
if (ref($anon_refs{$kk}) eq 'Tcl::Code'){ print "Code $kk\n" if ($debug); delete $anon_refs{$kk};}
}
print "_code_clear Tcl::Cmdbase\n" if ($debug);
for my $kk (keys %anon_refs) {
if (ref($anon_refs{$kk}) eq 'Tcl::Cmdbase'){ print "Code $kk\n" if ($debug); delete $anon_refs{$kk};}
}
}
sub Ev {
my @events = @_;
return bless \@events, "Tcl::Ev";
}
package Tcl::Code;
# purpose is to track CODE REFs still "in use"
# (often these are anon subs)
# each has ptr to the Tcl::Cmdbase entry that tracks the command table
# so to bless it to this package and then catch deleting it, so
# to do cleaning up
sub DESTROY {
# my $rsub = $_[0]->[0]; # dont really need it here anymore,
# my $interp = $_[0]->[1];
my $tclname = $_[0]->[2];
my $descrname = $_[0]->[3];
my $tclptr = $anon_refs{$tclname}->[0];
return unless ($tclptr); # should exist but be safe
$tclptr->[3]--;
unless($tclptr->[3]>0) {
delete $anon_refs{$tclname}; # kill the Cmdbase
}
else {
$anon_refs{$tclname}->[1]{$descrname}--;
unless ( $anon_refs{$tclname}->[1]{$descrname}>0) {
delete $anon_refs{$tclname}->[1]{$descrname}; # unregister me
}
}
}
package Tcl::Cmdbase;
# only purpose is to track CODE REFs passed to 'call' method
# (often these are anon subs)
# so to bless it to this package and then catch deleting it, so
# to do cleaning up
sub DESTROY {
# $_[0] points to a [Tcl::code,{$descrname=>1,...}] set
my $interp = $_[0]->[0]->[1];
my $tclname = $_[0]->[0]->[2];
return unless ($tclname);
if (defined $interp) {
$interp->DeleteCommand($tclname);
print "TCL::TRACE_DELETECOMMAND: $interp -> ( $tclname )\n" if TRACE_DELETECOMMAND();
}
}
package Tcl::List;
use overload '""' => \&as_string,
fallback => 1;
package Tcl::Var;
sub TIESCALAR {
my $class = shift;
my @objdata = @_;
unless (@_ == 2 || @_ == 3) {
require Carp;
Carp::croak('Usage: tie $s, Tcl::Var, $interp, $varname [, $flags]');
};
bless \@objdata, $class;
}
sub TIEHASH {
my $class = shift;
my @objdata = @_;
unless (@_ == 2 || @_ == 3) {
require Carp;
Carp::croak('Usage: tie %hash, Tcl::Var, $interp, $varname [, $flags]');
}
bless \@objdata, $class;
}
my %arraystates;
sub FIRSTKEY {
my $obj = shift;
die "STORE Usage: objdata @{$obj} $#{$obj}, not 2 or 3 (@_)"
unless @{$obj} == 2 || @{$obj} == 3;
my ($interp, $varname, $flags) = @$obj;
$arraystates{$varname} = $interp->invoke("array","startsearch",$varname);
my $r = $interp->invoke("array","nextelement",$varname,$arraystates{$varname});
if ($r eq '') {
delete $arraystates{$varname};
return undef;
}
return $r;
}
sub NEXTKEY {
my $obj = shift;
die "STORE Usage: objdata @{$obj} $#{$obj}, not 2 or 3 (@_)"
unless @{$obj} == 2 || @{$obj} == 3;
my ($interp, $varname, $flags) = @$obj;
my $r = $interp->invoke("array","nextelement",$varname,$arraystates{$varname});
if ($r eq '') {
delete $arraystates{$varname};
return undef;
}
return $r;
}
sub CLEAR {
my $obj = shift;
die "STORE Usage: objdata @{$obj} $#{$obj}, not 2 or 3 (@_)"
unless @{$obj} == 2 || @{$obj} == 3;
my ($interp, $varname, $flags) = @$obj;
$interp->invoke("array", "unset", "$varname");
#$interp->invoke("array", "set", "$varname", "");
}
sub DELETE {
my $obj = shift;
unless (@{$obj} == 2 || @{$obj} == 3) {
my @args = @_;
require Carp;
Carp::croak("STORE Usage: objdata @{$obj} $#{$obj}, not 2 or 3 (@args)");
}
my ($interp, $varname, $flags) = @{$obj};
my ($str1) = @_;
$interp->invoke("unset", "$varname($str1)"); # protect strings?
}
sub UNTIE {
my $ref = shift;
#print STDERR "UNTIE:$ref(@_)\n";
}
sub DESTROY {
my $ref = shift;
delete $anon_refs{$ref->[1]};
}
# This is the perl equiv to the C version, for reference
#
#sub STORE {
# my $obj = shift;
# croak "STORE Usage: objdata @{$obj} $#{$obj}, not 2 or 3 (@_)"
# unless @{$obj} == 2 || @{$obj} == 3;
# my ($interp, $varname, $flags) = @{$obj};
# my ($str1, $str2) = @_;
# if ($str2) {
# $interp->SetVar2($varname, $str1, $str2, $flags);
# } else {
# $interp->SetVar($varname, $str1, $flags || 0);
# }
#}
#
#sub FETCH {
# my $obj = shift;
# croak "FETCH Usage: objdata @{$obj} $#{$obj}, not 2 or 3 (@_)"
# unless @{$obj} == 2 || @{$obj} == 3;
# my ($interp, $varname, $flags) = @{$obj};
# my $key = shift;
# if ($key) {
# return $interp->GetVar2($varname, $key, $flags || 0);
# } else {
# return $interp->GetVar($varname, $flags || 0);
# }
#}
package Tcl;
=head1 Other Tcl interpreter methods
=over 2
=item export_to_tcl method
An interpreter method, export_to_tcl, is used to expose a number of perl
subroutines and variables all at once into tcl/tk.
B<export_to_tcl> takes a hash as arguments, which represents named parameters,
with following allowed values:
=over 4
=item B<namespace> => '...'
tcl namespace, where commands and variables are to
be created, defaults to 'perl'. If '' is specified - then global
namespace is used. A possible '::' at end is stripped.
=item B<subs> => { ... }
anonymous hash of subs to be created in Tcl, in the form /tcl name/ => /code ref/
=item B<vars> => { ... }
anonymous hash of vars to be created in Tcl, in the form /tcl name/ => /code ref/
=item B<subs_from> => '...'
a name of Perl namespace, from where all existing subroutines will be searched
and Tcl command will be created for each of them.
=item B<vars_from> => '...'
a name of Perl namespace, from where all existing variables will be searched,
and each such variable will be tied to Tcl.
=back
An example:
use strict;
use Tcl;
my $int = Tcl->new;
$tcl::foo = 'qwerty';
$int->export_to_tcl(subs_from=>'tcl',vars_from=>'tcl');
$int->Eval(<<'EOS');
package require Tk
button .b1 -text {a fluffy button} -command perl::fluffy_sub
button .b2 -text {a foo button} -command perl::foo
entry .e -textvariable perl::foo
pack .b1 .b2 .e
focus .b2
tkwait window .
EOS
sub tcl::fluffy_sub {
print "Hi, I am a fluffy sub\n";
}
sub tcl::foo {
print "Hi, I am foo\n";
$tcl::foo++;
}
=cut
sub export_to_tcl {
my $int = shift;
my %args = @_;
# name of Tcl package to hold tcl commands bound to perl subroutines
my $tcl_namespace = (exists $args{namespace} ? $args{namespace} : 'perl::');
$tcl_namespace=~s/(?:::)?$/::/;
# a batch of perl subroutines which tcl counterparts should be created
my $subs = $args{subs} || {};
# a batch of perl variables which tcl counterparts should be created
my $vars = $args{vars} || {};
# TBD:
# only => \@list_of_names
# argument to be able to limit the names to export to Tcl.
if (exists $args{subs_from}) {
# name of Perl package, which subroutines would be bound to tcl commands
my $subs_from = $args{subs_from};
$subs_from =~ s/::$//;
no strict 'refs';
for my $name (keys %{"$subs_from\::"}) {
#print STDERR "$name;\n";
if (defined &{"$subs_from\::$name"}) {
if (exists $subs->{$name}) {
next;
}
#print STDERR "binding sub '$name'\n";
$int->CreateCommand("$tcl_namespace$name", \&{"$subs_from\::$name"}, undef, undef, 1);
}
}
}
if (exists $args{vars_from}) {
# name of Perl package, which subroutines would be bound to tcl commands
my $vars_from = $args{vars_from};
$vars_from =~ s/::$//;
no strict 'refs';
for my $name (keys %{"$vars_from\::"}) {
#print STDERR "$name;\n";
if (defined ${"$vars_from\::$name"}) {
if (exists $vars->{$name}) {
next;
}
#print STDERR "binding var '$name' in '$tcl_namespace'\n";
local $_ = ${"$vars_from\::$name"};
tie ${"$vars_from\::$name"}, 'Tcl::Var', $int, "$tcl_namespace$name";
${"$vars_from\::$name"} = $_;
}
if (0) {
# array, hash - no need to do anything.
# (or should we?)
}
}
}
for my $subname (keys %$subs) {
#print STDERR "binding2 sub '$subname'\n";
$int->CreateCommand("$tcl_namespace$subname",$subs->{$subname}, undef, undef, 1);
}
for my $varname (keys %$vars) {
#print STDERR "binding2 var '$varname'\n";
unless (ref($vars->{$varname})) {
require 'Carp.pm';
Carp::croak("should pass var ref as variable bind parameter");
}
local $_ = ${$vars->{$varname}};
tie ${$vars->{$varname}}, 'Tcl::Var', $int, "$tcl_namespace$varname";
${$vars->{$varname}} = $_;
}
}
=item B<export_tcl_namespace>
extra convenience sub, binds to tcl all subs and vars from perl B<tcl::> namespace
=back
=cut
sub export_tcl_namespace {
my $int = shift;
$int->export_to_tcl(subs_from=>'tcl', vars_from=>'tcl');
}
=head1 AUTHORS
Malcolm Beattie, 23 Oct 1994
Vadim Konovalov, 19 May 2003
Jeff Hobbs, jeff (a) activestate . com, 22 Mar 2004
Gisle Aas, gisle (a) activestate . com, 14 Apr 2004
Special thanks for contributions to Jan Dubois, Slaven Rezic, Paul Cochrane,
Huck Finn, Christopher Chavez, SJ Luo.
=head1 COPYRIGHT
This program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
See http://www.perl.com/perl/misc/Artistic.html
=cut
#use Data::Dumper; print Dumper(\@codes)."codes \n ";
1;
|