This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Xtansia's profile picture

[CC1.3]String Utils Api

Started by Xtansia, 07 February 2012 - 05:32 AM
Xtansia #1
Posted 07 February 2012 - 06:32 AM
This my String Utils API.

Stats:
-Function Count = 43
-Line Count = 953
-Character Count = 34110
-File Size = 34KB
-Local Functions/Variables(Used by string functions) = 45

Function Listing:
SpoilertoCharTable(str) –Returns table of @str's chars
toByteTable(str) –Returns table of @str's bytes
fromCharTable(chars) –Returns str made of chracters in @chars
fromByteTable(bytes) –Returns str made of bytes in @bytes
contains(str,find) –Returns true if @str contains @find
startsWith(str,Start) –Check if @str starts with @Start
endsWith(str,End) –Check if @str ends with @End
trim(str) –Trim @str of initial/trailing whitespace
firstLetterUpper(str) –Capitilizes first letter of @str
titleCase(str) –Changes @str to title case
isRepetition(str, pat) –Checks if @str is a repetition of @pat
isRepetitionWS(str, pat) –Checks if @str is a repetition of @pat seperated by whitespaces
urlDecode(str) –Url decodes @str
urlEncode(str) –Url encodes @str
isEmailAddress(str) –Checks if @str is a valid email address
chunk(str, size) –Splits @str into chunks of length @size
find(str, match, startIndex) –Finds @match in @str optionally after @startIndex
seperate(str, divider) –Seperates @str on @divider
replace(str, from, to) –Replaces @from to @to in @str
jumble(str) –Jumbles @str
toBase(str, base) –Encodes @str in @base
fromBase(str, base) –Decodes @str from @base
toBinary(str) –Encodes @str in binary
fromBinary(str) –Decodes @str from binary
toOctal(str) –Encodes @str in octal
fromOctal(str) –Decodes @str from octal
toHex(str) –Encodes @str in hex
fromHex(str) –Decodes @str from hex
toBase36(str) –Encodes @str in Base36
fromBase36(str) –Decodes @str from Base36
toBase32(str) –Encodes @str in Base32
fromBase32(str) –Decodes @str from Base32
toBase64(str) –Encodes @str in Base64
fromBase64(str) –Decodes @str from Base64
rot13(str) –Rot13s @str
rot47(str) –Rot47s @str
SHA1(str) –Returns SHA1 Hash of @str
CRC32(str) –Returns CRC32 Hash of @str
FCS16(str) –Returns FCS16 Hash of @str
FCS32(str) –Returns FCS32 Hash of @str
encrypt(str, key) –Encrypts @str with @key
decrypt(str, key) –Decrypts @str with @key
setRandSeed(seed) –Sets random seed to @seed

Example Output:
Spoiler———-
toCharTable("Hello")
1-H
2-e
3-l
4-l
5-o
———-
fromCharTable()
Hello
———-
toByteTable("Hello")
1-72
2-101
3-108
4-108
5-111
———-
fromByteTable()
Hello
———-
contains("Hello", "Hell")
true
———-
startsWith("Hello", "H")
true
———-
endsWith("Hello", "o")
true
———-
trim(" Hello ")
Hello
———-
firstLetterUpper("hello")
Hello
———-
titleCase("hello world, how are you")
Hello World, How Are You
———-
isRepetition("HelloHelloHello", "Hello")
true
———-
isRepetitionWS("Hello Hello Hello", "Hello")
true
———-
urlEncode("Hello There World")
Hello+There+World
———-
urlDecode()
Hello There World
———-
isEmailAddress("cooldude@emailhost.com")
true
———-
chunk("123456789", 3)
1-123
2-456
3-789
———-
find("Hello World HeHe", "World")
7
———-
seperate("1.2.3.4.5", ".")
1-1
2-2
3-3
4-4
5-5
———-
replace("# # # #"," ","*")
#*#*#*#
———-
jumble("Hello World")
oHlord lWel
———-
toBase("Hello", 13)
57:7A:84:84:87
———-
fromBase()
Hello
———-
toBinary("Hello")
1001000:1100101:1101100:1101100:1101111
———-
fromBinary()
Hello
———-
toOctal("Hello")
110:145:154:154:157
———-
fromOctal()
Hello
———-
toHex("Hello")
48:65:6C:6C:6F
———-
fromHex()
Hello
———-
toBase36("Hello")
20:2T:30:30:33
———-
fromBase36()
Hello
———-
toBase32("Hello")
JBSWY3DP
———-
fromBase32()
Hello
———-
toBase64("Hello")
SGVsbG8=
———-
fromBase64()
Hello
———-
rot13("Hello")
Uryyb
———-
rot47("Hello")
w6==@
———-
SHA1("Hello")
f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0
———-
CRC32("Hello")
4157704578
———-
FCS16("Hello")
21548
———-
FCS32("Hello")
3671287311
———-
encrypt("Hello", "Key")
Q4Y3F3X495Z3L476L5Z2G374L4K4L4
———-
decrypt()
Hello
———-

