Saltu al enhavo

Modulo:kat

El Vikivortaro
 MODULO
Memtesto disponeblas sur la dokumentaĵa subpaĝo.
Ĉi tiu modulo estas multfoje bindita.
Se vi konas la eblajn sekvojn, tiam vi povas zorgeme ekredakti.
Se vi ne kuraĝas redakti tiam vi povas proponi la deziratan ŝanĝon en la diskutejo.



--[===[

MODULE "KAT"

"eo.wiktionary.org/wiki/Modulo:kat" <!--2025-Aug-16-->

Purpose: automatically generated content for category pages

Utilo: auxtogenerita enhavo por kategoriaj pagxoj

Manfaat: isi dibangkitkan secara otomatis untuk halaman kategori

Syfte: automatiskt genererat innehaall foer kategorisidor

Used by templates / Uzata far sxablonoj:
* only "kat" (not to be called from any other place)

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

Required images:
* "Vista-folder open.png"
* "Hidden icon.png"
* "User-invisible.svg"
* "Nuvola filesystems folder template.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: * no ordinary parameters
          * 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,
            or red error and tracking category part

All relevant information is peeked from the pagename and thus
ordinary parameters are not used.

To be called from category namespace only.

This module reads out the pagename, dissects it consulting relevant
lists (both built-in and via other modules), displays a suitable header
for the category, including a TOC if appropriate, hides the category
if appropriate, and cares about insertion into parent categories.

There are three steps all controlled by ONE common LUA table with patterns:
* identify the pagename resulting in a LUA table with details
* brew the visible text later showed in a box (plus determine icon and colour)
* brew the parent cat insertions

For the procesing here, following details are defined:
* LK langcode
* LL other langcode
* LN langname short (for example "dana" or "Ido")
* LU langname short uppercased (for example "Dana" or "Ido")
* LO langname short not own (empty if own)
* LY langname long (for example "bahasa Swedia")
* LV langname short uppercased not own (empty if own)
* LZ langname long not own (empty if own)
* WC word class (for example "substantivo")
* WD other word class
* GR grammar detail such as "uncountable noun"
* MI other detail such as "misspelling"
* WU word class uppercased (for example "Substantivo")
* TO topic
* TU topic uppercased
* LL language level 0...4 or 0...5 or "D"
* MT mortyp C I M N P U
* SK script code T S K P H C L
* SC langcode + script code zhT zhS zhK zhP jaK jaH jaR hrC hrL
* FR fragment (for example "peN-...-an" or "abelujo")
* LA link to appendix page raw
* LB link to appendix page showing langname short
* LJ link to appendix page showing langname short adjectivized and plural
Those are used in several contexts, but not the full set is applicable in
every context:
* pattern for parsing the pagename, they apprear in the string prefixed by "@"
* pattern for generating visible text, they apprear in the request string
  prefixed by "@" fed into PRIPL5NAVARE
* pattern for generating parent categories, they apprear in the request string
  prefixed by "@" fed into PRIPL5NAVARE
* LUA table passed from the parsing stage (Module:parsecatname) into the
  generation stage, the strings listed above appear as string keys without "@"

Types of categories "G H U T" by content and icon showed are:
* G generic
* H hidden
* U user
* T template

Types of categories by title structure are:
* simple raw such as "Substantivo" or "Kemio" or "Sveda"
  -> no fixed pattern at all
  -> cannot be validated without running through all lists (languages,
     word classes, topics)
* double raw such as "Substantivo (sveda)" or "Kemio (sveda)"
  -> insufficient fixed pattern such as "("...")"
  -> cannot be validated without running through all lists (languages,
     word classes, topics)
* mostly identifiable such as "El Sanskrito"
  -> contain a pattern than is sufficient for reliable identification
     together with absence of other pattern such as "("...")"
* reliably identifiable such as "Vortaro (indonezia)" or "El Sanskrito (Ido)"

Pattern for parsing the pagename can contain following variable elements:
* LK, LN, LU, LY, WC, GR, MI, WU, TO, TU, LL, MT, SK, SC, FR

The variable elements belong to three groups:
* directly listed: LK/LL LN WC/WD GR MI TO LL MT SK SC
* indirectly listed: LU LY WU TU
* fully free and not listed: FR

The identified title is reported in the form of a LUA table. Details are
standardized to a reduced set
* LK (convert from LN LU LY)
* WC (convert from WU)
* GR
* MI
* TO (convert from TU)
* LL
* MT
* SC
* FR

Unfortunately certain elements can occur two times, namely language
(for etymological cat:s) and word class (for derived words). The final
LUA table contains following string keys (type "nil" if unused):
* type -- one of many
* LK
* LL -- other language
* WC
* WD -- other word class
* GR
* MI
* TO
* LL
* MT
* SK -- without attached langcode
* FR
Those elements are used to brew the text (plus determine the icon and
colour) as well as parent categories.

Unfortunately wikis define some dialects and scripts that we have to support
natively here ie circumventing our central language and dialect tables:
* en-GB, en-CA (but NOT "en-US")
* de-AT
* zh-Hans, zh-Hant

So far 6 types of catname patterns after "Kategorio:" are recognized:
* G "El la anglosaksa" "El Sanskrito"
    * has opposite cat
    * in cat "Sanskrito" no hint and
      in cat "Laux fonta lingvo" langname is hint
* G "Laux stilo (indonezia)"
* H "Tradukoj (indonezia)" needs __HIDDENCAT__ except "Tradukoj (Esperanto)"
* U "Vikivortaristo mi"
    * in cat "Vikivortaristo laux lingvo" langcode is hint
* U "Vikivortaristo mi-D"
    * in cat "Vikivortaristo mi" level is hint
* G "Vortaro (indonezia)"
* T "SXablonaro -ay-"                                                 !!!FIXME!!! not yet "kat-specifa-lng"
    * in cat "Ajmara" no hint and
      in cat "SXablonaro pri specifa lingvo" langcode is hint
* H "Pagxoj kun sondosiero (tamila)"                                  !!!FIXME!!! not yet "kat-sono"
    * in cat "Tamila" with fixed hint "sond" and
      in cat "Pagxoj kun sondosiero" langname is hint

Tracking categories:
* #E02 ... #E05
* #E05 #E10 "Kategori:Erara uzo de sxablono" &
            "Kategori:Erara uzo de sxablono (kat)"
* #E17 "Kategori:Auxtokato kun nesubtenata pagxonomo"
* #E19 "Kategori:Auxtokato kun nekonata lingvonomo"

Certain pages with content "{{kat}}" can be created automatically
after editing
* MediaWiki:Babel-autocreate-text-main
* MediaWiki:Babel-autocreate-text-levels
but then we must detect ns 8 and display "{{kat}}" instead of doing
the default work, since pages in ns 8 STILL ARE PARSED.

]===]

local exporttable = {}

require('strict')

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

-- uncommentable strings (core site-related features)

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

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

-- strings (error circumfixes)

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

-- uncommentable table (error messages)

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

