Page 1 of 1 1
Topic Options
#99397 - 2003-03-09 05:52 PM RFC: "HOW-TO: Write a UDF" for FAQ Forum
Sealeopard Offline
KiX Master
*****

Registered: 2001-04-25
Posts: 11164
Loc: Boston, MA, USA
Please comment on the potential FAQ:

How to Write a UDF - Best Practices

Part I: Why UDFs ?
------------------


First, an introduction into why you want to write a UDF. The following section was provided by Richard Howarth and was originally posted under GOSUB vs. FUNCTION
<---- begin Richard Howarth's example ---->
quote:

An example might help.

Say you write an application that uses a database to store records. You want the very basic facilities like add a record, delete a record, find a record, list all records.

You could write these as subroutines in your application but there are two problems with that.
  • 1) The code may be useful in other applications - using subroutines means that you need to cut and past the code - a big overhead in script size and maintenance.
  • 2) You have to pass variables to/from the subroutine using global variables. This means that to use the subroutines you have to know what the variables are called, and your subroutine is in danger of overwriting variables that you are using in the main script.
You can solve "1" by simply putting each of your subroutines in a file on it's own, and CALLing the file. This will execute the script in the file as if it was in your main script. For example, you would place your add record routine in a file called "AddRecord.kix", and when you want to add a record you use the script command
code:
CALL "AddRecord.kix"

In this case your variables may be local, but you still need to know what they are, and you still need to be careful about overwriting variable in the main script.
Also there is the overhead of reading and parsing the file every time you make the CALL.

So, we come to the best all round option - libraries of functions.

Functions can be designed as black-boxes in almost all cases - there are a couple of exceptions which I'll note later.

The benefit of this is that you only ever have to know the function name and expected parameters. You don't need to know how it works, and you don't need to know what it is using as variables internally.

An additional but often overlooked benefit of functions is that you can set @ERROR on exiting the function. You use this in your main script to determine whether the function call worked or not.

For your database routines, you create a file called "SQLroutines.kix". In this file you create all of your functions:

code:
code:
Function fnAddRecord($sKey,$asRecord)
;... Add Record Code ...
$fnAddRecord=$iSuccess
Exit $iSuccess
EndFunction
Function fnDeleteRecord($sKey)
;... Delete Record Code ...
$fnDeleteRecord=$iSuccess
Exit $iSuccess
EndFunction
Function fnFindRecord($sSearchString)
;... Find Record Code ...
$fnFindRecord=$sKey
Exit $iSuccess
EndFunction
Function fnGetRecord($sKey)
;... Get Record Cose ...
$fnGetRecord=$asData
Exit $iSuccess
EndFunction
;and so-on ...

To use these functions you call the script file once at the start of the script. This has the effect of loading the functions into memory, where they will stay until you exit your main script. You can now call the functions as often as you like, without the overhead of reading and parsing the script file.

You call the function in the same way as internal KiXtart functions, so the following are all valid:

code:
$UserLogin='jdoe'
$UserData[0]='John'
$UserData[1]='Doe'
$UserData[2]='Trainee Clown'
If fnAddRecord($UserLogin,$UserData)
? 'ERROR: Could not add record for '+$UserLogin
? ''+@ERROR+': '+@SERROR
Else
? 'Record added OK.'
EndIf
If fnDelRecord('jdoe')
? 'ERROR: Could not delete record'
EndIf
$UserLogin=fnFindRecord('Clown')
If @ERROR
? "No clowns in this organisation."
Else
$UserData=fnGetRecord($UserLogin)
If @ERROR
? 'ERRROR: Invalid key '+$UserData
Else
? $UserData[1]+' '+$UserData[2]+' is a clown.'
EndIf
EndIf

Neat, huh?

For advanced usage the library method has another major advantage.

Say you want to deploy your database applications to many sites. Some are very small sites which use flat text files for storing data, some are medium sized sites which use Access for storing data and some are large sites which use LDAP or SQL for storing data.

Now you could write applications for each, but wouldn't it be easier to do something like this at the start of your script:

code:
$DATABASETYPE=fnGetDBType()
Select
Case $DATABASETYPE='TXT'
CALL 'TXTroutines.kix'
Case $DATABASETYPE='SQL'
CALL 'SQLroutines.kix'
Case $DATABASETYPE='ODBC'
CALL 'ODBCroutines.kix'
Case $DATABASETTYPE='LDAP'
CALL 'LDAProutines.kix'
EndSelect

or more simply:

code:
CALL fnGetDBType + 'routines.kix'

Each of these files will have a "fnAddRecord()" defined. Your script doesn't need to know which database method is being used when you add a record. The correct version will have been loaded by the "CALL" at the starte of your script.

You may remember that I mentioned that there are some cases when you cannot make a routine in a "black-box" fashion.

The first problem is when you want to keep some information and re-use it in your function. These types of variables are known as "static" variables in some languages.
For example, you may only want to set up a connection to your database once as it is an expensive thing to do in computer terms. At present the only way to keep this information between function calls is to define it in a GLOBAL variable. This variable will have the scope of your entire script, so can clash with your main script variables.

The other problem area is when you want to change the value of one of the parameters. A simple classic example is the "POP" routine. This takes a stack, "pops" the top element off the stack and returns the popped off element. Consider a function to "pop" off the first character in a string:

code:
Function fnPopString($sString)
If $sString=''
Exit 1
EndIf
$fnPopString=Left($sString,1)
$sString=SubStr($sString,2)
Exit 0
EndFunction

Looks good, eh?

Well no, it won't work. The variable $sString is LOCAL to the function, and is destroyed on exit. It is a copy of the string that you passed in your main script. Passing this type of information is known as "passing by value"

For functions which need to change variables outside their scope you need to use a method known as "pass by reference", or "pass by address". This method passes a pointer to the original variable so that it can be manipulated in the function.

KiXtart does not currently support pass by reference, so again you will need to use GLOBAL variables to achieve the result.

There is an advanced coding technique which you can use to abstract the variable name, but the variable you want to change still must be global.

<---- end Richard Howarth's example ---->

Part II: Recommended UDF Format
------------------


The following recommended UDF format is based on the most restrictive settings in KiXtart in order to make the UDF as universal as possible. End-users should not have to worry about their script settings in order to run this UDF. The most restrictive settings are
code:
SETOPTION('Explicit','On')
SETOPTION('NoVarsInStrings','On')
SETOPTION('CaseSensitivity','On')

  • EXPLICIT: This option requires to DIM all variables used in your UDF
  • NOVARSINSTRINGS: This option requires to not use variables within string delimiters
  • CASESENSITIVITY: This option requires to compare strings by explicitly converting them to LCASE or UCASE if you do not want case sensitivity to be a deciding factor
Input VariablesYour code should also check input variables for correct format and content. This will ensure that only correct paarameters are used iside your code. Incorrect values might have unexpected side-effects.

Error Codes
When exiting a function in case of errrors, set an appropriate error code with EXIT. Windows error codes can be found under Microsoft Windows SDK: System Error Codes.

The Header
Fill out all header sections with meaningful and explanatory content. This makes it easier for KiXtart novices to understand the UDF and what parameters it needs to accomplish it's function.

Dependencies
A UDF does not need to check for the existance of its dependencies. Dependencies are noted in the UDF header. Also, it does not need to check for the correct KiXtart version. Missing dependencies and incorrect KiXtart versions will already lead to error messages on the command prompt when running the script.

External Utilities
If a UDF is dependent on the presence of other components that might not be present o a computer, then this should be noted in either the DEPENDENCIES or REMARKS section, but preferably the first one. Third-party utilities should be referenced including a source where it is available. For example, Windows 9x/NT do not necessarily have the Windows Management Interface (WMI) or the Active Directory Service Interface (ADSI) installed.

In general, KiXtart UDF strive to emulate the format of native KiXtart functions, both in functionality (e.g. setting of error codes) and documentation. This ensures consistency across different UDF writes with their diverse backgrounds.

At first, all these guidelines and best practices might seem like overkill to you. However, the UDF Library so far contains over 300 UDF and it is still growing. Extensive documentation also makes it easier to maintain UDFs across different releses of KiXtart.

