Modulo:mfarado

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

MODULE "MFARADO" (kategorio vortfarado vortgrupigado)

"eo.wiktionary.org/wiki/Modulo:mfarado" <!--2022-Aug-14-->

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

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

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

Syfte: skapar synligt innehaall (ruta) och 2 eller fler 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)
- "mrooteo" in turn requiring template "radikoj" (only if activated)

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

Incoming: * 1 ordinary anonymous and optional parameter
            * only permitted value is "K" (uppercase letter) to indicate
              "compound quasi-root", crucial for -eo-, permitted for
              other languages, otherwise it must be omitted altogether,
              gives an extra category "Kunmetita kvazauxelemento" only
              for -eo- (without lang name consistent with other categories
              from "mrooteo")
          * 3 hidden named and optional parameters
            * "pagenameoverridetestonly=" can directly cause #E01
            * "nsnumberoverridetestonly=" can indirectly cause #E04
            * "detrc=" no error possible

Returned: * one string with visible part (HTML box) and category part

Most of the relevant information is peeked from the pagename and thus
ordinary parameters are barely needed.

To be called from category namespace only.

]===]

local exporttable = {}

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

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

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

  -- 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 constrrteo = "Modulo:mrooteo"     -- EO
  -- local constrrteo = "Modul:mrooteo"      -- ID
  local constrapxq = "Aldono"             -- EO (used only via "lfialdigu")
  -- local constrapxq = "Lampiran"           -- ID (used only via "lfialdigu")
  local constrkatq = "Kategorio"          -- EO (used only via "lfikatigu")
  -- local constrkatq = "Kategori"           -- ID (used only via "lfikatigu")

  -- 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 = {'by','dc','ll','jp','art','cmn','deu','eng','epo','fra','gem','ger','ido','lat','por','rus','spa','swe','tup','zxx'} -- 1...20

  -- 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;<big>&#91;&#93;</big>&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) !!!FIXME!!!
  -- 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...#E99, note that #E00 and #E01 are NOT supposed to be included here

  local contaberaroj = {}
  contaberaroj[2] = 'Maksimume unu parametro estas transprenata far \\@, legu gxian dokumentajxon'   -- EO #E02
  -- contaberaroj[2] = 'Tidak lebih dari satu parameter bisa dimakan oleh \\@, bacalah dokumentasinya'  -- ID #E02
  contaberaroj[3] = 'La sola permesita valoro de anonima parametro por \\@ estas "K"'                -- EO #E03
  -- contaberaroj[3] = 'Isi satu-satunya parameter anonim yang benar untuk \\@ adalah "K"'              -- ID #E03
  contaberaroj[4] = 'Erara nomspaco (estu 14, "Kategorio:") por \\@'                                 -- EO #E04
  -- contaberaroj[4] = 'Ruang nama salah (sebaiknya 14, "Kategori:") untuk \\@'                         -- ID #E04
  contaberaroj[5] = 'Evidente nevalida lingvokodo en \\@'                                -- EO #E05
  -- contaberaroj[5] = 'Kode bahasa jelas-jelas salah dalam \\@'                            -- ID #E05
  contaberaroj[6] = 'Erara pagxonomo (neniu el 3) por \\@'                               -- EO #E06
  -- contaberaroj[6] = 'Nama halaman salah (tidak satu dari 3) untuk \\@'                   -- ID #E06
  contaberaroj[7] = 'Erara pagxonomo por \\@'                                            -- EO #E07
  -- contaberaroj[7] = 'Nama halaman salah untuk \\@'                                       -- ID #E07
  contaberaroj[8] = 'Submodulo "mrooteo" ne redonis tabelon'                             -- EO #E08
  -- contaberaroj[8] = 'Submodul "mrooteo" gagal memberi kembali tabel'                     -- EO #E08

  -- 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"
  contabboasting[10] = "Kunmetita kvazauxelemento"

  -- uncommentable EO vs ID constant tables (dual use: mortyp:s and scripts via "lfseizetext")

  -- "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["zhK"] = "komuna"          -- 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 related to submodules

  local constrtblc0 = "0" -- lang name in site language

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

  local conboouseeox  = true   -- "true" to enable eo-specific submodule
  local conbookodlng  = false  -- "true" to allow long codes like "zh-min-nan"
  local conboomiddig  = false  -- "true" to allow middle digit "s7a"

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

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

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

  -- SPECIAL VAR:S

