Saltu al enhavo

Modulo:ligald

El Vikivortaro
 MODULO
Memtesto disponeblas sur la paĝo Ŝablono:ligald.

--[===[

MODULE "LIGALD" (ligu al aldono)

"eo.wiktionary.org/wiki/Modulo:ligald" <!--2025-Oct-16-->

Purpose: links to appendix page from a pink box located at
         the left margin of the screen

Utilo: ligas al aldona pagxo el rugxeta skatolo lokigita cxe
       maldekstra flanko de la ekrano

Manfaat: menyediakan pranala ...

Syfte: laenkar till appendixsida fraan en skaer ...

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaend av mallar:
* only "ligald"

Required submodules / Bezonataj submoduloj /
Submodul yang diperlukan / Behoevda submoduler:
* "loaddata-tbllingvoj" T76 T80 in turn requiring template "tbllingvoj" (EO)
* "loaddata-tblbahasa" T76 T80 in turn requiring template "tblbahasa" (ID)

This module can accept parameters whether sent to itself (own frame) or
to the parent (parent's frame). If there is a parameter "parentframe=true"
on the own frame then that own frame is discarded in favor of the
parent's one.

Incoming: * ONE anonymous and obligatory parameter
            * conditional parameter langcode (obligatory for alias
              lemma pages in ns ZERO, otherwise prohibited), more than
              one anonymous parameter is an error (strictly checked)
          * 2 hidden parameters
            * pagenameoverridetestonly=
            * nsnumberoverridetestonly=

Returned: * either box or error message, tracking cat:s only on error

This module is unbreakable (when called with correct module name
and function name). Every imaginable input from the parent and
from the imported modules will output either a useful result or
at least a helpful error string.

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio). Cxiu imagebla enigo de la vokanto kaj
de la importataj moduloj eldonos aux utilan rezulton aux
almenaux helpeman eraranoncan signocxenon.

Possible errors:
* <<#E01 Internal error in "Module:ligald">>
  Possible causes:
  * strings not uncommented
  * function "mw.title.getCurrentTitle().text" AKA "{{PAGENAME}}" failed
* <<#E02 Malica eraro en subprogramaro uzata far "SXablono:ligald">>
  Possible causes:
  * submodule "loaddata-tbllingvoj" not found
  * submodule "loaddata-tbllingvoj" caused unspecified failure
* <<#E03 Nombrigita eraro en subprogramaro uzata far "SXablono:ligald">>
  Possible causes:
  * submodule failed and returned valid error code
* <<#E08 Erara uzo de sxablono (ligald), maksimume UNU anonima parametro permeseblas>>
* <<#E09 Erara uzo de sxablono (ligald), anonima parametro malplena aux tro longa>>
* <<#E10 Erara uzo de sxablono (ligald), uzebla nur en nomspacoj NUL (kapvorto) kaj 14 (kategorio)>>
* <<#E11 Erara uzo de sxablono (ligald), evidente nevalida lingvokodo>>
* <<#E12 Erara uzo de sxablono (ligald), nekonata lingvokodo>>
* <<#E13 Erara uzo de sxablono (ligald), lingvokodo estas malpermesita por ne-aliasa kapvorto>>
* <<#E15 Erara uzo de sxablono (ligald), nekonata lingvonomo en kapvorta pagxonomo>>
* <<#E16 Erara uzo de sxablono (ligald), lingvokodo estas malpermesita por kategorio>>
* <<#E18 Erara uzo de sxablono (ligald), nevalida aux nesubtenata pagxonomo en kategorio>>

Possible tracking cat:s:
* [[Kategorio:Evidente nevalida lingvokodo]]         #E11
* [[Kategorio:Nekonata lingvokodo]]                  #E12
* [[Kategorio:Nekonata lingvokodo loke (ligald)]]    #E12
* [[Kategorio:Nekonata lingvokodo nome (fy)]]        #E12
* [[Kategorio:Erara pagxonomo]] NO DETAILS HERE      #E15 #E18
* [[Kategorio:Erara uzo de sxablono]]                all >=#E08 except #E11 #E12 #E15 #E18
* [[Kategorio:Erara uzo de sxablono (ligald)]]       all >=#E08 except #E11 #E12 #E15 #E18
* [[Kategorio:Erara uzo de sxablono (ligald, E18)]]  all >=#E08 except #E11 #E12 #E15 #E18

Misc:
* decide what to do based on ns and pagename and one parameter (see below)

Supported types of fullpagenames:
* a) "dana"      main langname
* b) "Ido"       main langname
* c) "taja"      alias langname
* d) "Latino"    alias langname
* e) "Kategorio:Ido"
* f) "Kategorio:Kapvorto (dana)"

Two intermediate strings ("early" and "latter"):
* boasting and linking step on success takes two strings
  from the evaluation step:
  * a) pagename "dana" main langname ->
    ''la "<b>dana</b>"'' and ''dana'' (latter becomes ''[[Aldono:Dana]]'')
  * b) pagename "Ido" main langname ->
    ''"<b>Ido</b>"'' and ''Ido'' (latter becomes ''[[Aldono:Ido]]'')
  * c) pagename "taja" alias langname ->
    ''la "<b>siama</b>" (la "<b>taja</b>")'' and ''siama'' (latter
    becomes ''[[Aldono:Siama]]'')
  * pagename "Latino" alias langname ->
    ''la "<b>latina</b>" ("<b>Latino</b>")'' and ''latina'' (latter
    becomes ''[[Aldono:Latina]]'')

Possible texts:
* a) Por pliaj informoj pri la "<b>dana</b>" rigardu
     la pagxon [[Aldono:Dana]].
* c) Por pliaj informoj pri la "<b>siama</b>" (la "<b>taja</b>") rigardu
     la pagxon [[Aldono:Siama]].
* e) Por pliaj informoj pri "<b>Ido</b>" rigardu
     la pagxon [[Aldono:Ido]].
* f) Por pliaj informoj pri la "<b>dana</b>" rigardu
     la pagxon [[Aldono:Dana]].

Strategy:
* check for up to 3 anon parameters and length
* if (more than ONE parameter found) then
  * #E08 maksimume
  * abort
* endif
* if (parameter here but length not sane) then
  * #E09
  * abort
* endif
* seize ns (separately as number)
* seize pagename (separately as unfull) (p)
* if (ns is other than ZERO or 14) then
  * #E10 nomspaco
  * abort
* endif
* if (langcode given) then
  * check obvious
  * if (obviously invalid) then
    * #E11 evidente
    * abort
  * endif
* endif
* if (ns is ZERO) then
  * if (langcode given) then
    * do forward query with langcode maybe getting langname (j)
    * if (query failed) then
      * #E02 subprogramaro or #E12 nekonata lingvokodo
      * abort
    * endif
    * compare both langnames (p) vs (j)
    * if (same) then
      * #E13 lingvokodo malpermesita por ne-aliasa
      * abort
    * endif
    * assign "early" intermediate string using both lang
      names, to both adding quot and bold unconditionally, as well
      as "la " as needed ie if final letter is not "o"
    * assign "latter" intermediate string from (j)
  * else
    * do reverse query with (p) maybe getting langcode
    * if (query failed) then
      * #E02 subprogramaro or #E15 nekonata lingvonomo en kapvorta
      * abort
    * endif
    * discard the found langcode
    * assign "early" intermediate string from (p), adding quot
      and bold unconditionally, as well as "la " as needed
      ie if final letter is not "o"
    * assign "latter" intermediate string from (p)
  * endif (langcode given) else
* else
  * if (langcode given) then
    * #E16 lingvokodo malpermesita por kategorio
    * abort
  * endif
  * if (NOT pagename (p) begins with uppercase) then
    * #E18 nevalida pagxonomo en kato
    * abort
  * endif
  * take pagename (p) and try to isolate language name from brackets (m)
  * if (isolate success) then
    * if (NOT remainder equal "Kapvorto ()") then
      * #E18 nevalida pagxonomo en kato
      * abort
    * endif
    * take pagename (p) unchanged (m)
  * else
    * take pagename (p) and convert it to lowercase (unicode
      restricted) unless final letter is "o" (m)
  * endif
  * do reverse query with (m) maybe getting langcode
  * if (NOT langcode found) then
    * #E18 nevalida pagxonomo en kato
    * abort
  * endif
  * discard the found langcode
  * assign "early" intermediate string from (m), adding quot
    and bold unconditionally, as well as "la " as needed
    ie if final letter is not "o"
  * assign "latter" intermediate string from (m)