local contaberaroj = {}
      contaberaroj[ 5] = 'Parametroj estas maldezirindaj por %@, legu gxian dokumentajxon'   -- EO #E05 change to unknown param
        -- contaberaroj[ 5] = 'Parameter tidak diterima oleh %@, bacalah dokumentasinya'        -- ID #E05 !!!FIXME!!!
      contaberaroj[10] = 'Erara nomspaco (estu 14, "Kategorio:") por %@'                     -- EO #E10
        -- contaberaroj[10] = 'Ruang nama salah (sebaiknya 14, "Kategori:") untuk %@'           -- ID #E10
      contaberaroj[13] = 'Forjxetas pagxonomon pro . :: // -- (( )) () trovite far %@'               -- EO #E13
        -- contaberaroj[13] = 'Nama halaman ditolak oleh karena . :: // -- (( )) () ditemukan oleh %@'  -- ID #E13
      contaberaroj[14] = 'Forjxetas pagxonomon pro parentezumo en %@'                        -- EO #E14
        -- contaberaroj[14] = 'Nama halaman ditolak oleh karena perkurungan dalam %@'           -- ID #E14
      contaberaroj[17] = 'Erara pagxonomo (neniu el 6) por %@'                               -- EO #E17
        -- contaberaroj[17] = 'Nama halaman salah (tidak satu dari 6) untuk %@'                 -- ID #E17
      contaberaroj[19] = 'Nekonata lingvonomo por %@'                                        -- EO #E19
        -- contaberaroj[19] = 'Nama bahasa yang tidak dikenal untuk %@'                         -- ID #E19

-- uncommentable table (boasting)

local contabboasting = {}

      contabboasting['g0'] = 'En aux sub cxi tiu'
      contabboasting['g1'] = 'kategorio trovigxas'
      contabboasting['g2'] = 'lingvokodo'

      contabboasting['elel0'] = 'kapvortoj derivitaj el'
      contabboasting['elel1'] = 'La kontrauxa kategorio estas'

      contabboasting['stil0'] = 'subkategorioj por kapvortoj en la lingvo'
      contabboasting['stil1'] = 'laux stilo AKK lingvoregistro'

      contabboasting['trad'] = 'kapvortoj kun tradukoj al la lingvo'

  contabboasting[30] = 'vortaroj (aliaj ol tuta vikivortaro mem), kiuj specifas por la lingvo'
  contabboasting[31] = 'La esperantan vikivortaron vi jam uzas'  -- if code is "eo"
  contabboasting[32] = 'La'                                      -- if it exists
  contabboasting[33] = 'Bedauxrinde ne disponeblas'              -- if it lacks
  contabboasting[34] = 'vikivortaro'                    -- in both cases
  contabboasting[35] = 'provu'                          -- if it lacks
  contabboasting[36] = 'kaj ecx la kodo estas okupita'  -- if it lacks and ... (for example "als", has "??" in /c6/)

  contabboasting.isnl = 'subkategorioj por uzantoj kiuj indikis sian nivelon en'

  contabboasting.islv0 = 'uzantoj kiuj indikis sian nivelon en'
  contabboasting.islv1 = 'je'

-- main list with catname patterns for the parsing stage and more  !!!FIXME!!! LEGACY DEPRECATED

  -- * [1] parsing method
  --       * 3 -> sepbracket
  --       * 4 "el"-type with "la" trouble
  --       * 5 isto with level
  --       * 6 isto without level
  -- * [2] pattern for parsing method (outer string for 3 | unused for 4 5 6)
  -- * [3] unused forever
  -- * [4] (char) type by content and icon G H U missing T
  -- * [5] (boo) categorize in lang (for example in "Kategori:Sveda" or in "Kategori:Greka antikva") with no hint
  -- * [6] (char) type of hint in extra parent cat
  --   * ""  empty string to deactive this cat
  --   * "N" langname lowercase -- "Ido -> ido"
  --   * "K" langcode
  --   * "L" level -- "0" "1" "2" "3" "4" "D" -> "d"
  -- * [7] name of extra parent cat (by lang or by level or by something else)
  --       (empty if none, but [6] is the control, also empty if cat
  --       used but name not needed)

local contabtypesofcade = {}
  contabtypesofcade['elel'] = {4, '',           '', 'G', true,  'N', 'Laux fonta lingvo'}           -- "El la anglosaksa"
  contabtypesofcade['stil'] = {3, 'Laux stilo', '', 'G', true,  'N', 'Laux stilo kaj lingvo'}       -- "Laux stilo (indonezia)"
  contabtypesofcade['trad'] = {3, 'Tradukoj',   '', 'H', true,  'N', 'Laux traduko'}                -- "Tradukoj (indonezia)"
  contabtypesofcade['isnl'] = {6, '',           '', 'U', false, 'K', 'Vikivortaristo laux lingvo'}  -- "Vikivortaristo mi"
  contabtypesofcade['islv'] = {5, '',           '', 'U', false, 'L', ''}                            -- "Vikivortaristo mi-D"
  contabtypesofcade['varo'] = {3, 'Vortaro',    '', 'G', true,  'N', 'Vortaro laux lingvo'}         -- "Vortaro (indonezia)"

-- main list with generation patterns  !!!FIXME!!! NEW this is NOT implemented yet

-- * [1] (string) deviations (for example prohibition against
--                certain languages or allowed extra ones)
-- * [2] (char) output type by content and icon G H U T
-- * [3] reserved
-- * [4]...[7] (n*string) patterns for parent cat:s (optional hint separated
--              by wall "|", hint goes always though PRIBREWCATHINT)

local contabgene = {}
contabgene.elel = {''     , 'G', '', '@LU', 'Laux fonta lingvo|@LN'           } -- "El la anglosaksa"
contabgene.stil = {''     , 'G', '', '@LU', 'Laux stilo kaj lingvo|@LN'       } -- "Laux stilo (indonezia)"
contabgene.trad = {'noeo' , 'H', '', '@LU', 'Laux traduko|@LN'                } -- "Tradukoj (indonezia)"
contabgene.isnl = {''     , 'U', '',        'Vikivortaristo laux lingvo|@LK'  } -- "Vikivortaristo mi"
contabgene.islv = {''     , 'U', '',        'Vikivortaristo@LK|@LL'           } -- "Vikivortaristo mi-D"
contabgene.varo = {''     , 'G', '', '@LU', 'Vortaro laux lingvo|@LN'         } -- "Vortaro (indonezia)"

-- tracking cat:s --

local contabtrako = {}
      contabtrako.submod = 'Submodulo fusxigxis (kat)'           -- #E02 ... #E05
      contabtrako.nesupa = 'Auxtokato kun nesubtenata pagxonomo' -- #E17
      contabtrako.nekoli = 'Auxtokato kun nekonata lingvonomo'   -- #E19
      contabtrako.cetgen = 'Erara uzo de sxablono'
      contabtrako.cetkat = 'Erara uzo de sxablono (kat)'

-- icons and descriptions for types of category by content

local contabicon = {} -- G H U T
contabicon['G'] = 'Vista-folder open.png'  -- generic
contabicon['H'] = 'Hidden icon.png'        -- hidden
contabicon['U'] = 'User-invisible.svg'     -- user, alternative image "Nuvola apps personal unisex.svg"
contabicon['T'] = 'Nuvola filesystems folder template.png' -- template

