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.

[SOLVED] catch errors, dont make eggy die

Help for those learning Tcl or writing their own scripts.
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

[SOLVED] catch errors, dont make eggy die

Post by raider2k »

hi folks ^^

this time im looking for a way to catch errors that usually crash the eggy WITHOUT crashing it. ive seen this somewhere once, i tried it already with

Code: Select all

set error ""
foreach script {
alltools.tcl
cmd_resolve.tcl
whatever-script-one-per-line.tcl
} {
catch {source scripts/$script} error
if {$error != ""} {putcmdlog "\002SCRIPT ERROR\002:$script\: $::errorInfo"}
}
from http://forum.egghelp.org/viewtopic.php?p=63899#63899

and with

Code: Select all

catch {unbind raw - PRIVMSG *raw:irc:msg}
catch {unbind raw - PRIVMSG *raw:PRIVMSG} 
from http://forum.egghelp.org/viewtopic.php? ... ight=catch

the first one only put out one line of the error, tried to work it around with split "\n" and/or foreach but no go so far. the second didnt do anything at all.

so im not sure if im missing anything or just did it the wrong way
Last edited by raider2k on Mon Mar 22, 2010 2:08 am, edited 1 time in total.
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

That script is not quite properly written; I'd rather suggest something along these lines:

Code: Select all

proc loadsafe {scripts} {
  foreach script $scripts {
    if {[catch [list uplevel #0 [list source $script]] err]} {
      putcmdlog "Error loading $script: $err"
      foreach line [split $::errorInfo "\n"] {
        putcmdlog $line
      }
    }
  }
}

loadsafe [list scripts/alltools.tcl scripts/cmd_resolve.tcl scripts/and_so_on.tcl]
Keep in mind that the loadsafe proc expects a list of scripts, even if you only load one script. If you fancy having one script per line, then this should work just fine:

Code: Select all

...
loadsafe [list scripts/alltools.tcl \
  scripts/cmd_resolve.tcl \
  scripts/and_so_on.tcl]

The second piece of code you've linked is only supposed to safely remove two raw bindings from your eggdrop.

Edit: script is now source'd at the global level, by suggestion from user.
Last edited by nml375 on Wed Mar 24, 2010 11:13 am, edited 1 time in total.
NML_375
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

Post by raider2k »

both are just examples, aka the original code from that forum thread, of course i modified it to my needs ;)

im wondering a bit about your examples - where is $scripts in foreach script $scripts coming from? and where is $::errorInfo coming from? im not able to follow the pointer from one to the other in that case, sorry :> (i see that $::errorInfo is also in the original thread, didnt pay much attention to it - is it some kind of global var?)
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

Post by raider2k »

Code: Select all

proc loadscript { scripts } {
        foreach script $scripts {
                if { [catch { source $script } err] } {
                        putlog "ERROR LOADING $script: $err"
                        foreach x [split $::errorInfo "\n"] {
                              putlog "$x"
             
                        }
                }
        }
}

set scripts {
scripts/something.tcl
scripts/debug.tcl
scripts/externalscript.tcl
scripts/sql.tcl
}

loadscript $scripts
i did it like this and it works like a charm, dont want the scripts i like to load to be in one line, like to have it organized and structured and the other code you posted with " \ " at the end didnt really work out, regardless of how i turned and turned it, didnt work - so i did it differently and it works, tested and works :)

thx again nml ^^
Last edited by raider2k on Mon Mar 22, 2010 12:22 pm, edited 1 time in total.
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

First, your questions..

I placed my code in a proc I called "loadsafe", that takes one parameter (the list of scripts). So $scripts comes from the proc argument list..

Next, $::errorInfo:
This is a global variable (see the namespace operator ::), that contains details regarding the latest error that occured. Since I use a full namespace path when accessing the variable, I don't have to use the global command.

Regarding your code, I would recommend that you do not build your list of scripts that way. You'll get away with it most of the times, but if your filenames contains characters such as { or }, you might be up for a nasty surprise (plus it's really bad coding practise that we see all too often).

Regarding your issues with the list command and using backslash (\), did you place a newline immediately after the backslash? It is solely used to escape the newline, so there cannot be any spaces or other characters between the \ and the newline.
NML_375
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

Post by raider2k »

yup, this is true, figured where scripts comes from when i copied and modified it to my needs ^^

yup, namespace global var, got that, just wasnt really sure ^^

im wondering a bit whats so wrong about my code, having escapable character in FILENAMES isnt going to happen at all, i NEVER used and never will use special chars in a filename, is there anything i need to be aware of or is it only characters which need to be escape in case of?
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

