This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Strange issues in TCL

Help for those learning Tcl or writing their own scripts.
Post Reply
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Strange issues in TCL

Post by dj-zath »

Recently, I upgraded from a hosted box running FreeBSD.. to a CoLocation box running Linux.

I was running an older version of eggdrop on the bsd box, but opted to run the latest TCL and eggdrop on this new box..

everything compiled and installed without any problems.. untill...

I went to load a custom script I wrote to manage my radio station interface..

BZZZZT! the script COMPLETELY FAILS!

okay, I thought, I figured I'd probably have to change things and would begin rewriting the script from scratch..

then I discovered WHY the script failed... and I'd like to know if this was intentional or whatnot... and how to FIX it...


I didn't get very far- in fact, right on the first piece of code/procs..

I have discovered the following:

1 timers will NOT run, period.
2 a proc will NOT load another proc... (list does NOTHING)
3 can't have a 'if{}' statement outside a proc (such as in the eggdrop.conf file)

an 'if' statement will run inside a proc, but ONLY if a GLOBAL varable is set; "local" varables don't work in 'if' statements- in or out of procs

so, the following piece of code:

Code: Select all

if {![info exists StartLoop]} {
       utimer 5 [list RunLoop]
       set StartLoop 1
}

proc RunLoop {} {
       utimer 5 [list Action]
       return 1
}

does NOT work at all! this piece of code has THREE failures in it (mentioned above) and thats as far as I got!

I'm SURE there are more and more "odd failures" but the bot itself seems to run just FINE.. its as if the failures are INTENTIONAL or as if the bot was designed to no longer allow the use of the internal TCL scripting to run external functions...

I have tried reinstalling Eggdrop and TCL but nothing has improved

I have tried clean installs, clean uninstalls, YUM and manual installs- make static.. you name it!

I am totally STUMPED on this one!

anyone offer some help?? thanks!

DjZ
:) :)
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Could you provide some additional information, such as version of eggdrop, version of tcl, the full source of your custom script, any generated/logged errors?
  1. Odd, do you get any errors logged or such?
  2. The list command does not call another proc, it simply generates a tcl-list. It is used with timers to prevent code injection and remote exploits, such as command substitutions ([command]) and variable evaluation.
    This would rather be a side-effect of your first problem.
  3. This is very odd. Tcl should not make any difference to what commands are available simply by changing the command execution context. Did you get any errors when trying this?
  4. Variable context must be considered. A globalspace variable is not implicitly made available to the environment of a proc, but must explicitly be made available using the upvar or global commands. A local variable within a proc will only persist through the execution of the proc, and is wiped once the proc ends. Any variable already created within the current instance of the proc should be usable with if, aswell as any other command, without having to link it to a globalspace variable.
Personally, I can not see any reason for the posted code not working (I am assuming there is a proc named Action aswell somewhere in your script). I also assume the pasted code is executed at global context, and StartLoop has not been set prior running this code.
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

hi and thanks for your prompt reply!

a couple things to explain reguarding your inquiry.

1.. the "code" I provided above was just an example- it assumes a proc named "Action" exists.

2.. the actual code I have for the radio interface is rather lengthy.. and I'd rather not post it since it contains sensitive information (of course, I could "bleep out" that info.. but I might miss something... and that will bite me in the ass later on :-))

3. I think the code itself would work once the "base" parts are figured out..

I have began to use "debug mode".. and now I see the error messages ( call it a brain fart or what!) The error messages still don't make sense, however, but at least I see whats happening...

the args/flags for called procs are being dropped and/or ignored

note the following example:

heres a example piece of code I put in:

Code: Select all

bind pub -n !test Test

proc   Test {} {
    utimer 5 [list Go]
}


proc     Go {nick uhost hand chan arg} {
     putserv "PRIVMSG #Test : Operation Complete."
}

then I type !test in the chatroom:

I receive the error in debug:

Tcl error [Test]: wrong # args: should be "Go nick uhost hand chan arg"

and, of course, the operation fails.


This suggests that flags/args from the "called" proc are simply being ignored and, of course, then "error out" and fail...

thats basically all I have at this time.. I'm still "debugging" this and trying out other ideas to see if I can figure it out.. the MOST LIKELY thing is I probably am forgetting something that which USED to work when omitted- but now REQUIRED and I just don't know what it is!

again, thanks for your time, help and input!

DjZ
:) :)
User avatar
speechles
Revered One
Posts: 1398
Joined: Sat Aug 26, 2006 10:19 pm
Location: emerald triangle, california (coastal redwoods)

