how to specify number of iterations in a lua pattern match attempt? - lua

I have the following lua code: (based on another post here on stackoverflow)
local chunks = {ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")}
if (#chunks == 4) then
for _,v in pairs(chunks) do
if (tonumber(v) < 0 or tonumber(v) > 255) then
return false
end
end
return true
else
return false
end
Trouble with this logic for validating IPv4 Addresses is that when I test addresses like "1.2.3.4.5", the variable "chunks" still evaluates to 4.
How can I change this pattern so that it passes ONLY when there are exactly four octets?
Thanks.

You can use the anchor patterns ^ and $ which mean "match at beginning of string" and "match at end of string" respectively at the beginning/end of your pattern to require the match capture the entire string:
local chunks = {ip:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")}

Related

Lua length of Frame for Parsing

I have an binary file with shows glibberish infos if i open it in Notepad.
I am working on an plugin to use with wireshark.
So my problem is that I need help. I am reading in an File and need to find 'V' '0' '0' '1' (0x56 0x30 0x30 0x31) in the File, because its the start of an Header, with means there is an packet inside. And I need to do this for the whole file, like parsing. Also should start the Frame with V 0 0 1 and not end with it.
I currently have an Code where I am searching for 0x7E and parse it. What I need is the length of the frame. For example V 0 0 1 is found, so the Length from V to the Position before the next V 0 0 1 in the File. So that I can work with the length and add it to an captured length to get the positions, that wireshark can work with.
For example my unperfect Code for working with 0x7E:
local line = file:read()
local len = 0
for c in (line or ''):gmatch ('.') do
len = len + 1
if c:byte() == 0x7E then
break
end
end
if not line then
return false
end
frame.captured_length = len
Here is also the Problem that the Frame ends with 7E which is wrong. I need something that works perfectly for 'V' '0' '0' '1'. Maybe I need to use string.find?
Please help me!
Thats an example how my file looks like if i use the HEX-Editor in Visual Studio Code.
Lua has some neat pattern tools. Here's a summary:
(...) Exports all captured text within () and gives it to us.
-, +, *, ?, "Optional match as little as possible", "Mandatory match as much as possible", "optional match as much as possible", "Optional match only once", respectively.
^ and $: Root to start or end of file, respectively.
We'll be using this universal input and output to test with:
local output = {}
local input = "V001Packet1V001Packet2oooV001aaandweredonehere"
The easiest way to do this is probably to recursively split the string, with one ending at the character before "V", and the other starting at the character after "1". We'll use a pattern which exports the part before and after V001:
local this, next = string.match(input, "(.-)V001(.*)")
print(this,next) --> "", "Packet1V001Packet2..."
Simple enough. Now we need to do it again, and we also need to eliminate the first empty packet, because it's a quirk of the pattern. We can probably just say that any empty this string should not be added:
if this ~= "" then
table.insert(output, this)
end
Now, the last packet will return nil for both this and next, because there will not be another V001 at the end. We can prepare for that by simply adding the last part of the string when the pattern does not match.
All put together:
local function doStep(str)
local this, next = string.match(str, "(.-)V001(.*)")
print(this,next)
if this then
-- There is still more packets left
if this ~= "" then
-- This is an empty packet
table.insert(output, this)
end
if next ~= "" then
-- There is more out there!
doStep(next)
end
else
-- We are the last survivor.
table.insert(output, str)
end
end
Of course, this can be improved, but it should be a good starting point. To prove it works, this script:
doStep(input)
print(table.concat(output, "; "))
prints this:
Packet1; Packet2ooo; aaandweredonehere

Dynamic number of parameters in redis.call / lua

i want to search by multiple MATCH from lua script, but the number of 'MATCH' depends on the script needs - i need to search for keys that match all words (in this case "aa", "bb") in any order and the number of words may be more (or less)
If its fixed its working like:
local result = redis.call("SCAN", 0, "MATCH", "*aa*", "MATCH", "*bb*")
how i can make it dynamic, where i can add as many MATCH as i need?
something like:
local match={}
for i=1, #ARGV do
table.insert(match, "MATCH")
table.insert(match, "*"..ARGV[i].."*")
end
local result = redis.call("SCAN", 0, match)
...i have tried to put that to string came up with error:
local match="SCAN 0 MATCH *aa* MATCH *bb*"
local result = redis.call(match)
Unknown Redis command called from Lua script
match in ("SCAN", 0, match) is being passed as a table, you need to unpack this.
Something similar on these lines
local match={}
match[1] = "SCAN"
match[2] = 0
for i=2, #ARGV do
match[#match+1] = "MATCH"
match[#match+1] = "*"..ARGV[i].."*"
end
redis.call(unpack(match))

Lua how to remove ".html" text from end of string

So I have a string in Lua and I want to remove all occurances ".html" text of the end of the string
local lol = ".com/url/path/stuff.html"
print(lol)
Output i want :
.com/url/path/stuff
local lol2 = ".com/url/path/stuff.html.html"
print(lol2)
Output i want :
.com/url/path/stuff
First, you could define a function like this:
function removeHtmlExtensions(s)
return s:gsub("%.html", "")
end
Then:
local lol = ".com/url/path/stuff.html"
local lol2 = ".com/url/path/stuff.html.html"
local path1 = removeHtmlExtensions(lol)
local path2 = removeHtmlExtensions(lol2)
print(path1) -- .com/url/path/stuff
print(path2) -- .com/url/path/stuff
There is a second value returned from the gsub method that indicates how many times the pattern was matched. It returns, for example, 1 with path1 and 2 with path2. (Just in case that info is useful to you):
local path2, occurrences = removeHtmlExtensions(lol2)
print(occurrences) -- 2
This can easily be done with a tail-recursive function like this:
local function foo(bar)
if bar:find('%.html$') then return foo(bar:sub(1, -5) else return bar end
end
in words:
If bar ends in '.html', remove the last 5 characters
and feed that into foo again (to remove any more occurrences)
Otherwise, return bar as it is
Benefits:
Tail recursive, so it can't overflow the stack even for very long chains '.html'
string.find is pretty fast when you anchor the search to the end of the string with $
string.sub is also rather fast compared to, for example, string.gsub (note that those two do completely different things, despite the similar name).

Call stack has exceeded maximum of depth of 100 ,Lua

function findWord(s,i)
-- find first word in given text
local j = i+1
while not _isWhite(s:byte(j)) and j < #s do -- getting error here
j = j + 1
end
return s:sub(i,j), j
end
function splitText(s,maxLen)
-- split text into chunks of maxLen length
rs ={}
local function _g(s,i,c,rs)
-- recursively split text
local function _f(s,i,c)
-- recursively find words and add each word to a chunk
local w,i = findWord(s,i)
if i == #s then return c..w end
if #(c..w) <= maxLen then
c = c..w
s = s:sub(i+1,#s,true)
return _f(s,1,c)
else
return c
end
end
rs[#rs+1] = _f(s,1,'')
i = i+#rs[#rs]
if i < #s then
local s = s:sub(i,#s,true)
return _g(s,1,'',rs)
else
return rs
end
end
return _g(s,1,'',rs)
end
I have above function to split a string, It has been working earlier but this time it started giving error "call stack has exceeded maximum of depth of 100, verify a function is not calling itself by accident."
Any idea why I might be getting this error, this behaviour seems random since I am quite sure about rest of the script and same split function has been working fine as well.
EDIT:
Yes, isWhiteSpace was provided to me and has the following code, I am not supposed to change it since it worked earlier . Here is isWhite function:
function _isWhite(byte)
return byte == 32 or byte == 9
end
So both _g and _f call themselves, and _g calls _f. So clearly the recursion-stop conditions you have are too weak. In _g I see
if i < #s then
local s = ...
return _g(s,1,'',rs)
else
return rs
end
which will stop when i >= #s. If this never happens, you will get infinite recursion. It is hard to say by looking at the code how i varies, but based on this line:
i = i+#rs[#rs]
it appears to by some value, but can't say if there is guarantee that stop condition will ever be reached. With _f it is worse: the stop recursion conditions are
if i == #s then return c..w end
and
#(c..w) > maxLen
Again very hard to say if this is strong enough: what if i is greater than #s, does the rest of the function work? Although findWord() returns i<#s for non empty s, not sure what will happen if s empty.
Best way to find out is to put some print statements that give you a trace of _g and _f and the parameters received, this will tell you clearly what stop conditions are being missed.

Use string.gsub to replace strings, but only whole words

I have a search replace script which works to replace strings. It already has options to do case insensitive searches and "escaped" matches (eg allows searching for % ( etc in the search.
How ever I have now been asked to match whole words only, I have tried adding %s to each end, but that does not match words at the end of a string and I can't then work out how to trap for the white-space items found to leave them intact during the replace.
Do I need to redo the script using string.find and add logic for the word checking or this possible with patterns.
The two functions I use for case insensitive and escaped items are as follows both return the pattern to search for.
-- Build Pattern from String for case insensitive search
function nocase (s)
s = string.gsub(s, "%a", function (c)
return string.format("[%s%s]", string.lower(c),
string.upper(c))
end)
return s
end
function strPlainText(strText)
-- Prefix every non-alphanumeric character (%W) with a % escape character, where %% is the % escape, and %1 is original character
return strText:gsub("(%W)","%%%1")
end
I have a way of doing what I want now, but it's inelegant. Is there a better way?
local strToString = ''
local strSearchFor = strSearchi
local strReplaceWith = strReplace
bSkip = false
if fhGetDataClass(ptr) == 'longtext' then
strBoxType = 'm'
end
if pWhole == 1 then
strSearchFor = '(%s+)('..strSearchi..')(%s+)'
strReplaceWith = '%1'..strReplace..'%3'
end
local strToString = string.gsub(strFromString,strSearchFor,strReplaceWith)
if pWhole == 1 then
-- Special Case search for last word and first word
local strSearchFor3 = '(%s+)('..strSearchi..')$'
local strReplaceWith3 = '%1'..strReplace
strToString = string.gsub(strToString,strSearchFor3,strReplaceWith3)
local strSearchFor3 = '^('..strSearchi..')(%s+)'
local strReplaceWith3 = strReplace..'%2'
strToString = string.gsub(strToString,strSearchFor3,strReplaceWith3)
end
have a way of doing what I want now, but it's inelegant. Is there a better way?
There is an undocumented feature of Lua's pattern matching library called the Frontier Pattern, which will let you write something like this:
function replacetext(source, find, replace, wholeword)
if wholeword then
find = '%f[%a]'..find..'%f[%A]'
end
return (source:gsub(find,replace))
end
local source = 'test testing this test of testicular footest testimation test'
local find = 'test'
local replace = 'XXX'
print(replacetext(source, find, replace, false)) --> XXX XXXing this XXX of XXXicular fooXXX XXXimation XXX
print(replacetext(source, find, replace, true )) --> XXX testing this XXX of testicular footest testimation XXX
do you mean if you pass nocase() foo, you want [fooFOO] instead of [fF][oO][oO]? if so, you could try this?
function nocase (s)
s = string.gsub(s, "(%a+)", function (c)
return string.format("[%s%s]", string.lower(c),
string.upper(c))
end)
return s
end
and if you want an easy way to split a sentence into words, you can use this:
function split(strText)
local words = {}
string.gsub(strText, "(%a+)", function(w)
table.insert(words, w)
end)
return words
end
once you've gotten the words split, it's pretty easy to iterate over the words in the table and do a full comparison against each word.

Resources