local qpiktbllki = {}    -- type "table" with type "function" inside
local qeorooteo = {}     -- type "table" with type "function" inside
local qboodetrc = true   -- from "detrc=true" but default is "true" !!!
local qstrtrace = '<br>' -- 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 & TWO IMPORTS VIA REQUIRE

qbooguard = ((type(constrpriv)~="string") or (type(constrplki)~="string") or (type(constrrteo)~="string") or (type(constrapxq)~="string") or (type(constrkatq)~="string") or (type(constrkoll)~="string"))
if (not qbooguard) then
  qpiktbllki = require(constrplki) -- can crash here despite guarding ??
  qbooguard = (type(qpiktbllki)~="table")
end--if
if ((not qbooguard) and conboouseeox) then
  qeorooteo = require(constrrteo) -- can crash here despite guarding ??
  qbooguard = (type(qeorooteo)~="table")
end--if

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

---- BUG FUNCTIONS [D] ----

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

-- Local function LFTRACEMSG

-- Enhance upvalue "qstrtrace".

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

-- no size limit, use this one also to import detrc text from submodule

-- upvalue "qstrtrace" must NOT be type "nil" on entry

-- uses upvalue "qboodetrc"

local function lftracemsg (strshortlineorbigtext)
  local strseparator = ''
  if (qboodetrc and (type(strshortlineorbigtext)=='string')) then
    if (string.find(strshortlineorbigtext,'<br>',1,true)) then
      strseparator = '<br>####################<br>'
      qstrtrace = qstrtrace .. strseparator .. strshortlineorbigtext .. strseparator .. "<br>"
    else
      qstrtrace = qstrtrace .. strshortlineorbigtext .. '.' .. "<br>" -- dot added !!!
    end--if
  end--if
end--function lftracemsg

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

-- Local function LFDMINISANI

-- Input  : * strdangerous -- must be type "string", empty legal

-- Output : * strsanitized -- can happen to be quasi-empty with <<"">>

-- Disallow: cross "#" 35 | apo "'" 39 | "<" 60 | ">" 62 | "[" 91 | "]" 93
-- To be called from "lfdshowvcore" <- "lfshowvar" only.

local function lfdminisani (strdangerous)

  local strsanitized = '"' -- begin quot
  local numlecnx = 0
  local numindabx = 1 -- ONE-based
  local numsigno = 0

  numlecnx = string.len (strdangerous)
  while true do
      if (numindabx>numlecnx) then
        break -- done string char after char
      end--if
      numsigno = string.byte (strdangerous,numindabx,numindabx)
      if ((numsigno<36) or (numsigno==39) or (numsigno==42) or (numsigno==58) or (numsigno==60) or (numsigno==62) or (numsigno==91) or (numsigno==93) or (numsigno>122)) then
        strsanitized = strsanitized .. '{' .. tostring (numsigno) .. '}'
      else
        strsanitized = strsanitized .. string.char (numsigno)
      end--if
      if ((numlecnx>50) and (numindabx==20)) then
        numindabx = numlecnx - 20 -- jump
        strsanitized = strsanitized .. '" ... "'
      else
        numindabx = numindabx + 1
      end--if
  end--while
  strsanitized = strsanitized .. '"' -- don't forget final quot

  return strsanitized

end--function lfdminisani

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

-- Local function LFDSHOWVCORE

-- Prebrew report about content of a variable (including optional full
-- listing of a table with integer indexes starting from ZERO). !!!FIXME!!!

-- Input  : * vardubious -- content (any type including "nil")
--          * strname -- name of the variable (string)
--          * vardescri -- optional, defa empty
--          * vartablim -- optional, defa 0