To install:
Spoiler-Download Zip
-place the StrUtils file into mods/ComputerCraft/lua/rom/apis/
-Load up CC

To use:
Spoiler-Use it just like any other API eg.
s,e = StrUtils.find("Hello", "e")
or
digits = StrUtils.seperate("0/65/0", "/")
or
encoded = StrUtils.encrypt("Hello", "CoolKey")

License:
SpoilerCopyright © 2012 Thomas Farr a.k.a tomass1996 [farr.thomas@gmail.com]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-Visible credit is given to the original author.
-The software is distributed in a non-profit way.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

If you need clarification on something just ask.
If you find any bugs or errors please report them.

Download:
GitHub
BitBucket
Projects:
GitHub
BitBucket
FuzzyPurp #2
Posted 07 February 2012 - 04:51 PM
Testing*
Espen #3
Posted 07 February 2012 - 07:28 PM
@tomass1996:
These are some very useful functions.
But why did you hide their source as bytecode? Don't you want other people to understand the inner workings?
Don't get me wrong, I'm not trying to get at you or sth. I'm just genuinely curious as to why you would hide these algorithms, most of which are already known anyway?
Xtansia #4
Posted 08 February 2012 - 02:53 AM
@tomass1996:
These are some very useful functions.
But why did you hide their source as bytecode? Don't you want other people to understand the inner workings?
Don't get me wrong, I'm not trying to get at you or sth. I'm just genuinely curious as to why you would hide these algorithms, most of which are already known anyway?

There is no real reason as such.
The only functions that would have a legit reason for hiding is the encryption and decryption algorithms,
I will probably end up posting it as standard code eventually anyway.
Casper7526 #5
Posted 08 February 2012 - 03:04 AM
Basically he just learned about compiled bytecode and wanted to play with it and use it in an application :P/>/>
Xtansia #6
Posted 08 February 2012 - 03:12 AM
Basically he just learned about compiled bytecode and wanted to play with it and use it in an application :P/>/>

That too…
Xtansia #7
Posted 08 February 2012 - 03:51 AM
Just realised there was an error in the API file where the path for the lib was rom/libs/StringUtils.lib instead of rom/libs/StringUtil.lib

So put up a new zip ppl may have gotten this error if you did just change the path in StrUtil file.
Xtansia #8
Posted 08 February 2012 - 08:29 AM
Another quick patch,
Minor stuff.
Espen #9
Posted 08 February 2012 - 11:58 AM
@tomass1996: These are some very useful functions. But why did you hide their source as bytecode? Don't you want other people to understand the inner workings? Don't get me wrong, I'm not trying to get at you or sth. I'm just genuinely curious as to why you would hide these algorithms, most of which are already known anyway?
There is no real reason as such. The only functions that would have a legit reason for hiding is the encryption and decryption algorithms, I will probably end up posting it as standard code eventually anyway.
Basically he just learned about compiled bytecode and wanted to play with it and use it in an application :P/>/>
*Understanding nod* .. as I said I was just curious and thought as much (it being programming exercise). Thanks for clarifying!

