Saltu al enhavo

Modulo:mfarado

El Vikivortaro
 MODULO
Memtesto disponeblas sur la paĝo Ŝablono:kat-farado.

--[===[

MODULE "MFARADO" (kategorio vortfarado vortgrupigado)

"eo.wiktionary.org/wiki/Modulo:mfarado" <!--2025-Oct-29-->

Purpose: brews visible content (box) and 2 or more cat insertions
         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 / Behoevda submoduler:
* "loaddata-tbllingvoj" T76 in turn requiring template "tbllingvoj" (EO)
* "loaddata-tblbahasa" T76 in turn requiring template "tblbahasa" (ID)
* "mrooteo" in turn requiring template "radikoj" (only if activated)

Required images:
* "File:Hidden icon.png"

Required templates:
* YES, see above

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

Incoming: * ONE ordinary anonymous and optional parameter
            * only permitted value is "K" (uppercase letter) to indicate
              "compound quasi-root", otherwise it must be omitted altogether,
              crucial for -eo-, permitted for other languages, gives an extra
              category "Kunmetita kvazauxelemento" only for -eo- (without lang
              name, consistently with other categories as named by "mrooteo")
          * 3 hidden named and optional parameters
            * "pagenameoverridetestonly=" can directly cause #E01
            * "nsnumberoverridetestonly=" can indirectly cause #E10
            * "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.

Errors:
* we generate errors #E01 ... #E17
* we import errors from two submodules

]===]

local exporttable = {}

require('strict')

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

-- uncommentable  !!!FIXME!!! peek this

      local strpiklangcode = 'eo'
        -- local strpiklangcode = 'id'
      local strmparent = string.char(0xC5,0x9C) .. 'ablono:kat-farado'
        -- local strmparent = 'Templat:kat-farado'

      local strpikapxns = "Aldono"           -- EO (used only via "lfikataldigu")
        -- local strpikapxns = "Lampiran"       -- ID (used only via "lfikataldigu")
      local strpikkatns = "Kategorio"        -- EO (used only via "lfikataldigu")
        -- local strpikkatns = "Kategori"       -- ID (used only via "lfikataldigu")

-- uncommentable

      local constringvoj = "Modulo:loaddata-tbllingvoj"  -- EO
        -- local constringvoj = "Modul:loaddata-tblbahasa"  -- ID
      local constrrteo   = "Modulo:mrooteo"              -- EO
        -- local constrrteo   = "Modul:mrooteo"             -- ID

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

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

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

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

-- surrogate transcoding table (only needed for EO)

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

-- constant table (error circumfixes)

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

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

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

local contaberaroj = {}
  contaberaroj[ 8] = 'Maksimume unu parametro estas transprenata far %@, legu gxian dokumentajxon'   -- EO #E08 !!!FIXME!!! change to unknown param #E06
  -- contaberaroj[ 8] = 'Tidak lebih dari satu parameter bisa dimakan oleh %@, bacalah dokumentasinya'  -- ID #E08
  contaberaroj[04] = 'Submodulo "mrooteo" ne redonis tabelon'                            -- EO #E04
  -- contaberaroj[04] = 'Submodul "mrooteo" gagal memberi kembali tabel'                    -- EO #E04
  contaberaroj[05] = 'Submodulo "mrooteo" redonis erarkodon'                             -- EO #E05
  -- contaberaroj[05] = 'Submodul "mrooteo" memberi kode galat'                             -- EO #E05
  contaberaroj[10] = 'Erara nomspaco (estu 14, "Kategorio:") por %@'                     -- EO #E10
  -- contaberaroj[10] = 'Ruang nama salah (sebaiknya 14, "Kategori:") untuk %@'             -- ID #E10
  contaberaroj[11] = 'Evidente nevalida lingvokodo en %@'                                -- EO #E11
  -- contaberaroj[11] = 'Kode bahasa jelas-jelas salah dalam %@'                            -- ID #E11
  contaberaroj[14] = 'Erara pagxonomo (neniu el 3) por %@'                               -- EO #E14
  -- contaberaroj[14] = 'Nama halaman salah (tidak satu dari 3) untuk %@'                   -- ID #E14
      contaberaroj[15] = 'Erara pagxonomo (cetere) por %@'                                   -- EO #E15
        -- contaberaroj[15] = 'Nama halaman salah (secara lain) untuk %@'                       -- ID #E15
      contaberaroj[17] = 'La sola permesita valoro de anonima parametro por %@ estas "K"'    -- EO #E17
        -- contaberaroj[17] = 'Isi satu-satunya parameter anonim yang benar untuk %@ adalah "K"'-- ID #E17

  -- 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 "prhseizetext")

  -- "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.matabg = '<table style="border:1px solid #A0A0A0; background:#F0F0F0; width:95%; margin:0.6em auto 0.6em auto; padding:0.6em; text-align:justify;">'
  contabhtml.hidden = '<tr><td style="width:30px;">[[File:Hidden icon.png|50px]]</td><td style="padding-left:1em;">'
  contabhtml.lowend = '</td></tr></table>'