Below is an example UDF illustrating the concepts mentioned above. Please study it carefully!
code:
dim $rc, $a, $b, $c, $d
$rc=SETOPTION('Explicit','On')
$rc=SETOPTION('NoVarsInStrings','On')
$rc=SETOPTION('CaseSensitivity','On')
$a='test'
$b=10
$c='whatever'
$d=11
$rc=UDFTemplate($a,$b,$c,$d)
? 'Error = '+@ERROR+' - '+@SERROR
? 'Result of UDFTemplate:'
? $rc

;FUNCTION UDFTemplate()
;
;ACTION Illustrates best-practices concepts to be used in UDFs
;
;AUTHOR Jens Meyer (sealeopard@usa.net)
;
;CONTRIBUTORS Richard Howarth
;
;VERSION 1.0
;
;KIXTART KiXtart 4.20
;
;SYNTAX UDFTEMPLATE(VAR1, VAR2 [, VAR3, VAR4])
;
;PARAMETERS VAR1
; Required string parameter
;
; VAR2
; Required integer parameter
;
; VAR3
; Optional string paarmeter
;
; VAR4
; Optional integer parameter with bit-wise settings:
; 1 - 'Bit 1 is set'
; 2 - 'Bit 2 is set'
; 4 - 'Bit 4 is set'
; 8 - 'Bit 8 is set'
; 16 - 'Bit 16 is set'
;
;RETURNS String containing words
;
;REMARKS Please study carefully the concepts used
;
;DEPENDENCIES none (actually, a brain)
;
;EXAMPLE dim $rc, $a, $b, $c, $d
; SETOPTION('Explicit','On')
; SETOPTION('NoVarsInStrings','On')
; SETOPTION('CaseSensitivity','On')
; $a='test'
; $b=10
; $c='whatever'
; $d=11
; $rc=UDFTemplate($a,$b,$c,$d)
; ? 'Error = '+@ERROR+' - '+@SERROR
; ? 'Result of UDFTemplate:'
; ? $rc
;
;KIXTART BBS http://www.kixtart.org/cgi-bin/ultimatebb.cgi?ubb=get_topic;f=12;t=000000
;
function UDFTemplate($var1, $var2, optional $var3, optional $var4)
; DIM all variables used inside the UDF
dim $helpvar, $helparray[4]

; set the default return value
$udftemplate=''

; perform checks on all input parameters to make sure they fit the requirements
; if necessary, exit the UDF and return an error code

; var1 must be a string, thus convert to string if it is not a string
if vartype($var1)<>8
$var1=''+$var1
endif

; var 2 must be an integer, thus convert var2 into an integer
$var2=val($var2)

; var3 must be a string, thus convert to string if it is not a string
; however, it is also an optional parameter, thus we set it to a default value
; if the parameter is not provided
if vartype($var3)
if vartype($var3)<>8
$var3=''+$var3
endif
else
; parameter has not been provided, thus set default value
$var3='default value'
endif

; var 4 must be an integer, if it is provided
if vartype($var4)
if vartype($var4)<>2 and vartype($var4)<>3
; variable is not an integer, thus exit with 'Invalid Parameter' error code
exit 87
endif
else
; parameter has not been provided, thus set default value
$var4=0
endif

? 'Content of variable $var1 = '+$var1
? 'Variable type of $var2 = '+vartype($var2)+' - '+vartypename($var2)
? 'Content of variable $var3 = '+$var3

; demonstrating bit-wise decisions
for $helpvar=0 to 4
if $var4 & ($helpvar+1)
$helparray[$helpvar]='Bit '+($helpvar+1)+' is set'
else
$helparray[$helpvar]='Bit '+($helpvar+1)+' is not set'
endif
next

$udftemplate=join($helparray,@CRLF)

; UDF ran successfully, thus set exit code to 0
; this is optional, however it will reset a potentially
; existing @ERROR back to 0
exit 0
endfunction