Another quick patch, Minor stuff.
Hehe, and as long as the source is closed you're the only person able to both notice and fix these things.
That (and my general curiosity of how things work) is the main reason why I asked in the first place. :P/>/>

But I have to respectfully disagree on what you said about the encryption code needing obfuscation.
The security of an encryption algorithm should never hinge on the obscurity of its inner workings, but on the key you use to encrypt a message with it.
Well, only if you want to be as secure as possible in principle as well.
Practically though, I can't really imagine anyone encrypting serious data. Most will use it for RPG reasons, so the real security might not be of major concern.^^

But If you're interested, here's some more interesting info about it:
http://en.wikipedia....fs%27_principle
http://en.wikipedia....ty_by_obscurity

One can never know enough. :D/>/>
Thanks again for clarification, take care!
Xtansia #10
Posted 09 February 2012 - 08:05 AM
Yay Open Source!
Xtansia #11
Posted 16 February 2012 - 09:51 AM
Added More functions,
SpoilerstartsWith(string,Start) –Check if @string starts with @Start
endsWith(string,End) –Check if @string ends with @End
trim(string) –Trim @string of initial/trailing whitespace
firstLetterUpper(string) –Capitilizes first letter of @string
titleCase(string) –Changes @string to title case
isRepetition(string, pat) –Checks if @string is a repetition of @pat
isRepetitionWS(string, pat) –Checks if @string is a repetition of @pat seperated by whitespaces
urlDecode(string) –Url decodes @string
urlEncode(string) –Url encodes @string
isEmailAddress(string) –Checks if @string is a valid email address
setRandSeed(seed) –Sets random seed to @seed

Zipfile now also contains a function listing for quick reference.
MewK #12
Posted 01 March 2012 - 09:48 AM
Hey tomass,

I'm planning to include your library in one of my projects. That's why I wanted to ask if you have any problems with that and furthermore under what kind conditions/license have you released this lib? My plan is to release my project under the MIT license with full credits. Would that be okay with you?

-MewK
ThePH #13
Posted 01 March 2012 - 10:03 AM
NOOOOO !
You outran me, I was gonna post something similar B)/>/>
nice work though :unsure:/>/>

Can I use some of it in my encoder API? (I will give you credit B)/>/> )
Xtansia #14
Posted 01 March 2012 - 12:15 PM
Hey tomass,

I'm planning to include your library in one of my projects. That's why I wanted to ask if you have any problems with that and furthermore under what kind conditions/license have you released this lib? My plan is to release my project under the MIT license with full credits. Would that be okay with you?

-MewK

Hey that would be fine with me,
I need to update the op but it's effectively the same license as here http://www.computercraft.info/forums2/index.php?/topic/74-pastebin-paste-downloader/page__fromsearch__1
It's a slightly altered mit license, basicly just that you can't sell my stuff.
BTW what's your project?
Xtansia #15
Posted 01 March 2012 - 12:23 PM
NOOOOO !
You outran me, I was gonna post something similar B)/>/>
nice work though :unsure:/>/>

Can I use some of it in my encoder API? (I will give you credit B)/>/> )

Thnx.
Same as above read the license in that post I'm going to update this op in the morning. Aslong as I am credited and you list/mark/flag the functions that are from mine.
MewK #16
Posted 01 March 2012 - 01:24 PM
Hey that would be fine with me,
I need to update the op but it's effectively the same license as here http://www.computerc...__fromsearch__1
It's a slightly altered mit license, basicly just that you can't sell my stuff.
BTW what's your project?

My main project is still the Midcraft Commander.

However I'm working on a API project right now. It will be called rwtk (Redworks Toolkit) and my plan is to integrate and replace the RW API.
The API will include an event-driven-application-framework for GUI based apps, a basic drawing lib that will be used by the app-framework, a small core framework for basic stuff like class-handling. To add more functionality I'm looking for good libraries to add. your string lib would be the first candidate, a proper turtle lib would be a next candidate. I will port Midcraft Commander to the new API and step by step all Redworks programs. A long term goal would be to implement a rwtk.netfs lib, that will allow to do file system actions across the network.