-- 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 qldingvoj = {}     -- type "table" and nested
local qeorooteo = {}     -- type "table" with type "function" inside
local qboodetrc = true   -- from "detrc=true" but default is "true" !!!
local qstrtrace = '<br>' -- for main & proc, debug report request by "detrc="
local qbooguard = false  -- only for the guard test, pass to other var ASAP

  -- GUARD AGAINST INTERNAL ERROR AND TWO IMPORTS

qbooguard = ((type(strpiklangcode)~='string') or (type(constringvoj)~='string') or (type(constrrteo)~='string') or (type(strpikapxns)~='string') or (type(strpikkatns)~='string') or (type(strmparent)~='string'))
if (not qbooguard) then
  qldingvoj = mw.loadData(constringvoj) -- can crash here
  qbooguard = (type(qldingvoj)~='table') -- seems to be always false
end--if
if ((not qbooguard) and conboouseeox) then
  qeorooteo = require(constrrteo) -- can crash here despite guarding ??
  qbooguard = (type(qeorooteo)~='table')
end--if

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

---- DEBUG FUNCTIONS [D] ----

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

-- Enhance upvalue "qstrtrace" with fixed text.

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

-- upvalue "qstrtrace" must NOT be type "nil" on entry (is inited to "<br>")

-- uses upvalue "qboodetrc"

local function prdtracemsg (strshortline)
  if (qboodetrc and (type(strshortline)=='string')) then
    qstrtrace = qstrtrace .. strshortline .. '.<br>' -- dot added !!!
  end--if
end--function prdtracemsg

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

-- Local function LFDIMPORTREPORT

-- Enhance upvalue "qstrtrace" with imported report.

-- use this one to import detrc text from submodule

-- upvalue "qstrtrace" must NOT be type "nil" on entry (is inited to "<br>")

-- uses upvalue "qboodetrc"

local function lfdimportreport (strshortlineorbigtext)
  local strseparator = ''
  if (qboodetrc and (type(strshortlineorbigtext)=='string')) then
    strseparator = '<br>&#35;######################&#35;<br>'
    qstrtrace = qstrtrace .. strseparator .. strshortlineorbigtext .. strseparator .. '<br>'
  end--if
end--function lfdimportreport

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

-- Local function LFDMINISANI

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

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

-- To be called from "prdshowvcore" <- "lfdshowvar" only.

-- * we absolutely must disallow: cross "#" 35 | apo "'" 39 |
--   star "*" 42 | dash 45 | colon 58 | "<" 60 | ">" 62 | "[" 91 | "]" 93
-- * spaces are showed as "{32}" if repetitive or at begin or at end

local function lfdminisani (strdangerous, numlimitdivthree)

  local strsanitized = '"' -- begin quot
  local num38len = 0
  local num38index = 1 -- ONE-based
  local num38signo = 0
  local num38prev = 0
  local boohtmlenc = false
  local boovisienc = false

  num38len = string.len (strdangerous)
  while true do
    boohtmlenc = false -- % reset on
    boovisienc = false -- % every iteration
    if (num38index>num38len) then -- ONE-based
      break -- done string char after char
    end--if
    num38signo = string.byte (strdangerous,num38index,num38index)
    if ((num38signo<43) or (num38signo==45) or (num38signo==58) or (num38signo==60) or (num38signo==62) or (num38signo==91) or (num38signo==93) or (num38signo>122)) then
      boohtmlenc = true
    end--if
    if ((num38signo<32) or (num38signo>126)) then
      boovisienc = true -- overrides "boohtmlenc"
    end--if
    if ((num38signo==32) and ((num38prev==32) or (num38index==1) or (num38index==num38len))) then
      boovisienc = true -- overrides "boohtmlenc"
    end--if
    if (boovisienc) then
      strsanitized = strsanitized .. '{' .. tostring (num38signo) .. '}'
    else
      if (boohtmlenc) then
        strsanitized = strsanitized .. '&#' .. tostring (num38signo) .. ';'
      else
        strsanitized = strsanitized .. string.char (num38signo)
      end--if
    end--if
    if ((num38len>(numlimitdivthree*3)) and (num38index==numlimitdivthree)) then
      num38index = num38len - numlimitdivthree -- jump forwards
      strsanitized = strsanitized .. '" ... "'
    else
      num38index = num38index + 1 -- ONE-based
    end--if
    num38prev = num38signo
  end--while
  strsanitized = strsanitized .. '"' -- don't forget final quot

  return strsanitized

