Modulo:mfarado

El Vikivortaro
Salti al navigilo Salti al serĉilo
--[===[

MODULE "MFARADO" (kategorio vortfarado vortgrupigado)

"eo.wiktionary.org/wiki/Modulo:mfarado" <!--2021-Mar-07-->

Purpose: brews visible content (box) and 2 category includes
         from pagename

Utilo: kreas videblan enhavon (skatolon) kaj 2 kategorienmetojn
       el pagxonomo

Manfaat: membutat isi terlihat (kotak) dan 2 masukan kategori dari
         nama halaman

Syfte: skapar synligt innehaall (ruta) och 2 kategoriinlaeggningar
       fraan sidnamn

Used by templates / Uzata far sxablonoj / Digunakan oleh templat:
- kat-farado (EO)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
- "mpiktbllki" in turn requiring "mbllingvoj" (EO) or "mtblbahasa" (ID)
- "mtbllingvoj" in turn requiring template "tbllingvoj" (EO)
- "mtblbahasa" in turn requiring template "tblbahasa" (ID)

Incoming: - no ordinary parameters
          - 3 hidden named parameters
            - "pagenameoverridetestonly=" can directly cause #E01
            - "nsnumberoverridetestonly=" can indirectly cause #E03
            - "detrc=" no error possible

Returned: - one string

Hidden parameters have to be sent to this module's own frame. Frame of the
caller will not work. No ordinary parameters are needed since all relevant
information is peeked from the pagename. To be called from category
namespace only.

]===]

local farado = {}

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

---- CONSTANTS ----

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

  -- uncommentable EO vs ID constant strings (core site-related features)

  local constrpriv = "eo"                 -- EO (privileged site language)
  -- local constrpriv = "id"                 -- ID (privileged site language)
  local constrplki = "Modulo:mpiktbllki"  -- EO
  -- local constrplki = "Modul:mpiktbllki"   -- ID
  local constrkatp = "Kategorio:"         -- EO
  -- local constrkatp = "Kategori:"          -- ID
  local constrapxp = "Aldono:"            -- EO
  -- local constrapxp = "Lampiran:"          -- ID

  -- constant table (ban list)

  -- add only obviously invalid access codes (2-letter or 3-letter)

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

  -- "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/zxx" "No linguistic content"

  local contabisbanned = {}
  contabisbanned = {'dc', 'll', 'art','deu','eng','epo','fra','lat','por','rus','spa','swe','tup','zxx'} -- 1...14

  -- constant table (surrogate transcoding table, only needed for EO)

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

  -- constant strings (error circumfixes)

  local constrelabg = '<span class="error"><b>'  -- lagom whining begin
  local constrelaen = '</b></span>'              -- lagom whining end
  local constrlaxhu = '&nbsp;&#91;&#93;&nbsp;'   -- lagom -> huge circumfix " [] "

  -- uncommentable EO vs ID string (caller name for error messages)

  local constrkoll = 'sxablono "kat-farado"'  -- EO augmented name of the caller (semi-hardcoded, we do NOT peek it)
  -- local constrkoll = 'templat "kat-farado"'   -- ID augmented name of the caller (semi-hardcoded, we do NOT peek it)

  -- uncommentable EO vs ID constant table (error messages)

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

  local contaberaroj = {}
  contaberaroj[2] = 'Neniuj parametroj estas transprenataj far \\@, legu gxian dokumentajxon'  -- EO #E02
  -- contaberaroj[2] = 'Tidak ada parameter yang dimakan oleh \\@, bacalah dokumentasinya'        -- ID #E02
  contaberaroj[3] = 'Erara nomspaco (estu 14, "Kategorio:") por \\@'                           -- EO #E03
  -- contaberaroj[3] = 'Ruang nama salah (sebaiknya 14, "Kategori:") untuk \\@'                   -- ID #E03
  contaberaroj[4] = 'Evidente nevalida lingvokodo en \\@'                                -- EO #E04
  -- contaberaroj[4] = 'Kode bahasa jelas-jelas salah dalam \\@'                            -- ID #E04
  contaberaroj[5] = 'Erara pagxonomo por \\@'                                            -- EO #E05
  -- contaberaroj[5] = 'Nama halaman salah untuk \\@'                                       -- ID #E05

  -- uncommentable EO vs ID constant tables (boasting)

  -- "numowntyp" -- 0 invalid | 1 vorto 2 frazo 3 vortgrupo

  local contabboasting = {}
  contabboasting[1] = "En cxi tiu kasxita kategorio trovigxas vortoj enhavantaj la morfemon" -- follows morpheme type by letter and name
  contabboasting[2] = "En cxi tiu kasxita kategorio trovigxas frazoj enhavantaj la vorton"
  contabboasting[3] = "En cxi tiu kasxita kategorio trovigxas vortgrupoj enhavantaj la vorton"

  -- uncommentable EO vs ID constant tables (mortyp:s and scripts)

  -- "nummortyp" -- 0 invalid | 67 C 73 I 77 M 78 N 80 P 85 U

  local contabmosc = {}
  contabmosc["C"]   = "cirkumfikson"    -- EO
  contabmosc["I"]   = "infikson"        -- EO
  contabmosc["M"]   = "memstaran"       -- EO
  contabmosc["N"]   = "nememstaran"     -- EO
  contabmosc["P"]   = "prefikson"       -- EO
  contabmosc["U"]   = "sufikson"        -- EO
  contabmosc["zhT"] = "tradicia"        -- EO
  contabmosc["zhS"] = "simpligita"      -- EO
  contabmosc["zhP"] = "pinjina"         -- EO
  contabmosc["jaK"] = "kangxia"         -- EO
  contabmosc["jaH"] = "hiragana"        -- EO
  contabmosc["jaR"] = "romanigita"      -- EO
  contabmosc["hrC"] = "cirila"          -- EO
  contabmosc["hrL"] = "latina"          -- EO

  -- constant table (HTML)

  local contabhtml = {}
  contabhtml[0] = '<table style="border:1px solid #A0A0A0; background:#F0F0F0; width:95%; margin:0.6em auto 0.6em auto; padding:0.6em; text-align:justify;">'
  contabhtml[1] = '<tr><td style="width:30px;">[[Image:Hidden icon.png|50px]]</td><td style="padding-left:1em;">'
  contabhtml[2] = '</td></tr></table>'

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

  local conboomiddig = false -- controls lng code checking, assign to "true" to allow middle digit "s7a"

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