It is important to me that the new API is good enough to be the defacto standard for CC software development. But to achieve that I have to get as many ppl on board as possible. The project will be hosted on GitHub, will have a full documentation and I hope that a few ppl want to help developing it.
Xtansia #17
Posted 01 March 2012 - 06:46 PM
Hey that would be fine with me.
I need to update the op but it's effectively the same license as here http://www.computerc...__fromsearch__1
It's a slightly altered mit license, basicly just that you can't sell my stuff.
BTW what's your project?

My main project is still the Midcraft Commander.

However I'm working on a API project right now. It will be called rwtk (Redworks Toolkit) and my plan is to integrate and replace the RW API.
The API will include an event-driven-application-framework for GUI based apps, a basic drawing lib that will be used by the app-framework, a small core framework for basic stuff like class-handling. To add more functionality I'm looking for good libraries to add. your string lib would be the first candidate, a proper turtle lib would be a next candidate. I will port Midcraft Commander to the new API and step by step all Redworks programs. A long term goal would be to implement a rwtk.netfs lib, that will allow to do file system actions across the network.

It is important to me that the new API is good enough to be the defacto standard for CC software development. But to achieve that I have to get as many ppl on board as possible. The project will be hosted on GitHub, will have a full documentation and I hope that a few ppl want to help developing it.

Cool,
I'm going to do a few wee updates to this,
And I'll be hosting it on github for easier updating.
Biohazard #18
Posted 01 March 2012 - 08:34 PM
Looks useful :unsure:/>/>. I'm downloading it, and good luck with extending it B)/>/>
FuzzyPurp #19
Posted 01 March 2012 - 08:54 PM
Hey that would be fine with me,
I need to update the op but it's effectively the same license as here http://www.computerc...__fromsearch__1
It's a slightly altered mit license, basicly just that you can't sell my stuff.
BTW what's your project?

My main project is still the Midcraft Commander.

However I'm working on a API project right now. It will be called rwtk (Redworks Toolkit) and my plan is to integrate and replace the RW API.
The API will include an event-driven-application-framework for GUI based apps, a basic drawing lib that will be used by the app-framework, a small core framework for basic stuff like class-handling. To add more functionality I'm looking for good libraries to add. your string lib would be the first candidate, a proper turtle lib would be a next candidate. I will port Midcraft Commander to the new API and step by step all Redworks programs. A long term goal would be to implement a rwtk.netfs lib, that will allow to do file system actions across the network.

It is important to me that the new API is good enough to be the defacto standard for CC software development. But to achieve that I have to get as many ppl on board as possible. The project will be hosted on GitHub, will have a full documentation and I hope that a few ppl want to help developing it.

Sweet, i hope tomass can get on board with us :unsure:/>/>
Xtansia #20
Posted 02 March 2012 - 03:37 AM
Another wee update:
Added:
toCharTable(string) –Returns table of @string's chars
toByteTable(string) –Returns table of @string's bytes
fromCharTable(chars) –Returns string made of chracters in @chars
fromByteTable(bytes) –Returns string made of bytes in @bytes

Now hosted on GitHub.
Added license to op.

Spoiler
Hey that would be fine with me,
I need to update the op but it's effectively the same license as here http://www.computerc...__fromsearch__1
It's a slightly altered mit license, basicly just that you can't sell my stuff.
BTW what's your project?

My main project is still the Midcraft Commander.

However I'm working on a API project right now. It will be called rwtk (Redworks Toolkit) and my plan is to integrate and replace the RW API.
The API will include an event-driven-application-framework for GUI based apps, a basic drawing lib that will be used by the app-framework, a small core framework for basic stuff like class-handling. To add more functionality I'm looking for good libraries to add. your string lib would be the first candidate, a proper turtle lib would be a next candidate. I will port Midcraft Commander to the new API and step by step all Redworks programs. A long term goal would be to implement a rwtk.netfs lib, that will allow to do file system actions across the network.

It is important to me that the new API is good enough to be the defacto standard for CC software development. But to achieve that I have to get as many ppl on board as possible. The project will be hosted on GitHub, will have a full documentation and I hope that a few ppl want to help developing it.