end--function lfdminisani

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

-- Prebrew report about content of a variable including optional full
-- listing of a table with numeric and string keys.                             !!!FIXME!!!

-- Input  : * vardubious  -- content (any type including "nil" is acceptable)
--          * str77name   -- name of the variable (string)
--          * vardescri   -- optional comment, default empty, begin with "@" to
--                           place it before name of the variable, else after
--          * varlim77tab -- optional limit, limits both string keys and
--                           numeric keys, default ZERO no listing

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

local function prdshowvcore (vardubious, str77name, vardescri, varlim77tab)

  local taballkeystring = {}
  local strtype = ''
  local strreport = ''
  local numindax = 0
  local numlencx = 0
  local numkeynumber = 0
  local numkeystring = 0
  local numkeycetera = 0
  local numkey77min = 999999
  local numkey77max = -999999
  local boobe77fore = false

  if (type(str77name)~='string') then
    str77name = '??' -- bite the bullet
  else
    str77name = '"' .. str77name .. '"'
  end--if

  if (type(vardescri)~='string') then
    vardescri = '' -- omit comment
  end--if
  if (string.len(vardescri)>=2) then
    boobe77fore = (string.byte(vardescri,1,1)==64) -- prefix "@"
    if (boobe77fore) then
      vardescri = string.sub(vardescri,2,-1) -- CANNOT become empty
    end--if
  end--if

  if (type(varlim77tab)~='number') then
    varlim77tab = 0 -- deactivate listing of a table
  end--if

  if ((vardescri~='') and (not boobe77fore)) then
    str77name = str77name .. ' (' .. vardescri .. ')' -- now a combo
  end--if

  strtype = type(vardubious)

  if (strtype=='table') then

    for k2k,v2v in pairs(vardubious) do
      if (type(k2k)=='number') then
        numkey77min = math.min (numkey77min,k2k)
        numkey77max = math.max (numkey77max,k2k)
        numkeynumber = numkeynumber + 1
      else
        if (type(k2k)=='string') then
          taballkeystring [numkeystring] = k2k
          numkeystring = numkeystring + 1
        else
          numkeycetera = numkeycetera + 1
        end--if
      end--if
    end--for

    strreport = 'Table ' .. str77name
    if ((numkeynumber==0) and (numkeystring==0) and (numkeycetera==0)) then
      strreport = strreport .. ' is empty'
    else
      strreport = strreport .. ' contains '
      if (numkeynumber==0) then
        strreport = strreport .. 'NO numeric keys'
      end--if
      if (numkeynumber==1) then
        strreport = strreport .. 'a single numeric key equal ' .. tostring (numkey77min)
      end--if
      if (numkeynumber>=2) then
        strreport = strreport .. tostring (numkeynumber) .. ' numeric keys ranging from ' .. tostring (numkey77min) .. ' to ' .. tostring (numkey77max)
      end--if
      strreport = strreport .. ' and ' .. tostring (numkeystring) .. ' string keys and ' .. tostring (numkeycetera) .. ' other keys'
    end--if

    if ((numkeynumber~=0) and (varlim77tab~=0)) then -- !!!FIXME!!!
      strreport = strreport .. ' ### content num keys :'
      numindax = numkey77min
        while true do
          if ((numindax>varlim77tab) or (numindax>numkey77max)) then
            break -- done table
          end--if
          strreport = strreport .. ' ' .. tostring(numindax) .. ' -> ' .. lfdminisani(tostring(vardubious[numindax]),30)
          numindax = numindax + 1
        end--while
    end--if

    if ((numkeystring~=0) and (varlim77tab~=0)) then -- !!!FIXME!!!
      strreport = strreport .. ' ### content string keys :'
    end--if

  else

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

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

  if ((vardescri~='') and boobe77fore) then
    strreport = vardescri .. ' : ' .. strreport -- very last step
  end--if

  return strreport

end--function prdshowvcore

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

-- Local function LFDSHOWVAR

-- Enhance upvalue "qstrtrace" with report about content of a variable
-- including optional full listing of a table with numeric and string keys.     !!!FIXME!!!