---- SPECIAL STUFF OUTSIDE MAIN FUNCTION ----

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

---- VAR:S ----

local qpiktbllki = {}    -- type "table" with type "function" inside
local qstrtrace = ''     -- for main & sub:s, debug report request by "detrc="
local qbooguard = false  -- only for the guard test, pass to other var ASAP

---- GUARD AGAINST INTERNAL ERROR & ONE IMPORT VIA REQUIRE ----

if ((type(constrpriv)~="string") or (type(constrplki)~="string") or (type(constrkatp)~="string") or (type(constrkoll)~="string")) then
  qbooguard = true
else
  qpiktbllki = require(constrplki) -- can crash here despite guarding ??
  if (type(qpiktbllki)~="table") then
    qbooguard = true
  end--if
end--if

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

---- ORDINARY LOCAL DEBUG FUNCTIONS ----

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

-- Local function LFTRACEMSG

-- for variables the other sub "lfshowvar" is preferable but in exceptional
-- cases it can be justified to send text containing variables to this sub

-- enhances global "qstrtrace" (may NOT be type "nil")

local function lftracemsg (strbigcrap)
  qstrtrace = qstrtrace .. "<br>" .. strbigcrap .. '.'
end--function lftracemsg

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

-- Local function LFSHOWVAR

-- Show content of a variable or list content of a table with
-- integer indexes starting from ZERO.

-- "vardescri" (def empty) and "vartablim" (def 0) are optional

-- enhances global "qstrtrace" (may NOT be type "nil")

local function lfshowvar (vardubious, strname, vardescri, vartablim)

  local strtype = ''
  local strreport = ''
  local numindax = 0
  local numlencx = 0
  local numsigno = 0

  if (type(vardescri)~="string") then
    vardescri = ''
  else
    vardescri = ' (' .. vardescri .. ')'
  end--if
  if (type(vartablim)~="number") then
    vartablim = 0
  end--if
  strname = '"' .. strname .. '"' .. vardescri -- combo

  strtype = type(vardubious)
  if ((strtype=="table") and (vartablim~=0)) then
    strreport = 'Table ' .. strname .. ' :'
    numindax = 0
    while (true) do
      if (numindax>vartablim) then
        break -- done table
      end--if
      strreport = strreport .. ' ' .. tostring (numindax) .. ' -> "' .. tostring (vardubious[numindax]) .. '"'
      numindax = numindax + 1
    end--while
  else
    strreport = 'Variable ' .. strname .. ' has type "' .. strtype .. '"'
    if (strtype=="string") then
      numlencx = string.len (vardubious)
      strreport = strreport .. ' and length ' .. tostring (numlencx)
      if (numlencx~=0) then
        strreport = strreport .. ' and content "'
        numindax = 1
        while (true) do
          if (numindax>numlencx) then
            break -- done string char after char
          end--if
          numsigno = string.byte (vardubious,numindax,numindax)
          if ((numsigno<36) or (numsigno>122) or (numsigno==91) or (numsigno==93) or (numsigno==42) or (numsigno==58)) then
            strreport = strreport .. '{' .. tostring (numsigno) .. '}'
          else
            strreport = strreport .. string.char (numsigno)
          end--if
          if ((numlencx>50) and (numindax==20)) then
            numindax = numlencx - 20 -- jump
            strreport = strreport .. '" ... "'
          else
            numindax = numindax + 1
          end--if
        end--while
        strreport = strreport .. '"' -- don't forget final quot
      end--if (numlencx~=0) then
    end--if
    if ((strtype~="string") and (strtype~="nil")) then
      strreport = strreport .. ' and content "' .. tostring (vardubious) .. '"'
    end--if
  end--if
  qstrtrace = qstrtrace .. "<br>" .. strreport .. '.'