-- Depends on functions :
-- [D] lfdminisani

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

  local strtype = ''
  local strreport = ''
  local numindax = 0
  local numlencx = 0
  local numkeynumber = 0
  local numkeycetera = 0

  if (type(vartablim)~="number") then
    vartablim = 0 -- deactivate listing of a table
  end--if
  if (type(vardescri)~="string") then
    vardescri = '' -- omit comment
  else
    vardescri = ' (' .. vardescri .. ')' -- augment only if used !!!
  end--if
  if (type(strname)~="string") then -- this must be below "type(vardescri)"
    strname = "??" -- bite the bullet
  else
    strname = '"' .. strname .. '"' .. vardescri -- combo
  end--if

  strtype = type(vardubious)

  if (strtype=="table") then

      for k,v in pairs(vardubious) do
        if type(k)=="number" then
          numkeynumber = numkeynumber + 1
        else
          numkeycetera = numkeycetera + 1
        end--if
      end--for

      strreport = 'Table ' .. strname .. ' contains ' .. tostring (numkeynumber)
      strreport = strreport .. ' numeric keys and ' .. tostring (numkeycetera)
      strreport = strreport .. ' other keys'

      if ((numkeynumber~=0) and (vartablim~=0)) then -- !!!FIXME!!!
        strreport = strreport .. ', content :'
        numindax = 0
        while true do
          if (numindax>vartablim) then
            break -- done table
          end--if
          strreport = strreport .. ' ' .. tostring (numindax) .. ' -> ' .. lfdminisani (tostring (vardubious[numindax]) )
          numindax = numindax + 1
        end--while
      end--if

  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 ' .. lfdminisani (vardubious)
        end--if
      else
        if (strtype~="nil") then
          strreport = strreport .. ' and content "' .. tostring (vardubious) .. '"'
        end--if
      end--if

  end--if (strtype=="table") else

  return strreport

end--function lfdshowvcore

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

-- Local function LFSHOWVAR

-- Enhance upvalue "qstrtrace" with report about content of a variable
-- (including optional full listing of a table with integer indexes
-- starting from ZERO). !!!FIXME!!!

-- Depends on functions :
-- [D] lfdminisani lfdshowvcore

-- upvalue "qstrtrace" must NOT be type "nil" on entry
-- uses upvalue "qboodetrc"

local function lfshowvar (varduubious, strnaame, vardeskkri, vartabljjm)

  if (qboodetrc) then
    qstrtrace = qstrtrace .. lfdshowvcore (varduubious, strnaame, vardeskkri, vartabljjm) .. '.' .. "<br>"
  end--if

end--function lfshowvar

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

---- MATH FUNCTIONS [E] ----

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

local function mathmin (xmindd, xminee)
  local resultmin = 0 -- min operator lacks in LUA :-(
  resultmin = xmindd
  if (resultmin>xminee) then
    resultmin = xminee
  end--if
  return resultmin
end--function mathmin

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

-- Write bit selected by ZERO-based index assigning it to "1" or "0".

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

local function mathbitwrit (numinkoming, numbityndex, boowrite)
  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] ----

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

-- Local function LFNUMTO2DIGIT

-- Convert integer 0...99 to 2-digit decimal string "00"..."99".

-- Depends on functions :
-- [E] mathmin mathdiv mathmod

local function lfnumto2digit (numzerotoninetynine)
  local strtwodig = '' -- always 2 digits
  numzerotoninetynine = math.floor (numzerotoninetynine) -- no transcendental
  numzerotoninetynine = mathmin (numzerotoninetynine,99) -- no trillions
  strtwodig = tostring(mathdiv(numzerotoninetynine,10)) .. tostring(mathmod(numzerotoninetynine,10))
  return strtwodig
end--function lfnumto2digit

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

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

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

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

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

-- Input  : * "numpossta" (ZERO-based)