-- Depends on procedures :
-- [D] lfdminisani prdshowvcore

-- upvalue "qstrtrace" must NOT be type "nil" on entry (is inited to "<br>")

-- uses upvalue "qboodetrc"

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

  if (qboodetrc) then
    qstrtrace = qstrtrace .. prdshowvcore (varduubious, strnaame, vardeskkri, vartabljjm) .. '.<br>' -- dot added !!!
  end--if

end--function lfdshowvar

-- *****************************
-- *    MATH PROCEDURES [E]    * ---------------------------------------
-- *****************************

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

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

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

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

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

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

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

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

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

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

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

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

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

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

local function prgpokestring (strinpokeout, numpokepoz, numpokeval)

-- Replace single octet in a string.

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

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

local numpokelen = 0

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

return strinpokeout

end--function prgpokestring

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

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

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

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

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

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

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

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

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

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

-- Check whether a given substring can be found at a given
-- position in a longer string (equality valid).

-- Input  : * strbig, strtiny
--          * numpos5sta (ZERO-based)

local function prgcmpsub (strbig, strtiny, numpos5sta)
  local numlen5big = 0
  local numlen5tiny = 0
  local boof5ound = false
  numlen5big = string.len (strbig)
  numlen5tiny = string.len (strtiny)
  if ((numlen5tiny>0) and ((numpos5sta+numlen5tiny)<=numlen5big)) then
    boof5ound = ( strtiny == (string.sub (strbig,(numpos5sta+1),(numpos5sta+numlen5tiny))) )
  end--if
  return boof5ound
end--function prgcmpsub

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

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

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

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

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

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

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

local function prulnutf8char (numbgoctet)
  local numlen1234x = 0
    if (numbgoctet<128) then
      numlen1234x = 1 -- $00...$7F
    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 prulnutf8char

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

local function prusrcasepairrest (strmy4set, num16val4in)

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

-- Input  : * strmy4set -- "eo" or "sv" or "GENE" (no "ASCII" here)
--          * num16val4in -- undecoded UINT16BE value, $C200 ... $DEFF

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

-- Called from PRUCOMPAREUTF8WCI PRUTRISTULETR PRUCASEAIOREST PRUCASEAIOFULL
-- PRUCOMPARECIBEGIN PRICASEADVJAEM2AL PRICASEADVJAEMFBAL PRICASEADVJAEM2FB.

-- This obeys "UTF8UT.TXT".

-- This is useful for UTF8-aware:
-- * adjusting a single letter or text to UPPER or lower case
-- * finding out whether a char is UPPER letter, lower letter, or non-letter
-- * efficient case insensitive comparison (do NOT call this if at least
--   one char is within the ASCII range, or two chars are equal even without
--   case insensitivity)

local num16upper = -1
local num16lower = -1

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

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

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

return num16upper, num16lower

end--function prusrcasepairrest

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

local function prucaseaiorest (strsel5set, strinco5cs, boowup5cas, boodo5all)

-- Adjust case of a single letter or longer string within defined
-- charset (ASCII + selectable extra subset of UTF8, small deltas only).

-- Input  : * strsel5set : "ASCII" (default, empty string or type "nil"
--                         will do too) "eo" "sv" "GENE"
--          * strinco5cs : single unicode letter (1 or 2 octet:s) or
--                         longer string
--          * boowup5cas : for desired output uppercase "true" and for
--                         lowercase "false"
--          * boodo5all  : "true" to adjust all letters, "false"
--                         only beginning letter

-- Output : * strinco5cs

-- Depends on procedures : (this is AIOREST)
-- [U] prulnutf8char prusrcasepairrest
-- [G] prgtestuc prgtestlc prgpokestring
-- [E] mathdiv mathmod

-- This obeys "UTF8UT.TXT".

-- This process never changes the length of a string in octet:s. Empty string
-- on input is legal and results in an empty string returned. When case is
-- adjusted, a 1-octet or 2-octet letter is replaced by another letter of same
-- length. Unknown seemingly valid char:s (1-octet ... 4-octet) are copied.
-- Broken UTF8 stream results in remaining part of the output string (from
-- one char to complete length of the incoming string) filled by "Z".

-- We peek max 2 values per iteration, and change the string in-place, doing
-- so strictly only if there indeed is a change. This is important for LUA
-- where the in-place write access must be emulated by means of a less
-- efficient procedure.

local numlong5den = 0 -- actual length of input string
local numokt6index = 0

local numcxa6unde = 0 -- undecoded UINT16BE of incoming
local numw6upper = 0 -- undecoded UINT16BE of found lower
local numw6lower = 0 -- undecoded UINT16BE of found UPPER