Below is an empty UDF to be used as a template
code:
;FUNCTION      Name_of_UDF
;
;ACTION Short description of purpose
;
;AUTHOR Name of author
;
;CONTRIBUTORS Name(s) of contributor(s)
;
;VERSION UDF version
;
;KIXTART Minimum required Kixtart version
;
;SYNTAX NAME_OF_UDF(PARAMETER 1, PARAMETER2 [, PARAMETER3])
;
;PARAMETERS PARAMETER1
; Description of first parameter
;
; PARAMETER2
; Description of second parameter
;
; PARAMETER3
; Description of optional first parameter
;
;RETURNS Type of return value
;
;REMARKS Additional remarks about the UDF
;
;DEPENDENCIES DependUDF @ http://www.kixtart.org/board/...
; Name and URL of UDSFs that this UDF depends on
;
;EXAMPLE A short functional example demonstrating the UDF
;
;KIXTART BBS http://www.kixtart.org/cgi-bin/ultimatebb.cgi?ubb=get_topic;f=12;t=000000
; URL the UDF was posted under
;
function Name_of_UDF($parameter 1, $parameter2, optional $parameter3)
endfunction



[ 10. March 2003, 00:29: Message edited by: sealeopard ]
_________________________
There are two types of vessels, submarines and targets.

Top
#99398 - 2003-03-09 08:23 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
MightyR1 Offline
MM club member
*****

Registered: 1999-09-09
Posts: 1264
Loc: The Netherlands
Looks OK to me...

A few notes/questions:
  • Must the "setoptions" be inside the UDF or not? If so, then the previous settings must be set just before the UDF returns / exists.
  • A UDF may never interact with the user, so no get / gets / ? / messagebox etc...
  • Must the UDF also check for existance of the other required UDFs / check the required KiX version?
  • The UDF could also be dependant on other things than UDFs, like WMI / OS version etc...
_________________________
Greetz,
Patrick Rutten

- We'll either find a way or make one...
- Knowledge is power; knowing how to find it is more powerful...
- Problems don't exist; they are challenges...

Top
#99399 - 2003-03-09 08:35 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Sealeopard Offline
KiX Master
*****

Registered: 2001-04-25
Posts: 11164
Loc: Boston, MA, USA
The SETOPTION are normally set at the beginning of a script if a user chooses so. They are used in this expale to illustrate the most restrictive settings possible when codign a UDF. If a user e.g. ahs NoVarsInString=ON a UDF might not work correctly of the UDF writer is using NoVarsInString=OFF (KiXtart default).

I for example like to use SETOPTION() in the Golf tournaments to the "delight" of a couple of participants [Big Grin]

Yes, UDFs can interact with the user. There are already a couple of UDFs that expand the GET/GETS with custom timeouts, or password-entry capabilities.

A UDF does not need to check for the existance of its dependencies. Dependencies are noted in the UDF header. Also, it does not need to check for the correct KiXtart version. Missing dependencies and incorrect KiXtart versions will already lead to error messages on the command prompt when running the script.

If a UDF is dependent on the presence of other components that might not be present o a computer, then thsi should be noted in either the DEPENDENCIES or REMARKS section, but preferably the first one. Third-party utilities should be referenced including a source where it is available.

I've updated the original post as necessary.
_________________________
There are two types of vessels, submarines and targets.

Top
#99400 - 2003-03-09 08:39 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
MightyR1 Offline
MM club member
*****

Registered: 1999-09-09
Posts: 1264
Loc: The Netherlands
Points taken,

no interaction was my comment because the Lonk told me to change my UDF to not interact with the user... [Wink]
_________________________
Greetz,
Patrick Rutten

- We'll either find a way or make one...
- Knowledge is power; knowing how to find it is more powerful...
- Problems don't exist; they are challenges...

Top
#99401 - 2003-03-10 12:17 AM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Les Offline
KiX Master
*****

Registered: 2001-06-11
Posts: 12734
Loc: fortfrances.on.ca
well... Jooel is a bit of a purist when it comes to UDFs... even more so than Jens. There is nothing to stop anyone from creating UDFs that behave more like subroutines with global var dependencies or interactions.

About the NoVarsInStrings... that one you have to be careful with, particularly if using Execute('$$bla bla bla'). Fortunately, SetOption() returns the current setting so that it may be tested and restored.

Jens,
Thanks to Richard and you for the contribution. When quoting Richard's contribution, may I suggest you delimit it with <snip> and </snip> or something similar. It is not clear where his contribution ends and your's continues.