Sweet, i hope tomass can get on board with us :unsure:/>/>

Yeh I'm all for it,
I'm not the most amazing at lua but I support it and the use of my api,
I'll contribute whatever I can.
Xtansia #21
Posted 03 March 2012 - 08:36 AM
Added: contains(string,find) –Returns true if @string contains @find
Xtansia #22
Posted 04 March 2012 - 10:10 AM
Another update:
Re-factoring of code,
Added:
CRC32(str) –Returns CRC32 Hash of @str
FCS16(str) –Returns FCS16 Hash of @str
FCS32(str) –Returns FCS32 Hash of @str
replace(str, from, to) –Replaces @from to @to in @str
Liraal #23
Posted 04 March 2012 - 12:24 PM
just to make it clear: does your API contain the necessary license note? If so, is it OK if I ship it with my OS?
Garthog #24
Posted 05 March 2012 - 12:39 AM
I've got an error when trying to decrypt unencrypted string. I'm making a crypted communication between turtles and a computer, but if someone broadcast a simple string like test, it will crash all my turtles with error : StrUtil:614 bad argument #1: invalid value

How to reproduce :


StrUtil.decrypt("test", "somekey")
Xtansia #25
Posted 05 March 2012 - 02:51 AM
just to make it clear: does your API contain the necessary license note? If so, is it OK if I ship it with my OS?

Yes the license is included, There is a separate license file included in the download, But it is also included at the top of the API file. Yes it is fine, as long as you clearly state on the thread that it is mine.

I've got an error when trying to decrypt unencrypted string. I'm making a crypted communication between turtles and a computer, but if someone broadcast a simple string like test, it will crash all my turtles with error : StrUtil:614 bad argument #1: invalid value

How to reproduce :


StrUtil.decrypt("test", "somekey")

It is because you are trying to decrypt an unencrypted string.
Garthog #26
Posted 05 March 2012 - 10:57 AM
Sure, but it shouldn't crash the whole program.
Xtansia #27
Posted 05 March 2012 - 11:23 AM
Sure, but it shouldn't crash the whole program.

Quick Fix:
Open String Utils change the decrypt function to:

function decrypt(str, key)  --Decrypts @str with @key
if not key then return nil end
str = tostring(str)
local key = SHA1(key)
local strLen = str:len()
local keyLen = key:len()
local j=1
local result = ""
for i=1, strLen, 2 do
  local ordStr = basen(tonumber(string.reverse(str:sub(i, i+1)),36),10)
  if j==keyLen then j=1 end
  local ordKey = string.byte(key:sub(j,j))
  local nxChar = ordStr-ordKey
  if nxChar<0 or nxChar>255 then nxChar = 0 end
  result = result..string.char(nxChar)
  j = j+1
end
return result:sub(11)
end
immibis #28
Posted 29 March 2012 - 11:39 AM