local numlong5bor = 0 -- expected length of single char

local numdel5ta = 0 -- quasi-signed ZERO or +1 or -1 or +32 or -32 or other

local numcha6r = 0 -- UINT8 beginning char
local numcha6s = 0 -- UINT8 later char (BIG ENDIAN, lower value here above)

local boowan6tlowr = false

local boobotch6d = false -- preASSume innocence -- NOT yet botched

  boowup5cas = (boowup5cas==true)
  boodo5all = (boodo5all==true)
  boowan6tlowr = (not boowup5cas)
  numlong5den = string.len (strinco5cs)

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

    if (numokt6index>=numlong5den) then
      break -- done complete string
    end--if
    numdel5ta   = 0 -- preASSume on every iteration
    numlong5bor = 1 -- preASSume on every iteration

    while true do -- inner fake loop (this is AIOREST)

      numcha6r = string.byte (strinco5cs,(numokt6index+1),(numokt6index+1))
      if (boobotch6d) then
        numdel5ta = 90 - numcha6r -- "Z" -- delta must be non-ZERO to write
        break -- fill with "Z" char:s
      end--if
      if ((not boodo5all) and (numokt6index~=0)) then -- loop can skip index ONE
        break -- no change
      end--if
      numlong5bor = prulnutf8char(numcha6r) -- now 1 ... 4 or ZERO
      if ((numlong5bor==0) or ((numokt6index+numlong5bor)>numlong5den)) then
        numlong5bor = 1 -- reassign to ONE !!!
        numdel5ta = 90 - numcha6r -- "Z" -- delta must be non-ZERO to write
        boobotch6d = true
        break -- truncated char or broken stream
      end--if
      if (numlong5bor>=3) then
        break -- no change
      end--if

      if (numlong5bor==1) then
        if (prgtestuc(numcha6r) and boowan6tlowr) then
          numdel5ta = 32 -- ASCII UPPER->lower
        end--if
        if (prgtestlc(numcha6r) and boowup5cas) then
          numdel5ta = -32 -- ASCII lower->UPPER
        end--if
        break -- ASCII, change or no change
      end--if (numlong5bor==1) then

      if (numlong5bor==2) then
        numcha6s = string.byte (strinco5cs,(numokt6index+2),(numokt6index+2)) -- only $80 to $BF
        numcxa6unde = numcha6r * 256 + numcha6s -- UINT16BE
        numw6upper, numw6lower = prusrcasepairrest (strsel5set,numcxa6unde)
        if ((numcxa6unde==numw6upper) and boowan6tlowr) then
          numdel5ta = numw6lower-numw6upper -- UPPER->lower
        end--if
        if ((numcxa6unde==numw6lower) and boowup5cas) then
          numdel5ta = numw6upper-numw6lower -- lower->UPPER
        end--if
      end--if (numlong5bor==2) then

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

    if ((numlong5bor==1) and (numdel5ta~=0)) then
      strinco5cs = prgpokestring (strinco5cs,numokt6index,(numcha6r+numdel5ta))
    end--if
    if ((numlong5bor==2) and (numdel5ta~=0) and (numdel5ta>=-63) and (numdel5ta<=63)) then
      strinco5cs = prgpokestring (strinco5cs,(numokt6index+1),(numcha6s+numdel5ta))
    end--if
    numokt6index = numokt6index + numlong5bor -- advance in incoming string

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

return strinco5cs

end--function prucaseaiorest

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

-- Local function LFIKATALDIGU

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

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

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

-- Local function LFIVALIDATELNKOADV

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

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

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

-- Depends on procedures :
-- [G] prgtestnum prgtestlc

-- 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 -- outer fake loop

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

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

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

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

  return booisvaladv

end--function lfivalidatelnkoadv

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

local function pripl2altwre (strbeforfill, numaskikodo, varsupstitu)  -- !!!FIXME!!! move to [I7]

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

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

-- Output : * strafterfill

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

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

  numlenbigtext = string.len (strbeforfill)

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

return strafterfill

end--function pripl2altwre

-- *******************************************
-- *    HIGH LEVEL STRING PROCEDURES [I5]    * -- surrogate processing
-- *******************************************

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

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

-- Output : * strutf8eo -- UTF8 string

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

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

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

local function prikodeosg (streosurr)

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

  numeoinplen = string.len(streosurr)

  while true do

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

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

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

  end--while

return strutf8eo

end--function prikodeosg

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

local function prhrecusurrstrtab (varinkommen, strlingkod, bookeys)