While I don't wish to put one above the other in importance, I think you should lead with guidelines before delving into Richard's well detailed examples. My concern is that it may overwhelm some early on.

[ 10. March 2003, 00:18: Message edited by: LLigetfa ]
_________________________
Give a man a fish and he will be back for more. Slap him with a fish and he will go away forever.

Top
#99402 - 2003-03-10 12:30 AM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Sealeopard Offline
KiX Master
*****

Registered: 2001-04-25
Posts: 11164
Loc: Boston, MA, USA
I'll wait what others have to say before deciding whether Richard's example or the Guidelines should come first. Maybe even splitting it up into two cross-linked FAQs similar to the TCP/IP Primer?
_________________________
There are two types of vessels, submarines and targets.

Top
#99403 - 2003-03-10 03:58 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Jochen Administrator Offline
KiX Supporter
*****

Registered: 2000-03-17
Posts: 6380
Loc: Stuttgart, Germany
Hey Jens, thats ok from my side (all points !)
Well, have to dig up my few udfs in order to verify the NoVarsInStrings policy [Eek!]

Plus IIRC I had some execute lines in some of these (Will see what I can change)

[ 10. March 2003, 16:03: Message edited by: jpols ]
_________________________



Top
#99404 - 2003-03-10 04:01 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Jack Lothian Offline
MM club member
*****

Registered: 1999-10-22
Posts: 1169
Loc: Ottawa,Ontario, Canada
It seems to cover all the bases but it might be too long. My experience is long write ups tend to intimindate or bore people & a significant number of people just opt out of the process when presented with very long complex texts.

Maybe you could shorten the section on "Why use UDFs to a paragraph or 2 & move the bulk of this discussion into a note or appendix at the end of the FAQ.

Part II is fine in my opinion.
_________________________
Jack

Top
#99405 - 2003-03-10 04:17 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Sealeopard Offline
KiX Master
*****

Registered: 2001-04-25
Posts: 11164
Loc: Boston, MA, USA
I'm actually leaning towards splitting it up into two parts:
UDFs Part I: Why write/use UDFs in KiXtart scripts
UDFs Part II: Recommended UDF Format and Best Preactices

The posts would be cross-rerferenced at the beginning and end.
_________________________
There are two types of vessels, submarines and targets.

Top
#99406 - 2003-03-10 04:33 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Glenn Barnas Administrator Offline
KiX Supporter
*****

Registered: 2003-01-28
Posts: 4396
Loc: New Jersey
Jack has a point, but there's a way to get around it.. not sure how to do it here, exactly.

Create the FAQ as a "readers digest" version.. just the main points with simple examples. Then have links to "More Info" and "Detailed Example" where appropriate. If you could summarize it into 1-2 printed pages, with links to the full article components, I think it would be more digestible.

It's about perspective... I wrote a Unix coursbook several years ago. About 120 pages at 12 point Time Roman. Got a lot of feedback from the first few courses that the material was "complex" or "hard to comprehend". I reformatted the book to use a font with a larger "x" height (taller lower case letters) and printed it at 14 point. It grew to almost 300 pages. I didn't change any text, yet never got another comment about the comprehension.

Glenn
_________________________
Actually I am a Rocket Scientist! \:D

Top
#99407 - 2003-03-10 05:13 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Les Offline
KiX Master
*****

Registered: 2001-06-11
Posts: 12734
Loc: fortfrances.on.ca
I find that even breaking up a post as several replies like 'chapters' makes it appear more palatable.
_________________________
Give a man a fish and he will be back for more. Slap him with a fish and he will go away forever.

Top
#99408 - 2003-03-10 05:13 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Richard H. Administrator Offline
Administrator
*****

Registered: 2000-01-24
Posts: 4946
Loc: Leatherhead, Surrey, UK
As the BB now allows you to reference individual threads in a post, it is possible to create a thread which is a real FAQ

The initial post would contain all the questions (and maybe summary info), each of which then points to a later post in the thread where the information is expanded.

I appreciate the original post I made is pretty dense when taken out of context, and I have no problem with it being cut'n'pasted into a form that is suitable for a general audience.

Top
#99409 - 2003-03-10 05:15 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Sealeopard Offline
KiX Master
*****