local contabdesc = {} -- G H U T
      contabdesc['G'] = ''
      contabdesc['H'] = 'kasxita'
      contabdesc['U'] = 'uzanta'
      contabdesc['T'] = 'sxablonara'

  -- table (HTML)

local contabhtml = {}
contabhtml.tabbeg = '<table style="border:1px solid #A0A0A0; background:#F0F0F0; width:95%; margin:0.6em auto 0.6em auto; padding:0.6em; text-align:justify;">'
contabhtml[3] = '<tr><td style="width:30px;">[[File:'
contabhtml[4] = '|50px]]</td><td style="padding-left:1em;">'
contabhtml.lowend = '</td></tr></table>'

-- uncommentable (override)

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

local contabovrd = {}
  -- contabovrd.sitelang = 'eo'                                     -- "en"
  -- contabovrd.sitelang = 'id'
  -- contabovrd.katprefi = 'Kategorio'                              -- "Category"
  -- contabovrd.katprefi = 'Kategori'
  -- contabovrd.apxprefi = 'Aldono'                                 -- "Appendix"
  -- contabovrd.apxprefi = 'Lampiran'
  -- contabovrd.parentfn = string.char(0xC5,0x9C) .. 'ablono:nope'  -- "Template:nope" (!!! no surr translation !!!)
  -- contabovrd.defortrc = true                                     -- force "detrc=true"

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

  -- SPECIAL VAR:S

local qldingvoj = {}     -- type "table" and nested
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 IMPORT ONE VIA LOADDATA ----

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