* endif (ns is ZERO) else
* if (success so far) then
  * brew the box from "contabbeg" and "contabend", from "contabmesagxoj",
    from "strpikapxns", as well as from the two intermediate
    strings ("early" and "latter")
* else
  * brew error message
  * brew tracking cat:s
* endif

]===]

local exporttable = {}

require('strict')

-- ***********************
-- *    CONSTANTS [O]    * ---------------------------------------------
-- ***********************

-- uncommentable (site-related features)

      local constringvoj = 'Modulo:loaddata-tbllingvoj'  -- EO
        -- local constringvoj = 'Modul:loaddata-tblbahasa'  -- ID

-- uncommentable constant table (error messages)

-- * #E02...#E98, holes permitted, #E00...#E12 globally reserved
-- * note that #E00 #E01 #E99 are NOT supposed to be included here
-- * separate "strmparent" (full) needed for "%@"

local contaberaroj = {}
      contaberaroj[02] = 'Malica eraro en submodulo uzata far %@'                      -- EO #E02
        -- contaberaroj[02] = 'Kesalahan jahat dalam submodul digunakan oleh %@'          -- ID #E02
      contaberaroj[03] = 'Nombrigita eraro en submodulo uzata far %@'                  -- EO #E03
        -- contaberaroj[03] = 'Kesalahan ternomor dalam submodul digunakan oleh %@'       -- ID #E03
      contaberaroj[10] = 'Erara uzo de %@, uzebla nur en nomspacoj NUL (kapvorto) kaj 14 (kategorio)'                 -- EO #E10
        -- contaberaroj[10] = 'Penggunaan salah %@, hanya bisa digunakan dalam ruang nama NOL (lema) dan 14 (kategori)'  -- ID #E10
      contaberaroj[11] = 'Evidente nevalida lingvokodo sendita al %@'                                                 -- EO #E11
        -- contaberaroj[11] = 'Kode bahasa jelas-jelas salah dikirim ke %@'                                              -- ID #E11
      contaberaroj[12] = 'Nekonata lingvokodo sendita al %@'                                                          -- EO #E12
        -- contaberaroj[12] = 'Kode bahasa tidak dikenal dikirim ke %@'                                                  -- ID #E12
      contaberaroj[18] = 'Erara uzo de %@, nevalida aux nesubtenata pagxonomo en kategorio'                           -- EO #E18
        -- contaberaroj[18] = 'Penggunaan salah %@, judul halaman kategori tidak valid atau tidak didukung'              -- ID #E18

-- uncommentable constant table (misc messages and tracking cat:s)

      local contabmesagxoj = {[0]='Por pliaj informoj pri ', ' rigardu la pagxon '}                                                               -- EO
      local contabtrako    = {[0]='Evidente nevalida lingvokodo', 'Nekonata lingvokodo', 'Nekonata lingvokodo loke', 'Nekonata lingvokodo nome'}  -- EO
      contabtrako[4] = 'Erara pagxonomo'        -- EO
      contabtrako[5] = 'Erara uzo de sxablono'  -- EO

-- constant strings (HTML table with one cell only)

local contabbeg = '<table style="margin:0.5em auto 0.5em 0;padding:0;border:1px solid #000000;"><tr><td style="padding:0.2em;background:#E0C0C0;">'
local contabend = '</td></tr></table>'

-- constant table -- ban list -- add obviously invalid access codes (2-letter or 3-letter) only

-- length of the list is NOT stored anywhere, the processing stops
-- when type "nil" is encountered, used by "lfivalidatelnkoadv" only

-- controversial codes (sh sr hr), (zh cmn)
-- "en.wiktionary.org/wiki/Wiktionary:Language_treatment" excluded languages
-- "en.wikipedia.org/wiki/Spurious_languages"
-- "iso639-3.sil.org/code/art" only valid in ISO 639-2
-- "iso639-3.sil.org/code/gem" only valid in ISO 639-2 and 639-5, "collective"
-- "iso639-3.sil.org/code/zxx" "No linguistic content"

local contabisbanned = {}
contabisbanned = {'by','dc','ll','jp','art','deu','eng','epo','fra','gem','ger','ido','lat','por','rus','spa','swe','tup','zxx'} -- 1...19

-- surrogate transcoding table (only needed for EO)

local contabtransluteo = {}
contabtransluteo[ 67] = 0xC488 -- CX
contabtransluteo[ 99] = 0xC489 -- cx
contabtransluteo[ 71] = 0xC49C -- GX
contabtransluteo[103] = 0xC49D -- gx
contabtransluteo[ 74] = 0xC4B4 -- JX
contabtransluteo[106] = 0xC4B5 -- jx
contabtransluteo[ 83] = 0xC59C -- SX
contabtransluteo[115] = 0xC59D -- sx
contabtransluteo[ 85] = 0xC5AC -- UX breve
contabtransluteo[117] = 0xC5AD -- ux breve

-- constant strings (error circumfixes)

local contabfel = {}
contabfel.mibg = '<span class="error">'     -- mini whining begin
contabfel.labg = '<b>' .. contabfel.mibg    -- lagom whining begin
contabfel.hubg = '<big>' .. contabfel.labg  -- huge whining begin
contabfel.mien = '</span>'                  -- mini whining end
contabfel.laen = contabfel.mien .. '</b>'   -- lagom whining end
contabfel.huen = contabfel.laen .. '</big>' -- huge whining end

-- constants to control behaviour from source AKA semi-hardcoded parameters

local conbookodlng  = false  -- "true" to allow long codes like "zh-min-nan"
local conboomiddig  = false  -- "true" to allow middle digit "s7a"

-- uncommentable (override)

-- * name of table MUST always be declared, OTOH elements are usually absent
-- * for testing only, values automatically peeked otherwise

local contabovrd = {}
  -- contabovrd.sitelang = 'eo'                                     -- "en"
  -- contabovrd.sitelang = 'id'
  -- contabovrd.katprefi = 'Kategorio'                              -- "Category"
  -- contabovrd.katprefi = 'Kategori'
  -- contabovrd.indprefi = 'Indekso'                                -- "Index"
  -- contabovrd.indprefi = 'Indeks'
  -- contabovrd.apxprefi = 'Aldono'                                 -- "Appendix"
  -- contabovrd.apxprefi = 'Lampiran'
  -- contabovrd.defortrc = true                                     -- force "detrc=true"

-- ****************************************
-- *    SPECIAL STUFF OUTSIDE MAIN [B]    * ----------------------------
-- ****************************************

  -- SPECIAL VAR:S

local qldingvoj = {}     -- type "table", metaized, has subtables, only data
local qbooguard = false  -- only for the guard test, pass to other var ASAP

---- GUARD AGAINST INTERNAL ERROR AND IMPORT ONE VIA LOADDATA ----

qbooguard = (type(constringvoj)~='string')
if (not qbooguard) then
  qldingvoj = mw.loadData(constringvoj) -- can crash here
  qbooguard = (type(qldingvoj)~='table') -- seems to be always false
end--if

------------------------------------------------------------------------

local function mathisintrange (numzjinput, numzjmin, numzjmax)
  local booisclean = false -- preASSume guilt
  if (type(numzjinput)=='number') then -- no non-numbers, thanks
    if (numzjinput==math.floor(numzjinput)) then -- no transcendental
      booisclean = ((numzjinput>=numzjmin) and (numzjinput<=numzjmax)) -- rang
    end--if
  end--if
  return booisclean
end--function mathisintrange

local function mathdiv (xdividens, xdivisero)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividens / xdivisero)
  return resultdiv
end--function mathdiv

local function mathmod (xdividendo, xdivisoro)
  local resultmod = 0 -- MOD operator is "%" and bitwise AND operator lack too
  resultmod = xdividendo % xdivisoro
  return resultmod
end--function mathmod

local function mathbitwrit (numinkoming, numbityndex, boowrite)  -- !!!FIXME!!! soon can be deleted
  local numpatched = 0
  local numcountup = 0
  local numweight = 1 -- single bit value 1 -> 2 -> 4 -> 8 ...
  local boosinglebit = false
  while true do
    if ((numinkoming==0) and (numcountup>numbityndex)) then
      break -- we have run out of bits on BOTH possible sources
    end--if
    if (numcountup==numbityndex) then
      boosinglebit = boowrite -- overwrite bit
    else
      boosinglebit = (mathmod(numinkoming,2)==1) -- pick bit
    end--if
    numinkoming = mathdiv(numinkoming,2) -- shift right
    if (boosinglebit) then
      numpatched = numpatched + numweight -- add one bit rtl only if true
    end--if
    numcountup = numcountup + 1 -- count up here until we run out of bits
    numweight = numweight * 2
  end--while
  return numpatched