function isEmailAddress(str) --Checks if @str is a valid email address
	if not str then return nil end
	str = tostring(str)
    if (str:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?")) then
	    return true
    else
	    return false
    end
end

Does this match all valid email addresses?
They don't necessarily contain dots or 2-4 letter TLDs. Or even alphabetic TLDs.

me@127.0.0.1 is a perfectly valid email address, so is me@stuff.music (even though .music doesn't exist) or me@foo or even "hi there!"@foo (if I interpreted RFC 822 correctly)
DerrikeG #29
Posted 25 May 2012 - 05:00 AM
function trim(str)  --Trim @str of initial/trailing whitespace
	if not str then return nil end
	str = tostring(str)
	return (str:gsub("^%s*(.-)%s*$", "%1"))
end
The above code could be written as:
function trim(str)  --Trim @str of initial/trailing whitespace
	return tostring(str):gsub("^%s*(.-)%s*$", "%1")
end
Both functions here would successfully return nil if passed nil, enabling you to return nil.Also assigning the variable as its tostringed self is unnecessary since you can pass the return of tostring to gsub as demonstrated without needing it set to a variable.
Wrapping the return in parenthesis only suppresses the gsub's additional return values but there's no reason to do that since it helps someone to determine whether or not the string was actually trimmed.
Graypup #30
Posted 01 July 2012 - 08:22 PM
Can I bundle your SHA1 in my OS so that I don't have a dependency problem?
Xtansia #31
Posted 02 July 2012 - 01:37 AM
Can I bundle your SHA1 in my OS so that I don't have a dependency problem?

Here's the file you'll need with only the SHA1 stuff in it for you: https://bitbucket.org/tomass1996/strutilsapi/raw/cb35fb5fc0ec/SHA1.lua
Graypup #32
Posted 02 July 2012 - 05:14 PM
I copied the contents of the file into my API. Thanks!
I'm trying to make the OS as secure as possible, so encryption certainly helped.
nintendofan345 #33
Posted 06 July 2012 - 01:35 AM
Does this work with the read() in the common password doors? I'm trying to use toHex like this:


StrUtil.toHex(read())

But when I run the program it fails with the following error:


startup:4: attempt to index ? (a nil value)

I really don't know how to fix this except by using read() by itself. Thanks in advance for any help.
MysticT #34
Posted 06 July 2012 - 01:44 AM
Does this work with the read() in the common password doors? I'm trying to use toHex like this:


StrUtil.toHex(read())

But when I run the program it fails with the following error:


startup:4: attempt to index ? (a nil value)

I really don't know how to fix this except by using read() by itself. Thanks in advance for any help.
Looks like the api is not loaded, it might be an installation error, maybe the file has another name.
EDIT:
Try with StrUtils instead of StrUtil, that should fix it
nintendofan345 #35
Posted 06 July 2012 - 02:26 AM
Oh, that worked, thank you.
Graypup #36
Posted 28 July 2012 - 11:46 PM
*fixed*
ETHANATOR360 #37
Posted 05 August 2012 - 11:31 PM
when i installed this i opened the lua prompt and put toOctal ("hi") and i got attempt to call nil
Xtansia #38
Posted 05 August 2012 - 11:36 PM
when i installed this i opened the lua prompt and put toOctal ("hi") and i got attempt to call nil

You need to put the name the api is saved as in front.
So if you saved it as StrUtil then you would do
StrUtil.toOctal("Hi")
ETHANATOR360 #39
Posted 06 August 2012 - 12:39 AM
thanks :P/>/>
ETHANATOR360 #40
Posted 06 August 2012 - 12:49 AM
i saved it as SU and typed SU.toOctal("hi") and got attempt to index a nil value. what am i doing wrong now?
MysticT #41
Posted 06 August 2012 - 12:51 AM
i saved it as SU and typed SU.toOctal("hi") and got attempt to index a nil value. what am i doing wrong now?
Have you loaded the api? You can load it with os.loadAPI, or placing it in rom/apis and restarting the computer.
ETHANATOR360 #42
Posted 06 August 2012 - 05:03 PM
its in the apis folder
ETHANATOR360 #43
Posted 06 August 2012 - 05:08 PM
never mind i had to restart the computer
ETHANATOR360 #44
Posted 06 August 2012 - 09:16 PM
i have a question what is rot13 and rot47
Xtansia #45
Posted 06 August 2012 - 11:38 PM
i have a question what is rot13 and rot47
Google 'Rot13 encoding' &amp; 'Rot47 encoding'
PixelToast #46
Posted 13 November 2012 - 10:39 AM
i have a question what is rot13 and rot47
rot 13 basically takes the alphabet and gets that letters opposite
e.g.
rotating a gives you n
and rotating n gives you a

so when you rotate any letter twice it will always be the original letter

rot 47 includes more than just letters
Espen #47
Posted 13 November 2012 - 10:48 AM
It has been three months since he asked that question. He probably googled it by now.^^
tupperkion #48
Posted 29 June 2013 - 12:50 PM
I'm going to create a logging system for my api network. The log will be stored on a file at the root of the computer. Will logging the credits count?
Cutecurtain #49
Posted 09 July 2013 - 09:53 AM
Very useful, I must say :)/> Nice tool!!!