-- ******************************
-- *    DEBUG PROCEDURES [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 to this

-- no size limit

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

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

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

-- To be called from "lfdshowvcore" <- "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

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

-- Local function LFDSHOWVCORE

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

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

local function lfdshowvcore (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 numkauntkyys = 0
  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 -- no listing of tables
  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!!! see "listtable.lua"
      strreport = strreport .. ' ### content num keys :'
      numindax = numkey77min
      numkauntkyys = 0
        while true do
          if ((numkauntkyys>=varlim77tab) or (numindax>numkey77max)) then
            break -- done table
          end--if
          strreport = strreport .. ' ' .. tostring(numindax) .. ' -> ' .. lfdminisani(tostring(vardubious[numindax]),30)
          numindax = numindax + 1 -- BEWARE does not always start from ZERO
          numkauntkyys = numkauntkyys + 1
        end--while
    end--if

    if ((numkeystring~=0) and (varlim77tab~=0)) then -- !!!FIXME!!! missing content
      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 lfdshowvcore

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

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

-- 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 .. lfdshowvcore (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] ----

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

local function prnnumto2digit (numzerotoninetynine)

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

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

  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 PROCEDURES [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 LFGPOKESTRING

-- Replace single octet in a string.

-- Input  : * strinpokeout -- empty legal
--          * numpokepoz   -- ZERO-based, out of range legal
--          * numpokeval   -- new 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 function lfgpokestring (strinpokeout, numpokepoz, numpokeval)
  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,(numpokelen-1)) .. string.char(numpokeval)
    end--if
  end--if (numpokelen>=2) then
  return strinpokeout
end--function lfgpokestring

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

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

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

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

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

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

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

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

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

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

---- UTF8 PROCEDURES [U] ----

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

-- Local function LFULNUTF8CHAR

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

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

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

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

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

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

local function lfusearchcasepair (strmyset, num16valin)

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

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

-- Output : * num16upper, num16lower -- ZERO if nothing found

-- Called from lfucompareutf8wci lfutristletr lfucaseallinone
-- lfucomparecibegin lficaseadvjaem.

  local num16upper = 0
  local num16lower = 0

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

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

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

  return num16upper, num16lower

end--function lfusearchcasepair

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

local function lfutristletr (strsel4set, strin4trist)

-- Evaluate char to tristate result (no letter vs uppercase letter
-- vs lowercase letter) within defined charset (ASCII + selectable
-- extra subset of UTF8).

-- Input  : * strsel4set  : "ASCII" (default, empty string or type "nil"
--                          will do too) "eo" "sv" (value "GENE" NOT here)      !!!FIXME!!! is GENE supposed to be supp or not ??
--          * strin4trist : single unicode char (1 or 2 octet:s) or
--                          longer string

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

-- Depends on procedures : (this is LFUTRISTLETR)
-- [U] lfulnutf8char lfusearchcasepair
-- [G] lfgtestuc lfgtestlc

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

local numlong4den = 0 -- actual length of input string

local numcxa4unde = 0 -- undecoded UINT16BE of incoming
local numw4upper = 0 -- undecoded UINT16BE of found lower
local numw4lower = 0 -- undecoded UINT16BE of found UPPER

local numlong4bor = 0 -- expected length of single char
local numcha4r = 0 -- UINT8 beginning char
local numcha4s = 0 -- UINT8 later char (BIG ENDIAN, lower value here above)

local numtype4x = 0 -- final result to be returned, preASSume invalid

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

    numlong4den = string.len (strin4trist)
    if (numlong4den==0) then
      break -- bad string length
    end--if
    numcha4r = string.byte (strin4trist,1,1)
    numlong4bor = lfulnutf8char(numcha4r)
    if ((numlong4bor==0) or (numlong4den<numlong4bor)) then
      break -- truncated char or invalid
    end--if

    if (numlong4bor==1) then
      if (lfgtestuc(numcha4r)) then
        numtype4x = 1
      end--if
      if (lfgtestlc(numcha4r)) then
        numtype4x = 2
      end--if
      break -- success ASCII
    end--if

    if (numlong4bor==2) then
      numcha4s = string.byte (strin4trist,2,2) -- only $80 to $BF cannot ovrfl
      numcxa4unde = numcha4r * 256 + numcha4s -- UINT16BE
      numw4upper, numw4lower = lfusearchcasepair (strsel4set,numcxa4unde)
      if (numcxa4unde==numw4upper) then
        numtype4x = 1
      end--if
      if (numcxa4unde==numw4lower) then
        numtype4x = 2
      end--if
    end--if

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

return numtype4x

end--function lfutristletr

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

local function lfucaserest (strsel6set, strinco6cs, boowup6cas, boodo6all)

-- Adjust (restricted) case of a single letter or longer string within
-- defined charset (ASCII + selectable minimal subset of UTF8). (this is REST)

-- Input  : * strsel6set : "ASCII" (default, empty string or type "nil"
--                         will do too) "eo" "sv" (value "GENE" NOT here)
--          * strinco6cs : single unicode letter (1 or 2 octet:s) or
--                         longer string
--          * boowup6cas : for desired output uppercase "true" and for
--                         lowercase "false"
--          * boodo6all  : "true" to adjust all letters, "false"
--                         only beginning letter

-- Output : * strinco6cs

-- Depends on procedures : (this is REST)
-- [U] lfulnutf8char lfusearchcasepair
-- [G] lfgpokestring lfgtestuc lfgtestlc

-- 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 numlong6den = 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 numlong6bor = 0 -- expected length of single char

local numdel6ta = 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 boodo6adj = true -- preASSume innocence -- continue changing
local boobotch6d = false -- preASSume innocence -- NOT yet botched

  boowup6cas = (boowup6cas==true)
  boodo6all = (boodo6all==true)
  boowan6tlowr = (not boowup6cas)
  numlong6den = string.len (strinco6cs)

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

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

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

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

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

      if (numlong6bor==2) then
        numcha6s = string.byte (strinco6cs,(numokt6index+2),(numokt6index+2)) -- only $80 to $BF
        numcxa6unde = numcha6r * 256 + numcha6s -- UINT16BE
        numw6upper, numw6lower = lfusearchcasepair (strsel6set,numcxa6unde)
        if ((numcxa6unde==numw6upper) and boowan6tlowr) then
          numdel6ta = numw6lower-numw6upper -- UPPER->lower
        end--if
        if ((numcxa6unde==numw6lower) and boowup6cas) then
          numdel6ta = numw6upper-numw6lower -- lower->UPPER
        end--if
      end--if (numlong6bor==2) then

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

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

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

return strinco6cs

end--function lfucaserest

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

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

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

-- Local function LFIKATPALDIGU

-- Brew cat insertion (no extra colon ":") or link to cat (with extra
-- colon ":") or link to page (appendix, other ns, even ns ZERO) from
-- 3 elements with optimization.

-- Input  : * strprephyx -- ns prefix without colon, empty or
--                          non-string if not desired ie ns ZERO
--          * strkataldnomo
--          * strhintvisi -- right part, empty or non-string if not desired
--          * numkattxtrakol -- ZERO for non-cat, ONE for cat insertion
--                              (needed for optimization), TWO for extra
--                              colon ie "colon rule" link to cat

local function lfikatpaldigu (strprephyx, strkataldnomo, strhintvisi, numkattxtrakol)
  local strtigatipwiki = ''
  if (type(strprephyx)~='string') then -- optional
    strprephyx = ''
  end--if
  if (type(strhintvisi)~='string') then -- optional
    strhintvisi = ''
  end--if
  if ((numkattxtrakol==1) and (strhintvisi==strkataldnomo)) then
    strhintvisi = '' -- optimize: default is without ns prefix for cat:s
  end--if
  if (strprephyx~='') then
    strkataldnomo = strprephyx .. ':' .. strkataldnomo -- now prefix plus name
  end--if
  if ((numkattxtrakol~=1) and (strhintvisi==strkataldnomo)) then
    strhintvisi = '' -- optimize: default is with ns prefix if such is present
  end--if
  if (numkattxtrakol==2) then
    strkataldnomo = ':' .. strkataldnomo -- ":Category"... apply "colon rule"
  end--if
  if (strhintvisi=='') then
    strtigatipwiki = '[[' .. strkataldnomo .. ']]'
  else
    strtigatipwiki = '[[' .. strkataldnomo .. '|' .. strhintvisi .. ']]'
  end--if
  return strtigatipwiki
end--function lfikatpaldigu

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

local function pribrewcathint (strsignaro, strhinthink, bookeepuk)  -- !!!FIXME!!! REPLACE

-- Brew sorting hint/key for wiki category by lowering all letters and
-- removing junk chars other than known letters and numbers. All ASCII and
-- selected non-ASCII letters do count as such. Particularly ban spaces
-- and dashes. Controllably UTF8-aware.

-- Input  : * strhinthink -- empty is useless but cannot
--                           cause major harm
--          * strsignaro  -- "ASCII" (default, empty string or type "nil"  !!!FIXME!!!
--                           will do too) "eo" "sv" NOPE "GENE"
--          * bookeepuk   -- keep unknown non-ASCII (by default dropped
--                           for sets other than "GENE", for NOPE "GENE"
--                           always kept)

-- Output : * strhasiil -- risk of empty

-- Depends on procedures : (restricted LFUCASEREST)
-- [U] lfulnutf8char lfutristletr lfucaserest
-- [G] lfgpokestring lfgtestnum lfgtestuc lfgtestlc
-- [E] mathdiv mathmod

-- Simplified strategy:
-- * numbers unchanged
-- * ASCII lowercase unchanged
-- * ASCII uppercase lowered
-- * unknown non-ASCII dropped for sets other than "GENE" unless bookeepuk
-- * non-ASCII sent to case sub with attempt to lower
-- * everything else dropped
-- * broken stream aborts and gives empty result


  local strhasiil = ''
  local strnew7char = ''
  local numstrleon = 0
  local numeindx = 1 -- ONE-based
  local numczaar = 0
  local numczanx = 0 -- pre-picked next char
  local numettfyra = 0

  numstrleon = string.len (strhinthink)

  while true do -- outer genuine loop over source string

    if (numeindx>numstrleon) then
      break -- empty input is useless but cannot cause major harm
    end--if

    numczaar = string.byte (strhinthink,numeindx,numeindx)
    numeindx = numeindx + 1 -- do INC here
    numettfyra = lfulnutf8char (numczaar) -- 1...4 or ZERO on error
    if (numettfyra==0) then
      strhasiil = ''
      break -- broken stream -> bugger all -- exit outer loop
    end--if
    numczanx = 0 -- preASSume none
    if (numeindx<=numstrleon) then -- pick but do NOT INC
      numczanx = string.byte (strhinthink,numeindx,numeindx)
    end--if

    while true do -- inner fake loop
      if (numettfyra==1) then
        if (lfgtestnum(numczaar) or lfgtestlc(numczaar)) then
          strnew7char = string.char (numczaar)
          break -- numbers and ASCII lowercase pass unchanged
        end--if
        if (lfgtestuc(numczaar)) then
          strnew7char = string.char (numczaar+32) -- lower ASCII letter
          break -- lower it
        end--if
      end--if (numettfyra==1) then
      if ((numettfyra==2) and ((strsignaro=='eo') or (strsignaro=='sv') or (strsignaro=='GENE'))) then
        strnew7char = string.char(numczaar,numczanx) -- preASSume unchanged
        if ((not bookeepuk) and (strsignaro~='GENE')) then -- drop unknown
          if (lfutristletr(strsignaro,strnew7char)==0) then -- no known letter
            strnew7char = '' -- discard char
            break
          end--if
        end--if
        -- if (strsignaro=='GENE') then -- below: want lower, do ONE only
          -- strnew7char = lfucasegene (strnew7char,false,false)
        -- else
          strnew7char = lfucaserest (strsignaro,strnew7char,false,false)
        -- end--if
        break -- done with lowercased or unchanged char
      end--if ((numettfyra==2) and ...
      strnew7char = '' -- anything else -> discard char
      break -- finally to join mark
    end--while -- inner fake loop -- join mark

    strhasiil = strhasiil .. strnew7char -- ASCII char or UTF8 char or ""
    numeindx = numeindx + numettfyra - 1 -- do ADD here

  end--while -- outer genuine loop over source string

  return strhasiil

end--function pribrewcathint

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

-- Local function LFISEPBRACKET

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

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

-- Output : * boosaxes, strinner, strouter

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

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

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

local function lfisepbracket (strsep33br, numxmin33len)

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

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

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

  return boosaxes, strinner, strouter

end--function lfisepbracket

-- *******************************************
-- *    HIGH LEVEL STRING PROCEDURES [I7]    * -- placeholderism
-- *******************************************

local function pripl2altwre (strbeforfill, numaskikodo, varsupstitu)

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

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

-- Output : * strafterfill

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

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

  numlenbigtext = string.len (strbeforfill)

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

return strafterfill

end--function pripl2altwre

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

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

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

-- Output : * strutf8eo -- UTF8 string

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

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

-- To be called ONLY from "prhrecusurrstrtab".

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

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

-- Local function LFIAKUPLUEO

-- Accusativize or pluralize an Esperanto term (minimal version).

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

local function lfivarilingvonomo (strlang29name, numtip29mon)

-- Variate langname in Esperanto to desired form (short, long with "la",
-- long with "lingvo", long with both) and word class (root, AJ, AV).

-- Input  : * strlang29name : such as "Ido" or "dana" or "samea suda"
--          * numtip29mon   : 0 native -- 1 la (yes AJ, no SB) --
--                            2 li (yes AJ, no SB) -- 3 lu (yes AJ, no SB) --
--                            4 sf (root) -- 5 aj -- 6 av (genuine, surrogate)

-- Depends on procedures :
-- [U] lfulnutf8char lfusearchcasepair lfucaserest
-- [G] lfgpokestring lfgtestuc lfgtestlc

-- note that
-- * for types 1 "la" and 2 "li"
--   * we add the stuff to native AJ only, never to native SB
-- * for type 5 ie "aj"
--   * fails for "Malnova Volapuko"
-- * for type 6 ie "av"
--   * we change word class for a singleword SB langname
--     resulting in something like "ide"
--   * we change word class for a singleword AJ langname
--     resulting in something like "svede"
--   * we can add "la" to a multiword langname resulting in
--     something like "en la samea suda" or "en Malnova Volapuko"

  local boois29noun = false
  local boomul29words = false

  if ((numtip29mon>=1) and (numtip29mon<=6) and (string.len(strlang29name)>=3)) then

    boois29noun = (string.byte(strlang29name,-1,-1)==111) -- "o"
    boomul29words = (string.find(strlang29name,' ',1,true)~=nil) -- plain text

    if ((numtip29mon>=4) and (not ((numtip29mon==6) and boomul29words))) then
      strlang29name = lfucaserest ('eo',string.sub(strlang29name,1,-2),false,false) -- cut out root and lower it
    end--if

    if (((numtip29mon==1) or (numtip29mon==3)) and (not boois29noun)) then
      strlang29name = 'la ' .. strlang29name -- well pluralizable elsewhere
    end--if
    if (((numtip29mon==2) or (numtip29mon==3)) and (not boois29noun)) then
      strlang29name = strlang29name .. ' lingvo' -- well pluralizable elsewhere
    end--if

    if (numtip29mon==5) then
      strlang29name = strlang29name .. 'a' -- root -> AJ
    end--if

    if (numtip29mon==6) then
      if (boomul29words) then -- surrogate AV, with "en", with or without "la"
        if (boois29noun) then
          strlang29name = 'en ' .. strlang29name
        else
          strlang29name = 'en la ' .. strlang29name
        end--if
      else
        strlang29name = strlang29name .. 'e' -- root -> AV (was singleword AJ or SB)
      end--if
    end--if

  end--if ((numtip29mon>=1) and ...

  return strlang29name -- same var

end--function lfivarilingvonomo

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

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

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

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

local function 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. Optional transcoding of eo and NOPE sv surrogates
-- (via 2 separate procedures). Optionally string keys/indexes are transcoded
-- as well.

-- Input  : * varinkommen -- type "string" or "table"
--          * strlingkod -- langcode "eo"
--          * 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 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 prhbrewerr5hupa (numeror5code, strparent5nm)

-- Brew error sev huge, one line, insertable parent.

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

-- Output : * stryt5sux -- 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.

local stryt5sux = ''

  stryt5sux = contabfel.hubg .. pripl2altwre(prhconstructerar(numeror5code,true),64,strparent5nm) .. contabfel.huen

return stryt5sux

end--function prhbrewerr5hupa

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

  -- general tab

  local tablg78ysubt = {}
  local tablg80lefty = {}
  local tabonelang = {} -- subtable for one language

  -- peeked stuff

  local strpiklangcode = '' -- "en" privileged site language
  local strpikkatns    = '' -- "Category"
  local strpikapxns    = '' -- "Appendix"
  local strpikparent   = ''

  -- general str

  local strtump     = '' -- temp

  local strpagenam  = '' -- "{{PAGENAME}}" o "pagenameoverridetestonly"
  local strruangna  = '' -- "{{NAMESPACENUMBER}}" o "nsnumberoverridetestonly"
  local strkodbah   = '' -- directly picked or translated from "strnambah"
  local strnambah   = '' -- language name (short)
  local strnambauc  = '' -- language name with uppercased begin letter
  local strnambalc  = '' -- language name with lowercased begin letter
  local strnambala  = '' -- language name (long) with "la " if needed
  local strnambaaj  = '' -- language name (force AJ)
  local strlauxcatip = '' -- N K L
  local strlauxcatnm = '' -- name of extra cat (exp or brewed from parent cat)
  local strcol6q     = '' -- subdomain, "-" same, "??" stolen
  local strcol7q     = '' -- status 2 digits
  local straliawk    = ''
  local strenkonduko = '' -- begin of text with type by content
  local straldonsh   = '' -- link to appendix page (short)
  local straldonlg   = '' -- link to appendix page (long)
  local strboxtext   = ''
  local striconbc    = '' -- picked based on "G" or "H" or "U"
  local strdescbc    = '' -- picked based on "G" or "H" or "U"
  local strtypeof    = '' -- type of cat

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

  -- general num

  local numerr       = 0 -- 1 internal
  local num2statcode = 0
  local numlevel     = 0 -- ASCII thus 48...52 or 69 for "D"

  -- general boo

  local boohidden     = false -- whether to hide the cat
  local boolangcat    = false
  local bootaroexists = false
  local boocodestolen = false
  local bootimp       = false

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

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

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

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

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

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

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

  ---- PEEK STUFF THAT IS NOT OVERRIDDEN SEMIGENEROUS ----

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

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

  -- full "strpikparent" is used for error messages
  -- no "strpikpareuf" here

  if (numerr==0) then
    strpiklangcode = contabovrd.sitelang or mw.getContentLanguage():getCode() or 'en'              -- privileged site language
    strpikkatns    = contabovrd.katprefi or (mw.site.namespaces[ 14] or {})['name'] or 'Category'  -- standard namespace
    strpikapxns    = contabovrd.apxprefi or (mw.site.namespaces[102] or {})['name'] or 'Appendix'  -- custom namespace
    strpikparent   = contabovrd.parentfn or arxframent:getParent():getTitle() or 'Template:nope'   -- fullpagename
    if ((type(strpiklangcode)~='string') or (type(strpikkatns)~='string') or (type(strpikapxns)~='string') or (type(strpikparent)~='string')) then
      numerr = 1 -- #E01 internal (unlikely)
    end--if
  end--if (numerr==0) then

  lfdshowvar (numerr,'numerr','@Peeking from MW with override done, error code should be ZERO')
  lfdshowvar (strpiklangcode,'strpiklangcode')
  lfdshowvar (strpikkatns,'strpikkatns')
  lfdshowvar (strpikapxns,'strpikapxns')
  lfdshowvar (strpikparent,'strpikparent')

  ---- PROCESS MESSAGES ----

  if (numerr==0) then
    contaberaroj = prhrecusurrstrtab (contaberaroj, strpiklangcode, nil)
    contabboasting = prhrecusurrstrtab (contabboasting, strpiklangcode, nil)
    contabtypesofcade = prhrecusurrstrtab (contabtypesofcade, strpiklangcode,nil) -- nested
    contabgene = prhrecusurrstrtab (contabgene, strpiklangcode,nil) -- nested
    contabtrako = prhrecusurrstrtab (contabtrako, strpiklangcode, nil)
    contabdesc = prhrecusurrstrtab (contabdesc, strpiklangcode, nil)
  end--if

  lfdshowvar (numerr,'numerr')

  ---- PICK TWO SUBTABLES T78 T80 FROM ONE IMPORT ----

  -- here risk of #E02 #E03

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

  while true do -- fake loop

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

    num2statcode, bootimp = lfhvali1status98code (qldingvoj[2]) -- from "loaddata-tbllingvoj"
    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['T78']
    if (type(vartymp)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if
    tablg78ysubt = vartymp -- y-index -> subtable (one for every langcode)

    vartymp = qldingvoj['T80']
    if (type(vartymp)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if
    tablg80lefty = vartymp -- left-most column -> y-index (langname to langcode)

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

  lfdshowvar (numerr,'numerr','@After attempt to seize tables T78 T80')
  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 (contabovrd.defortrc) then
    prdtracemsg ('Force "detrc=true"')
  else
    if (arxsomons['detrc']=='true') then
      prdtracemsg ('Param "detrc=true" seized')
    else
      qboodetrc = false -- was preassigned to "true"
      qstrtrace = '' -- shut up now
    end--if
  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

  ---- CHECK NS ----

  if ((numerr==0) and (strruangna=='8')) then
    numerr = 99 -- #E99 -- special treatment of ns "MediaWiki:"
  end--if

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

  lfdshowvar (numerr,'numerr','@After seizure of pagename and ns')
  lfdshowvar (strpagenam,'strpagenam')
  lfdshowvar (strruangna,'strruangna')

  ---- DISSECT THE PAGENAME ----  !!!FIXME!!! DEPRECATED

  -- the pagename comes in "strpagenam"

  -- "contabtypesofcade" (string keys) has subtables (ONE-based keys)
  -- * [1] parsing method (3 -> sepbracket | 4,5,6)
  -- * [2] pattern for parsing method
  -- * [3] reserved
  -- * [4] (char) type by content G H U missing T
  -- * [5] (boo) categorize in lang
  -- * [6] (char) categorize lang N K L (empty string to deactive)
  -- * [7] name of extra cat

  -- here we do NOT validate the language name in any way beyond length >= 2

  -- things like "Tradukoj (**)" will pass here, will get caught below

  -- "Tradukoj (Esperanto)" will pass here and will pass the validation
  -- of language name too, this must be caught even later

  -- things like "El la **" will pass here, will get caught below

  -- things like "El la Ido" will pass here and will pass the validation
  -- of language name too, this must be caught even later

  -- output from here (risk for #E01 and #E17):
  -- * strnambah or strkodbah   (from pagename)
  -- * numlevel                 (from pagename)
  -- * strtypeof                (from table "key", string with 4 char:s)
  -- * boolangcat               (from [5])
  -- * boohidden                (indirectly from [4])
  -- * striconbc and strdescbc  (indirectly from [4])
  -- * strlauxcatip             (from [6] N K L can be empty if none)
  -- * strlauxcatnm             (from [7] can be empty if none)

  if (numerr==0) then

    strtypeof = ''

    do -- scope
      local strdebin = '' -- from "lfisepbracket" can be needed
      local strdebut = '' -- from "lfisepbracket" can be needed
      local strau2tr = '' -- pattern from [2]
      local strby4content = '' -- from [4] "G" or "H" or "U"
      local numpanalength = 0
      local numpars1met = 0
      local numverlastch = 0
      local numprelastch = 0
      local boodebrako = false
      local booparsdan = false

      numpanalength = string.len(strpagenam)
      numverlastch = string.byte(strpagenam,-1,-1)
      if (numpanalength>=2) then
        numprelastch = string.byte(strpagenam,-2,-2)
      end--if
      boodebrako, strdebin, strdebut = lfisepbracket (strpagenam, 2)

      for k7k,v7v in pairs(contabtypesofcade) do
        if ((type(k7k)~='string') or (type(v7v)~='table')) then
          prdtracemsg ('Aborting "for in pairs" due to broken internal table (k,v)')
          numerr = 1 -- #E01 internal
          break
        end--if
        lfdshowvar (k7k,'k7k','iterating over possible patterns')
        booparsdan = false -- reset on every iter
        numpars1met = v7v[1]
        strau2tr = v7v[2] -- empty if not used
        strby4content = v7v[4] -- G H U
        if ((type(strau2tr)~='string') or (type(strby4content)~='string')) then
          prdtracemsg ('Aborting "for in pairs" due to broken internal table (pos 2,4)')
          numerr = 1 -- #E01 internal
          break
        end--if
        boohidden = (strby4content=='H')
        striconbc = contabicon [strby4content]
        strdescbc = contabdesc [strby4content]
        if ((type(striconbc)~='string') or (type(strdescbc)~='string')) then
          prdtracemsg ('Aborting "for in pairs" due to broken table "GHU-icon-desc"')
          numerr = 1 -- #E01 internal
          break
        end--if
        boolangcat = v7v[5]
        strlauxcatip = v7v[6] -- N K L empty if not desired
        strlauxcatnm = v7v[7] -- empty if not used or brewed from parent cat
        if (numpars1met==3) then -- based on lfisepbracket
          booparsdan = true
          prdtracemsg ('Trying parsing method 3')
          if (boodebrako and ((strau2tr..' ()')==strdebut)) then
            strtypeof = k7k
            strnambah = strdebin
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 3')
            break
          end--if
        end--if
        if (numpars1met==4) then -- "el"-type
          booparsdan = true
          prdtracemsg ('Trying parsing method 4')
          if ((numpanalength>=8) and (string.sub(strpagenam,1,6)=='El la ')) then
            strtypeof = k7k
            strnambah = string.sub(strpagenam,7,-1)
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 4 with "la"')
            break
          end--if
          if ((numpanalength>=5) and (string.sub(strpagenam,1,3)=='El ')) then
            strtypeof = k7k
            strnambah = string.sub(strpagenam,4,-1)
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 4 without "la"')
            break
          end--if
        end--if
        if (numpars1met==5) then -- isto with level "Vikivortaristo mi-D"
          booparsdan = true
          prdtracemsg ('Trying parsing method 5')
          if (((numpanalength==19) or (numpanalength==20)) and (string.sub(strpagenam,1,15)=='Vikivortaristo ') and (numprelastch==45)) then
            if (((numverlastch>=48) and (numverlastch<=52)) or (numverlastch==68)) then
              strtypeof = k7k
              if (numpanalength==19) then
                strkodbah = string.sub(strpagenam,16,17) -- 2-letter code
              else
                strkodbah = string.sub(strpagenam,16,18) -- 3-letter code
              end--if
              numlevel = numverlastch -- ASCII
              lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 5')
              break
            end--if
          end--if
        end--if
        if (numpars1met==6) then -- isto without level
          booparsdan = true
          prdtracemsg ('Trying parsing method 6')
          if (((numpanalength==17) or (numpanalength==18)) and (string.sub(strpagenam,1,15)=='Vikivortaristo ')) then
            strtypeof = k7k
            if (numpanalength==17) then
              strkodbah = string.sub(strpagenam,16,17) -- 2-letter code
            else
              strkodbah = string.sub(strpagenam,16,18) -- 3-letter code
            end--if
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 6')
            break
          end--if
        end--if
        if (not booparsdan) then
          prdtracemsg ('Aborting "for in pairs" due to unknown parsing method')
          numerr = 1 -- #E01 internal
          break
        end--if
      end--for

      if (strtypeof=='') then -- NOT found
        numerr = 17 -- #E17 no valid pattern
        prdtracemsg ('Exited "for in pairs" with pattern NOT found')
      end--if

    end--do scope

  end--if

  lfdshowvar (numerr,'numerr','after dissection')
  lfdshowvar (boohidden,'boohidden')
  lfdshowvar (strkodbah,'strkodbah','maybe langcode is here')
  lfdshowvar (strnambah,'strnambah','maybe langname is here')

  ---- REVERSE-QUERY NAME -> CODE IF NEEDED ----

  if ((numerr==0) and (strkodbah=='')) then
    strkodbah = tablg80lefty[strnambah]
    if (type(strkodbah)~='string') then
      numerr = 19 -- #E19 valid pattern but invalid language
    end--if
  end--if

  lfdshowvar (numerr,'numerr','after reverse query via T80')
  lfdshowvar (strkodbah,'strkodbah','maybe picked here')

  ---- ALWAYS PREPARE FOR FORWARD QUERIES ----

  -- error when picking tabonelang should be impossible
  -- if strkodbah comes from tablg80lefty

  if (numerr==0) then
    tabonelang = tablg78ysubt[strkodbah] -- get subtable from T78
    if (type(tabonelang)~='table') then
      numerr = 19 -- #E19 valid pattern but invalid language
      tabonelang = {} -- guard against indexing error
    end--if
  end--if

  ---- PERFORM LAST STEP OF FORWARD QUERY IF NEEDED ----

  if ((numerr==0) and (strnambah=='')) then
    strnambah = tabonelang[0] -- /c0/
    if (type(strnambah)~='string') then
      numerr = 1 -- #E01 internal (should be impossible)
    end--if
  end--if

  lfdshowvar (numerr,'numerr','after forward query via "tabonelang"')
  lfdshowvar (strnambah,'strnambah','maybe picked here')

  ---- REASSIGN WORD CLASS AND LETTER CASE ----

  if (numerr==0) then
    strnambauc = lfucaserest ('eo',strnambah,true,false) -- uppercase
    strnambalc = lfucaserest ('eo',strnambah,false,false) -- lowecase for the hint
    strnambala = lfivarilingvonomo (strnambah,1) -- long with "la "
    strnambaaj = lfivarilingvonomo (strnambah,5) -- adjective yes we can
  end--if

  lfdshowvar (strnambauc,'strnambauc')
  lfdshowvar (strnambalc,'strnambalc')
  lfdshowvar (strnambala,'strnambala')
  lfdshowvar (strnambaaj,'strnambaaj')

  ---- CATCH SOME FAULTY PAGENAMES ----

  -- incoming "strtypeof" and "strpagenam"

  -- "Tradukoj (Esperanto)" is illegal

  -- things like "El la Ido" or "El hispana" are illegal

  if (numerr==0) then
    while true do -- fake loop
      if (strpagenam=='Tradukoj (Esperanto)') then
        prdtracemsg ('rejecting "Tradukoj (Esperanto)"')
        numerr = 17 -- #E17 no valid pattern
        break
      end--if
      strtump = "El " .. strnambala
      if ((strtypeof=='elel') and (strpagenam~=strtump)) then
        prdtracemsg ('rejecting malformed "el"-type due to "' .. strpagenam .. '" <> "'.. strtump .. '"')
        numerr = 17 -- #E17 no valid pattern
        break
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark
  end--if

  ---- PICK COLUMNS 6 AND 7 AND BREW LINK IF NEEDED ----

  -- this gives "straliawk" raw link and "bootaroexists" and "boocodestolen"

  if ((numerr==0) and (strtypeof=='varo')) then

    strcol6q = tabonelang[6] -- subdomain ie non-standard code /c6/
    if ((type(strcol6q)~='string') or (strcol6q=='-')) then
      strcol6q = strkodbah -- then ASSume same
    end--if
    strcol7q = tabonelang[7] -- status 2 digits /c7/
    if (type(strcol7q)~='string') then
      strcol7q = '00' -- ASSume nope
    end--if
    boocodestolen = (strcol6q=='??')
    if (not boocodestolen) then
      bootaroexists = ((string.byte(strcol7q,2,2))==50) -- got a "2" there ??
      straliawk = 'http://' .. strcol6q .. '.wiktionary.org' -- even if inexis
    end--if

    prdtracemsg ('2 queries via T78 done')
    lfdshowvar (straliawk,'straliawk')
    lfdshowvar (bootaroexists,'bootaroexists')
    lfdshowvar (boocodestolen,'boocodestolen')

  end--if ((numerr==0) and (strtypeof=='varo')) then

  ---- PREBREW INTRO AND LINK TO APPENDIX PAGE ----

  -- incoming "strdescbc" "strkodbah" "strnambah" "strnambauc" "strnambala"

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

  -- result "strenkonduko" will look like (indeed space at end)
  -- "En aux sub cxi tiu kategorio trovigxas "
  -- or
  -- "En aux sub cxi tiu uzanta kategorio trovigxas "

  -- result "straldonsh" will look like (no space at end)
  -- "<b>[[Aldono:Dana|dana]]</b> (lingvokodo <code>-da-</code>)"

  -- result "straldonlg" will look like (no space at end)
  -- "<b>[[Aldono:Dana|la dana]]</b> (lingvokodo <code>-da-</code>)"

  if (numerr==0) then
    if (strdescbc~='') then
      strdescbc = strdescbc .. ' ' -- below insert extra word incl space after
    end--if
    strenkonduko = contabboasting['g0'] .. ' ' .. strdescbc .. contabboasting['g1'] .. ' '
    strtump = '</b> (' .. contabboasting['g2'] .. ' <code>-' .. strkodbah .. '-</code>)'
    straldonsh = '<b>' .. lfikatpaldigu(strpikapxns,strnambauc,strnambah,0) .. strtump -- (short)
    straldonlg = '<b>' .. lfikatpaldigu(strpikapxns,strnambauc,strnambala,0) .. strtump -- (long)
  end--if (numerr==0) then

  ---- PREBREW TEXT ----

  -- this is controlled by "strtypeof"
  -- incoming also "striconbc" "strdescbc"
  -- further "numlevel"

  -- this is the latest occasion where #E01 can arise

  if (numerr==0) then

    bootimp = false

    if (strtypeof=='elel') then -- "el"-type
      strtump = lfikatpaldigu(strpikkatns,'Deveno ('..strnambah..')','',2) -- colon
      strboxtext = strenkonduko .. contabboasting['elel0'] .. ' ' .. straldonlg .. '. ' .. contabboasting['elel1'] .. ' ' .. strtump .. '.'
      bootimp = true
    end--if

  if (strtypeof=='stil') then -- stilo
    strboxtext = strenkonduko .. contabboasting['stil0'] .. ' ' .. straldonsh .. ' ' .. contabboasting['stil1'] .. '.'
    bootimp = true
  end--if

  if (strtypeof=='trad') then -- tradukoj
    strboxtext = strenkonduko .. contabboasting['trad'] .. ' ' .. straldonsh .. '.'
    bootimp = true
  end--if

  if (strtypeof=='varo') then -- vortaro
    strboxtext = strenkonduko .. contabboasting[30] .. ' ' .. straldonsh .. '.'
    if (strkodbah=='eo') then
      strtump = contabboasting[31] .. '.'
    else
      if (bootaroexists) then
        strtump = '[' .. straliawk .. ' ' .. contabboasting[32] .. ' ' .. strnambaaj .. ' ' .. contabboasting[34] .. ']'
      else
        strtump = contabboasting[33] .. ' ' .. strnambaaj .. ' ' .. contabboasting[34]
        if (boocodestolen) then
          strtump = strtump .. ', ' .. contabboasting[36] .. '.'
        else
          strtump = strtump .. '. ([' .. straliawk .. ' ' .. contabboasting[35] ..'])'
        end--if
      end--if (bootaroexists) else
    end--if
    strboxtext = strboxtext .. '<br><br><div style="text-align:center;">' .. strtump .. '</div>'
    bootimp = true
  end--if (strtypeof=='varo') then

  if (strtypeof=='isnl') then
    strboxtext = strenkonduko .. contabboasting['isnl'] .. ' ' .. straldonlg .. '.'
    bootimp = true
  end--if

  if (strtypeof=='islv') then
    strboxtext = strenkonduko .. contabboasting['islv0'] .. ' ' .. straldonlg .. ' ' .. contabboasting['islv1'] .. ' "' .. string.char(numlevel) .. '".'
    strlauxcatnm = string.sub(strpagenam,1,-3) -- cut off 2 letters
    bootimp = true -- "strlauxcatnm" is generated from parent cat
  end--if

    if (not bootimp) then
      numerr = 1 -- #E01 internal
      lfdshowvar (strtypeof,'strtypeof','inconsistent, failed to brew box text, #E01')
    end--if

  end--if (numerr==0) then

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

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

  ---- WHINE IF YOU MUST #E02...#E98 SIMPLE THERE IS A PARENT ----

  if ((numerr>=2) and (numerr<=98)) then
    strviserr = prhbrewerr5hupa(numerr,('"'..strpikparent..'"'))
  end--if

  ---- SPECIAL #E99 ----

  if (numerr==99) then
    strviserr = '{{kat}}'
  end--if

  ---- BREW THE VISIBLE BOX PART AROUND THE TEXT ----

  -- incoming "striconbc" and "strboxtext"

  if (numerr==0) then
    strvisgud = contabhtml.tabbeg .. contabhtml[3] .. striconbc .. contabhtml[4] .. strboxtext .. contabhtml.lowend
  end--if

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

  -- incoming:
  -- * "boolangcat" and "strlauxcatip" and "strlauxcatnm"
  -- * "strnambah" and "strnambauc" and "strnambalc"
  -- * "numlevel"
  -- * "boohidden"

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

  -- brew "__HIDDENCAT__" if needed as well as up to 2 category insertions

  if (numerr==0) then
    do -- scope
      local strkeysorthint = ''
      local boobrewthatone = false
      if (boolangcat) then -- cat insertion no hint
        strinvkat = lfikatpaldigu(strpikkatns,strnambauc,'',1)
      end--if
      if (strlauxcatip=='N') then -- langname
       strkeysorthint = pribrewcathint('eo',strnambah,false) -- must clean up
       boobrewthatone = true
      end--if
      if (strlauxcatip=='K') then -- langcode
       strkeysorthint = strkodbah -- no cleanup needed
       boobrewthatone = true
      end--if
      if (strlauxcatip=='L') then -- level
       strkeysorthint = string.char(numlevel) -- cleanup needed for "D" only
       boobrewthatone = true
      end--if
      if (boobrewthatone) then -- cat insertion custom hint
        strinvkat = strinvkat .. lfikatpaldigu(strpikkatns,strlauxcatnm,strkeysorthint,1)
      end--if
      if (boohidden) then
        strinvkat = '__HIDDENCAT__' .. strinvkat
      end--if
    end--do scope
  end--if (numerr==0) then

  ---- BREW THE TRACKING CATEGORY PART #E02...#E98 ----

  while true do -- fake loop
    if ((numerr<2) or (numerr>98)) then -- success or internal or special
      break
    end--if
    if ((numerr>=2) and (numerr<=5)) then
      strtrakat = lfikatpaldigu(strpikkatns,contabtrako.submod,1) -- #E02 ... #E05
      break
    end--if
    if (numerr==17) then
      strtrakat = lfikatpaldigu(strpikkatns,contabtrako.nesupa,1) -- #E17
      break
    end--if
    if (numerr==19) then
      strtrakat = lfikatpaldigu(strpikkatns,contabtrako.nekoli,1) -- #E19
      break
    end--if
    strtrakat = lfikatpaldigu(strpikkatns,contabtrako.cetgen) .. lfikatpaldigu(strpikkatns,contabtrako.cetkat,1)
    break -- finally
  end--while -- fake loop -- join mark

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

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

  prdtracemsg ('Ready to return string glued together from 1 + 4 parts')
  lfdshowvar (strvisgud,'strvisgud')
  lfdshowvar (strinvkat,'strinvkat')
  lfdshowvar (strviserr,'strviserr')
  lfdshowvar (strtrakat,'strtrakat')

  if (numerr==0) then
    strret = strvisgud .. strinvkat -- for #E00 only
  else
    strret = strviserr .. strtrakat -- also for internal #E01 and special #E99
  end--if
  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