end--function lfshowvar

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

---- ORDINARY LOCAL MATH FUNCTIONS ----

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

local function mathdiv (xdividend, xdivisor)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividend / xdivisor)
  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

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

---- ORDINARY LOCAL STRING FUNCTIONS ----

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

-- test whether char is an ASCII digit "0"..."9", return boolean

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

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

-- test whether char is an ASCII uppercase letter, return boolean

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

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

-- test whether char is an ASCII lowercase letter, return boolean

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

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

-- Local function LFCMPSUB

-- Check whether a given substring can be found at a given
-- position in a longer string.

-- "numpossta" is ZERO-based

local function lfcmpsub (strbig, strtiny, numpossta)
  local numlenbig = 0
  local numlentiny = 0
  local boofound = false
  numlenbig = string.len (strbig)
  numlentiny = string.len (strtiny)
  if ((numlentiny>0) and ((numpossta+numlentiny)<=numlenbig)) then
    strbig = string.sub (strbig,(numpossta+1),(numpossta+numlentiny))
    boofound = (strbig==strtiny)
  end--if
  return boofound
end--function lfcmpsub

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

---- ORDINARY LOCAL CONVERSION FUNCTIONS ----

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

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"mathmod".

local function lfnumto2digit (numzerotoninetynine)
  local strtwodig = ''
  strtwodig = mathdiv(numzerotoninetynine,10) .. mathmod(numzerotoninetynine,10)
  return strtwodig
end--function lfnumto2digit

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

---- ORDINARY LOCAL UTF8 FUNCTIONS ----

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

-- Local function LFUTF8LENGTH

-- Measure length of a single UTF8 char, return ZERO if invalid.

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

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

-- Output : * numlen1234x (1...4 or ZERO if invalid)