end--function mathbitwrit

------------------------------------------------------------------------

---- NUMBER CONVERSION FUNCTIONS [N] ----

------------------------------------------------------------------------

-- Convert integer 0...99 to decimal ASCII string always 2 digits "00"..."99".

-- Depends on procedures :
-- [E] mathisintrange mathdiv mathmod

local function prnnumto2digit (numzerotoninetynine)
  local strtwodig = '??' -- always 2 digits
  if (mathisintrange(numzerotoninetynine,0,99)) then
    strtwodig = tostring(mathdiv(numzerotoninetynine,10)) .. tostring(mathmod(numzerotoninetynine,10))
  end--if
  return strtwodig
end--function prnnumto2digit

------------------------------------------------------------------------

---- LOW LEVEL STRING FUNCTIONS [G] ----

------------------------------------------------------------------------

local function prgstringrange (varvictim, nummini, nummaxi)

  local nummylengthofstr = 0
  local booveryvalid = false -- preASSume guilt
  if (type(varvictim)=='string') then
    nummylengthofstr = string.len(varvictim)
    booveryvalid = ((nummylengthofstr>=nummini) and (nummylengthofstr<=nummaxi))
  end--if

return booveryvalid

end--function prgstringrange

------------------------------------------------------------------------

local function prgpokestring (strinpokeout, numpokepoz, numpokeval)

-- Replace single octet in a string.

-- Input  : * strinpokeout -- empty legal but nothing done then
--          * numpokepoz   -- ZERO-based, out of range legal
--          * numpokeval   -- new UINT8 value

-- This is inefficient by design of LUA. The caller is responsible to
-- minimize the number of invocations of this, in particular, not to
-- call if the new value is equal the existing one.

local numpokelen = 0

  numpokelen = string.len(strinpokeout)
  if ((numpokelen==1) and (numpokepoz==0)) then
    strinpokeout = string.char(numpokeval) -- totally replace
  end--if
  if (numpokelen>=2) then
    if (numpokepoz==0) then
      strinpokeout = string.char(numpokeval) .. string.sub(strinpokeout,2,numpokelen)
    end--if
    if ((numpokepoz>0) and (numpokepoz<(numpokelen-1))) then
      strinpokeout = string.sub(strinpokeout,1,numpokepoz) .. string.char(numpokeval) .. string.sub(strinpokeout,(numpokepoz+2),numpokelen)
    end--if
    if (numpokepoz==(numpokelen-1)) then
      strinpokeout = string.sub(strinpokeout,1,numpokepoz) .. string.char(numpokeval)
    end--if
  end--if (numpokelen>=2) then

return strinpokeout

end--function prgpokestring

------------------------------------------------------------------------

local function lfgtestnum (numkaad)
  local boodigit = false
  boodigit = ((numkaad>=48) and (numkaad<=57))
  return boodigit
end--function lfgtestnum

local function lfgtestuc (numkode)
  local booupperc = false
  booupperc = ((numkode>=65) and (numkode<=90))
  return booupperc
end--function lfgtestuc

local function lfgtestlc (numcode)
  local boolowerc = false
  boolowerc = ((numcode>=97) and (numcode<=122))
  return boolowerc
end--function lfgtestlc

------------------------------------------------------------------------

---- UTF8 FUNCTIONS [U] ----

------------------------------------------------------------------------

-- Local function LFULNUTF8CHAR

-- Evaluate length of a single UTF8 char in octet:s.

-- Input  : * numbgoctet  -- beginning octet of a UTF8 char

-- Output : * numlen1234x -- unit octet, number 1...4, or ZERO if invalid

-- Does NOT thoroughly check the validity, looks at ONE octet only.

local function lfulnutf8char (numbgoctet)
  local numlen1234x = 0
    if (numbgoctet<128) then
      numlen1234x = 1 -- $00...$7F -- ANSI/ASCII
    end--if
    if ((numbgoctet>=194) and (numbgoctet<=223)) then
      numlen1234x = 2 -- $C2 to $DF
    end--if
    if ((numbgoctet>=224) and (numbgoctet<=239)) then
      numlen1234x = 3 -- $E0 to $EF
    end--if
    if ((numbgoctet>=240) and (numbgoctet<=244)) then
      numlen1234x = 4 -- $F0 to $F4
    end--if
  return numlen1234x
end--function lfulnutf8char

------------------------------------------------------------------------

local function prusrcasepairrest (strmy4set, num16valin)  -- !!!FIXME!!! NOT yet used

-- Search pair of undecoded UTF8 values (UPPER, lower) based on
-- ONE value (unrolled loop).

-- Input  : * strmy4set -- "eo" or "sv" or (no "GENE" no "ASCII" here)
--          * num16valin -- undecoded UINT16BE

-- Output : * num16upper, num16lower -- both -1 if nothing found

local num16upper = -1
local num16lower = -1

  local function xxxdicompare (xxxupper, xxxlower) -- only 3 upvalues used
    local xxxfound = false -- preASSume no hit
    if ((num16valin==xxxupper) or (num16valin==xxxlower)) then
      num16upper = xxxupper
      num16lower = xxxlower
      xxxfound = true
    end--if
  end--function xxxdicompare

  while true do -- fake loop
    if (strmy4set~='eo') then
      break -- do NOT check
    end--if
    if (xxxdicompare(0xC488,0xC489)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC49C,0xC49D)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC4A4,0xC4A5)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC4B4,0xC4B5)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC59C,0xC59D)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC5AC,0xC5AD)) then
      break -- found it
    end--if
    break -- finally
  end--while -- fake loop

  while true do -- fake loop
    if (strmy4set~='sv') then
      break -- do NOT check
    end--if
    if (xxxdicompare(0xC384,0xC3A4)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC385,0xC3A5)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC389,0xC3A9)) then
      break -- found it
    end--if
    if (xxxdicompare(0xC396,0xC3B6)) then
      break -- found it
    end--if
    break -- finally
  end--while -- fake loop

return num16upper, num16lower

end--function prusrcasepairrest

------------------------------------------------------------------------

-- Local function LFUTRISTLETR  !!!FIXME!!! replace (order susah reversed)

-- Evaluate char (from ASCII + selectable extra set from UTF8) to
-- tristate result (no letter vs uppercase letter vs lowercase letter).

-- Input  : * strin5trist : single unicode char (1 or 2 octet:s) or
--                          longer string
--          * strsel4set  : "ASCII" (default, empty string or type "nil"
--                          will do too) "eo" "sv" (value "GENE" NOT here)

-- Output : * numtype5x : 0 no letter or invalid UTF8 -- 1 upper -- 2 lower

-- Depends on procedures : (this is LFUTRISTLETR)
-- [U] lfulnutf8char
-- [G] lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

-- Possible further char:s or fragments of such are disregarded, the
-- question answered is "Is there one uppercase or lowercase letter
-- available at begin?".