Post by speechles »

Your problem is painfully obvious and the error given to you clearly spells it out.

Code: Select all

    utimer 5 [list Go] 
You are invoking the procedure go, with no parameters. Yet in your procedure declartion below:

Code: Select all

proc     Go {nick uhost hand chan arg} { 
You are expecting there to be 5 passed: nick, uhost, hand, chan, arg

You need to include these arguments when invoking the timer passing them to the go procedure, or eliminate them from the go procedure as I have done below.

Code: Select all

bind pub n !test Test 

proc   Test {nick uhost hand chan arg} {
    utimer 5 [list Go]
}


proc     Go { } {
     putserv "PRIVMSG #Test : Operation Complete."
} 
When you use a bind, it will expect to pass a set amount of parameters (in your case: nick, uhost, hand, chan and the remaining user text given). Your procedure header parameters must match this amount as well. If you invoke the go procedure without passing arguments you must also leave the parameter field empty within the procedure header.
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

As speechles pointed out, you must always make sure function calls have the right number of arguments. Different bindings will call the associated function with a set of arguments depending on the type of the binding, and hence your proc must match that. Also, keep in mind that there is no automatic variable injection or such when calling a function from another function..

Since your problems started when you upgraded your eggdrop, I would guess it is related to one of a few binding types that has been altered in a non-backwards compatible manner. These are notc, part, and mode (pre 1.3.18 ); see the tcl-commands.doc document for further information on compability with these commands, including portable code.
NML_375
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

DING!

okay okay! I get cha!

yeah, the old code didn't require sending the args/flags along with a run/list command... (though I DID include them in the "controlled" proc..)

since the writing of the previous post, I have "worked around" as many issues as I could... mainly sticking all the "timer-controlled" procs (updates) into a SINGLE proc and then binding that to "time" (the number of globals is SCARY!)

I still noticed that NO expressions or if/else works unless I set those particular varables as a global even if the if statment is still inside the SAME proc as the varable! setting the varable as a global works- even though that varable will NEVER be used outside the proc (isn't that a whaste of resources??) for this reason, I'm cirrently at 46 "global" varables and expect to climb to over 100! (I hope there isn't a LIMIT!)

because it was requested: and now I can access the web at this terminal)

heres the ACTUAL code, from the OLD Bot, reguarding the timers..

Code: Select all


##### Run Timers #####

if      {![info exists loop]} {
        utimer 5 [list RunLoop1]
        utimer 6 [list RunLoop2]
        utimer 97 [list RunLoop3]
        utimer 98 [list RunLoop4]
        set loop 1
}

proc    RunLoop1 {} {
        [list GetInfo]
        utimer 5 [list RunLoop1]
        return 1
}

proc    RunLoop2 {} {
        [list OtsSwitch]
        utimer 5 [list RunLoop2]
        return 1
}


proc    RunLoop3 {} {
        [list UpteTitle]
        utimer 5 [list RunLoop3]
        return 1
}

proc    RunLoop4 {} {
        [list UpteSong]
        utimer 5 [list RunLoop4]
        return 1
}

this code WORKED in the old Bot FLAWLESSLY..

but, as it was pointed out.. I was WAY beihind and I know I misssed a LOT of upgrades.. I'm "re"learning TCL now so I could figure out the NEW limits on what I can and can't do...

the NEW code for the "timers" section has been changed to:

Code: Select all

### Get Server Info ###

bind time - "* * * * *" ServInfo
"GetInfo" (sockets), "OtsSwitch" (auto playout control), "UpteTitle" (updates the current cast title) and "UpteSong" (updates the current song name) have all been combined to a single proc called "ServInfo" and, though its now SLOWED to updating once a minute.. (old code did it once every 5 seconds) it was the best I could do!

Now, with Speechless's suggestions, I will try reworking the timers section once again.. I surely didn't catch the concept of including the arg/flasg on the end of the exec/list section (I tried everything ELSE)

One thins I see going wrong... is will I have to pass the args all the way down to the end procs and will that be an issue? I guess I'll find out!

in the meantime, thanks everyone for your help! I appreciate it.. I'm working hard to redesign all of this.. and.. perhaps, with all your help, I'll not only get the NEW Bot up and running, but I plan to ELIMINATE the old Bot's reliance on PHP for the web-side of things.. I think the new Bot could do a BETTER job than running php in a shell!

more exciting details to come! if you want to find out more, I invite you to my chat network at this link (the website is still down as the BOT will be generating it DYNAMICALLY!) see? eggdrop in action!

Using Flash: http://www.warp-radio.com/warp-chat-flash.html

you can join via your favorite IRC client too:

server irc.warp-radio.com:6667

channel #WARP-Radio

come by and say hi :-) (sorry if this seemed poachy- but I am writing the bot in realtime and it does make progress in that chat netowrk!)

again, thanks for all your help.. I believe it will be resolved soon enough

-DjZ-
:) :)
d
dj-zath
Op
Posts: 134
Joined: Sat Nov 15, 2008 6:49 am
Contact:

Post by dj-zath »

okay.. I think I got it working...

heres the NEW code..

Code: Select all

if      {![info exists loop]} {
        utimer 5 [list RunLoop1 (args)]
        set loop 1
}

proc    RunLoop1 {args} {
        ServInfo (args)
        utimer 5 [list RunLoop1 (args)]
        return 1
}
I still have to varify if, indeed, the first part is only running once or if it is looping on its own...


and now.... I got a different but [somewhat] simular issue..

I need to do the following:

1.. Strip ASCII/web codes(not html) from incoming "winamp" title strings

2.. Then need to put them back in (after the strings were minpolated)

this sounds strange, I need to explain...

I'm actually pulling in from one server- and passing to another.. I need to strip all the "&nb#23"'s and stuff from the incoming shoutcast titles.. then after I rearrange, add new tags, etc.. (including displaying in PLAIN text to irc) I need to import them BACK to the stream title (spaces becomes _'s for example)

I tried hunting for command sets and the like- but I never DID get this one figured out.. but since I'm "in the neighborhood" (so to speak) I thought I'd toss it out to you, the experts!

any input or suggestions would be appreciated!

and, again, thanks in advance for your help and support!

DjZ
:) :)
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

A few thoughts comes into mind reading your last two posts.
I still noticed that NO expressions or if/else works unless I set those particular varables as a global even if the if statment is still inside the SAME proc as the varable! setting the varable as a global works- even though that varable will NEVER be used outside the proc (isn't that a whaste of resources??) for this reason, I'm cirrently at 46 "global" varables and expect to climb to over 100! (I hope there isn't a LIMIT!)
Could you post just one example of this. As long as a variable exists within the same instance of a proc before it is read, there should be no problem. If/else constructs should work independent of variables, as these would be expanded as the whole conditional is simply run through expr and the result tested for true/false. Hence if/else really doesn't care what's within the conditional as long as expr don't complain.

Global variables have the only limit of available names and memory. If you are afraid you'll get overrun with that many global variables, consider using a separate namespace.

heres the ACTUAL code, from the OLD Bot, reguarding the timers..

Code: Select all

if      {![info exists loop]} {
        utimer 5 [list RunLoop1]
        utimer 6 [list RunLoop2]
        utimer 97 [list RunLoop3]
        utimer 98 [list RunLoop4]
        set loop 1
}

proc    RunLoop1 {} {
        [list GetInfo]
        utimer 5 [list RunLoop1]
        return 1
}
...
I am very puzzled why you're using
  • rather than just GetInfo. Same with the other procs in your script.. Otherwize, there is nothing that sticks out that would stop it from working on a current eggdrop. As long as there exists no variable named "loop" at the current context, the timers would be started, and loops kept running.
    heres the NEW code..

    Code: Select all

    if      {![info exists loop]} {
            utimer 5 [list RunLoop1 (args)]
            set loop 1
    }
    
    proc    RunLoop1 {args} {
            ServInfo (args)
            utimer 5 [list RunLoop1 (args)]
            return 1
    }
    Did you intentionally use "args" as an argument name here, fully knowing it's special properties? If not, I would strongly suggest against using that name, and suggest you go for anything else but that.
    "utimer 5 [list RunLoop1 (args)]" most likely will not produce the result you expect. Once the timer triggers, RunLoop will be called with one single parameter, being literally "(args)". To actually do the variable substitution, you'll have to use $varname.
    Same goes within your RunLoop1 proc when you call ServInfo...
    I need to do the following:

    1.. Strip ASCII/web codes(not html) from incoming "winamp" title strings

    2.. Then need to put them back in (after the strings were minpolated)
    Your best tool for data mining would probably be regexp/regsub, using regular expressions. In some simpler cases, some of the subcommands of string may be helpful as well.
NML_375
Post Reply