local function lfutf8length (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 lfutf8length

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

-- Local function LFCASEREST

-- Adjust case of a single letter (restricted), only ASCII
-- and 6 extra EO letters.

-- Input  : * strucinuut : single unicode letter (1 or 2 octet:s)
--          * booupcas   : for desired uppercase "true" and for
--                         lowercase "false"

-- Output : * strucinuut : (same var, "ZZ" on failure)

-- This will not give useful results for any non-ASCII char:s other
-- than uppercase and lowercase EO letters (CX GX HX JX SX UX
-- cx gx hx jx sx ux). Invalid non-ASCII input generates "ZZ".

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "STRING FUNCTIONS"\"lftestuc" and "STRING FUNCTIONS"\"lftestlc" and
-- "UTF8 FUNCTIONS"\"lfutf8length".

local function lfcaserest (strucinuut, booupcas)

  local numlaengdn = 0 -- length from "string.len"
  local numchaerr = 0 -- UINT8 beginning char
  local numchaess = 0 -- UINT8 later char (BIG ENDIAN, lower value here)
  local numchareel = 0 -- UINT8 code relative to beginning of block $00...$FF
  local numdeta = 0 -- SINT8 +32 or -32 or +1 or -1 or ZERO
  local numtheemp = 0

  local boowantlowr = false
  local booiisuppr = false
  local booiislowr = false
  local boovalid = true

  numlaengdn = string.len (strucinuut)
  boowantlowr = (not booupcas)
  boovalid = ((numlaengdn==1) or (numlaengdn==2))
  if (boovalid) then
    numchaerr = string.byte (strucinuut,1,1)
    boovalid = ((lfutf8length(numchaerr))==numlaengdn) -- mismatch with length
  end--if

  if (boovalid and (numlaengdn==1)) then
    booiisuppr = lftestuc(numchaerr)
    booiislowr = lftestlc(numchaerr)
    if (booiisuppr and boowantlowr) then
      numdeta = 32 -- ASCII UPPER->lower
    end--if
    if (booiislowr and booupcas) then
      numdeta = -32 -- ASCII lower->UPPER
    end--if
  end--if

  if (boovalid and (numlaengdn==2)) then
    boovalid = ((numchaerr==196) or (numchaerr==197))
  end--if
  if (boovalid and (numlaengdn==2)) then
    numchaess = string.byte (strucinuut,2,2)
    numchareel = (numchaerr-196)*64 + (numchaess-128) -- begins at $C4
    numtheemp = numchareel -- temp var to protect "numchareel" from trashing
    numtheemp = numtheemp - (mathmod(numtheemp,2)) -- very bad way to do AND
    boovalid = ((numtheemp==8) or (numtheemp==28) or (numtheemp==36) or (numtheemp==52) or (numtheemp==92) or (numtheemp==108))
  end--if
  if (boovalid and (numlaengdn==2)) then
    booiislowr = ((mathmod(numchareel,2))==1) -- UC even (ordinary)
    booiisuppr = not booiislowr
    if (booiisuppr and boowantlowr) then
      numdeta = 1 -- UPPER->lower
    end--if
    if (booiislowr and booupcas) then
      numdeta = -1 -- lower->UPPER
    end--if
  end--if

  if (boovalid) then
    if ((numlaengdn==1) and (numdeta~=0)) then
      strucinuut = string.char (numchaerr + numdeta) -- no risk of carry here
    end--if
    if ((numlaengdn==2) and (numdeta~=0)) then
      strucinuut = string.char (numchaerr) .. string.char (numchaess + numdeta)
    end--if
  else
    strucinuut = "ZZ" -- illegal non-ASCII char
  end--if

  return strucinuut -- same var for input and output !!!

end--function lfcaserest

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

-- Local function LFXCASEULT

-- Adjust letter case of beginning letter or all letters in a word or group of
-- words to upper or lower, limited unicode support (restricted LFCASEREST).

-- See LFFIXCASE for a simple ASCII version.

-- Input  : * strenigo : word or group of words (may be empty)
--          * booupcas : "true" for uppercase and "false" for lowercase
--          * boodoall : "true" to adjust all letters, "false" only beginning

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "STRING FUNCTIONS"\"lftestuc" and "STRING FUNCTIONS"\"lftestlc" and
-- "UTF8 FUNCTIONS"\"lfutf8length" and "UTF8 FUNCTIONS"\"lfcaserest"
-- (restricted LFCASEREST).

local function lfxcaseult (strenigo, booupcas, boodoall)

  local numlein = 0
  local numposi = 1 -- octet position ONE-based
  local numcut = 0 -- length of an UTF8 char
  local bootryadj = false -- try to adjust single char
  local strte7mp = ""
  local strelygo = ""

  numlein = string.len (strenigo)
  while (true) do
    if (numposi>numlein) then
      break -- done
    end--if
    bootryadj = (boodoall or (numposi==1))
    numcut = lfutf8length(string.byte(strenigo,numposi,numposi))
    if ((numcut==0) or ((numposi+numcut-1)>numlein)) then
      numcut = 1 -- skip ie copy one faulty octet
      bootryadj = false
    end--if
    strte7mp = string.sub (strenigo,numposi,(numposi+numcut-1)) -- 1...4 oct
    if (bootryadj) then
      strte7mp = lfcaserest(strte7mp,booupcas) -- (restricted LFCASEREST)
    end--if
    strelygo = strelygo .. strte7mp -- this can be slow
    numposi = numposi + numcut
  end--while
  return strelygo

end--function lfxcaseult

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

---- ORDINARY LOCAL HIGH LEVEL FUNCTIONS ----

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

-- Local function LFBREWERROR

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

-- We need 3 const strings "constrelabg", "constrelaen",
-- "constrlaxhu" and const table "contaberaroj".

-- This sub depends on "CONVERSION FUNCTIONS"\"lfnumto2digit".

local function lfbrewerror (numerrorcode)
  local vardeskrip = 0
  local strytsux = '#E'
  vardeskrip = contaberaroj[numerrorcode] -- risk of type "nil"
  if (type(vardeskrip)=="string") then
    strytsux = strytsux .. lfnumto2digit(numerrorcode) .. ' ' .. vardeskrip
  else
    strytsux = strytsux .. '??'
  end--if
  strytsux = constrlaxhu .. constrelabg .. strytsux .. constrelaen .. constrlaxhu
  return strytsux
end--function lfbrewerror

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

-- Local function LFSEIZETEXT

-- seize text from "contabmosc", return "??" if index (string) unknown

local function lfseizetext (straccesskey)
  local vartext = 0
  local strgoodtext = ''
  vartext = contabmosc[straccesskey]
  if (type(vartext)=="string") then
    strgoodtext = vartext
  else
    strgoodtext = "??"
  end--if
  return strgoodtext
end--function lfseizetext

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

-- Local function LFCHKKODINV

-- Check whether a string (intended to be a language code) contains only 2
-- or 3 lowercase letters 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 can't cause any harm)
--          * numnokod -- "0" ... "3" how special codes "-" "??" should pass
--          * boodigit -- "true" to allow digit in middle position
--          * boonoban -- "true" to skip test against ban table

-- Output : * booisbadlk -- bool "true" if the string is evil

-- This sub depends on "STRING FUNCTIONS"\"lftestnum"
-- and "STRING FUNCTIONS"\"lftestlc".

-- We need const table "contabisbanned".

local function lfchkkodinv (strqooq, numnokod, boodigit, boonoban)
  local varomongkosong = "" -- for check against the ban list
  local booisbadlk = false -- pre-assume good
  local numchiiar = 0
  local numukurran = 0
  local numindeex = 0 -- ZERO-based
  while (true) do -- fake (outer) loop
    if ((strqooq=="-") and ((numnokod==1) or (numnokod==3))) then
      break -- to join mark -- good
    end--if
    if ((strqooq=="??") and ((numnokod==2) or (numnokod==3))) then
      break -- to join mark -- good
    end--if
    numukurran = string.len (strqooq)
    if ((numukurran<2) or (numukurran>3)) then
      booisbadlk = true
      break -- to join mark -- evil
    end--if
    numchiiar = string.byte (strqooq,1,1)
    if (lftestlc(numchiiar)==false) then
      booisbadlk = true
      break -- to join mark -- evil
    end--if
    numchiiar = string.byte (strqooq,numukurran,numukurran)
    if (lftestlc(numchiiar)==false) then
      booisbadlk = true
      break -- to join mark -- evil
    end--if
    if (numukurran==3) then
      numchiiar = string.byte (strqooq,2,2)
      if ((boodigit==false) or (lftestnum(numchiiar)==false)) then
        if (lftestlc(numchiiar)==false) then
          booisbadlk = true
          break -- to join mark -- evil
        end--if
      end--if ((boodigit==false) or (lftestnum(numchiiar)==false))
    end--if
    if (boonoban==false) then
      while (true) do -- ordinary inner loop
        varomongkosong = contabisbanned[numindeex+1] -- number of elem unknown
        if (type(varomongkosong)~="string") then
          break -- abort inner loop (then fake outer loop) due to end of table
        end--if
        numukurran = string.len (varomongkosong)
        if ((numukurran<2) or (numukurran>3)) then
          break -- abort inner loop (then fake outer loop) due to faulty table
        end--if
        if (strqooq==varomongkosong) then
          booisbadlk = true
          break -- abort inner loop (then fake outer loop) due to violation
        end--if
        numindeex = numindeex + 1 -- ZERO-based
      end--while -- ordinary inner loop
    end--if (boonoban==false) then
    break -- finally to join mark
  end--while -- fake loop -- join mark
  return booisbadlk
end--function lfchkkodinv

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

-- Local function LFFILLNAME

-- Replace placeholder "\@" "\\@" by augmented name of the caller.

-- The caller name is submitted to us as a parameter thus we
-- do NOT access any constants and do NOT have to peek it either.

local function lffillname (strmessage,strcaller)

  local strhasill = ''
  local numstrloen = 0
  local numindfx = 1 -- ONE-based
  local numcjar = 0
  local numcjnext = 0

  numstrloen = string.len (strmessage)

  while (true) do
    if (numindfx>numstrloen) then
      break -- empty input is useless but cannot cause major harm
    end--if
    numcjar = string.byte (strmessage,numindfx,numindfx)
    numindfx = numindfx + 1
    numcjnext = 0
    if (numindfx<=numstrloen) then
      numcjnext = string.byte (strmessage,numindfx,numindfx)
    end--if
    if ((numcjar==92) and (numcjnext==64)) then
      strhasill = strhasill .. strcaller -- invalid input is caller's risk
      numindfx = numindfx + 1 -- skip 2 octet:s of the placeholder
    else
      strhasill = strhasill .. string.char (numcjar)
    end--if
  end--while

  return strhasill

end--function lffillname

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

-- Local function LFKODEOSG

-- Transcode X-surrogates (without "\", thus for example "kacxo",
-- NOT "ka\cxo") to UTF-8 cxapeloj in a string (EO only).

-- Input  : * strsurr -- string (empty is useless but can't cause major harm)

-- Output : * strcxapeloj

-- We need const table "contabtransiltable".

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"mathmod".

local function lfkodeosg (strsurr)
  local varpeek = 0
  local strcxapeloj = ''
  local numinputl = 0
  local numininx = 0 -- ZERO-based source index
  local numknark = 0 -- current char (ZERO is NOT valid)
  local numknarp = 0 -- previous char (ZERO is NOT valid)
  local numlow = 0
  local numhaj = 0
  numinputl = string.len(strsurr)
  while (true) do
    if (numininx==numinputl) then
      break
    end--if
    numknark = string.byte(strsurr,(numininx+1),(numininx+1))
    numininx = numininx + 1
    numhaj = 0 -- pre-assume no translation
    if ((numknarp~=0) and ((numknark==88) or (numknark==120))) then -- got "x"
      varpeek = contabtransiltable[numknarp] -- UINT16 or nil
      if (varpeek~=nil) then
        numlow = mathmod (varpeek,256)
        numhaj = mathdiv (varpeek,256)
      end--if
    end--if
    if (numhaj~=0) then
      strcxapeloj = strcxapeloj .. string.char(numhaj,numlow)
      numknark = 0 -- invalidade current char
    else
      if (numknarp~=0) then -- add previous char only if valid
        strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
      end--if
    end--if
    numknarp = numknark -- copy to previous even if invalid
  end--while
  if (numknarp~=0) then -- add previous and last char only if valid
    strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
  end--if
  return strcxapeloj
end--function lfkodeosg

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

---- MAIN EXPORTED FUNCTION ----

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

function farado.ek (arxframent)

  -- general unknown type

  local vartmp = 0     -- variable without type multipurpose

  -- special type "args" AKA "arx"

  local arxourown = 0  -- metaized "args" from our own "frame"

  -- general str read from pagename

  local strpagenam  = "" -- "{{PAGENAME}}" o "pagenameoverridetestonly"
  local strruangna  = "" -- "{{NAMESPACENUMBER}}" o "nsnumberoverridetestonly"
  local strkodbah   = ""

  -- general str

  local strafixnam  = "" -- 1 of 6 values from "nummortyp"
  local strnambah   = "" -- language name (without prefix "Bahasa")
  local strmorfio   = "" -- with quotes and maybe link
  local straldono   = "" -- link or not and cannot be empty
  local strlingvoo  = "" -- big speech and cannot be empty
  local strskripto  = "" -- big speech but can also be empty
  local strbigsnack = ""

  local strtmp      = "" -- temp

  local strviserr   = "" -- visible error
  local strvisgud   = "" -- visible good output
  local strinvkat   = "" -- invisible category part
  local strret      = "" -- final result string

  -- general num read from pagename

  local numowntyp = 0 -- 0 invalid | 1 vorto 2 frazo 3 vortgrupo
  local nummortyp = 0 -- 0 invalid | 67 C 73 I 77 M 78 N 80 P 85 U
  local numskript = 0 -- script code, all uppercase ASCII accepted

  -- general num

  local numerr    = 0 -- 1 inter 2 param 3 ns 4 evidente 5 pagename
  local numlong   = 0 -- length of pagename
  local numposnam = 0 -- pos in pagename
  local numoct    = 0
  local numodt    = 0
  local numlenko  = 0 -- 2 or 3 only
  local numtamp   = 0
  local numtump   = 0

  -- general boo

  local bootrace  = false  -- from "detrc=true"
  local booknown  = false

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

  -- later reporting of #E01 may NOT depend on uncommentable strings

  qstrtrace = '<br>This is "mfarado", requested "detrc" report.'

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

  ---- FILL IN ERROR MESSAGES AND TRANSCODE EO IF NEEDED ----

  -- placeholder "\@" "\\@" is replaced by augmented name of the caller
  -- from "constrkoll" in any case, for example 'sxablono "test"'
  -- or 'templat "test"'

  -- only for EO the X-substitution is subsequently performed

  if (numerr==0) then
    numtamp = 2 -- start with #E02
    numtump = 0 -- tolerance for holes
    while (true) do
      vartmp = contaberaroj[numtamp] -- risk of type "nil"
      if (type(vartmp)=="string") then -- number of messages is NOT hardcoded
        numtump = 0
        strtmp = lffillname (vartmp,constrkoll)
        if (constrpriv=="eo") then
          strtmp = lfkodeosg (strtmp)
        end--if
        contaberaroj[numtamp] = strtmp
      else
        numtump = numtump + 1
      end--if
      if (numtump==4) then -- max 3 consecutive holes
        break
      end--if
      numtamp = numtamp + 1 -- TWO-based
    end--while
    contabboasting[1] = lfkodeosg (contabboasting[1])
    contabboasting[2] = lfkodeosg (contabboasting[2])
    contabboasting[3] = lfkodeosg (contabboasting[3])
    contabmosc["jaK"] = lfkodeosg (contabmosc["jaK"])
  end--if

  ---- GET THE ARX ----

  arxourown = arxframent.args -- "args" from our own "frame"
  if (type(arxourown)~="table") then
    arxourown = {} -- guard against indexing error before any indexing
    numerr = 1 -- #E01 internal
  end--if

  ---- PROCESS 3 HIDDEN NAMED PARAMS ----

  -- 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

  -- "detrc=" must be seized independently on "numerr"
  -- even if we already suck, but type "table" must be ensured !!!

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

  strruangna = ""
  if (numerr==0) then
    vartmp = arxourown['nsnumberoverridetestonly']
    if (type(vartmp)=="string") then
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=4)) then
        strruangna = vartmp -- empty is not legal but ignore crime
      end--if
    end--if
  end--if

  if (arxourown['detrc']=='true') then
    bootrace = true
  end--if

  if (bootrace) then
    lfshowvar (numerr,'numerr','done with hidden parameters') -- "qstrtrace"
  end--if

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

  -- later reporting of #E01 may NOT depend on uncommentable strings

  -- must be 1...120 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 bhnd "strpagenam" empty
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartmp -- pagename here (empty is NOT legal)
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

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

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

  -- reporting of this error #E01 may NOT depend on
  -- uncommentable strings as "constrkoll" and "contaberaroj"

  -- do NOT use sub "lfbrewerror", report our name (NOT of template) and in EN

  if (numerr==1) then
    strtpm = '#E01 Internal error in module "mfarado".'
    strviserr = constrlaxhu .. constrelabg .. strtpm .. constrelaen .. constrlaxhu
  end--if

  ---- DISALLOW PARAMS ----

  if (arxourown[1]~=nil) then
    numerr = 2 -- #E02 param
  end--if

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

  if ((numerr==0) and (strruangna=='')) then
    vartmp = mw.title.getCurrentTitle().namespace -- this is a number
    if (type(vartmp)=="number") then -- this can leave bhnd "strruangna" empty
      if ((vartmp>=0) and (vartmp<=9999)) then
        strruangna = tostring(vartmp) -- empty is not legal but ignore crime
      end--if
    end--if
  end--if

  if (numerr==0) then
    if (strruangna~="14") then
      numerr = 3 -- #E03 ns
    end--if
  end--if

  if (bootrace) then
    lfshowvar (strruangna,'strruangna','namespace seized') -- "qstrtrace"
  end--if

  ---- DISSECT THE PAGENAME ----

  -- There are 3 possible types:
  -- * "Vorto -en- enhavanta morfemon U (-ist)" 1
  -- * "Frazo -en- enhavanta vorton (nope)" 2
  -- * "Vortgrupo -en- enhavanta (nope)" 3
  -- There can be 2 more char:s namely space and script code.

  nummortyp = 0
  numskript = 0
  while (true) do -- fake loop
    if (numerr~=0) then
      break
    end--if
    numlong = string.len (strpagenam)
    numowntyp = 0 -- 0 invalid | 1 vorto 2 frazo 3 vortgrupo
    if (lfcmpsub(strpagenam,"Vorto -",0)) then
      numowntyp = 1
      numposnam = 7
    end--if
    if (lfcmpsub(strpagenam,"Frazo -",0)) then
      numowntyp = 2
      numposnam = 7
    end--if
    if (lfcmpsub(strpagenam,"Vortgrupo -",0)) then
      numowntyp = 3
      numposnam = 11
    end--if
    if (numowntyp==0) then
      numerr = 5 -- #E05 not found
      break
    end--if
    if (bootrace) then
      lfshowvar (numowntyp,'numowntyp','detected "owntyp"') -- "qstrtrace"
    end--if
    if ((numposnam+4)>numlong) then -- "numposnam" is ZERO-based
      numerr = 5 -- #E05 truncated
      break
    end--if
    numlenko = 0 -- 0 invalid
    numoct = string.byte (strpagenam,(numposnam+3),(numposnam+3)) -- +2 ZERO-b
    numodt = string.byte (strpagenam,(numposnam+4),(numposnam+4)) -- +3 ZERO-b
    if (bootrace) then
      lftracemsg ('Seized 2 border codes ' .. tostring(numoct) .. ' and ' .. tostring(numodt)) -- "qstrtrace"
    end--if
    if ((numoct==45) and (numodt~=45)) then
      numlenko = 2
    end--if
    if ((numoct~=45) and (numodt==45)) then
      numlenko = 3
    end--if
    if (numlenko==0) then
      numerr = 5 -- #E05 not found
      break
    end--if
    strkodbah = string.sub (strpagenam,(numposnam+1),(numposnam+numlenko))
    numposnam = numposnam + numlenko + 1 -- jump over code and "-"
    if (bootrace) then
      lftracemsg ('Extracted lng code, it is "' .. strkodbah .. '"') -- "qstrtrace"
    end--if
    if (lfchkkodinv(strkodbah,0,conboomiddig,false)) then
      numerr = 4 -- #E04 bad code
      break
    end--if
    numtamp = 0
    if (numowntyp==1) then
      if (lfcmpsub(strpagenam," enhavanta morfemon ",numposnam)) then
        numtamp = 20
      end--if
    end--if
    if (numowntyp==2) then
      if (lfcmpsub(strpagenam," enhavanta vorton ",numposnam)) then
        numtamp = 18
      end--if
    end--if
    if (numowntyp==3) then
      if (lfcmpsub(strpagenam," enhavanta ",numposnam)) then
        numtamp = 11
      end--if
    end--if
    if (numtamp==0) then
      numerr = 5 -- #E05 not found
      break
    end--if
    numposnam = numposnam + numtamp
    if (numowntyp==1) then
      if ((numposnam+2)>numlong) then -- "numposnam" is ZERO-based
        numerr = 5 -- #E05 truncated
        break
      end--if
      nummortyp = string.byte (strpagenam,(numposnam+1),(numposnam+1))
      numodt = string.byte (strpagenam,(numposnam+2),(numposnam+2))
      if ((nummortyp~=67) and (nummortyp~=73) and (nummortyp~=77) and (nummortyp~=78) and (nummortyp~=80) and (nummortyp~=85)) then
        numerr = 5 -- #E05 invalid code
        break
      end--if
      if (numodt~=32) then
        numerr = 5 -- #E05 no space char
        break
      end--if
      numposnam = numposnam + 2 -- jump over uppercase code and space
      if (bootrace) then
        lftracemsg ('Seized "mortyp", it is ' .. tostring(nummortyp)) -- "qstrtrace"
      end--if
    end--if
    if ((numposnam+3)>numlong) then -- "numposnam" is ZERO-based
      numerr = 5 -- #E05 truncated
      break
    end--if
    numoct = string.byte (strpagenam,(numlong-1),(numlong-1))
    numodt = string.byte (strpagenam, numlong,    numlong   )
    if ((numoct==32) and lftestuc(numodt)) then
      numskript = numodt
      numlong = numlong - 2
      if (bootrace) then
        lftracemsg ('Seized script code, it is ' .. tostring(numskript)) -- "qstrtrace"
      end--if
    end--if
    numoct = string.byte (strpagenam,(numposnam+1),(numposnam+1))
    numodt = string.byte (strpagenam,numlong,numlong)
    if ((numoct~=40) or (numodt~=41)) then
      numerr = 5 -- #E05 need "(" and ")"
      break
    end--if
    strpagenam = string.sub (strpagenam,(numposnam+2),(numlong-1)) -- reduce
    if (bootrace) then
      lfshowvar (strpagenam,'strpagenam','extracted lemma') -- "qstrtrace"
    end--if
    break
  end--while -- fake loop

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

  -- reporting of errors #E02...#E99 depends on uncommentable strings
  -- and name of the caller filled in from "constrkoll"

  if (numerr>1) then
    strviserr = lfbrewerror(numerr)
  end--if

  ---- PEEK 2 STRIGNS AND BUILD ONE LONG NONSENSE TEXT ----

  -- "numowntyp" -- 0 invalid | 1 vorto 2 frazo 3 vortgrupo
  -- "nummortyp" -- 0 invalid | 67 C 73 I 77 M 78 N 80 P 85 U

  -- text in "contabboasting" ends with a word

  -- after this block the text in "strbigsnack" will end with either
  -- a word or a closing arc bracket ")"

  if (numerr==0) then
    strbigsnack = contabboasting [numowntyp]
    if (numowntyp==1) then
      strtmp = string.char (nummortyp)
      strafixnam = lfseizetext (strtmp)
      strbigsnack = strbigsnack .. ' ' .. strtmp .. ' (' .. strafixnam .. ')'
    end--if
  end--if

  ---- PEEK LNG NAME VIA SUBMODULE ----

  if (numerr==0) then
    strnambah = qpiktbllki.ek { args = { strkodbah , 0 } }
    booknown = (strnambah~='-') and (strnambah~='=') -- "false" is criminal
  end--if

  ---- PREBREW LINK TO APPENDIX PAGE AND STUFF AND SCRIPT DETAILS ----

  -- "strpagenam" has been reduced to the raw lemma but dashes can persist

  -- string "constrapxp" is appendix prefix and includes the colon ":"

  if (numerr==0) then
    if (nummortyp==78) then
      strmorfio = '"' .. strpagenam .. '"' -- do NOT link nonstandalone roots
    else
      strmorfio = '"[[' .. strpagenam .. ']]"'
    end--if
    if (booknown) then
      straldono = "la lingvo [[" .. constrapxp .. lfxcaseult(strnambah,true,false) .. "|" .. strnambah .. "]]"
    else
      straldono = "nekonata lingvo"
    end--if
    strlingvoo = ' en (lingvokodo -' .. strkodbah .. '-) '.. straldono
    if (booknown and (numskript~=0)) then
      strtmp = strkodbah .. string.char (numskript) -- gives fe "jaH"
      strskripto = ' per la skripto ' .. string.char (numskript) .. ' (' .. lfseizetext (strtmp) .. ')'
    else
      strskripto = ''
    end--if
  end--if

  ---- BREW THE VISIBLE BOX PART ----

  if (numerr==0) then
    strvisgud = contabhtml[0] .. contabhtml[1] .. strbigsnack .. ' ' .. strmorfio .. strlingvoo .. strskripto .. '.' .. contabhtml[2]
  end--if

  ---- BREW THE INVISIBLE CATEGORY PART ----

  -- brew 2 category includes (not if lng code is unknown) and "__HIDDENCAT__"

  -- both will have the same sorting hint ... if the lemma begins with a
  -- dash then remove it so that "-o" falls under "O" not under "-" ...
  -- but keep possible irrelevant trailing dash

  -- the cat:s are for example "Vortfarado|"... and "Vortfarado (angla)|"...

  -- "strpagenam" has been reduced to the raw lemma but dashes can persist

  -- string "constrkatp" is cat prefix and includes the colon ":"

  if (numerr==0) then
    strinvkat = '__HIDDENCAT__'
    if (booknown) then
      numlong = string.len (strpagenam) -- now the raw lemma only
      numoct = string.byte (strpagenam,1,1)
      if (numoct==45) then
        strpagenam = string.sub (strpagenam,2,numlong) -- remove Boulder Dash
      end--if
      strinvkat = strinvkat .. '[[' .. constrkatp .. 'Vortfarado|' .. strpagenam .. ']]'
      strinvkat = strinvkat .. '[[' .. constrkatp .. 'Vortfarado (' .. strnambah .. ')|' .. strpagenam .. ']]'
    end--if
  end--if

  ---- RETURN THE JUNK STRING ----

  strret = strviserr .. strvisgud .. strinvkat
  if (bootrace) then
    strret = "<br>" .. qstrtrace .. "<br><br>" .. strret
  end--if
  return strret

end--function

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

return farado