-- Process (transcode) either a single string, or all string items in a
-- table (even nested) using any type of keys/indexes (such as a holey
-- number sequence and non-numeric ones). Items with a non-string non-table
-- value are kept unchanged. Transcoding of eo and NOPE sv surrogates done via
-- separate procedures, optionally string keys/indexes are transcoded
-- as well.

-- Input  : * varinkommen -- type "string" or "table"
--          * strlingkod -- langcode "eo" or NOPE "sv" to transcode
--                          surrogates, anything else results in just
--                          copying the table
--          * bookeys -- transcode keys too (preferably either "true"
--                       or type "nil")

-- Depends on procedures :
-- [I5] prikodeosg (only if trans of eo X-surrogates desired)
-- [I5] NOPE prikodsvsg
-- [E] mathdiv mathmod (via "prikodeosg" and NOPE "prikodsvsg")

-- Depends on constants :
-- * table "contabtransluteo" inherently holey (via "prikodeosg")
-- * NOPE table "contabtranslutsv"

-- We always fully rebrew tables from scratch, thus do NOT replace
-- single elements (doing so would break "in pairs").

local varnky = 0 -- variable without type
local varutmatning = 0
local boodone = false

  if (type(varinkommen)=='string') then
    if (strlingkod=='eo') then
      varutmatning = prikodeosg (varinkommen) -- surr
      boodone = true
    end--if
    -- if (strlingkod=='sv') then
      -- varutmatning = prikodsvsg (varinkommen) -- surr
      -- boodone = true
    -- end--if
  end--if

  if (type(varinkommen)=='table') then
    varutmatning = {} -- brew new table from scratch
    for k4k,v4v in pairs(varinkommen) do -- nothing done if table empty
      if ((bookeys==true) and (type(k4k)=='string')) then
        varnky = prhrecusurrstrtab (k4k, strlingkod, nil) -- RECURSION
      else
        varnky = k4k
      end--if
      if ((type(v4v)=='string') or (type(v4v)=='table')) then
        v4v = prhrecusurrstrtab (v4v, strlingkod, bookeys) -- RECURSION
      end--if
      varutmatning[varnky] = v4v -- write same or diff place in dest table
    end--for
    boodone = true
  end--if

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

return varutmatning

end--function prhrecusurrstrtab

-- ************************************                   error handling
-- *    HIGH LEVEL PROCEDURES [H4]    * --------------------------------
-- ************************************

local function prhvali1status98code (varvalue)

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

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

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

local function prhconstructerar (numerar3code, boopeek3it)

-- Construct partial error message maybe peeking description.

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

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

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

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

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

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

return stryt3sux

end--function prhconstructerar

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

local function prhbrewerr8subm (numeror8code, strparent8nm, numsub8kodo)

-- Brew error sev huge, submodule, 2 lines, insertable parent.

-- Input  : * numeror8code -- TWO-based error code 2 ... 98 (resistant
--                            against invalid data type, giving "??" on such)
--          * strparent8nm -- name of parent (not used and should be type
--                            "nil" if message does NOT contain "%@")
--          * numsub8kodo  -- 1 ... 98 (resistant against invalid data type,
--                            send type "nil" if no submodule involved)

-- Output : * stryt8sux -- message with HTML

-- Depends on procedures :
-- [H4] prhconstructerar
-- [I7] pripl2altwre
-- [G] prgstringrange
-- [N] prnnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * table contabfel with 2 elements .hubg .huen
-- * table contaberaroj TWO-based (holes permitted)

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

-- We DO NOT eat the name of the guilty submodule since it is included
-- in the error code #E03 #E05 and its message. Also for the submodule
-- we report code only, no description.

local stryt8sux = ''

  stryt8sux = contabfel.hubg .. pripl2altwre(prhconstructerar(numeror8code,true),64,strparent8nm) .. contabfel.huen
  if (mathisintrange(numsub8kodo,1,98)) then
    stryt8sux = stryt8sux .. '<br>' .. contabfel.hubg .. 'Submodule reports ' .. prhconstructerar (numsub8kodo,false) .. contabfel.huen
  end--if

return stryt8sux

end--function prhbrewerr8subm

-- ************************************
-- *    HIGH LEVEL PROCEDURES [H5]    * --------------------------------
-- ************************************