local function lfutristletr (strsel4set, strin5trist)

  local numtype5x = 0 -- preASSume invalid
  local numlong5den = 0 -- actual length of input string
  local numlong5bor = 0 -- expected length of single char
  local numcha5r = 0 -- UINT8 beginning char
  local numcha5s = 0 -- UINT8 later char (BIG ENDIAN, lower value here above)
  local numcxa5rel = 0 -- UINT8 code relative to beginning of block $00...$FF
  local numtem5p = 0

  local boois5uppr = false
  local boois5lowr = false

  while true do -- fake loop -- this is LFUTRISTLETR

    numlong5den = string.len (strin5trist)
    if (numlong5den==0) then
      break -- bad string length
    end--if
    numcha5r = string.byte (strin5trist,1,1)
    numlong5bor = lfulnutf8char(numcha5r)
    if ((numlong5bor==0) or (numlong5den<numlong5bor)) then
      break -- truncated char or invalid
    end--if

    if (numlong5bor==1) then
      boois5uppr = lfgtestuc(numcha5r)
      boois5lowr = lfgtestlc(numcha5r)
      break -- success with ASCII, almost done
    end--if

    numcha5s = string.byte (strin5trist,2,2) -- only $80 to $BF
    numcxa5rel = (mathmod(numcha5r,4)*64) + (numcha5s-128) -- 4 times 64

    if ((strsel4set=='eo') and ((numcha5r==196) or (numcha5r==197))) then
      numtem5p = mathbitwrit (numcxa5rel,0,false) -- bad way to do AND $FE
      if ((numtem5p==8) or (numtem5p==28) or (numtem5p==36) or (numtem5p==52) or (numtem5p==92) or (numtem5p==108)) then
        boois5uppr = (numtem5p==numcxa5rel) -- UC below, block of 1
        boois5lowr = not boois5uppr
        break -- success with -eo-, almost done
      end--if
    end--if ((strsel4set=='eo') and ...

    if ((strsel4set=='sv') and (numcha5r==195)) then
      numtem5p = mathbitwrit (numcxa5rel,5,false) -- bad way to do AND $DF
      if ((numtem5p==196) or (numtem5p==197) or (numtem5p==201) or (numtem5p==214)) then
        boois5uppr = (numtem5p==numcxa5rel) -- UC below, block of 32
        boois5lowr = not boois5uppr
        break -- success with -sv-, almost done
      end--if
    end--if ((strsel4set=='sv') and ...

    break -- finally to join mark -- unknown non-ASCII char is a fact :-(
  end--while -- fake loop -- join mark

  if (boois5uppr) then
    numtype5x = 1
  end--if
  if (boois5lowr) then
    numtype5x = 2
  end--if

  return numtype5x

end--function lfutristletr

------------------------------------------------------------------------

-- Local function PRUCASEAIOREST  !!!FIXME!!! new name old code -> replace (order susah reversed)

-- Adjust (restricted) case of a single letter (from ASCII + selectable
-- extra set from UTF8) or longer string. (this is REST)

-- Input  : * strinco6cs : single unicode letter (1 or 2 octet:s) or
--                         longer string
--          * booup6cas  : for desired output uppercase "true" and for
--                         lowercase "false"
--          * boodo6all  : "true" to adjust all letters, "false"
--                         only beginning letter
--          * strsel6set

-- Output : * strinco6cs

-- Depends :
-- [U] lfulnutf8char
-- [G] lfgtestuc lfgtestlc prgpokestring
-- [E] mathdiv mathmod mathbitwrit

-- This process never changes the length of a string in octet:s. Empty string
-- on input is legal and results in an empty string returned. When case is
-- adjusted, a 1-octet or 2-octet letter is replaced by another letter of same
-- length. Unknown valid char:s (1-octet ... 4-octet) are copied.

local function prucaseaiorest (strsel6set, strinco6cs, booup6cas, boodo6all)

  local numlong6den = 0 -- actual length of input string
  local numokt6index = 0
  local numlong6bor = 0 -- expected length of single char

  local numdel6ta = 0 -- quasi-signed +32 or -32 or +1 or -1 or ZERO

  local numcha6r = 0 -- UINT8 beginning char
  local numcha6s = 0 -- UINT8 later char (BIG ENDIAN, lower value here above)
  local numcxa6rel = 0 -- UINT8 code relative to beginning of block $00...$FF

  local numtem6p = 0

  local boowan6tlowr = false
  local boois6uppr = false
  local boois6lowr = false

  local boodo6adj = true -- preASSume innocence -- continue changing
  local boobotch6d = false -- preASSume innocence -- NOT yet botched

  booup6cas = not (not booup6cas)
  boowan6tlowr = (not booup6cas)
  numlong6den = string.len (strinco6cs)

  while true do -- genuine loop over incoming string (this is REST)

    if (numokt6index>=numlong6den) then
      break -- done complete string
    end--if
    if ((not boodo6all) and (numokt6index~=0)) then -- loop can skip index ONE
      boodo6adj = false
    end--if
    boois6uppr  = false -- preASSume on every iteration
    boois6lowr  = false -- preASSume on every iteration
    numdel6ta   = 0 -- preASSume on every iteration
    numlong6bor = 1 -- preASSume on every iteration

    while true do -- fake loop (this is REST)

      numcha6r = string.byte (strinco6cs,(numokt6index+1),(numokt6index+1))
      if (boobotch6d) then
        numdel6ta = 90 - numcha6r -- "Z" -- delta must be non-ZERO to write
        break -- fill with "Z" char:s
      end--if
      if (not boodo6adj) then
        break -- copy octet after octet
      end--if
      numlong6bor = lfulnutf8char(numcha6r)
      if ((numlong6bor==0) or ((numokt6index+numlong6bor)>numlong6den)) then
        numlong6bor = 1 -- reassign to ONE !!!
        numdel6ta = 90 - numcha6r -- "Z" -- delta must be non-ZERO to write
        boobotch6d = true
        break -- truncated char or broken stream
      end--if
      if (numlong6bor>=3) then
        break -- copy UTF8 char, no chance for adjustment
      end--if

      if (numlong6bor==1) then
        boois6uppr = lfgtestuc(numcha6r)
        boois6lowr = lfgtestlc(numcha6r)
        if (boois6uppr and boowan6tlowr) then
          numdel6ta = 32 -- ASCII UPPER->lower
        end--if
        if (boois6lowr and booup6cas) then
          numdel6ta = -32 -- ASCII lower->UPPER
        end--if
        break -- success with ASCII and one char almost done
      end--if

      numcha6s = string.byte (strinco6cs,(numokt6index+2),(numokt6index+2)) -- only $80 to $BF
      numcxa6rel = (mathmod(numcha6r,4)*64) + (numcha6s-128) -- 4 times 64

    if ((strsel6set=='eo') and ((numcha6r==196) or (numcha6r==197))) then
      numtem6p = mathbitwrit (numcxa6rel,0,false) -- bad way to do AND $FE
      if ((numtem6p==8) or (numtem6p==28) or (numtem6p==36) or (numtem6p==52) or (numtem6p==92) or (numtem6p==108)) then
        boois6uppr = (numtem6p==numcxa6rel) -- UC below, block of 1
        boois6lowr = not boois6uppr
        if (boois6uppr and boowan6tlowr) then
          numdel6ta = 1 -- UPPER->lower
        end--if
        if (boois6lowr and booup6cas) then
          numdel6ta = -1 -- lower->UPPER
        end--if
        break -- success with -eo- and one char almost done
      end--if
    end--if ((strsel6set=='eo') and ...

    if ((strsel6set=='sv') and (numcha6r==195)) then
      numtem6p = mathbitwrit (numcxa6rel,5,false) -- bad way to do AND $DF
      if ((numtem6p==196) or (numtem6p==197) or (numtem6p==201) or (numtem6p==214)) then
        boois6uppr = (numtem6p==numcxa6rel) -- UC below, block of 32
        boois6lowr = not boois6uppr
        if (boois6uppr and boowan6tlowr) then
          numdel6ta = 32 -- UPPER->lower
        end--if
        if (boois6lowr and booup6cas) then
          numdel6ta = -32 -- lower->UPPER
        end--if
        break -- success with -sv- and one char almost done
      end--if
    end--if ((strsel6set=='sv') and ...

      break -- finally to join mark -- unknown non-ASCII char is a fact :-(
    end--while -- fake loop -- join mark (this is REST)

    if ((numlong6bor==1) and (numdel6ta~=0)) then -- no risk of carry here
      strinco6cs = prgpokestring (strinco6cs,numokt6index,(numcha6r+numdel6ta))
    end--if
    if ((numlong6bor==2) and (numdel6ta~=0)) then -- no risk of carry here
      strinco6cs = prgpokestring (strinco6cs,(numokt6index+1),(numcha6s+numdel6ta))
    end--if
    numokt6index = numokt6index + numlong6bor -- advance in incoming string

  end--while -- genuine loop over incoming string (this is REST)

  return strinco6cs

end--function prucaseaiorest

------------------------------------------------------------------------

---- HIGH LEVEL STRING PROCEDURES [I] ----

------------------------------------------------------------------------

-- Local function LFIKATALDIGU

-- Brew cat insertion (no extra colon ":") or link to
-- appendix from 3 elements.

local function lfikataldigu (strprefixx, strkataldnomo, strhintvisi)
  local strrbkma = ''
  if (type(strhintvisi)=='string') then
    strrbkma = '[[' .. strprefixx .. ':' .. strkataldnomo .. '|' .. strhintvisi .. ']]'
  else
    strrbkma = '[[' .. strprefixx .. ':' .. strkataldnomo .. ']]'
  end--if
  return strrbkma
end--function lfikataldigu

------------------------------------------------------------------------

-- Local function LFISEPBRACKET

-- Separate bracketed part of a string and return the inner and outer
-- part, the outer one with the brackets. There must be exactly ONE "("
-- and exactly ONE ")" in correct order.

-- Input  : * strsep33br
--          * numxmin33len -- minimal length of inner part, must be >= 1

-- Output : * boosaxes, strinner, strouter

-- Note that for length of hit ZERO ie "()" we would have "begg" + 1 = "endd"
-- and for length of hit ONE ie "(x)" we have "begg" + 2 = "endd".

-- Example: "crap (NO)" -> len = 9
--           123456789
-- "begg" = 6 and "endd" = 9
-- Expected result: "NO" and "crap ()"

-- Example: "(XX) YES" -> len = 8
--           12345678
-- "begg" = 1 and "endd" = 4
-- Expected result: "XX" and "() YES"

local function lfisepbracket (strsep33br, numxmin33len)

  local strinner = ''
  local strouter = ''
  local num33idx = 1 -- ONE-based
  local numdlong = 0
  local num33wesel = 0
  local numbegg = 0 -- ONE-based, ZERO invalid
  local numendd = 0 -- ONE-based, ZERO invalid
  local boosaxes = false -- preASSume guilt

  numdlong = string.len (strsep33br)
  while true do
    if (num33idx>numdlong) then
      break -- ONE-based -- if both "numbegg" "numendd" non-ZERO then maybe
    end--if
    num33wesel = string.byte(strsep33br,num33idx,num33idx)
    if (num33wesel==40) then -- "("
      if (numbegg==0) then
        numbegg = num33idx -- pos of "("
      else
        numbegg = 0
        break -- damn: more than 1 "(" present
      end--if
    end--if
    if (num33wesel==41) then -- ")"
      if ((numendd==0) and (numbegg~=0) and ((numbegg+numxmin33len)<num33idx)) then
        numendd = num33idx -- pos of ")"
      else
        numendd = 0
        break -- damn: more than 1 ")" present or ")" precedes "("
      end--if
    end--if
    num33idx = num33idx + 1
  end--while

  if ((numbegg~=0) and (numendd~=0)) then
    boosaxes = true
    strouter = string.sub(strsep33br,1,numbegg) .. string.sub(strsep33br,numendd,-1)
    strinner = string.sub(strsep33br,(numbegg+1),(numendd-1))
  end--if

  return boosaxes, strinner, strouter

end--function lfisepbracket

------------------------------------------------------------------------

-- Local function LFIVALIDATELNKOADV

-- Advanced test whether a string (intended to be a language code) is valid
-- containing only 2 or 3 lowercase letters, or 2...10 char:s and with some
-- dashes, or maybe a digit in middle position or maybe instead equals to "-"
-- or "??" and maybe additionally is not included on the ban list.

-- Input  : * strqooq -- string (empty is useless and returns
--                       "true" ie "bad" but cannot cause any major harm)
--          * booyesdsh -- "true" to allow special code dash "-"
--          * booyesqst -- "true" to allow special code doublequest "??"
--          * booloonkg -- "true" to allow long codes such as "zh-min-nan"
--          * boodigit -- "true" to allow digit in middle position
--          * boonoban -- (inverted) "true" to skip test against ban table

-- Output : * booisvaladv -- true if string is valid

-- Depends on procedures :
-- [G] lfgtestnum lfgtestlc

-- Depends on constants :
-- * table "contabisbanned"

-- Incoming empty string is safe but type "nil" is NOT.

-- Digit is tolerable only ("and" applies):
-- * if boodigit is "true"
-- * if length is 3 char:s
-- * in middle position

-- Dashes are tolerable (except in special code "-") only ("and" applies):
-- * if length is at least 4 char:s (if this is permitted at all)
-- * in inner positions
-- * NOT adjacent
-- * maximally TWO totally
-- There may be maximally 3 adjacent letters, this makes at least ONE dash
-- obligatory for length 4...7, and TWO dashes for length 8...10.

local function lfivalidatelnkoadv (strqooq, booyesdsh, booyesqst, booloonkg, boodigit, boonoban)

  local varomongkosong = 0 -- for check against the ban list
  local numchiiar = 0
  local numukurran = 0
  local numindeex = 0 -- ZERO-based -- two loops
  local numadjlet = 0 -- number of adjacent letters (max 3)
  local numadjdsh = 0 -- number of adjacent dashes (max 1)
  local numtotdsh = 0 -- total number of dashes (max 2)
  local booislclc = false
  local booisdigi = false
  local booisdash = false
  local booisvaladv = true -- preASSume innocence -- later final verdict here

  while true do -- fake (outer) loop

    if (strqooq=="-") then
      booisvaladv = booyesdsh
      break -- to join mark -- good or bad
    end--if
    if (strqooq=="??") then
      booisvaladv = booyesqst
      break -- to join mark -- good or bad
    end--if
    numukurran = string.len (strqooq)
    if ((numukurran<2) or (numukurran>10)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if
    if (not booloonkg and (numukurran>3)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if

    numindeex = 0
    while true do -- inner genuine loop over char:s
      if (numindeex>=numukurran) then
        break -- done -- good
      end--if
      numchiiar = string.byte (strqooq,(numindeex+1),(numindeex+1))
      booisdash = (numchiiar==45)
      booisdigi = lfgtestnum(numchiiar)
      booislclc = lfgtestlc(numchiiar)
      if (not (booislclc or booisdigi or booisdash)) then
        booisvaladv = false
        break -- to join mark -- inherently bad char
      end--if
      if (booislclc) then
        numadjlet = numadjlet + 1
      else
        numadjlet = 0
      end--if
      if (booisdigi and ((numukurran~=3) or (numindeex~=1) or (not boodigit))) then
        booisvaladv = false
        break -- to join mark -- illegal digit
      end--if
      if (booisdash) then
        if ((numukurran<4) or (numindeex==0) or ((numindeex+1)==numukurran)) then
          booisvaladv = false
          break -- to join mark -- illegal dash
        end--if
        numadjdsh = numadjdsh + 1
        numtotdsh = numtotdsh + 1 -- total
      else
        numadjdsh = 0 -- do NOT zeroize the total !!!
      end--if
      if ((numadjlet>3) or (numadjdsh>1) or (numtotdsh>2)) then
        booisvaladv = false
        break -- to join mark -- evil
      end--if
      numindeex = numindeex + 1 -- ZERO-based
    end--while -- inner genuine loop over char:s

    if (not boonoban) then -- if "yesban" then
      numindeex = 0
      while true do -- lower inner genuine loop
        varomongkosong = contabisbanned[numindeex+1] -- number of elem unknown
        if (type(varomongkosong)~='string') then
          break -- abort inner loop (then outer fake loop) due to end of table
        end--if
        numukurran = string.len (varomongkosong)
        if ((numukurran<2) or (numukurran>3)) then
          break -- abort inner loop (then outer fake loop) due to faulty table
        end--if
        if (strqooq==varomongkosong) then
          booisvaladv = false
          break -- abort inner loop (then outer fake loop) due to violation
        end--if
        numindeex = numindeex + 1 -- ZERO-based
      end--while -- lower inner genuine loop
    end--if (not boonoban) then

    break -- finally to join mark
  end--while -- fake loop -- join mark

  return booisvaladv

end--function lfivalidatelnkoadv

------------------------------------------------------------------------

local function pripl2altwre (strbeforfill, numaskikodo, varsupstitu)

-- Process all anon and fixed placeholders "%@" or "%~".

-- Input  : * strbeforfill -- request string with placeholders to be filled
--                            in, no placeholders or empty input is useless
--                            but cannot cause major harm
--          * numaskikodo  -- ASCII code of placeholder type, 64 for "%@" or
--                            126 for "%~"
--          * varsupstitu  -- substitute, either string (same content reused
--                            if multiple placeholders), or ZERO-based table
--                            (with one element per placeholder such as
--                            {[0]="none","neniu"}), length 1...80, further
--                            sanitization must be done elsewhere

-- Output : * strafterfill

-- Depends on procedures :
-- [G] prgstringrange

local varpfiller    = 0  -- risky picking
local strufiller    = '' -- final validated filler
local strafterfill  = ''
local numlenbigtext = 0  -- len of strbeforfill
local numsfrcindex  = 0  -- char index ZERO-based
local numinsrtinde  = 0  -- index in table ZERO-based
local numtecken0d   = 0
local numtecken1d   = 0

  numlenbigtext = string.len (strbeforfill)

  while true do
    if (numsfrcindex>=numlenbigtext) then
      break -- empty input is useless but cannot cause major harm
    end--if
    numtecken0d = string.byte(strbeforfill,(numsfrcindex+1),(numsfrcindex+1))
    numsfrcindex = numsfrcindex + 1 -- INC here
    numtecken1d = 0 -- preASSume none
    if (numsfrcindex<numlenbigtext) then -- pick but do NOT INC
      numtecken1d = string.byte(strbeforfill,(numsfrcindex+1),(numsfrcindex+1))
    end--if
    if ((numtecken0d==37) and (numtecken1d==numaskikodo)) then -- "%@" "%~"
      numsfrcindex = numsfrcindex + 1 -- INC more, now totally + 2
      varpfiller = 0 -- preASSume nothing available
      strufiller = '??' -- preASSume nothing available
      if (type(varsupstitu)=='string') then
        varpfiller = varsupstitu -- take it as-is (length check below)
      end--if
      if (type(varsupstitu)=='table') then
        varpfiller = varsupstitu [numinsrtinde] -- risk of type "nil"
        numinsrtinde = numinsrtinde + 1 -- INC tab index on every placeholder
      end--if
      if (prgstringrange(varpfiller,1,80)) then -- restrict
        strufiller = varpfiller -- now the substitute is finally accepted
      end--if
    else
      strufiller = string.char (numtecken0d) -- no placeholder -> copy octet
    end--if
    strafterfill = strafterfill .. strufiller -- add one of 4 possible cases
  end--while

return strafterfill

end--function pripl2altwre

------------------------------------------------------------------------

-- Local function LFIKODEOSG  !!!FIXME!!! replace

-- Transcode eo X-surrogates to cxapeloj in a single string (eo only).

-- Input  : * streosurr -- ANSI string (empty is useless but cannot
--                                      cause major harm)

-- Output : * strutf8eo -- UTF8 string

-- Depends on procedures :
-- [E] mathdiv mathmod

-- Depends on constants :
-- * table "contabtransluteo" inherently holy

-- To be called ONLY from "lfhrecusurrstrtab".

-- * the "x" in a surr pair is case insensitive,
--   for example both "kacxo" and "kacXo" give same result
-- * avoid "\", thus for example "ka\cxo" would get converted but the "\" kept
-- * double "x" (both case insensitive) prevents conversion and becomes
--   reduced to single "x", for example "kacxxo" becomes "kacxo"

local function lfikodeosg (streosurr)

  local vareopeek = 0
  local strutf8eo = ''
  local numeoinplen = 0
  local numinpinx = 0 -- ZERO-based source index
  local numknar0k = 0 -- current char
  local numknaf1x = 0 -- next char (ZERO is NOT valid)
  local numknaf2x = 0 -- post next char (ZERO is NOT valid)
  local boonext1x = false
  local boonext2x = false
  local boosudahdone = false

  numeoinplen = string.len(streosurr)

  while true do

    if (numinpinx>=numeoinplen) then
      break
    end--if

    numknar0k = string.byte(streosurr,(numinpinx+1),(numinpinx+1))
    numknaf1x = 0 -- preASSume no char
    numknaf2x = 0 -- preASSume no char
    if ((numinpinx+1)<numeoinplen) then
      numknaf1x = string.byte(streosurr,(numinpinx+2),(numinpinx+2))
    end--if
    if ((numinpinx+2)<numeoinplen) then
      numknaf2x = string.byte(streosurr,(numinpinx+3),(numinpinx+3))
    end--if

    boonext1x = ((numknaf1x==88) or (numknaf1x==120)) -- case insensitive
    boonext2x = ((numknaf2x==88) or (numknaf2x==120)) -- case insensitive
    boosudahdone = false
    if (boonext1x and boonext2x) then -- got "xx"
      strutf8eo = strutf8eo .. string.char(numknar0k,numknaf1x) -- keep one "x" only
      numinpinx = numinpinx + 3 -- eaten 3 written 2
      boosudahdone = true
    end--if
    if (boonext1x and (not boonext2x)) then -- got yes-"x" and no-"x"
      vareopeek = contabtransluteo[numknar0k] -- UINT16 or type "nil"
      if (type(vareopeek)=='number') then
        strutf8eo = strutf8eo .. string.char(mathdiv(vareopeek,256),mathmod(vareopeek,256)) -- add UTF8 char
        numinpinx = numinpinx + 2 -- eaten 2 written 2
        boosudahdone = true
      end--if
    end--if
    if (not boosudahdone) then
      strutf8eo = strutf8eo .. string.char(numknar0k) -- copy char
      numinpinx = numinpinx + 1 -- eaten 1 written 1
    end--if

  end--while

  return strutf8eo

end--function lfikodeosg

------------------------------------------------------------------------

-- Local function LFICONDIADDLAUQB

local function lficondiaddlauqb (stringvonomo)
  if (string.byte(stringvonomo,-1,-1)==97) then -- 'a'
    stringvonomo = 'la ' .. stringvonomo
  end--if
  stringvonomo = '"<b>' .. stringvonomo .. '</b>"'
  return stringvonomo
end--function lficondiaddlauqb

-- ***********************************
-- *    HIGH LEVEL PROCEDURES [H]    * ---------------------------------
-- ***********************************

-- Local function LFHVALI1STATUS98CODE  !!!FIXME!!! replace

-- Depends on procedures :
-- [E] mathisintrange

local function lfhvali1status98code (varvalue)
  local boovalid = false -- preASSume guilt
  while true do -- fake loop
    if (varvalue==0) then
      break -- success thus keep false since no valid error code ;-)
    end--if
    if (mathisintrange(varvalue,1,99)) then
      boovalid = true -- got an error and valid error code returned
    else
      varvalue = 255 -- failed to return valid status code
    end--if
    break -- finally to join mark
  end--while -- fake loop -- join mark
  return varvalue, boovalid
end--function lfhvali1status98code

------------------------------------------------------------------------

local function prhconstructerar (numerar3code, boopeek3it)

-- Construct partial error message maybe peeking description.

-- Input  : * numerar3code -- 1 ... 98 or 2 ... 98 (resistant against
--                            invalid data type, giving "??" on such)
--          * boopeek3it   -- do peek description #E02...#E98 from table

-- Depends on procedures :
-- [N] prnnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * maybe table contaberaroj TWO-based (holes permitted)

-- To be called ONLY from PRHBREWERR4HUNP PRHBREWERR5HUPA
-- PRHBREWERR6SLNP PRHBREWERR7SLPA PRHBREWERR8SUBM PRHBREWERR9DETA.

local vardes3krip = 0
local numbottom3limit = 1
local stryt3sux = '#E'

  if (boopeek3it) then
    numbottom3limit = 2 -- #E01 is a valid code for submodule only
  end--if
  if (mathisintrange(numerar3code,numbottom3limit,98)) then
    stryt3sux = stryt3sux .. prnnumto2digit(numerar3code)
    if (boopeek3it) then
      vardes3krip = contaberaroj[numerar3code] -- risk of type "nil"
      if (type(vardes3krip)=='string') then
        stryt3sux = stryt3sux .. ' ' .. vardes3krip
      else
        stryt3sux = stryt3sux .. ' ??' -- no text found
      end--if
    end--if (boopeek3it) then
  else
    stryt3sux = stryt3sux .. '??' -- no valid error code
  end--if

return stryt3sux

end--function prhconstructerar

------------------------------------------------------------------------

-- Local function LFHBREWERROR  !!!FIXME!!! replace and 1 -> 2 params

-- Input  : * numerar7code -- 2 ... 99

-- Depends on procedures :
-- [H] prhconstructerar
-- [N] prnnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * 3 strings
-- * table contaberaroj TWO-based (holes permitted)

-- #E02...#E99, note that #E00 and #E01 are NOT supposed to be included here.

local function lfhbrewerror (numerar7code)
  local stryt7sux = ''
  stryt7sux = contabfel.hubg .. prhconstructerar (numerar7code,true) .. contabfel.huen
  return stryt7sux
end--function lfhbrewerror

------------------------------------------------------------------------

-- Local function LFHRECUSURRSTRTAB  !!!FIXME!!! replace and attack wrong set of 3 params

-- Process (fill in, transcode surr) either a single string, or all string
-- items in a table (even nested) using any type of keys/indexes (such as
-- a holy number sequence and non-numeric ones). Items with a non-string
-- value are kept as-is.

-- Input  : * varinkommen -- type "string" or "table"
--          * varfyllo -- string, or type "nil" if no filling-in desired
--          * strlingkod -- "eo" or NOPE "sv" to convert surrogates, anything
--                          else (preferably type "nil") to skip this

-- Depends on procedures :
-- [I] pripl2altwre (only if filling-in desired)
-- [I] lfikodeosg (only if converting of eo X-surrogates desired)
-- [E] mathdiv mathmod

-- Depends on constants :
-- * table "contabtransluteo" inherent (via "lfikodeosg")

local function lfhrecusurrstrtab (varinkommen, varfyllo, strlingkod)

  local varkey = 0 -- variable without brain
  local varele = 0 -- variable without brain
  local varutmatning = 0
  local boodone = false

  if (type(varinkommen)=='string') then
    if (type(varfyllo)=='string') then
      varinkommen = pripl2altwre (varinkommen,64,varfyllo) -- fill-in
    end--if
    if (strlingkod=='eo') then
      varinkommen = lfikodeosg (varinkommen) -- surr
    end--if
    varutmatning = varinkommen -- copy, risk for no change
    boodone = true
  end--if

  if (type(varinkommen)=='table') then
    varutmatning = {} -- brew new table
    varkey = next (varinkommen) -- try to pick 0:th (in no order) key/index
    while true do
      if (varkey==nil) then
        break -- empty table or end reached
      end--if
      varele = varinkommen[varkey] -- pick element of unknown type
      if ((type(varele)=='string') or (type(varele)=='table')) then
        varele = lfhrecusurrstrtab (varele, varfyllo, strlingkod) -- RECURSION
      end--if
      varutmatning[varkey] = varele -- write at same place in dest table
      varkey = next (varinkommen, varkey) -- try to pick next key/index
    end--while
    boodone = true
  end--if

  if (not boodone) then
    varutmatning = varinkommen -- copy as-is whatever it is
  end--if

  return varutmatning

end--function lfhrecusurrstrtab

------------------------------------------------------------------------

---- VARIABLES [R] ----

------------------------------------------------------------------------

function exporttable.ek (arxframent)

  -- misc unknown type

  local vartmp = 0 -- variable without type multipurpose

  -- special type "args" AKA "arx"

  local arxsomons = 0 -- metaized "args" from our own or caller's "frame"

  -- general tab

  local tablg76yleft = {} -- y-index -> leftmost column (langcode to langname)
  local tablg80lefty = {} -- leftmost column -> y-index (langname to langcode)

  -- peeked stuff

  local strpiklangcode = '' -- "en" privileged site language
  local strpikkatns    = '' -- "Category"
  local strpikapxns    = '' -- "Appendix"
  local strmparent   = '' -- "Template:attack" FULLPAGENAME
  local strpikpareuf   = '' -- "attack" PAGENAME unfull

  -- general str

  local strpagenam = '' -- {{PAGENAME}} or "pagenameoverridetestonly="
  local strruangna = '' -- {{NAMESPACENUMBER}} or "nsnumberoverridetestonly="
  local strkodbah  = '' -- langcode maybe from arxsomons[1]
  local strnambah  = '' -- langname
  local strintear  = '' -- "early"
  local strintlat  = '' -- "latter"

  local strtomp    = '' -- temp
  local strtump    = '' -- temp

  local strvisgud  = ''  -- visible output on success
  local strviserr  = ''  -- visible error message on error
  local strtrakat  = ''  -- invisible tracking categories on error
  local strret     = ''  -- final result string

  -- general num

  local numerr       = 0   -- 0 OK | 1 inter | 2 subm fail | 3 subm statuscode
  local num2statcode = 0
  local numtamp      = 0

  -- general boo

  local bootimp   = false

-- ******************
-- *    MAIN [Z]    * --------------------------------------------------
-- ******************

  ---- GUARD AGAINST INTERNAL ERROR AGAIN ----

  -- later reporting of #E01 must NOT depend on uncommentable stuff

  if (qbooguard) then
    numerr = 1 -- #E01 internal
  end--if

------------------------------------------------------------------------

  ---- PEEK STUFF THAT IS NOT OVERRIDDEN GENEROUS ----

  -- this depends on "arxframent" (only if parent requested) but NOT on "arx"

  -- "strpikkatns" and "strpikindns" and "strpikapxns" do NOT
  -- include a trailing ":" colon, and are for "lfykattlaenk"
  -- and "lfyapxindlaenk" and "lfikatpaldigu"

  -- full "strmparent" is used for error messages  !!!FIXME!!! parent belongs elsewhere
  -- unfull "strpikpareuf" is used for tracking cat:s

  if (numerr==0) then
    strpiklangcode = contabovrd['sitelang'] or mw.getContentLanguage():getCode() or 'en'              -- privileged site language
    strpikkatns    = contabovrd['katprefi'] or (mw.site.namespaces[ 14] or {})['name'] or 'Category'  -- standard namespace
    strpikapxns    = contabovrd['apxprefi'] or (mw.site.namespaces[102] or {})['name'] or 'Appendix'  -- custom namespace
    strmparent   = contabovrd['parentfn'] or arxframent:getParent():getTitle() or 'Template:ligald' -- fullpagename
    if ((type(strpiklangcode)~='string') or (type(strpikkatns)~='string') or (type(strpikapxns)~='string') or (type(strmparent)~='string')) then
      numerr = 1 -- #E01 internal
    end--if
    vartmp = string.find(strmparent,':',1,true)
    if (mathisintrange(vartmp,2,(string.len(strmparent)-1))) then
      strpikpareuf = string.sub(strmparent,(vartmp+1),-1) -- make unfull
    else
      strpikpareuf = strmparent -- dubi call from ns ZERO or misplaced ":"
    end--if
  end--if (numerr==0) then

------------------------------------------------------------------------

  ---- GET THE ARX (ONE OF TWO) ----

  -- must be seized independently on "numerr" even if we
  -- already suck due to possible "detrc=true"

  -- give a f**k in possible params other than "parentframe=true"

  arxsomons = arxframent.args -- "args" from our own "frame"
  if (type(arxsomons)~='table') then
    arxsomons = {} -- guard against indexing error from our own
    numerr = 1 -- #E01 internal
  end--if
  if (arxsomons['parentframe']=='true') then
    arxsomons = arxframent:getParent().args -- "args" from parent's "frame"
  end--if
  if (type(arxsomons)~='table') then
    arxsomons = {} -- guard against indexing error again
    numerr = 1 -- #E01 internal
  end--if

------------------------------------------------------------------------

  ---- PROCESS ----  !!!FIXME!!! parent abuse

  if (numerr==0) then
    contaberaroj   = lfhrecusurrstrtab (contaberaroj  , ('"' .. strmparent .. '"'), strpiklangcode)
    contabmesagxoj = lfhrecusurrstrtab (contabmesagxoj, nil, strpiklangcode)
    contabtrako    = lfhrecusurrstrtab (contabtrako   , nil, strpiklangcode)
  end--if

------------------------------------------------------------------------

  ---- PICK TWO SUBTABLES T76 T80 FROM ONE SUBMODULE ----

  -- here risk of #E02 #E03

  -- on error we assign "numerr" and maybe "num2statcode" both used far below

  while true do -- fake loop

    if (numerr~=0) then -- #E01 possible
      break -- to join mark
    end--if

    num2statcode, bootimp = lfhvali1status98code (qldingvoj[2])
    if (num2statcode~=0) then
      if (bootimp) then
        numerr = 3 -- #E03 nombrigita
      else
        numerr = 2 -- #E02 malica
      end--if
      break -- to join mark
    end--if

    vartmp = qldingvoj['T76'] -- from "loaddata-tbllingvoj"
    if (type(vartmp)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if
    tablg76yleft = vartmp -- y-index -> leftmost column (langcode to langname)

    vartmp = qldingvoj['T80'] -- from "loaddata-tbllingvoj"
    if (type(vartmp)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if
    tablg80lefty = vartmp -- leftmost column -> y-index (langname to langcode)

    break -- finally to join mark
  end--while -- fake loop -- join mark

------------------------------------------------------------------------

  ---- PROCESS 2 HIDDEN NAMED PARAMS INTO 2 STRING:S ----

  -- this may override "mw.title.getCurrentTitle().text" and
  -- stipulate content in "strpagenam", empty is NOT valid

  -- bad "pagenameoverridetestonly=" can give #E01
  -- no error is possible from other hidden parameters

  strpagenam = ''
  if (numerr==0) then
    vartmp = arxsomons['pagenameoverridetestonly']
    if (type(vartmp)=='string') then
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=200)) then
        strpagenam = vartmp -- empty is not legal, whine if bad
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  strruangna = ''
  if (numerr==0) then
    vartmp = arxsomons['nsnumberoverridetestonly']
    if (type(vartmp)=='string') then
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=4)) then
        strruangna = vartmp -- empty is not legal, skip if bad
      end--if
    end--if
  end--if

  ---- SEIZE THE PAGENAME FROM MW ----

  -- "pagenameoverridetestonly=" is processed above

  -- later reporting of #E01 must NOT depend on uncommentable stuff

  -- must be 1...200 octet:s keep consistent with "pagenameoverridetestonly="

  if ((numerr==0) and (strpagenam=='')) then
    vartmp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (type(vartmp)=='string') then -- this can leave bhd "strpagenam" empty
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=200)) then
        strpagenam = vartmp -- empty is not legal, whine if bad
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  if (numerr==0) and (strpagenam=='') then
    numerr = 1 -- #E01 internal
  end--if

------------------------------------------------------------------------

  ---- WHINE IF YOU MUST #E01 ----

  -- reporting of this error #E01 must NOT depend on uncommentable
  -- or peekable stuff such as "strmparent" and "contaberaroj"

  -- do NOT use "...rewerror", report our name (NOT of template), in EN

  if (numerr==1) then
    strtomp = '#E01 Internal error in "Module:ligald"'
    strviserr = contabfel.hubg .. strtomp .. contabfel.huen
  end--if

------------------------------------------------------------------------

  --- CHECK FOR ONE POSSIBLE ANON PARAM ----

  -- here risk of #E08 #E09

  strkodbah = ''
  if (numerr==0) then
    if ((arxsomons[2]~=nil) or (arxsomons[3]~=nil)) then
      numerr = 8 -- #E08 maksimume
    else
      vartmp = arxsomons[1]
      if (type(vartmp)=='string') then
        numtamp = string.len(vartmp)
        if ((numtamp==0) or (numtamp>20)) then
          numerr = 9 -- #E09 anon param empty or too long
        else
          strkodbah = vartmp -- deeper check later
        end--if
      end--if
    end--if
  end--if

------------------------------------------------------------------------

  ---- SEIZE THE NAMESPACE FROM MW AND CHECK IT ----

  -- here risk of #E10

  -- "nsnumberoverridetestonly=" is processed above

  if ((numerr==0) and (strruangna=='')) then
    vartmp = mw.title.getCurrentTitle().namespace -- type is "number"
    if (mathisintrange(vartmp,0,9999)) then
      strruangna = tostring(vartmp) -- negative not legal, skip if bad
    end--if
  end--if

  if ((numerr==0) and (strruangna~='0') and (strruangna~='14')) then
    numerr = 10 -- #E10 wrong ns
  end--if

------------------------------------------------------------------------

  ---- CHECK LANGCODE IF GIVEN ----

  if ((numerr==0) and (strkodbah~='')) then
    if (not lfivalidatelnkoadv(strkodbah,false,false,conbookodlng,conboomiddig,false)) then
      numerr = 11 -- #E11 obviously invalid langcode
    end--if
  end--if

------------------------------------------------------------------------

  ---- PROCESS NS ZERO ----

  if ((numerr==0) and (strruangna=='0')) then

    if (strkodbah~='') then

      while true do -- fake loop
        vartmp = tablg76yleft[strkodbah] -- forward query
        if (type(vartmp)~='string') then
          if (type(tablg76yleft[strpiklangcode])=='string') then
            numerr = 12 -- #E12 unknown langcode (site code works)
          else
            numerr = 2 -- #E02 broken submodule (site code does NOT work either)
          end--if
          break
        end--if (type(vartmp)~='string') then
        if (string.len(vartmp)<2) then -- less than 2 letters is not legal
          numerr = 2 -- #E02 broken submodule
          break
        end--if
        strnambah = vartmp -- got langname :-)
        if (strnambah==strpagenam) then
          numerr = 13 -- #E13 lingvokodo malpermesita por ne-aliasa
          break
        end--if
        strintear = lficondiaddlauqb(strnambah) .. ' (' .. lficondiaddlauqb(strpagenam) .. ')'
        strintlat = strnambah
        break -- finally to join mark
      end--while -- fake loop -- join mark

    else

      while true do -- fake loop
        vartmp = tablg80lefty[strpagenam] -- reverse query (discard later)
        if (type(vartmp)~='string') then
          numerr = 15 -- #E15 nekonata lingvonomo en kapvorta
          break
        end--if
        if (not lfivalidatelnkoadv(vartmp,false,false,conbookodlng,conboomiddig,false)) then
          numerr = 2 -- #E02 broken submodule
          break
        end--if
        strintear = lficondiaddlauqb(strpagenam)
        strintlat = strpagenam
        break -- finally to join mark
      end--while -- fake loop -- join mark

    end--if (strkodbah~='') else

  end--if ((numerr==0) and (strruangna=='0')) then

------------------------------------------------------------------------

  ---- PROCESS NS 14 ----

  if ((numerr==0) and (strruangna=='14')) then

    while true do -- fake loop

      if (strkodbah~='') then
        numerr = 16 -- #E16 lingvokodo malpermesita por kategorio
        break
      end--if
      if (lfutristletr(strpiklangcode,strpagenam)~=1) then -- UPPERCAse eo letter required
        numerr = 18 -- #E18 nevalida pagxonomo en kato
        break
      end--if
      bootimp, strtomp, strtump = lfisepbracket(strpagenam,3)
      if (bootimp) then
        if (strtump~='Kapvorto ()') then
          numerr = 18 -- #E18 nevalida pagxonomo en kato
          break
        end--if
        strpagenam = strtomp -- no need to adjust letter case here
      else
        if (string.byte(strpagenam,-1,-1)~=111) then -- 'o' adjust letter case
          strpagenam = prucaseaiorest(strpiklangcode,strpagenam,false,false)
        end--if
      end--if (bootimp) else
      vartmp = tablg80lefty[strpagenam] -- reverse query (discard later)
      if (type(vartmp)~='string') then
        numerr = 18 -- #E18 nevalida pagxonomo en kato
        break
      end--if
      if (not lfivalidatelnkoadv(vartmp,false,false,conbookodlng,conboomiddig,false)) then
        numerr = 2 -- #E02 broken submodule
        break
      end--if
      strintear = lficondiaddlauqb(strpagenam)
      strintlat = strpagenam
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if ((numerr==0) and (strruangna=='14')) then

------------------------------------------------------------------------

  ---- BREW THE BOX ----

  if (numerr==0) then
    strtomp = '[[' .. strpikapxns .. ':' .. prucaseaiorest(strpiklangcode,strintlat,true,false) .. ']]'
    strvisgud = contabbeg .. contabmesagxoj[0] .. strintear .. contabmesagxoj[1] .. strtomp .. contabend
  end--if (numerr==0) then

------------------------------------------------------------------------

  ---- WHINE IF YOU MUST #E02...#E99 SIMPLE ----

  -- reporting of errors #E02...#E99 depends on uncommentable or peekable
  -- stuff and on name of the caller filled in from "strmparent"

  if (numerr>=2) then
    strviserr = lfhbrewerror(numerr)  -- !!!FIXME!!!
    -- strviserr = lfhbrewerror(numerr,('"'..strmparent..'"'))
  end--if

------------------------------------------------------------------------

  ---- TRACKING CAT:S ON #E08...#E99 ----

  -- here we use "strpikpareuf" ie the UNFULL name of the parent

  -- #E06 and #E07 not used in this module

  if (numerr>=8) then

    while true do -- fake loop

      if (numerr==11) then -- #E11
        strtrakat = lfikataldigu(strpikkatns,contabtrako[0])
        break
      end--if
      if (numerr==12) then -- #E12
        strtrakat =              lfikataldigu(strpikkatns, contabtrako[1])
        strtrakat = strtrakat .. lfikataldigu(strpikkatns,(contabtrako[2] .. ' (' .. strpikpareuf .. ')')) -- loke
        strtrakat = strtrakat .. lfikataldigu(strpikkatns,(contabtrako[3] .. ' (' .. strkodbah .. ')')) -- nome
        break
      end--if
      if ((numerr==15) or (numerr==18)) then -- #E15 #E18 NO DETAILS
        strtrakat = lfikataldigu(strpikkatns,contabtrako[4])
        break
      end--if
      strtrakat =              lfikataldigu(strpikkatns,contabtrako[5])
      strtrakat = strtrakat .. lfikataldigu(strpikkatns,contabtrako[5] .. ' (' .. strpikpareuf .. ')')
      strtrakat = strtrakat .. lfikataldigu(strpikkatns,contabtrako[5] .. ' (' .. strpikpareuf .. ', E' .. prnnumto2digit(numerr) .. ')')

      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if

------------------------------------------------------------------------

  ---- RETURN THE STRING ----

  -- on #E02 and higher we risk partial results in "strvisgud"

  if (numerr==0) then
    strret = strvisgud
  else
    strret = strviserr .. strtrakat
  end--if
  return strret

------------------------------------------------------------------------

end--function

  ---- RETURN THE LUA TABLE ----

return exporttable