local function lfgcmpsub (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
    boofound = ( strtiny == (string.sub (strbig,(numpossta+1),(numpossta+numlentiny))) )
  end--if
  return boofound
end--function lfgcmpsub

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

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

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

-- Local function LFULNUTF8CHAR

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

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

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

-- Does NOT thoroughly check the validity, looks at 1 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 LFCASEREST

-- Adjust case of a single letter (restricted), only ASCII
-- plus a very limited set of 2-octet UTF8 letters. (this is REST)

-- Input  : * strucinrsut : single unicode letter (1 or 2 octet:s)
--          * booupcas    : for desired uppercase "true" and for
--                          lowercase "false"
--          * numselset   : 0 ASCII -- 2 eo -- 5 sv (value 255 NOT here)

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

-- Depends on functions : (this is REST)
-- [U] lfulnutf8char
-- [G] lftestuc lftestlc
-- [E] mathdiv mathmod mathbitwrit

-- Unknown non-ASCII input strictly returns "ZZ"

-- Defined sets:
-- 2: 2 x 6 uppercase and lowercase -eo- (CX GX HX JX SX UX cx gx hx jx sx ux)
--    upper CX $0108 GX $011C HX $0124 JX $0134 SX $015C UX $016C lower +1
-- 5: 2 x 4 uppercase and lowercase -sv- (AA AE OE EE aa ae oe ee)
--    upper AE $00C4 AA $00C5 EE $00C9 OE $00D6 lower +$20

local function lfcaserest (strucinrsut, booupcas, numselset)

  local numlaengdn = 0 -- length from "string.len"
  local numchaerr = 0 -- UINT8 beginning char
  local numchaess = 0 -- UINT8 later char (BIG ENDIAN, lower value here up)
  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 = false -- preASSume guilt

  booupcas = not (not booupcas)
  boowantlowr = (not booupcas)

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

    numlaengdn = string.len (strucinrsut)
    if ((numlaengdn<1) or (numlaengdn>2)) then -- only 1 or 2 accepted
      break -- bad string length
    end--if
    numchaerr = string.byte (strucinrsut,1,1)
    if ((lfulnutf8char(numchaerr))~=numlaengdn) then
      break -- mismatch with length
    end--if

    if (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
      boovalid = true
      break -- success with ASCII, almost done
    end--if

    numchaess = string.byte (strucinrsut,2,2) -- only $80 to $BF
    numchareel = (mathmod(numchaerr,4)*64) + (numchaess-128) -- 4 times 64

    if ((numselset==2) and ((numchaerr==196) or (numchaerr==197))) then -- eo
      numtheemp = mathbitwrit (numchareel,0,false) -- bad way to do AND $FE
      if ((numtheemp==8) or (numtheemp==28) or (numtheemp==36) or (numtheemp==52) or (numtheemp==92) or (numtheemp==108)) then
        booiisuppr = (numtheemp==numchareel) -- UC below and even (ordinary)
        booiislowr = not booiisuppr
        if (booiisuppr and boowantlowr) then
          numdeta = 1 -- UPPER->lower
        end--if
        if (booiislowr and booupcas) then
          numdeta = -1 -- lower->UPPER
        end--if
        boovalid = true
        break -- success with -eo-, almost done
      end--if
    end--if ((numselset==2) and ...

    if ((numselset==5) and (numchaerr==195)) then -- sv
      numtheemp = mathbitwrit (numchareel,5,false) -- bad way to do AND $DF
      if ((numtheemp==196) or (numtheemp==197) or (numtheemp==201) or (numtheemp==214)) then
        booiisuppr = (numtheemp==numchareel) -- UC below and bit is ZERO
        booiislowr = not booiisuppr
        if (booiisuppr and boowantlowr) then
          numdeta = 32 -- UPPER->lower
        end--if
        if (booiislowr and booupcas) then
          numdeta = -32 -- lower->UPPER
        end--if
        boovalid = true
        break -- success with -sv-, almost done
      end--if
    end--if ((numselset==5) and ...

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

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

    if (not boovalid) then
      strucinrsut = "ZZ" -- unknown non-ASCII char
      break -- helvete
    end--if

    if (numdeta==0) then
      break -- nothing to do
    end--if

    if (numlaengdn==1) then
      strucinrsut = string.char (numchaerr + numdeta) -- no risk of carry here
      break -- done
    end--if

    strucinrsut = string.char (numchaerr) .. string.char (numchaess + numdeta)

    break -- finally to join mark
  end--while -- lower fake loop -- join mark (this is REST)

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

end--function lfcaserest

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

-- Local function LFUCASESTR

-- Adjust case of beginning letter or of all letters in a word or group
-- of words to upper or lower, with limited and adjustable unicode support.

-- Input  : * strenigo  : word or group of words (may be empty)
--          * boouprcas : "true" for uppercase and "false" for lowercase
--          * boodooall : "true" to adjust all letters, "false" only beginning
--          * numslsaet : 0 ASCII -- 2 eo -- 5 sv -- 255 generic

-- Depends on functions : (restricted LFCASEREST)
-- [U] lfulnutf8char lfcaserest
-- [G] lftestuc lftestlc
-- [E] mathdiv mathmod mathbitwrit

local function lfucasestr (strenigo, boouprcas, boodooall, numslsaet)

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

  boouprcas = not (not boouprcas)
  boodooall = not (not boodooall)
  numlein = string.len (strenigo)

  while true do
    if (numposi>numlein) then
      break -- done
    end--if
    bootryadj = (boodooall or (numposi==1))
    numcut = lfulnutf8char(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
      -- if (numslsaet==255) then
        -- strte7mp = lfcasegene(strte7mp,boouprcas) -- (generous LFCASEGENE)
      -- else
        strte7mp = lfcaserest(strte7mp,boouprcas,numslsaet) -- (restricted LFCASEREST)
      -- end--if
    end--if
    strelygo = strelygo .. strte7mp -- this can be slow
    numposi = numposi + numcut -- done 1...4 octet:s
  end--while

  return strelygo

end--function lfucasestr

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

---- HIGH LEVEL STRING FUNCTIONS [I] ----

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

local function lfialdigu (strprefiix, straldnomo, strvisib)
  local strbamb = ''
  if (type(strvisib)=="string") then
    strbamb = '[[' .. strprefiix .. ':' .. straldnomo .. '|' .. strvisib .. ']]'
  else
    strbamb = '[[' .. strprefiix .. ':' .. straldnomo .. ']]'
  end--if
  return strbamb
end--function lfialdigu

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

local function lfikatigu (strprefixx, strkatnomo, strhint)
  local strbkmb = ''
  if (type(strhint)=="string") then
    strbkmb = '[[' .. strprefixx .. ':' .. strkatnomo .. '|' .. strhint .. ']]'
  else
    strbkmb = '[[' .. strprefixx .. ':' .. strkatnomo .. ']]'
  end--if
  return strbkmb
end--function lfikatigu

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

---- HIGH LEVEL FUNCTIONS [H] ----

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

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

-- !!!FIXME!!! obsolete version

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 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 functions :
-- [G] lftestnum lftestlc

-- 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 -- ordinary inner 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 = lftestnum(numchiiar)
      booislclc = lftestlc(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 -- ordinary inner loop over char:s

    if (not boonoban) then -- if "yesban" then
      numindeex = 0
      while true do -- ordinary lower 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
          booisvaladv = false
          break -- abort inner loop (then fake outer loop) due to violation
        end--if
        numindeex = numindeex + 1 -- ZERO-based
      end--while -- ordinary lower inner loop
    end--if (not boonoban) then

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

  return booisvaladv

end--function lfivalidatelnkoadv

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

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

-- !!!FIXME!!! obsolete version

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

-- !!!FIXME!!! obsolete version

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

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

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

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

function exporttable.ek (arxframent)

  -- general unknown type

  local vartymp = 0      -- variable without type multipurpose

  -- special type "args" AKA "arx"

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

  -- general table

  local tabrooteo = {}   -- from submodule

  -- general str

  local strpagenam  = "" -- "{{PAGENAME}}" o "pagenameoverridetestonly"
  local strmorfwrd  = "" -- morpheme or word extracted from pagename
  local strmorfrnd  = "" -- morpheme or word but possible leading dash removed
  local strruangna  = "" -- "{{NAMESPACENUMBER}}" o "nsnumberoverridetestonly"
  local strkodbah   = ""
  local strnambah   = "" -- language name (short without prefix "Bahasa")
  local strdetaleo  = "" -- from submodule, -eo- only so far
  local strkatojeo  = "" -- from submodule, -eo- only so far

  local strmorphdesk = "" -- description of morpheme (empty if word instead)
  local strmorfio    = "" -- with quotes and maybe link
  local strlingvoo   = "" -- big speech and cannot be empty
  local strskripto   = "" -- big speech but can also be empty

  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 4 ns 5 evidente 6 pagename 1 of 3 ...
  local numlong   = 0 -- length of pagename
  local numposnam = 0 -- pos in pagename
  local numoct    = 0
  local numodt    = 0
  local numlenko  = 0 -- 0...10 only but ZERO and ONE invalid soon later
  local numtamp   = 0
  local numtump   = 0 -- !!!FIXME!!! use sub

  -- general boo

  local booiskay  = false -- from "K"
  local booknown  = false -- from submodule

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

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

  lftracemsg ('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"'

  if (numerr==0) then

    numtamp = 2 -- start with #E02 !!!FIXME!!! this is obsolete
    numtump = 0 -- tolerance for holes
    while true do
      vartymp = contaberaroj[numtamp] -- risk of type "nil"
      if (type(vartymp)=="string") then -- number of messages is NOT hardcoded
        numtump = 0
        strtmp = lffillname (vartymp,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])
    contabboasting[10] = lfkodeosg (contabboasting[10])
    contabmosc["jaK"] = lfkodeosg (contabmosc["jaK"]) -- !!!FIXME!!! do all

  end--if

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

  -- must be seized independently on "numerr" even if we already suck

  -- give a f**k in possible params other than "caller=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['caller']=="true") then
    arxsomons = arxframent:getParent().args -- "args" from caller's "frame"
  end--if
  if (type(arxsomons)~="table") then
    arxsomons = {} -- guard against indexing error again
    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 above !!!

  -- no "nocat" here :-\

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

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

  if (arxsomons["detrc"]=="true") then
    lftracemsg ('Param "detrc=true" seized')
  else
    qboodetrc = false -- was preassigned to "true"
    qstrtrace = '' -- shut up now
  end--if

  lfshowvar (numerr,'numerr','done with hidden params')

  ---- 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
    vartymp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (type(vartymp)=="string") then -- this can leave bhd "strpagenam" empty
      numtamp = string.len(vartymp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartymp -- 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
    strtmp = '#E01 Internal error in module "mfarado".'
    strviserr = constrlaxhu .. constrelabg .. strtmp .. constrelaen .. constrlaxhu
  end--if

  ---- PROCESS ANON PARAMS ----

  if (numerr==0) then
    if (arxsomons[2]~=nil) then
      numerr = 2 -- #E02 too many
    end--if
  end--if
  if (numerr==0) then
    vartymp = arxsomons[1]
    booiskay = (vartymp=='K')
    if ((not booiskay) and (vartymp~=nil)) then
      numerr = 3 -- #E03 only "K" is legal
    end--if
  end--if

  lfshowvar (numerr,'numerr','processed anon params')
  lfshowvar (booiskay,'booiskay')

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

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

  if (numerr==0) then
    if (strruangna~="14") then
      numerr = 4 -- #E04 ns
    end--if
  end--if

  lfshowvar (numerr,'numerr','namespace seized')
  lfshowvar (strruangna,'strruangna')

  ---- 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 follow 2 more char:s namely space and script code.

  -- All cat:s will later receive the same sorting hint ... if the morpheme
  -- or word begins with a dash then remove it so that "-o" falls under "O"
  -- instead of under "-" ... still keep possible irrelevant trailing dash.

  nummortyp = 0
  numskript = 0

  while true do -- fake loop -- outer

    if (numerr~=0) then
      break
    end--if

    numlong = string.len (strpagenam)
    numowntyp = 0 -- 0 invalid | 1 vorto 2 frazo 3 vortgrupo
    if (lfgcmpsub(strpagenam,"Vorto -",0)) then
      numowntyp = 1
      numposnam = 7
    end--if
    if (lfgcmpsub(strpagenam,"Frazo -",0)) then
      numowntyp = 2
      numposnam = 7
    end--if
    if (lfgcmpsub(strpagenam,"Vortgrupo -",0)) then
      numowntyp = 3
      numposnam = 11
    end--if
    if (numowntyp==0) then
      numerr = 6 -- #E06 none of 3 supported types
      break
    end--if

    lfshowvar (numowntyp,'numowntyp','after first ONE-of-THREE validation step')

    strkodbah = '' -- read char by char until "- " reached or error
    numlenko = 0 -- valid values are 2...10 but later further checks follow
    while true do -- genuine inner loop
      if ((numposnam+1)>=numlong) then -- "numposnam" is ZERO-based
        numerr = 7 -- #E07 truncated incoming string
        break
      end--if
      numposnam = numposnam + 1 -- INC-before-pick compensates for ONE-based
      numoct = string.byte (strpagenam,(numposnam  ),(numposnam  ))
      numodt = string.byte (strpagenam,(numposnam+1),(numposnam+1))
      if ((numoct==45) and (numodt==32)) then
        numposnam = numposnam + 1 -- jump over the space too
        break -- found "- "
      end--if
      if (numlenko>=10) then
        numerr = 7 -- #E07 too long monstercode "zh-min-nan-max" :-D
        break
      end--if
      strkodbah = strkodbah .. string.char(numoct)
      numlenko = numlenko + 1
    end--while -- genuine inner loop
    if (numerr~=0) then
      break -- do not forget to exit outer loop too
    end--if

    lftracemsg ('Lang code (extraction up to 10, not yet validated) is "' .. strkodbah .. '"')

    if (not lfivalidatelnkoadv(strkodbah,false,false,conbookodlng,conboomiddig,false)) then
      numerr = 5 -- #E05 bad lang code
      break
    end--if

    lftracemsg ('The lang code is valid')

    numtamp = 0
    if (numowntyp==1) then
      if (lfgcmpsub(strpagenam,"enhavanta morfemon ",numposnam)) then
        numtamp = 19
      end--if
    end--if
    if (numowntyp==2) then
      if (lfgcmpsub(strpagenam,"enhavanta vorton ",numposnam)) then
        numtamp = 17
      end--if
    end--if
    if (numowntyp==3) then
      if (lfgcmpsub(strpagenam,"enhavanta ",numposnam)) then
        numtamp = 10
      end--if
    end--if
    if (numtamp==0) then
      numerr = 7 -- #E07 not found
      break
    end--if
    numposnam = numposnam + numtamp -- jump over it

    lftracemsg ('After second ONE-of-THREE validation step')

    if (numowntyp==1) then
      if ((numposnam+2)>numlong) then -- "numposnam" is ZERO-based
        numerr = 7 -- #E07 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 = 7 -- #E07 invalid code
        break
      end--if
      if (numodt~=32) then
        numerr = 7 -- #E07 no space char
        break
      end--if
      numposnam = numposnam + 2 -- jump over uppercase code and space

      lftracemsg ('Seized "mortyp", it is ' .. tostring(nummortyp))

    end--if

    if ((numposnam+3)>numlong) then -- "numposnam" is ZERO-based
      numerr = 7 -- #E07 truncated (two brackets plus min length one -> 3)
      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 -- trim it off

      lftracemsg ('Seized script code, it is ' .. tostring(numskript))

    end--if

    numoct = string.byte (strpagenam,(numposnam+1),(numposnam+1))
    numodt = string.byte (strpagenam,numlong,numlong)
    if ((numoct~=40) or (numodt~=41)) then
      numerr = 7 -- #E07 need "(" and ")"
      break
    end--if
    strmorfwrd = string.sub (strpagenam,(numposnam+2),(numlong-1)) -- extract

    numoct = string.byte (strmorfwrd,1,1)
    if (numoct==45) then
      strmorfrnd = string.sub (strmorfwrd,2,-1) -- remove Boulder Dash
    else
      strmorfrnd = strmorfwrd -- copy
    end--if

    lfshowvar (strmorfwrd,'strmorfwrd','extracted morpheme or word')
    lfshowvar (strmorfrnd,'strmorfrnd','without dash')

    break

  end--while -- fake loop -- outer

  lfshowvar (numerr,'numerr','after dissection')

  ---- 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 LANG NAME VIA SUBMODULE ----

  -- the x-index is "c0" lang name in site language

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

  lfshowvar (numerr,'numerr','submodule "lki" probably called')
  lfshowvar (strnambah,'strnambah')

  ---- PEEK DETAILS VIA SUBMODULE IF ONLY 4 + 1 CONDITIONS ARE MET ----

  -- submodule requires two named parameters "in=" and "givetable=true"

  -- returned table has elements 0...4 :
  -- 0 error code (ZERO OK)
  -- 1 strvisgud
  -- 2 strinvkat
  -- 3 strvisred -- we use this reduced text
  -- 4 qstrtrace

  -- Examples of full and reduced text:
  -- * <<La elemento "mov" (tipo N) troveblas en BRO5 kaj OA6.>>
  --                              <<troveblas en BRO5 kaj OA6>>
  -- * <<La elemento "-unt-" (tipo
  --   I) ne troveblas en iu listo kaj do estas neoficiala.>>
  --    <<ne troveblas en iu listo kaj do estas neoficiala>>

  strdetaleo = '' -- later concat:ed if non-empty
  strkatojeo = '' -- later concat:ed in any case, but can be empty

  if ((numerr==0) and (numowntyp==1) and (strkodbah=='eo') and conboouseeox) then

    if (booiskay) then

      strkatojeo = lfikatigu(constrkatq,contabboasting[10],strmorfrnd) -- got bad "K" but remaining 4 are met

    else

      strtmp = '' -- preASSume
      if (qboodetrc) then
        strtmp = 'true' -- also from submodule
      end--if
      tabrooteo = qeorooteo.ek { args = { ['in']=strpagenam , ['givetable']='true' , ['detrc']=strtmp } }
      if (type(tabrooteo)=="table") then
        numtamp = tabrooteo[0]
        if (numtamp==0) then
          strdetaleo = tabrooteo[3] -- reduced
          strkatojeo = tabrooteo[2]
        else
          numerr = numtamp + 20 -- #E21 and higher
        end--if
        if (qboodetrc) then
          lftracemsg (tabrooteo[4]) -- import it
        end--if
      else
        numerr = 8 -- #E08
      end--if

    end--if (booiskay) else

  end--if ((numerr==0) and (numowntyp==1) and ...

  lfshowvar (numerr,'numerr','submodule "rooteo" maybe called')
  lfshowvar (strdetaleo,'strdetaleo')
  lfshowvar (strkatojeo,'strkatojeo')

  ---- PREBREW DESCRIPTION OF MORPHEME ----

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

  -- incoming from above "strdetaleo" (empty if not applicable)

  -- note that "booiskay" from "K" and "strdetaleo" exclude
  -- each other but they can even be both absent

  -- after this block the text fragment in "strmorphdesk" will either
  -- * begin with a space and end with either a word or a
  --   closing arc bracket ")" (NOT a space in any case)
  -- or
  -- * be empty

  strmorphdesk = ''
  if ((numerr==0) and (numowntyp==1)) then

    do -- scope
    local strpriskribo = '' -- 1 of 6 values from "nummortyp"
    local strmortypletter = ''

    strmortypletter = string.char (nummortyp)
    strpriskribo = lfseizetext (strmortypletter) -- letter to word
    if (booiskay) then
      strpriskribo = strpriskribo .. ' kunmetitan' -- "contabmosc" also accusa
    end--if
    if (strdetaleo~='') then
      strpriskribo = strpriskribo .. ', ' .. strdetaleo
    end--if
    strmorphdesk = ' ' .. strmortypletter .. ' (' .. strpriskribo .. ')'

    end--do scope

  end--if

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

  -- "strmorfwrd" contains the raw morpheme or word but dashes can persist
  -- "strmorfrnd" is without leading dash

  -- string "constrapxq" is appendix prefix and excludes the colon ":"

  -- this outputs 3 fragments:
  -- * trivial "strmorfio" no spaces
  -- * intricate "strlingvoo" begins with space ends with arc bracket ")"
  -- * medium "strskripto" begins with space ends with arc bracket ")"

  if (numerr==0) then

    do -- scope
    local straldono = '' -- link or not and cannot be empty
    local strlngandskr = '' -- "jaH"

    if (nummortyp==78) then -- "N"
      strmorfio = '"' .. strmorfwrd .. '"' -- do NOT link nonstandalone roots
    else
      strmorfio = '"[[' .. strmorfwrd .. ']]"'
    end--if
    if (booknown) then
      straldono = 'la lingvo ' .. lfialdigu(constrapxq,(lfucasestr(strnambah,true,false,2)),strnambah)
    else
      straldono = 'nekonata lingvo'
    end--if
    strlingvoo = ' en '.. straldono .. ' (lingvokodo -' .. strkodbah .. '-)'
    if (booknown and (numskript~=0)) then
      strlngandskr = strkodbah .. string.char (numskript) -- gives fe "jaH"
      strskripto = ' per la skribo ' .. string.char (numskript) .. ' (' .. lfseizetext (strlngandskr) .. ')'
    else
      strskripto = ''
    end--if

    end--do scope

  end--if

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

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

  -- text in "contabboasting" ends with a word (not space)

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

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

  -- incoming from above "strkatojeo" (empty if not applicable)

  -- "strmorfwrd" contains the raw morpheme or word but dashes can persist
  -- "strmorfrnd" is without leading dash

  -- string "constrkatq" is cat prefix and excludes the colon ":"

  -- brew "__HIDDENCAT__" as well as 2 category includes (not if lang code
  -- is unknown) and possibly further ones from submodule or "K" (if -eo-)

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

  if (numerr==0) then
    strinvkat = '__HIDDENCAT__' .. strkatojeo
    if (booknown) then
      strinvkat = strinvkat .. lfikatigu(constrkatq,'Vortfarado',strmorfrnd)
      strinvkat = strinvkat .. lfikatigu(constrkatq,('Vortfarado (' .. strnambah .. ')'),strmorfrnd)
    end--if
  end--if

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

  strret = strviserr .. strvisgud .. strinvkat
  if (qboodetrc) then -- "qstrtrace" declared separately outside main function
    strret = "<br>" .. qstrtrace .. "<br><br>" .. strret
  end--if
  return strret

end--function

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

return exporttable