local function prhseizetext (straccesskey)

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

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

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

  local tablg76yleft = {}
  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 strtump      = "" -- temp

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

  -- general num

  local numerr       = 0 -- 1 inter
  local num2statcode = 0
  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 boo

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

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

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

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

  prdtracemsg ('This is "Module:mfarado", requested "detrc" report')

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

  ---- PEEK STUFF ----  !!!FIXME!!!

    -- local strpiklangcode = '' -- "en" privileged site language
    -- local strpikkatns    = '' -- "Category"
    -- local strpikapxns    = '' -- "Appendix"
    -- local strmparent     = '' -- "Template:nope" FULLPAGENAME

  ---- MESSAGES ----

  if (numerr==0) then
    contaberaroj   = prhrecusurrstrtab (contaberaroj, strpiklangcode, nil)
    contabboasting = prhrecusurrstrtab (contabboasting, strpiklangcode, nil)
    contabmosc     = prhrecusurrstrtab (contabmosc, strpiklangcode, nil)
  end--if

  ---- PICK ONE SUBTABLE T76 ----

  -- here risk of #E02 #E03

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

  while true do -- fake loop

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

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

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

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

  lfdshowvar (numerr,'numerr','@Picked subtable T76')
  lfdshowvar (num2statcode,'num2statcode')

  ---- 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 "parentframe=true"

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

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

  strpagenam = '' -- using vartymp here
  if (numerr==0) then -- get pagename (error if bad, silent if absent)
    vartymp = arxsomons['pagenameoverridetestonly']
    if (type(vartymp)=='string') then
      if (prgstringrange(vartymp,1,200)) then -- empty or too long NOT legal
        strpagenam = vartymp
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  strruangna = '' -- using vartymp here
  if (numerr==0) then -- get namespace (silent if bad, silent if absent)
    vartymp = arxsomons['nsnumberoverridetestonly']
    if (prgstringrange(vartymp,1,4)) then -- empty or too long NOT legal
      strruangna = vartymp
    end--if
  end--if

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

  lfdshowvar (numerr,'numerr','@Done with hidden params')

  ---- SEIZE PAGENAME AND NS FROM MW ----

  -- "pagenameoverridetestonly=" "nsnumberoverridetestonly=" processed above

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

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

  if ((numerr==0) and (strpagenam=='')) then -- get pagename (error if bad)
    vartymp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (prgstringrange(vartymp,1,200)) then -- empty or too long NOT legal
      strpagenam = vartymp -- cannot be left empty
    else
      numerr = 1 -- #E01 internal
    end--if
  end--if

  if ((numerr==0) and (strruangna=='')) then -- get namespace (silent if bad)
    vartymp = mw.title.getCurrentTitle().namespace -- type is "number"
    if (mathisintrange(vartymp,0,9999)) then -- negative NOT legal but silent
      strruangna = tostring(vartymp) -- can be left empty, check below required
    end--if
  end--if

  lfdshowvar (numerr,'numerr','@Pagename and namespace seized')
  lfdshowvar (strpagenam,'strpagenam')
  lfdshowvar (strruangna,'strruangna')

  ---- CHECK NS ----

  if ((numerr==0) and (strruangna~='14')) then -- must be "Category:"
    numerr = 10 -- #E10 called from wrong ns
  end--if

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

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

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

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

  ---- PROCESS ANON PARAMS ----

  if ((numerr==0) and (arxsomons[2]~=nil)) then
    numerr = 8 -- #E08 too many  !!!FIXME!!! rework to #E06
  end--if
  if (numerr==0) then
    vartymp = arxsomons[1]
    booiskay = (vartymp=='K')
    if ((not booiskay) and (vartymp~=nil)) then
      numerr = 17 -- #E17 only "K" is legal
    end--if
  end--if

  lfdshowvar (numerr,'numerr','@Processed anon params')
  lfdshowvar (booiskay,'booiskay')

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

  do -- scope
    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 numtymp   = 0

    while true do -- outer fake loop

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

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

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

    strkodbah = '' -- read char by char until "- " DASHSPACE reached or error
    numlenko = 0 -- valid values are 2...10 but later further checks follow
    while true do -- inner genuine loop
      if ((numposnam+1)>=numlong) then -- "numposnam" is ZERO-based
        numerr = 15 -- #E15 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 = 15 -- #E15 too long monstercode "zh-min-nan-max" :-D
        break
      end--if
      strkodbah = strkodbah .. string.char(numoct)
      numlenko = numlenko + 1
    end--while -- inner genuine loop
    if (numerr~=0) then
      break -- do not forget to exit outer loop too
    end--if

    lfdshowvar (strkodbah,'strkodbah','@Langcode (extraction up to 10 char:s, not yet validated)')

    if (not lfivalidatelnkoadv(strkodbah,false,false,conbookodlng,conboomiddig,false)) then
      numerr = 11 -- #E11 bad langcode
      break
    end--if

    prdtracemsg ('Langcode is structurally valid but not yet sure whether known')

    numtymp = 0
    if (numowntyp==1) then
      if (prgcmpsub(strpagenam,"enhavanta morfemon ",numposnam)) then
        numtymp = 19
      end--if
    end--if
    if (numowntyp==2) then
      if (prgcmpsub(strpagenam,"enhavanta vorton ",numposnam)) then
        numtymp = 17
      end--if
    end--if
    if (numowntyp==3) then
      if (prgcmpsub(strpagenam,"enhavanta ",numposnam)) then
        numtymp = 10
      end--if
    end--if
    if (numtymp==0) then
      numerr = 15 -- #E15 not found
      break
    end--if
    numposnam = numposnam + numtymp -- jump over it

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

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

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

    end--if

    if ((numposnam+3)>numlong) then -- "numposnam" is ZERO-based
      numerr = 15 -- #E15 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 prgtestuc(numodt)) then
      numskript = numodt
      numlong = numlong - 2 -- trim it off

      prdtracemsg ('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 = 15 -- #E15 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

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

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

  end--do scope

  lfdshowvar (numerr,'numerr','after dissection')

  ---- PEEK LANGNAME VIA T76 ----

  if (numerr==0) then
    strnambah = tablg76yleft[strkodbah]
    booknown = (type(strnambah)=='string') and (strnambah~='-') and (strnambah~='=') -- "false" is criminal
  end--if

  lfdshowvar (numerr,'numerr','@After peeking langname from "tablg76yleft"')
  lfdshowvar (strnambah,'strnambah')
  lfdshowvar (booknown,'booknown')

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

  -- submodule "rooteo" 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 = lfikataldigu(strpikkatns,contabboasting[10],strmorfrnd) -- got bad "K" but remaining 4 are met

    else

      strtump = '' -- preASSume
      if (qboodetrc) then
        strtump = 'true' -- also from submodule
      end--if
      tabrooteo = qeorooteo.ek { args = { ['in']=strpagenam , ['givetable']='true' , ['detrc']=strtump } }
      if (type(tabrooteo)=='table') then
        num2statcode = tabrooteo[0]
        if (num2statcode==0) then
          strdetaleo = tabrooteo[3] -- reduced
          strkatojeo = tabrooteo[2]
        else
          numerr = 5 -- #E05 other mo, nombrigita !!!FIXME!!! use prhvali1status98code
        end--if
        if (qboodetrc) then
          lfdimportreport (tabrooteo[4]) -- import it
        end--if
      else
        numerr = 4 -- #E04 other mo, "ne redonis tabelon"
      end--if

    end--if (booiskay) else

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

  lfdshowvar (numerr,'numerr','@Submodule "rooteo" maybe called')
  lfdshowvar (strdetaleo,'strdetaleo')
  lfdshowvar (strkatojeo,'strkatojeo')

  ---- WHINE IF YOU MUST #E02...#E98 WITH TWO SUBMODULES ----

  -- reporting of errors #E02...#E98 depends on uncommentable
  -- stuff and on pickable "strmparent"

  if ((numerr>=2) and (numerr<=98)) then
    if ((numerr==3) or (numerr==5)) then -- #E03 #E05
      strviserr = prhbrewerr8subm(numerr,('"'..strmparent..'"'),num2statcode) -- 2 lines
    else
      strviserr = prhbrewerr8subm(numerr,('"'..strmparent..'"'),nil) -- no code from submo, one line only
    end--if
  end--if

  ---- 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 = prhseizetext (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 "strpikapxns" 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 ' .. lfikataldigu(strpikapxns,(prucaseaiorest('eo',strnambah,true,false)),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) .. ' (' .. prhseizetext (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.matabg .. contabhtml.hidden .. contabboasting [numowntyp] .. strmorphdesk .. ' ' .. strmorfio .. strlingvoo .. strskripto .. '.' .. contabhtml.lowend
  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 "strpikkatns" is cat prefix and excludes the colon ":"

  -- brew "__HIDDENCAT__" as well as 2 cat insertions (not if langcode
  -- 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 .. lfikataldigu(strpikkatns,'Vortfarado',strmorfrnd)
      strinvkat = strinvkat .. lfikataldigu(strpikkatns,('Vortfarado (' .. strnambah .. ')'),strmorfrnd)
    end--if
  end--if

  ---- RETURN THE 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 exporttable.ek

  ---- RETURN THE OUTER TABLE ----

return exporttable