--[===[
MODULE "MPICKLINES" (pick lines)
"eo.wiktionary.org/wiki/Modulo:mpicklines" <!--2019-Nov-09-->
Purpose: picks a part of a text defined by line indexes, intended
for news but usable for other purposes as well
Utilo: elprenas parton de teksto difinita per linioindeksoj, intencita
por novajxoj sed uzebla ankaux alimaniere
Manfaat: ...
Syfte: plockar fram en del av en text definierad med hjaelp av radindex,
avsett foer nyheter men anvaendbar aeven foer andra syften
Used by templates / Uzata far sxablonoj:
- novajxoj
Required submodules / Bezonataj submoduloj:
- none / neniuj
Incoming: - nothing (seized from caller's frame instead)
Returned: - picked text or (customizable) error message
Parameters seized from the caller's frame:
- 3 anonymous obligatory parameters
- name of the source template holding the
incoming text (5...100 octet:s) with namespace
prefix, for example "Templat:kabar-sumber"
- earliest line to pick (ZERO-based, 0...99'999)
- last line to pick (ZERO-based, 0...99'999)
- 1 named optional parameter "err="
- alternative output string to replace the "=" error
message (1...64 char:s, default is "=") (this works even
for errors coming from anonymous parameters)
An error is generated if:
- more or less than 3 anonymous obligatory parameters have been supplied
- the template name is obviously faulty
- earliest line index or last line index is faulty or out of range
- "last line index" < "earliest line index" (but they may be equal)
- pointed source template does not exist
- text from the source template, even after stripping of blank
lines, is empty (<4 octet:s)
- text from the source template is faulty (see below)
- text from the source template is too short and end of
text is reached before earliest line index, ie
"amount of lines" <= "earliest line index" (must be bigger)
It is NOT per se an error if "amount of lines" < "last line index".
Example:
- 3 incoming lines
- earliest line index can be ZERO to 2
- (0,2) returns all 3 lines
- (0,5) returns all 3 lines
- (2,5) returns 1 line
- (3,5) is an error
Text structure:
Every line must begin with "* " (star and space) and end with an EOL.
Trailing spaces are NOT tolerated. Minimum line length is 3 octet:s
including the "* " but excluding EOL, maximum line length is 400 octet:s.
Blank lines, even multiple, are allowed at beginning and end of the text.
Single blank lines are allowed anywhere in the text. All blank lines
are skipped from counting. Multiple blank lines are prohibited inside
the text. All text, even outside of the selection is checked.
]===]
local picklines = {}
------------------------------------------------------------------------
---- ORDINARY LOCAL LOW LEVEL FUNCTIONS ----
------------------------------------------------------------------------
-- Local function LFDECINP
-- Convert string (1...13 octet:s) with decimal digits to positive
-- integer number (0...4'000'000'000) ignoring possible inside apo:s
-- 65'536 good -- 655'36 ugly but accepted -- 6''''55''''36 ugly but accepted
-- '65536 rejected -- 65536' rejected
-- Input : - strin
-- Output : - numaout (4'294'967'295 on error)
local function lfdecinp (strin)
local numaout = 0
local numleen = 0
local numinxx = 1 -- ONE-based -- counts up
local numokkt = 0
numleen = string.len (strin)
if ((numleen<1) or (numleen>13)) then
numaout = 4294967295 -- damn
else
while (true) do
numokkt = string.byte (strin,numinxx,numinxx)
if (numokkt==39) then
if ((numinxx==1) or (numinxx==numleen)) then
numaout = 4294967295
break -- damn (may not begin or end with apo)
end--if
else
if ((numokkt<48) or (numokkt>57) or (numaout>400000000)) then
numaout = 4294967295
break -- damn (bad char or out of range)
end--if
numaout = numaout * 10 + (numokkt - 48)
end--if
if (numinxx==numleen) then
break -- done (hopefully success, but not yet sure)
end--if
numinxx = numinxx + 1
end--while
if (numaout>4000000000) then
numaout = 4294967295 -- damn (out of range)
end--if
end--if
return numaout
end--function lfdecinp
------------------------------------------------------------------------
---- MAIN EXPORTED FUNCTION ----
------------------------------------------------------------------------
function picklines.ek (arxframent)
-- general unknown type
local vartmp = 0 -- variable without type
-- special type "args" AKA "arx"
local arxcaller = 0 -- metaized "args" from caller's "frame" (NOT our own)
-- general "str"
local strtplnam = "" -- name of the source template from args[1]
local strerrequ = "=" -- default or alterna string to replace "=" on error
local strtmp = ""
local strtext = "" -- huge string from the source template
local strret = "" -- output string
-- general "num"
local numlong = 0 -- length of parameter
local numearl = 0 -- earliest line from args[2]
local numlast = 0 -- last line from args[3]
local numbegwh = 0 -- ONE-based -- of whole text after stripping
local numlaswh = 0 -- ONE-based -- of whole text after stripping
local numbegse = 0 -- ONE-based -- of selection
local numlasse = 0 -- ONE-based -- of selection
local numlinx = 0 -- line index -- ZERO-based -- counts up
local numokix = 0 -- overall octet index -- ONE-based -- counts up
local numokyx = 0 -- additional octet index -- ONE-based -- counts up
local numoct = 0 -- temp some cool char
local numokt = 0 -- temp some silly char
local numoqt = 0 -- temp some great char
-- general "boo"
local booerr = false -- fatal error flag
---- GET THE ARXCALLER ----
arxcaller = arxframent:getParent().args -- "args" from caller's "frame"
---- SEIZE 3 OBLIGATORY ANONYMOUS PARAMETERS SUBMITTED TO CALLER ----
while (true) do -- fake loop
if ((arxcaller[1]==nil) or (arxcaller[2]==nil) or (arxcaller[3]==nil) or (arxcaller[4])) then
booerr = true -- need 3 obligatory params, 4 are not appreciated
break -- to join mark
end--if
strtplnam = arxcaller[1] -- string 5...100 -- #1
numlong = string.len (strtplnam)
if ((numlong<5) or (numlong>100)) then
booerr = true -- must be 5...100 octet:s
break -- to join mark
end--if
strtmp = arxcaller[2] -- string 1...6 -- #2
numlong = string.len (strtmp)
if ((numlong<1) or (numlong>6)) then
booerr = true -- must be 1....6 octet:s
break -- to join mark
end--if
numearl = lfdecinp (strtmp)
if (numearl>99999) then
booerr = true -- damn again
break -- to join mark
end--if
strtmp = arxcaller[3] -- string 1...6 -- #3
numlong = string.len (strtmp)
if ((numlong<1) or (numlong>6)) then
booerr = true -- must be 1....6 octet:s
break -- to join mark
end--if
numlast = lfdecinp (strtmp)
if ((numlast<numearl) or (numlast>99999)) then
booerr = true -- damn again
break -- to join mark
end--if
break -- finally to join mark
end--while -- fake loop -- join mark
---- SEIZE 1 OPTIONAL NAMED PARAMETER SUBMITTED TO CALLER ----
if (booerr==false) then
vartmp = arxcaller["err"]
if (type(vartmp)=="string") then
numlong = string.len (vartmp)
if ((numlong>0) and (numlong<65)) then
strerrequ = vartmp -- 1...64 octet:s
end--if
end--if
end--if
---- CHECK WHETHER THE POINTED TEMPLATE EXISTS AT ALL AND EXPAND IT ----
-- we do NOT assign "booerr" to "true" but may leave "strtext" empty instead
if (booerr==false) then
strtext = ""
strtmp = arxframent:callParserFunction ('#ifexist:'..strtplnam,'1','0')
if (strtmp=='1') then
vartmp = arxframent:expandTemplate { title = strtplnam }
if ((type(vartmp))=='string') then
strtext = vartmp -- may be empty
end--if
end--if (strtmp=='1') then
end--if
---- STRIP OFF SOME BLANK LINES AND REJECT POSSIBLE EMPTY STUFF ----
-- note that incoming "strtext" may be empty as hell, or become empty
-- or too short only during stripping
-- we always add an EOL to the text and keep one EOL left at
-- the end after stripping
-- numbegwh, numlaswh -- whole text
-- "+3" check means at least 4 octet:s inclusive
if (booerr==false) then
strtext = strtext .. string.char (10)
numbegwh = 1 -- ONE-based
numlaswh = string.len (strtext) -- ONE-based -- may NOT be 0 due added EOL
while (true) do
if ((numbegwh+3)>numlaswh) then
booerr = true -- damn again -- empty NOT appreciated !!!
break
end--if
numoct = string.byte (strtext,numbegwh,numbegwh)
numokt = string.byte (strtext,(numlaswh-1),(numlaswh-1)) -- keep 1 EOL
if ((numoct~=10) and (numokt~=10)) then
break -- done
end--if
if (numoct==10) then
numbegwh = numbegwh + 1
end--if
if (numokt==10) then
numlaswh = numlaswh - 1
end--if
end--while
end--if
---- CARRY OUT THE HARD SEARCH WORK ----
-- trailing spaces $20 are prohibited
-- trailing UTF8 NBSP-spaces $A0 ($C2,$A0) are prohibited
-- numbegwh, numlaswh -- whole text
-- numbegse, numlasse -- selection
-- both "numokix" and "numokyx" are ONE-based for "string.byte"
-- "+3" check means at least 4 octet:s inclusive
if (booerr==false) then
numlinx = 0 -- line index -- ZERO-based
numokix = numbegwh -- octet index -- ONE-based -- overall
while (true) do
if ((numokix+3)>numlaswh) then
booerr = true -- damn -- need at least 4 octet:s left
break
end--if
numoct = string.byte (strtext,numokix,numokix) -- "*"
numokt = string.byte (strtext,(numokix+1),(numokix+1)) -- " "
numoqt = string.byte (strtext,(numokix+2),(numokix+2)) -- NOT EOL," ","*"
if ((numoct~=42) or (numokt~=32) or (numoqt==10) or (numoqt==32) or (numoqt==42)) then
booerr = true -- damn
break -- outer loop
end--if
numokyx = numokix + 3 -- 3 char:s already done
while (true) do
numoct = string.byte (strtext,numokyx,numokyx)
if (numoct==10) then
numokt = string.byte (strtext,(numokyx-1),(numokyx-1)) -- no underfl
if ((numokt==32) or (numokt==160)) then
booerr = true -- damn -- trailing whitespace
end--if
break -- inner loop -- good or bad -- found an EOL here
end--if
if ((numokyx-numokix)==400) then
booerr = true -- damn -- line too long
break -- inner loop
end--if
numokyx = numokyx + 1 -- looking for EOL ending the line
end--while
if (booerr==true) then
break -- outer loop -- damn
end--if
if (numlinx==numearl) then
numbegse = numokix -- note that we have "numlasse" if have "numbegse"
end--if
if ((numlinx>=numearl) and (numlinx<=numlast)) then
numlasse = numokyx -- last inclusive, it is always an EOL, repeatedly
end--if
numlinx = numlinx + 1 -- one line checked and counted
if (numokyx==numlaswh) then
break -- outer loop -- this is THE ONLY HAPPY EXIT from the outer loop
end--if
numokix = numokyx + 1 -- jump over the EOL
numoct = string.byte (strtext,numokix,numokix) -- maybe one more EOL
if (numoct==10) then
numokix = numokix + 1 -- jump over the extra EOL, legal blank line
end--if
end--while
if (numbegse~=0) then -- note that we have "numlasse" if have "numbegse"
strtext = string.sub (strtext,numbegse,numlasse) -- perform the cut
else
booerr = true -- damn -- final bad luck
end--if
end--if (booerr==false)
---- PREPARE ----
if (booerr==true) then
strret = strerrequ -- error -- string "strret" was preset to empty
else
strret = strtext -- cool
end--if
---- RETURN THE JUNK STRING ----
return strret
end--function
---- RETURN THE JUNK LUA TABLE ----
return picklines