The problem is that tcl lists are very delicate, while alot of coders/users think having one item per line automatically makes the string a list. The tcl interpreter will try it's best to parse the string as a list when using foreach, lindex, etc. but every now and then the string is too poorly formatted to make sense of.
In your case, you'll most likely get away with it, as I said in my previous post. But since this forum is alot about helping people code (better), I like to point out issues like these. If you get too comfortable with this "sloppy" (no offense) coding, it's very easy to start using them in cases where this really becomes an issue.

All this said, if it works for you, of course feel free to do it any way you feel comfortabe. I just wan't you to be aware of posible issues.
NML_375
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

Post by raider2k »

ok, im always interested in becoming better, its just not clear enough to me what exactly i wasnt doing so properly.

is it about those " \ "'s ? if you dont mind please post me an example based on my code, i think that would help me out :)

btw: i also remember you telling me to use split $text before i lindex it in another post some time ago, still remember and also using it ;)
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

The improper thing is that you are handling a string as if it was a list. You can craft strings that tcl manages to interpret as lists, though it still is a string. For trivial list elements, this is fairly easy, but simply adding a space into the element makes it non-trivial. If you're not careful, it'll catch you offguard, giving you a nice headache.. Or if you use this with a Windows-style filesystem (using \ instead of /}).

You can indeed use split to convert a string into a proper list, and using lindex on a string is technically the same improper issue. However, "split $text" would often be used when the content of $text comes from a remote user, where you have no control what-so-ever of it's original content (hence you cannot quarantee it to be a valid list structure).

Actually, that's quite a good example how "sloppy" coding might sneak up and stab you in the back.. You get comfortable using something like this:

Code: Select all

set keywords "hello dolly"
foreach key $keywords {
  bind msg - $key msg:hello
}
This is not proper, but since we're dealing with trivial elements, we get away with it - in fact, if we didn't read the manpages for foreach, we could come to believe that this will add a binding for each word of $keywords. We might even use it when writing a script to add multiple users to the bots userlist:

Code: Select all

proc addusers {handle idx text} {
  foreach user $text {
    if {[adduser $user [hostmask $user![getchanhost $user]]]} {
      setuser $user PASS $user
    }
  }
}
It works well in a few simple tests... then you want to add someone named {badboy{ ... Now it doesn't work anymore, and you don't understand why it fails all of a sudden...
The solution here, is to use split to convert the string into a proper list.


To put it very simple, the difference between your code and mine, is that I use the list command to let tcl build a list, while you craft something resembling a list well enough for tcl to understand it.
NML_375
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

Post by raider2k »

i think ... im confused right now :shock:
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Ahh, sorry.
I guess the simplest way of putting it is this:
Whenever a command expects a parameter value to be a list, make sure you are providing a list - not a string.
The simplest way of creating a list, is either to use the list command - or to split a string into a list. If you try to handcraft lists, you're bound to run into trouble sooner or later.

Oh, and regarding the examples in the previous post:
The first example demonstrates an improper script where I 'get away' with it because my string resembles a list well enough for tcl to understand it.
The second extends the improper scripting by accepting 'untrusted data' as a list - and it appears to work in most cases. Except in some non-trivial cases, where the script all of a sudden seems to stop working.

Your script is similar to example 1 right now. The step to something like example 2 isn't that far away...
NML_375
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

Post by raider2k »

mhm, i think now i know what you mean :)
very interesting, i hope ill keep that in mind :D
thx nml ^^
User avatar
user
 
Posts: 1452
Joined: Tue Mar 18, 2003 9:58 pm
Location: Norway

Post by user »

nml375 wrote:

Code: Select all

if {[catch {source $script} err]} {
You are invoking 'source' in the scope of the proc, which means the code inside the sourced files is also executed in this scope. ("global" variables become local to the proc etc.) Use 'uplevel' to have it invoked in the right scope:

Code: Select all

if {[catch [list uplevel #0 [list source $script]] err]} {
Have you ever read "The Manual"?
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Nice catch, user. I'll update my post in a minute or so..
NML_375
r
raider2k
Op
Posts: 140
Joined: Tue Jan 01, 2008 10:42 am

Post by raider2k »

W O N D E R F U L L :D

because right now i wanted to ask why its coming up with some errors here and there, i think missing uplevel was the answer to that not-yet-asked-question :)

thx again for help, thx to both of you :)

btw: since the loadsource proc does a list on script from foreach scripts, am i safe to use set scripts { .... } now? im still a bit confused regarding that as i want to put each script into its own line, starting from the very first line until the last one, "set scripts [list" into the line before the first and "]" into the line after the last one. doing this now results in an error telling me that bot doesnt understand command "scripts/reg.tcl"
Post Reply