Registered: 2001-04-25
Posts: 11164
Loc: Boston, MA, USA
Actually, a really good idea. The first post could just be an 'Executive Summary' plus the TOC. Each subsequent post are the chapters.
_________________________
There are two types of vessels, submarines and targets.

Top
#99410 - 2003-03-10 05:20 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Les Offline
KiX Master
*****

Registered: 2001-06-11
Posts: 12734
Loc: fortfrances.on.ca
Richard,
I believe when you say "individual threads in a post" you really mean "individual posts in a thread".

This is a very good point to raise when linking to specific information in a rather long thread. I touch upon that subject HERE.
_________________________
Give a man a fish and he will be back for more. Slap him with a fish and he will go away forever.

Top
#99411 - 2003-03-17 03:39 AM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Sealeopard Offline
KiX Master
*****

Registered: 2001-04-25
Posts: 11164
Loc: Boston, MA, USA
New FAQ How to write a UDF has been posted in the FAQ Forum
_________________________
There are two types of vessels, submarines and targets.

Top
#99412 - 2003-03-17 03:57 AM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Les Offline
KiX Master
*****

Registered: 2001-06-11
Posts: 12734
Loc: fortfrances.on.ca
Thanks Jens... job well done!

I assume the missing linebreak:
==========================================Why do you want ot write a UDF?

is not intentional, so will edit them for you.
_________________________
Give a man a fish and he will be back for more. Slap him with a fish and he will go away forever.

Top
#99413 - 2003-03-17 05:14 AM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Lonkero Administrator Offline
KiX Master Guru
*****

Registered: 2001-06-05
Posts: 22346
Loc: OK
uhm...
purist, right.

you know that I'm just right! [Mad]

thanks to jens, I finally started loving that novarsinstrings too! [Big Grin]
_________________________
!

download KiXnet

Top
#99414 - 2003-03-17 08:02 AM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
NTDOC Administrator Offline
Administrator
*****

Registered: 2000-07-28
Posts: 11623
Loc: CA
A purist eh!...

Jens,

Could I ask you to please update or have Les update your excellent FAQ to use the "approved" UDF Template. Yours does not match the one in the UDF Guidelines.

http://www.kixtart.org/board/ultimatebb.php?ubb=get_topic;f=12;t=000250

Thanks

Top
#99415 - 2003-03-17 03:52 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Sealeopard Offline
KiX Master
*****

Registered: 2001-04-25
Posts: 11164
Loc: Boston, MA, USA
Yes, the missing linebreak was most likely unintentional.

I also updated the UDF Template with the "approved" version.

[ 17. March 2003, 15:52: Message edited by: sealeopard ]
_________________________
There are two types of vessels, submarines and targets.

Top
#99416 - 2003-03-17 05:54 PM Re: RFC: "HOW-TO: Write a UDF" for FAQ Forum
Les Offline
KiX Master
*****

Registered: 2001-06-11
Posts: 12734
Loc: fortfrances.on.ca
OK, so you can edit the FAQ after moving. I had asked Henri if Moderators could be given the permission but he didn't think it could be done.

Moderators need no longer fear that their contributions to the FAQ will fall out of their control.

Good to know. I always feel uneasy about editing someone else's work.

I would like to extend a test to a non-moderator contribution that gets moved to the FAQ forum. Perhaps something like Bryce's 'Starters Guide'. Actually the current one has Kent as the author, so bad example. Richard H's KiXCrypt would be a better test.
_________________________
Give a man a fish and he will be back for more. Slap him with a fish and he will go away forever.

Top
Page 1 of 1 1


Moderator:  Arend_, Allen, Jochen, Radimus, Glenn Barnas, ShaneEP, Ruud van Velsen, Mart 
Hop to:
Shout Box

Who's Online
0 registered and 248 anonymous users online.
Newest Members
gespanntleuchten, DaveatAdvanced, Paulo_Alves, UsTaaa, xxJJxx
17864 Registered Users

Generated in 0.079 seconds in which 0.028 seconds were spent on a total of 12 queries. Zlib compression enabled.

Search the board with:
superb Board Search
or try with google:
Google
Web kixtart.org