Saltu al enhavo

Modulo:mtagg

El Vikivortaro
 MODULO
Memtesto disponeblas sur la paĝo Ŝablono:k.
Ĉ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 "MTAGG" (tag)

"eo.wiktionary.org/wiki/Modulo:mtagg" <!--2024-Oct-03-->
"id.wiktionary.org/wiki/Modul:mtagg"

Purpose: adds extra information ("tag") to a single meaning of a word
         or the complete lemma (covering all meanings), showed as
         an inline list of coloured hints (that can contain links
         and images), and generates cat insertions

Utilo: aldonas na kroma informaro ("etikedo") al signifo de vorto
       aux la tuta kapvorto (kovrante cxiujn signifojn), montrite kiel
       enlinia listo kun koloraj konsiloj (kiuj povas enhavi ligilojn
       kaj bildojn), kaj generas kategorienmetojn

Manfaat: menambahkan informasi ekstra ("tag") ke arti kata atau lema ...

Syfte: tillaegger extra information ...

Used by templates / Uzata far sxablonoj / Digunakan oleh templat:
* k (EO) , k (ID)

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

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

Incoming: * 2...25 anonymous obligatory parameters
            * language code (2 or 3 lowercase letters or more if enabled)
            * 1...24 further parameters elements of the main control
              chain, each 2...50 octet:s (see below)
          * 3 hidden parameters
            * "pagenameoverridetestonly=" can cause #E01
            * "nocat=" no error possible
            * "detrc=" no error possible

Returned: * one string intended to be showed after the numbered "#" list sign
            if relating to a meaning, or after the bulletized "*" list sign
            if relating to the complete lemma

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

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

Structure of the control chain:
- Groups and elements:
  - The chain may contain 1...24 elements belonging to following
    groups (see "contablimahuruf"):
    - "T" translingual -- "FFC8C8" pink
    - "D" dialect      -- "FFF0C0" orange
    - "G" grammar      -- "FFFFB0" yellow or "D0FFD0" green for "eo"
    - "F" field        -- "D0F0FF" bluish cyan
    - "S" style        -- "FFD0FF" violet
    - "C" other        -- "D8D8D8" grey
    There can be several elements belonging to same group, and groups can be
    omitted, but the order of groups is crucial, at least one element and one
    group is required, and same type of group must be introduced exactly one
    time. The elements are separated by wall "|", and groups are introduced by
    an UPPERCase letter followed by a colon and space ie ": " at the beginning
    of an element (NOT by a separate element).
- Special char:s:
  - Absolutely reserved:
    - wall "|" (wikisyntax) used to separate elements
      as well as params of the call
    - curly brackets "{" and "}" in double or higher multiplicity
      (wikisyntax) used to begin and end the call
    - rectangular brackets "[" and "]" in double or higher
      multiplicity (wikisyntax) absolutely prohibited
    - TAB and LF absolutely prohibited
  - In some positions reserved:
    - space " "
    - colon ":" (reserved after whitelisted name only)
    - dot "." (reserved at end of element only)
    - dash "-" (reserved at end of element only)
    - percent-sign "%" and and-sign "&"
- Examples:
  - <<D: % predominantly-|qeb.|G: VRF:|% only in negative contexts|S: sla.|C: % about abstract stuff>>
  - <<G: VIT.-|% kun prepozicio "pri"|% ecx-|VTR.|C: % celante personon>>
  - <<MS: ark.>>
  - <<RS: ark.>>
- Global reduction prefixes (recognized at the very beginning of the
  chain only):                                                                  !!!FIXME!!! imple incomplete
  - The prefix "M" should be used if the template is called from
    other places than a definition or overview above the definitions,
    it reduces the generosity of the output in following ways:
    - no categories
    - no colours
    - no links
    - no tooltips
    - no images
  - The prefix "R" removes the categories only. As opposed to "nocat=true"      !!!FIXME!!! nocat is DEPRECATED
    it does not suppress tracking categories. Furthermore "nocat=true" must
    not be used on lemma pages.
- Types of elements:
  - An element may be of one of two types:
    * plain text element, must NOT end with a dot, categorized by
      default, cannot provide a translated category, cannot suppress
      the language-dependent category, cannot provide a link, tooltip
      or image, as a base rule any text of sane length without reserved
      char:s is permitted, but some texts may                                   !!!FIXME!!! not checked yet
      be either simply disallowed, or disallowed in favor of an
      abbreviation (see below), or have one of the element prefixes (see
      below) obligatory or prohibited, or be reserved for a certain group
    * abbreviation element (2...12 char:s of a limited set (ASCII UPPERCase,    !!!FIXME!!! not checked yet
      ASCII lowercase, EO UPPERCase, EO lowercase, dash "-") will be
      peeked from a list, specific to a group (thus same abbreviation
      may be defined in different groups with different meanings, error if
      not found, categorized by default, may contain a translated category,
      may suppress the language-dependent category, may contain a link, may
      contain a tooltip, may contain an image), 2 subtypes:
      * "dot-type" ending with a dot "." (thus 3...13 char:s totally)
      * "colon-type" ie abbreviation element with extra information with
        2 more subsubtypes:
        * 3 UPPERCase letters followed by colon only for
          automatic extra information
        or
        * 3 UPPERCase letters followed by colon and space ": " and at least
          one more octet of manual extra information
        both automatic and manual only available for explicitly whitelisted
        triples (only "VRF" "VRG" "VRH" so far) that can be used in 3 ways:
        * "VRG." no extra information
        * "VRG:" automatic extra information
        * "VRG: ta sig an" manual extra information
- Element prefixes (usable with both plain text elements and abbreviation
  elements, but latter only the subtype without extra content ie "dot-type"):
  * By default an element generates both visible text and a category. If
    only one of those effects is desirable then an element prefix (one
    non-letter char followed by obligatory space) can be used:
    * "%" show text but do not categorize ("nopercatcent")
    * "&" categorize only but do not show text
- Element suffix for join request (usable with both plain text elements
  and abbreviation elements, even "colon-type") is a dash "-". Element
  having it will be joined with following element (show space only and keep
  continuous colour, instead of white semicolon plus space "; " between). Not
  more than 2 elements can be joined, and exactly one of those must use the
  prefix "%" too (show text but do not categorize), and none of them may
  have prefix "&" (categorize only but do not show text).
- Special rules for some groups:
  * group "T" translingual
    * the language code must be "mul", otherwise #E23
    * the language-dependent category is always omitted (as it obviously
      would not make sense) and control code "1" in abbreviation expansion
      is thus prohibited
  * group "D" dialect
    * special language-dependent mode of translation, see below
    * the language-dependent category is always omitted (as it obviously
      would not make sense) and control code "1" in abbreviation expansion
      is thus prohibited
- Translation of abbreviation elements:                                         !!!FIXME!!! add exclusive language code ie 6 -> 7 sections
  - The translation is provided by LUA tables. All elements are named and
    use a key/index (2...12 UPPERCase or lowercase letters, always without
    dot "." or colon ":", special rule applies for the dialect) and a value
    (expansion string) with 1 to 6 sections separated by walls "|". Missing
    sections are legal if at least the earliest one is present, truly
    empty ones (without dash "-" placeholder) are not.
  - The sections are:
    - (required) visible text and auto category
    - (optional) differring category
      * string without "Category:" prefix and without unnecessary
        capitalization, for example "fiziko"
      * default is same as the visible text
      * use dash "-" to "jump over" this section and take same
        as the visible text
    - (optional) link (lowercase string), default is no link,
      use dash "-" to "jump over" this section ie omit link
    - (optional) tooltip, default is no tooltip, use dash "-" to
      "jump over" this section ie omit tooltip
    - (optional) image (string without "File:" prefix),
      default is no image, image precedes the text in the output,
      use dash "-" to "jump over" this section ie omit image
    - (optional) control code
      * "1" (digit ONE) suppress language-dependent category, this is useful
        for language peculiarities that exist in one language only, by
        default both the generic and language-dependent category include are
        generated, note that this effect CANNOT be achieved with a plain
        text element, and this code is prohibited in groups T and D where
        this effect is always active
- Dialect abbreviations (D):
  Two types are defined:
  - state  -- 2 UPPERCase letters (code of a state, example "FI" or "US")       !!!FIXME!!! "RP" "GA"
  - region -- 3...8 lowercase letters (regions that are not states)
  A dialect abbreviation is specific for one language code, for example "UK"
  meaning "British" is valid with "en" but not with "sv". It could have some
  other meaning within an other language. Note that for example only "UK" has
  to be used in the control chain in the template call, but the index is
  "en-UK" in the imported LUA table, and finally "en" is the z-dimension
  whereas "UK" is the y-dimension in the separate module. Following special
  rules apply:
  * language code enters the translation process
  * in the LUA table, the LUA key/index is prepended by the language code
    (example: "en" -> en-ZA")
  * both the visible text and the category later generated by the module
    are prefixed by language name obtained from that lang code (example:
    "ZA" -> "Afrika Selatan" -> "Inggris Afrika Selatan", or
    "MX" -> "meksiklanda" -> "hispana meksiklanda"), and generated in two
    versions, one with small text for the screen, and one raw for the category
- Grammar abbreviations (G):
  There is a special handling of some types of verbs. For this reason, all
  7 abbreviations related to transitivity of verbs consist of 3 UPPERCase
  letters, but only 3 of them (VRF VRG VRH) can have the subtype "abbreviation
  element with extra information" (colon-type), besides the ordinary
  "abbreviation element" (dot-type).
  - VTR - transitive verb
  - VDT - doubly transitive verb
  - VOD - transitive verb with required object (in languages where the
          object usually can be omitted and thus implied)
  - VIT - intransitive verb
  - VRF - reflexive form of verb
  - VRG - exclusively reflexive verb (non-reflexive form does not exist)
  - VRH - quasi-exclusively reflexive verb (non-reflexive form exists but
          has a different meaning)
  For VRF VRG VRH the reflexive form can be autogenerated (depends on
  language code, unsupported language gives #E28) or manually provided.
- Field abbreviations (F):
  (see list below)
- Style abbreviations (S):
  (see list below)
- Other abbreviations (C):
  (see list below)

Following errors can occur:
- #E01 Internal error in module "mtagg"
  Possible causes:
  - strings not uncommented
  - function "mw.title.getCurrentTitle().text" AKA "{{PAGENAME}}" failed
  - pagename is invalid such as empty or too long or contains
    invalid brackets []{} or more than one consecutive apo, even if
    coming from "pagenameoverridetestonly="
- #E13 Erara uzo de sxablono "k", legu gxian dokumentajxon            !!!FIXME!!!
  Possible causes (early detected obvious problems with parameters):
  * less than 2 or more than 25 parameters supplied
  * wrong length of single parameter (must be 2...60 octet:s)
- #E03 Eraro en subsxablonoj uzataj far sxablono "k"                  !!!FIXME!!!
  Possible causes:
  - submodule failure (or not found ??) or returned invalid
- #E04 Evidente nevalida lingvokodo en sxablono "k"
  Possible causes:
  - the earliest parameter
- #E05 Nekonata lingvokodo en sxablono "k"
  Possible causes:
  - the earliest parameter
- #E06 Erara uzo de sxablono "k" pro rezervita signo en la stircxeno            !!!FIXME!!! NOT yet used
- #E07 Gxenerala eraro en la stircxeno
  Possible causes:
  * string too short (<2 octet:s) after removing valid
    prefixes (before removing this gives #E13 instead)
- #E15 Nevalida kodo de grupo en la stircxeno ... indekso
  Possible causes:
  * found UPPERCase letter different from T D G F S C + ": "
- #E16 Redunda aux nevalida grupa prefikso en la stircxeno
  Possible causes:
  - stuff like "D: D:" or "C: F:" ie redundant or conflicting group
    prefixes (note that the order of groups is checked only later)
  - stuff like "% % " "% & " ie redundant or conflicting element prefixes
  - stuff like "% F:" ie reversed order between group
    prefix and element prefix
- #E17 Erara ordo de grupoj en la stircxeno
  Possible causes:
  - wrong order, ie prefix of lower group appearing later
- #E19 Ripeta grupa prefikso en la stircxeno
  Possible causes:
  - prefix of same group appearing again later
- #E20 Enhavo ekstere de grupo
  Possible causes:
  - no group defined at the beginning
- #E21 Elementa prefikso uzata kun mallongiga elemento kun kroma enhavo
  Possible causes:
  - element prefix used together with abbreviation element
    with extra content ("colon-type"), this is prohibited
- #E23 Grupo T uzata kun lingvokodo alia ol mul
  Possible causes: !!!FIXME!!!
- #E24 Nekonata translingva mallongigo en la stircxeno
  Possible causes: !!!FIXME!!!
- #E25 Nekonata dialekta mallongigo en la stircxeno
  Possible causes: !!!FIXME!!!
- #E26 Nekonata kvargrupa (G F S C) mallongigo en la stircxeno
  Possible causes: !!!FIXME!!!
- #E28 Ne eblas auxtogeneri kroman enhavon
  Possible causes:
  - not supported for given language
- #E29 Nevalida kunigpostulo
  Possible causes:
  - rule that exactly one of two elements must have "%" is violated
  - attempt to merge more than two elements

EO:

Kategorio:Fiziko
Kategorio:Fiziko (angla)
Kategorio:Fiziko (Esperanto)

ID: !!!FIXME!!! NOT yet

Kategori:Mamalia
Kategori:id:Mamalia

]===]

local exporttable = {}

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

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

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

-- uncommentable submodules

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

-- uncommentable EO vs ID table (caller name for error messages and tracking cat:s)

      local contabkoll = {'SXablono:','k'}  -- EO name of the caller (semi-hardcoded, we do NOT peek it)  !!!FIXME!!! drop this
        -- local contabkoll = {'Templat:','k'} -- ID name of the caller (semi-hardcoded, we do NOT peek it)

-- surrogate transcoding table (only needed for EO)

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

-- constant strings (error circumfixes)

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

-- constant strings and table (tooltip and misc to be sent to the screen)

local contabscrmisc = {}
contabscrmisc['tootip'] = 'style="border-bottom:1px dotted; cursor:help;"' -- lousy tooltip  !!!FIXME!!! NOT yet used

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

local contablimahuruf = {} -- T D G F S C
  contablimahuruf [1] = {'T','FFC8C8',''} -- pink
  contablimahuruf [2] = {'D','FFF0C0',''} -- orange
  contablimahuruf [3] = {'G','FFFFB0','D0FFD0'} -- yellow, green for "eo" (valid irrespective of "strpiklangcode")
  contablimahuruf [4] = {'F','D0F0FF',''} -- bluish cyan (bluebluegreen)
  contablimahuruf [5] = {'S','FFD0FF',''} -- violet
  contablimahuruf [6] = {'C','D8D8D8',''} -- grey

-- uncommentable EO vs ID table (tracking cat:s)

local contabtrako = {}
      contabtrako [0] = 'Erara uzo de sxablono'
      contabtrako [1] = 'Evidente nevalida'
      contabtrako [2] = 'Nekonata'
      contabtrako [3] = 'lingvokodo'
      contabtrako [4] = 'loke'
      contabtrako [5] = 'nome'

-- uncommentable EO vs ID table (error messages)

-- #E02...#E99, holes permitted, separate "contabkoll" needed for "\\@"
-- note that #E00 and #E01 are NOT supposed to be included here

local contaberaroj = {}
  contaberaroj[02] = 'Malica eraro en subprogramaro uzata far \\@'               -- EO #E02
  -- contaberaroj[02] = 'Kesalahan jahat dalam subprogram digunakan oleh \\@'       -- ID #E02
  contaberaroj[03] = 'Nombrigita eraro en subprogramaro uzata far \\@'           -- EO #E03
  -- contaberaroj[03] = 'Kesalahan ternomor dalam subprogram digunakan oleh \\@'    -- ID #E03
  contaberaroj[13] = 'Erara uzo de \\@, legu gxian dokumentajxon'                    -- EO #E13 !!!FIXME!!! must be 8
  -- contaberaroj[13] = 'Penggunaan salah \\@, bacalah dokumentasinya'                  -- ID #E13
  contaberaroj[04] = 'Evidente nevalida lingvokodo sendita al \\@'                   -- EO #E04 !!!FIXME!!! double use
  -- contaberaroj[04] = 'Kode bahasa jelas-jelas salah dikirim ke \\@'                  -- ID #E04
  contaberaroj[05] = 'Nekonata lingvokodo sendita al \\@'                            -- EO #E05 !!!FIXME!!! double use
  -- contaberaroj[05] = 'Kode bahasa tidak dikenal dikirim ke \\@'                      -- ID #E05
  contaberaroj[06] = 'Riservita signo en la stircxeno por \\@'                       -- EO #E06 !!!FIXME!!! must be 13
  -- contaberaroj[06] = 'Karakter direservasi dalam rantai pengendali untuk \\@'        -- ID #E06
  contaberaroj[07] = 'Gxenerala eraro en la stircxeno por \\@'                       -- EO #E07 -> 14
  -- contaberaroj[07] = 'Kesalahan umum dalam rantai pengendali untuk \\@'              -- ID #E07
  contaberaroj[15] = 'Nevalida kodo de grupo en la stircxeno por \\@, indekso \\~, subtenataj T D G F S C'       -- EO #E15
  -- contaberaroj[15] = 'Huruf golongan salah dalam rantai pengendali untuk \\@, indeks \\~, didukung T D G F S C'  -- ID #E15
  contaberaroj[16] = 'Redunda aux nevalida grupa prefikso en la stircxeno por \\@'                               -- EO #E16
  -- contaberaroj[16] = 'Prefiks golongan redundan atau tidak valid dalam rantai pengendali untuk \\@'              -- ID #E16
  contaberaroj[17] = 'Erara ordo de grupoj en la stircxeno por \\@, estu T D G F S C'                       -- EO #E17
  -- contaberaroj[17] = 'Urutan golongan salah dalam rantai pengendali untuk \\@, sebaiknya T D G F S C'       -- ID #E17
  contaberaroj[19] = 'Ripeta grupa prefikso en la stircxeno por \\@'                                        -- EO #E19
  -- contaberaroj[19] = 'Prefiks golongan ulang dalam rantai pengendali untuk \\@'                             -- ID #E19
  contaberaroj[20] = 'Enhavo ekstere de grupo en la stircxeno por \\@'                                      -- EO #E20
  -- contaberaroj[20] = 'Keterangan luar golongan dalam rantai pengendali untuk \\@'                           -- ID #E20
  contaberaroj[21] = 'Elementa prefikso uzata kun mallongigo kroma en la stircxeno por \\@'                 -- EO #E21
  -- contaberaroj[21] = 'Prefiks element digunakan dengan singkatan ekstra dalam rantai pengendali untuk \\@'  -- ID #E21
  contaberaroj[23] = 'Grupo T uzata kun lingvokodo alia ol mul en la stircxeno por \\@'                     -- EO #E23
  -- contaberaroj[23] = 'Golongan T digunakan dengan kode berbeda dari mul dalam rantai pengendali untuk \\@'  -- ID #E23
  contaberaroj[24] = 'Nekonata translingva mallongigo en la stircxeno por \\@'                              -- EO #E24
  -- contaberaroj[24] = 'Singkatan antarbahasa tidak dikenal dalam rantai pengendali untuk \\@'                -- ID #E24
  contaberaroj[25] = 'Nekonata dialekta mallongigo en la stircxeno por \\@'                                 -- EO #E25
  -- contaberaroj[25] = 'Singkatan dialek tidak dikenal dalam rantai pengendali untuk \\@'                     -- ID #E25
  contaberaroj[26] = 'Nekonata kvargrupa (G F S C) mallongigo en la stircxeno por \\@'                      -- EO #E26
  -- contaberaroj[26] = 'Singkatan empat golongan (G F S C) tidak dikenal dalam rantai pengendali untuk \\@'   -- ID #E26
  contaberaroj[28] = 'Ne eblas auxtogeneri kroman enhavon en \\@'                                           -- EO #E28
  -- contaberaroj[28] = 'Tidak berhasil membangkitkan konten ekstra secara otomatis dalam \\@'                 -- ID #E28
  contaberaroj[29] = 'Nevalida kunigpostulo en \\@'                                                         -- EO #E29
  -- contaberaroj[29] = 'Permintaan tempel yang tidak valid dalam \\@'                                         -- ID #E29

-- uncommentable EO vs ID group "T"

local contabtt = {} -- "T" -- translingual -- vis cat link tool image -- NOT ctl here -- code must be "mul"
      contabtt ['ASKI'] = 'askia signo'
      contabtt ['ARAB'] = 'araba cifero'
      contabtt ['ROMA'] = 'roma cifero'             -- EO single unicode character even if multiple digits
      contabtt ['ASKL'] = 'askia litero'
      contabtt ['LATI'] = 'latina litero'
      contabtt ['CIRI'] = 'cirila litero'
      contabtt ['CNTR'] = 'cxina signo tradicia'
      contabtt ['CNSI'] = 'cxina signo simpligita'
      contabtt ['CNKO'] = 'cxina signo komuna'
      contabtt ['JAKA'] = 'japana signo kanjxia'
      contabtt ['TIBE'] = 'tibeta signo'
      contabtt ['GRKO'] = 'greka aux kopta signo'
      contabtt ['MATE'] = 'matematika signo'
      contabtt ['IFAS'] = 'IFA-signo'
      contabtt ['VALU'] = 'signo de valuto'
      contabtt ['BILD'] = 'bilda signo'
      contabtt ['VALSI'] = 'simbolo de valuto'
      contabtt ['MATSI'] = 'matematika simbolo'
      contabtt ['KEMSI'] = 'kemia simbolo'          -- EO (YES "Pu" but NO "DDT")

-- uncommentable EO vs ID group "D" -- dialect -- dialekto -- vis cat link tool image -- NOT ctl here

local contabgg = {} -- "G" -- grammar -- vis(obligatory) cat link tool image ctl
  contabgg["kofr"] = "kofrovorto"               -- EO (en: smog transponder, eo: foklea, id: miras, sv: flextid bankomat)
  -- contabgg["kofr"] = "kata lakuran"             -- ID
  contabgg["miks"] = "miksajxo"                 -- EO (eo: volapugajxo, sv: snarkofag)
  -- contabgg["miks"] = "kata campuran"            -- ID
  contabgg["deta"] = "derivajxo de tabelvorto"  -- EO (nur eo: "iama", "kialo") (!!! tabelvorto mem estas vortospeco !!!)
  -- contabgg["deta"] = "derivasi kata tabel"      -- ID (hanya eo)
  contabgg["sing"] = "nursingulara|substantivo nursingulara"      -- EO (nur SB: en: "music" , eo: "kupro")
  -- contabgg["sing"] = "tidak terhitung|nomina tidak terhitung"     -- ID (hanya SB)
  contabgg["plpl"] = "nurplurala|substantivo nurplurala"          -- EO (nur SB: en: "trousers" , eo: "okulvitroj")
  -- contabgg["plpl"] = "selalu jamak|nomina selalu jamak"           -- ID (hanya SB)
  contabgg["sapl"] = "samformplurala|substantivo samformplurala"  -- EO (nur SB: en: "sheep")
  -- contabgg["sapl"] = "jamak bentuk sama|nomina jamak bentuk sama" -- ID (hanya SB)
  contabgg["nutr"] = "utruma(n)|substantivo utruma(n)"            -- EO (nur sv, eble ecx da: nur SB)
  -- contabgg["nutr"] = "utrum(n)|nomina utrum(n)"                   -- ID (hanya sv, mungkin bahkan da)
  contabgg["tneu"] = "neuxtruma(t)|substantivo neuxtruma(t)"      -- EO (nur sv, eble ecx da: nur SB)
  -- contabgg["tneu"] = "neutrum(t)|nomina neutrum(t)"               -- ID (hanya sv, mungkin bahkan da)
  contabgg["kole"] = "kolektiva|substantivo kolektiva"            -- EO
  -- contabgg["kole"] = "kolektif|nomina kolektif"                   -- ID
  contabgg["senk"] = "sen komparado|adjektivo sen komparado"      -- EO (nur AJ -- en "trigonometric")
  -- contabgg["senk"] = "tanpa komparasi|adjektif tanpa komparasi"   -- ID (hanya AJ -- en "trigonometric")
  contabgg["stat"] = "loka statika|adverbo loka statika"          -- EO (nur AV)
  -- contabgg["stat"] = "tempat statik|adverbia tempat statik"       -- ID (hanya AV)
  contabgg["dyna"] = "loka dinamika|adverbo loka dinamika"        -- EO (nur AV)
  -- contabgg["dyna"] = "tempat dinamik|adverbia tempat dinamik"     -- ID (hanya AV)
  contabgg["onom"] = "onomatopeo"                                 -- EO (nur IN)
  -- contabgg["onom"] = "onomatope"                                  -- ID (hanya IN)
  contabgg["ava"..string.char(197,173)] = "-aux|esperanta adverbo finigxanta per (-aux)|-|-|-|1"
  contabgg["ajly"]                      = "-ly|angla adjektivo finigxanta per (-ly)|-|-|-|1"
  contabgg["VTR"]  = "transitiva|verbo transitiva"                           -- EO (nur VE)
  contabgg["VDT"]  = "duoble transitiva verbo|verbo duoble transitiva"       -- EO (nur VE)
  contabgg["VOD"]  = "transitiva verbo kaj objekto deviga|verbo transitiva"  -- EO (nur VE)
  contabgg["VIT"]  = "netransitiva|verbo netransitiva"                       -- EO (nur VE)
  contabgg["VRF"]  = "refleksiva formo|verbo refleksiva"                     -- EO (nur VE)
  contabgg["VRG"]  = "nepre refleksiva verbo|verbo refleksiva"               -- EO (nur VE)
  contabgg["VRH"]  = "kvazauxnepre refleksiva verbo|verbo refleksiva"        -- EO (nur VE)

local contabf = {} -- "F" -- field -- fako -- vis(obligatory) cat link tool image ctl
      contabf["pers"] = "persona nomo"                          -- EO (nur SB)
        -- contabf["pers"] = "nama orang"                          -- ID (hanya SB)
      contabf["lokn"] = "loknomo"                               -- EO (nur SB)
        -- contabf["lokn"] = "nama tempat"                         -- ID (hanya SB)

local contabs = {} -- "S" -- stilo, lingvoregistro -- vis(obligatory) cat link tool image ctl
      contabs["sla"] = "slanga"                                 -- EO
        -- contabs["sla"] = "bahasa gaul"                          -- ID
      contabs["vul"] = "vulgara"                                -- EO
        -- contabs["vul"] = "bahasa jahat"                         -- ID

local contabc = {} -- "C" -- misc -- ceteraj -- vis(obligatory) cat link tool image ctl
      contabc["idi"]  = "idioma vortgrupo"               -- EO (eo: "nigra sxafo") (esprimo/idiomajxo) (subklaso de vortospeco vortgrupo GR)
        -- contabc["idi"]  = "kumpulan kata idioma"         -- ID
      contabc["par"]  = "idioma vortoparo"               -- EO (sv: "hugget som stucket") (subklaso de vortospeco vortgrupo GR)
        -- contabc["par"]  = "dua kata idioma"              -- ID
      contabc["prov"] = "proverbo"                       -- EO (sv: "Lika barn leka baest.") (subklaso de vortospeco frazo KA)
        -- contabc["prov"] = "peribahasa"                   -- ID
      contabc["bas"]  = "baza vortoprovizo"              -- EO
        -- contabc["bas"]  = "kosa kata dasar"              -- ID
      contabc["plen-inv"] = "vorto de la plena inventaro de Kotapedia|-|-|-|-|1"  -- EO suppress language-dependent category

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

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

-- uncommentable (override)

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

local contabovrd = {}
  -- contabovrd['sitelang'] = 'eo'
  -- contabovrd['sitelang'] = 'id'
  -- contabovrd['katprefi'] = 'Kategorio'
  -- contabovrd['katprefi'] = 'Kategori'

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

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

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

require('strict')

  -- SPECIAL VAR:S

local qldingvoj = {}     -- type "table" and nested
local qldlektoj = {}     -- type "table" and nested
local qbooguard = false  -- only for the guard test, pass to other var ASAP
local qboodetrc = true   -- from "detrc=true" but default is "true" !!!
local qstrtrace = '<br>' -- for main & sub:s, debug report request by "detrc="

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

qbooguard = ((type(constringvoj)~='string') or (type(constrlektoj)~='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) then
  qldlektoj = mw.loadData(constrlektoj) -- can crash here
  qbooguard = (type(qldlektoj)~='table') -- seems to be always false
end--if

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

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

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

-- Local function LFDTRACEMSG

-- Enhance upvalue "qstrtrace" with fixed text.

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

-- no size limit

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

-- uses upvalue "qboodetrc"

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

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

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

-- Depends on functions :
-- [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 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 k,v in pairs(vardubious) do
      if (type(k)=='number') then
        numkey77min = math.min (numkey77min,k)
        numkey77max = math.max (numkey77max,k)
        numkeynumber = numkeynumber + 1
      else
        if (type(k)=='string') then
          taballkeystring [numkeystring] = k
          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 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 functions :
-- [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 FUNCTIONS [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 (xdividend, xdivisor)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividend / xdivisor)
  return resultdiv
end--function mathdiv

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

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

-- Local function MATHBITWRIT

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

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

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

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

-- Local function MATHBITTEST

-- Find out whether single bit selected by ZERO-based index is "1" / "true".

-- Result has type "boolean".

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

local function mathbittest (numincoming, numbitindex)
  local boores = false
  while true do
    if ((numbitindex==0) or (numincoming==0)) then
      break -- we have either reached our bit or run out of bits
    end--if
    numincoming = mathdiv(numincoming,2) -- shift right
    numbitindex = numbitindex - 1 -- count down to ZERO
  end--while
  boores = (mathmod(numincoming,2)==1) -- pick bit
  return boores
end--function mathbittest

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

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

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

-- Local function LFNUMTO2DIGIT

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

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

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

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

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

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

-- Local function LFGSTRINGRANGE

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

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

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

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

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

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

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

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

-- Local function LFGTRIMSPCM

-- Trim leading and trailing spaces in a single line (minimal).

-- Input  : * strkanskevit -- string, empty or only spaces tolerable, codes
--                            below 32 discouraged as not handled in a useful
--                            way, type other than "string" NOT permitted

-- There is also "mw.text.trim".

local function lfgtrimspcm (strkanskevit)
  local str51rezulto = ''
  local numpos51beg = 1 -- ONE-based
  local numpos51end = 0 -- ONE-based
  local num51char = 0
  numpos51end = string.len(strkanskevit)
  while true do -- analyze begin
    if (numpos51beg>numpos51end) then
      break -- empty or consists of only spaces
    end--if
    num51char = string.byte(strkanskevit,numpos51beg,numpos51beg)
    if (num51char~=32) then
      break
    end--if
    numpos51beg = numpos51beg + 1
  end--while -- analyze begin
  while true do -- analyze trailer
    if (numpos51end<numpos51beg) then
      break -- empty or consists of only spaces
    end--if
    num51char = string.byte(strkanskevit,numpos51end,numpos51end)
    if (num51char~=32) then
      break
    end--if
    numpos51end = numpos51end - 1
  end--while -- analyze trailer
  if (numpos51end>=numpos51beg) then
    str51rezulto = string.sub(strkanskevit,numpos51beg,numpos51end)
  end--if
  return str51rezulto
end--function lfgtrimspcm

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

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

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

-- Local function LFULNUTF8CHAR

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

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

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

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

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

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

-- Local function LFUCASEREST

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

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

-- Output : * strinco6cs

-- Depends on functions : (this is REST)
-- [U] lfulnutf8char
-- [G] lfgpokestring lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

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

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

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

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

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

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

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

  local numtem6p = 0

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

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

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

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

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

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

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

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

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

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

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

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

    if ((numlong6bor==1) and (numdel6ta~=0)) then -- no risk of carry here
      strinco6cs = 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 -- genuine loop over incoming string (this is REST)

  return strinco6cs

end--function lfucaserest

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

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

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

-- Local function LFIBANMULTI

-- Test string for validity by banning listed single char:s by multiplicity.

-- Input  : * "strkoneven" -- even and 2...24, wrong length gives
--                            "true", tolerated multiplicity "0"..."9"
--          * "strsample" -- 0...1'024, empty gives "false",
--                           too long gives "true"

-- Output : * "booisevil" -- "true" if evil

-- Depends on functions :
-- [G] lfgtestnum
-- [E] mathmod

-- Incoming control string "strkoneven" with pairs of char:s, for
-- example "'2&0" will tolerate 2 consecutive apo:s but
-- not 3, and completely ban the and-sign "&".

local function lfibanmulti (strkoneven, strsample)

  local booisevil = false
  local numkonlen = 0 -- length of control string
  local numsamlen = 0 -- length of sample string
  local numinndex = 0 -- ZERO-based outer index
  local numinneri = 0 -- ZERO-based inner index
  local numchear  = 0
  local numnexxt  = 0
  local nummultiq = 1 -- counted multiplicity
  local numcrapp  = 0 -- from "strkoneven" char to test
  local numvrapp  = 0 -- from "strkoneven" multiplicity limit

  numsamlen = string.len (strsample)
  if (numsamlen~=0) then
    numkonlen = string.len (strkoneven)
    booisevil = (numkonlen<2) or (numkonlen>24) or (mathmod(numkonlen,2)~=0) or (numsamlen>1024)
    while true do -- outer loop
        if (booisevil or (numinndex>=numsamlen)) then
          break
        end--if
        numchear = string.byte (strsample,(numinndex+1),(numinndex+1))
        if (numchear==0) then
          booisevil = true -- ZERO is unconditionally prohibited
          break
        end--if
        numinndex = numinndex + 1
        numnexxt = 0
        if (numinndex~=numsamlen) then
          numnexxt = string.byte (strsample,(numinndex+1),(numinndex+1))
        end--if
        if (numchear==numnexxt) then
          nummultiq = nummultiq + 1
        end--if
        if ((numchear~=numnexxt) or (numinndex==numsamlen)) then
          numinneri = 0
          while true do -- innner loop
            if (numinneri==numkonlen) then
              break
            end--if
            numcrapp = string.byte (strkoneven,(numinneri+1),(numinneri+1))
            numvrapp = string.byte (strkoneven,(numinneri+2),(numinneri+2))
            if (not lfgtestnum(numvrapp)) then
              booisevil = true -- crime in control string detected
              break
            end--if
            if ((numchear==numcrapp) and (nummultiq>(numvrapp-48))) then
              booisevil = true -- multiplicity crime in sample string detected
              break
            end--if
            numinneri = numinneri + 2 -- ZERO-based inner index and STEP 2
          end--while -- innner loop
          if (booisevil) then
            break
          end--if
          nummultiq = 1 -- restart from ONE !!!
        end--if ((numchear~=numnexxt) or (numinndex==numsamlen)) then
    end--while -- outer loop
  end--if (numsamlen~=0) then

  return booisevil

end--function lfibanmulti

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

-- Local function LFIVALIDATELNKOADV

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

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

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

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

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

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

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

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

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

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

  while true do -- fake (outer) loop

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

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

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

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

  return booisvaladv

end--function lfivalidatelnkoadv

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

-- Local function LFIFILLINX

-- Replace placeholders "\@" "\\@" or "\~" "\\~" by given substitute string.

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

-- Output : * strafterfill

-- Depends on functions :
-- [G] lfgstringrange

local function lfifillinx (strbeforfill, numaskikodo, varsupstitu)

  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==92) 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 (lfgstringrange(varpfiller,1,60)) then -- !!!FIXME!!! nowiki and other sanitization
        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 lfifillinx

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

-- Local function LFIKODEOSG

-- 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 functions :
-- [E] mathdiv mathmod

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

-- To be called ONLY from "lfhfillsurrstrtab".

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

local function lfikodeosg (streosurr)

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

  numeoinplen = string.len(streosurr)

  while true do

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

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

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

  end--while

  return strutf8eo

end--function lfikodeosg

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

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

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

-- Local function LFHREBUILDTABLE

-- Create a copy of a table, even nested.

-- A table from "mw.loadData" is static. It MUST NOT be unhogged, otherwise
-- mess can arise, and it MUST NOT be modified, violation gives an error.
-- Any subtable picked from a table from "mw.loadData" is still static, no
-- data has been copied. Function "mw.clone" applied to such a table crashes
-- on the spot instead of creating a copy.

local function lfhrebuildtable (vareniro)
  local vareliro = 0
  if (type(vareniro)=='table') then
    vareliro = {}
    for kkk,vvv in pairs (vareniro) do
      if (type(vvv)=='table') then
        vareliro[kkk] = lfhrebuildtable(vvv) -- RECURSION
      else
        vareliro[kkk] = vvv -- here COPY value of elem, NOT a "pointer" to it
      end--if
    end--for
  else
    vareliro = vareniro -- return untouched
  end--if
  return vareliro
end--function lfhrebuildtable

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

-- Local function LFHVALI1STATUS99CODE

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

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

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

-- Local function LFHCONSTRUCTERAR

-- Input  : * numerar6code -- 1 ... 99 or 2 ... 99 (resistent against invalid
--                            data type, giving "??" then)
--          * boopeek6it -- do peek description #E02...#E99 from table

-- Depends on functions :
-- [N] lfnumto2digit
-- [E] mathisintrange mathdiv mathmod

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

-- To be called ONLY from lfhbrewerror, lfhbrewerrsm,
-- lfhbrewerrsvr, lfhbrewerrinsi.

local function lfhconstructerar (numerar6code, boopeek6it)
  local vardes6krip = 0
  local numbottom6limit = 1
  local stryt6sux = '#E'
  if (boopeek6it) then
    numbottom6limit = 2 -- #E01 is a valid code for submodule only
  end--if
  if (mathisintrange(numerar6code,numbottom6limit,99)) then
    stryt6sux = stryt6sux .. lfnumto2digit(numerar6code)
    if (boopeek6it) then
      vardes6krip = contaberaroj[numerar6code] -- risk of type "nil"
      if (type(vardes6krip)=='string') then
        stryt6sux = stryt6sux .. ' ' .. vardes6krip
      else
        stryt6sux = stryt6sux .. ' ??' -- no text found
      end--if
    end--if (boopeek6it) then
  else
    stryt6sux = stryt6sux .. '??' -- no valid error code
  end--if
  return stryt6sux

end--function lfhconstructerar

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

-- Local function LFHBREWERRINSI

-- Brew error message with insertable details severity huge.

-- Input  : * numerar9code -- TWO-based error code 2 ... 99 (resistent
--                            against invalid data type, giving "??" on such)
--          * tabdeta9ils  -- ZERO-based table, for example
--                            {[0]="none","neniu"}, one element per placeholder
--                            "\~" "\\~" located in description for error
--                            code in question

-- Depends on functions :
-- [H] lfhconstructerar
-- [I] lfifillinx
-- [G] lfgstringrange
-- [N] lfnumto2digit
-- [E] mathisintrange mathdiv mathmod

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

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

-- Placeholder "\@" "\\@" is used elsewhere, thus here we use "\~" "\\~".

local function lfhbrewerrinsi (numerar9code, tabdeta9ils)
  local stryt9sux = ''
  stryt9sux = lfifillinx(lfhconstructerar(numerar9code,true),126,tabdeta9ils)
  stryt9sux = constrlaxhu .. constrelabg .. stryt9sux .. constrelaen .. constrlaxhu
  return stryt9sux
end--function lfhbrewerrinsi

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

-- Local function LFHFILLSURRSTRTAB

-- Process (fill in, transcode surr) either a single string, or all string
-- items in a table using any type of keys/indexes (such as a holy number
-- sequence and non-numeric ones). Items with a non-string value are kept
-- as-is. For filling in own name, and converting eo and sv surrogates
-- (via 3 separate sub:s).

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

-- Depends on functions :
-- [I] lfifillinx (only if filling-in desired)
-- [I] lfikodeosg (only if converting of eo X-surrogates desired)
-- [I] lfikodsvsg (only if converting of sv blackslash-surrogates desired)
-- [E] mathdiv mathmod (via "lfikodeosg" and "lfikodsvsg")

-- Depends on constants :
-- * table "contabtransluteo" inherently holy (via "lfikodeosg")
-- * table "contabtranslutsv" inherently holy (via "lfikodsvsg")

local function lfhfillsurrstrtab (varinkommen, varfyllo, strlingkod)

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

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

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

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

  return varutmatning

end--function lfhfillsurrstrtab

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

-- Local function LFHFINDPHASE

-- Input  : * numphase -- 1...6 or type "nil"
--          * numasciiphase -- ASCII code or type "nil"

-- Output : * vartbrezulto -- on success table with indexes 0...3
--                            [0] phase [1] letter [2] xx color [3] eo color

-- Depends on constants :
-- * nested table "contablimahuruf" index 1...6

-- Exactly ONE of the TWO available types of input hint must be provided.

local function lfhfindphase (numphase, numasciiphase)
  local varpickero = 0
  local vartbrezulto = 0 -- type "nil" if NOT found
  local numtabudex = 1 -- ONE-based
  while true do
    varpickero = contablimahuruf [numtabudex] -- risk of "nil"
    if (type(varpickero)~="table") then
      vartbrezulto = nil
      break -- end reached and NOT found
    end--if
    if ((numphase==numtabudex) or (numasciiphase==string.byte(varpickero[1]))) then
      vartbrezulto = varpickero -- table indexex 1...3
      vartbrezulto[0] = numtabudex -- add found phase index 1...6 to index 0
      break -- found
    end--if
    numtabudex = numtabudex + 1
  end--while
  return vartbrezulto
end--function lfhfindphase

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

-- Local function LFHGETWARNA

-- Depends on functions :
-- [H] lfhfindphase

-- Depends on constants :
-- * nested table "contablimahuruf" index 1...6

local function lfhgetwarna (numfaazzo, boospecialeo)
  local tabtaabo = {}
  local strkolooro = ''
  tabtaabo = lfhfindphase (numfaazzo, nil) or {} -- by index -- table or "nil"
  if (boospecialeo) then
    strkolooro = tabtaabo[3] -- risk of "nil" -- only for "G" and -eo-
  else
    strkolooro = tabtaabo[2] -- risk of "nil"
  end--if
  if (type(strkolooro)~='string') then -- protect against stupidity
    strkolooro = '505050'
  end--if
  if (string.len(strkolooro)~=6) then -- protect against stupidity
    strkolooro = '505050'
  end--if

  return strkolooro
end--function lfhgetwarna

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

-- Local function LFHANALPREF

-- Analyze and remove possible group prefixes and element prefixes.

-- Input  : * strelemento

-- Output : * strelemento : valid prefixes removed on success, emptied on err
--          * numzagrupfn : 0: OK and nothing found
--                          1...6: found "T" ... "C" |
--                          11: unknown letter |
--                          12: redundant prefix | 13: became empty
--          * numzaelemfn : 0: OK and nothing found
--                          1: found "%" vis only
--                          2: found "&" cat only

-- Depends on functions :
-- [H] lfhfindphase
-- [G] lfgtestuc

-- Depends on constants :
-- * nested table "contablimahuruf" index 1...6

-- here we do NOT bother about the order of groups -- T:1 D:2 G:3 F:4 S:5 C:6
-- uppercase letter + colon + space is an attempted group prefix
-- "%" show text do not categorize -- "&" categorize do not show text
-- combined prefix possible "D: & " in this order ie group then element
-- minimal remaining length after removing all prefixes is 2
-- error codes 11..13 are specific for this sub, no #E99 here

local function lfhanalpref (strelemento)

  local varlimarisk = 0
  local numllenq = 0
  local numa0a = 0
  local numa1a = 0
  local numa2a = 0
  local numzagrupfn = 0
  local numzaelemfn = 0
  local booremvd = false -- assign to true if any valid prefix found

  while true do -- genuine loop but no index

    booremvd = false -- reset to false on every iteration
    numllenq = string.len(strelemento)
    if (numllenq<2) then
      break -- no chance for a prefix
    end--if
    numa0a = string.byte (strelemento,1,1)
    numa1a = string.byte (strelemento,2,2)
    numa2a = 0 -- preASSume
    if (numllenq>2) then
      numa2a = string.byte (strelemento,3,3)
    end--if

    if (lfgtestuc(numa0a) and (numa1a==58) and (numa2a==32)) then -- upper ": "
      varlimarisk = lfhfindphase (nil, numa0a) -- by letter -- table or "nil"
      if (type(varlimarisk)~="table") then
        numzagrupfn = 11 -- unknown letter
        break
      end--if
      if ((numzagrupfn~=0) or (numzaelemfn~=0)) then -- no group after element
        numzagrupfn = 12 -- redundant or wrong order
        break
      end--if
      numzagrupfn = varlimarisk [0] -- eat it, number 1...6 guaranteed
      strelemento = string.sub(strelemento,4,-1) -- remove 3, risk of empty
      booremvd = true
    end--if

    if (((numa0a==37) or (numa0a==38)) and (numa1a==32)) then -- "%" or "&"
      if (numzaelemfn~=0) then -- element after group OK, redu still illegal
        numzagrupfn = 12 -- redundant
        break
      end--if
      numzaelemfn = numa0a - 36 -- eat it, 1 or 2 only
      strelemento = string.sub(strelemento,3,-1) -- remove 2, risk of empty
      booremvd = true
    end--if

    if (not booremvd) then
      break -- out of prefixes :-(
    end--if

  end--while -- genuine loop but no index

  if (string.len(strelemento)<2) then
    numzagrupfn = 13 -- need at least 2 octet:s left :-(
  end--if

  if (numzagrupfn>9) then
    strelemento = '' -- punishment on error
  end--if

  return strelemento, numzagrupfn, numzaelemfn

end--function lfhanalpref

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

-- Local function LFHANALSUFF

-- Analyze and remove possible suffixes (joiner dash, abbreviation
-- colon with or without extra content, abbreviation dot).

-- Input  : * strelemitno
--          * tabpositivelist : for example {"ONE","TWO"} to allow
--                              "ONE:" "ONE:"... and "TWO:" "TWO:"...

-- Output : * numlokaaerr
--          * strelemitno : valid suffixes removed on success,
--                          string emptied on error
--          * booreqjoin  : "true" if joiner dash "-" found at end
--          * booindeedab : "true" if either type "3 UPPERCase + colon" or
--                          dot "." found at end
--          * strekstera  : extra content if available, colon ":" for
--                          "colon-type" without extra content

-- Two of three possible discoveries ("colon-type" and "dot-type")
-- are mutually exclusive, but joiner dash is independent.

-- Type "3 UPPERCase + colon" ("colon-type") is prioritized over dot
-- ("dot-type"), but only possible for whitelisted triples supplied
-- via "tabpositivelist". If not on the whitelist, then the "dot-type"
-- has a chance.

local function lfhanalsuff (strelemitno, tabpositivelist) -- !!!FIXME!!! colon NOT yet supp

  local varfromwhite = 0
  local strekstera = '' -- preASSume empty
  local numlokaaerr = 0 -- preASSume innocence
  local numleenj = 0
  local numb77b = 0
  local numwhiteindex = 1 -- ONE-based
  local booreqjoin = false
  local booindeedab = false

  numleenj = string.len(strelemitno)
  numb77b = string.byte(strelemitno,-1,-1)

  while true do -- fake loop

    if (numb77b==45) then -- joiner dash "-"
      if (numleenj<2) then
        numlokaaerr = 17 -- bad ie alone dash
        break
      end--if
      booreqjoin = true -- NOT further restricted by length of string !!!
      strelemitno = string.sub(strelemitno,1,-2) -- chop it off
      numleenj =  numleenj - 1 -- must adjust
      numb77b = string.byte(strelemitno,-1,-1) -- must be REDONE !!!
    end--if

    while true do -- inner genuine loop
      varfromwhite = tabpositivelist[numwhiteindex]
      break -- !!!FIXME!!! colon here
    end--while

    if (numb77b==46) then -- abbreviation dot "."
      if ((numleenj<2) or (numleenj>12)) then
        numlokaaerr = 36 -- bad dot                         !!!FIXME!!! incomplete check limited set
        break
      end--if
      booindeedab = true
      strelemitno = string.sub(strelemitno,1,-2) -- chop it off
    end--if

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

  if (numlokaaerr~=0) then -- return both empty on error
    strelemitno = ''
    strekstera = ''
  end--if

  return numlokaaerr, strelemitno, booreqjoin, booindeedab, strekstera

end--function lfhanalsuff

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

-- Local function LFHSPLITTOTABLE

-- Convert one string with elements separated by dedicated separator (CSV
-- SSV WSV) char into separate non-empty strings stored in a LUA table.

-- Input  : * strdinding -- must NOT contain codes below 32
--          * numseparator -- 44 for comma "," (CSV) or 59 for semicolon ";"
--                            or 124 for wall "|"
--          * strreservph -- reserved other placeholder prohibited as
--                           element (empty here if nothing to be prohibited)
--          * num37min -- minimal number of elements (>= 1)
--          * num37max -- maximal number of elements (>= 1)

-- Output : * num37status -- ZERO success, possible errors E40 E41 E42 E43
--          * numsubstrinx -- number of elements found, also valid on E42,
--                            max 5 too many, ZERO on other errors
--          * tabyeahsplit -- ZERO-based indexes, placeholder "-" possible,
--                            empty string impossible, empty table {} on error

-- Depends on functions :
-- [G] lfgtrimspcm (NOPE alternatively "lfgtrimwhites" usable too)

-- * empty elements must use dash "-" as a placeholder (applies to both
--   the incoming string and the returned table, but there is other
--   sub "lfhnildashtoempty")
-- * excessive spaces are not a problem, here they are trimmed
--   away from single elements (before and after element, not inner)
-- * we run up to 5 elements over limit in order to give useful report on E42

-- Errors:
-- * E40 discovered an empty element
-- * E41 discovered an element literally equal the reserved other placeholder
-- * E42 number of elements in the input string out of range
-- * E43 other error

-- Valid (assuming reserved other placeholder is "=", separator char
-- is wall "|", minimum is ONE, maximum 6):
-- * "-"
-- * "-|-|-|      -|-"
-- * "a"
-- * "     a" (leading and trailing spaces are legal)
-- * "jamak sama|nomina jamak bentuk sama"
-- * "jamak sama|nomina jamak bentuk sama|-"
-- * "jamak sama|nomina jamak bentuk sama|-|-|-|hej  " (6 elements)

-- Invalid (assuming reserved other placeholder is "=", separator char
-- is wall "|", minimum is ONE, maximum 6):
-- * ""
-- * "proton|  |antiproton" (inner empty element)
-- * "jamak sama|nomina jamak bentuk sama|" (empty element at end)
-- * "kato|   =    |lemniskato" (reserved other placeholder used as element)
-- * "jamak sama|nomina jamak bentuk sama|-|-|-|hej|nej" (too much
--   with 6 walls and 7 elements)

local function lfhsplittotable (strdinding,numseparator,strreservph,num37min,num37max)

  local vartmmp = 0
  local tabyeahsplit = {}
  local stronesub = ''

  local numinlenin = 0
  local numchrindexx = 1 -- ONE-based
  local numsubstrinx = 0
  local num37status = 0 -- preASSume innocence

  numinlenin = string.len(strdinding)
  if (numinlenin==0) then
    num37status = 43 -- E43 hopefully impossible
  end--if
  if ((numseparator~=44) and (numseparator~=59) and (numseparator~=124)) then
    num37status = 43 -- E43 hopefully impossible
  end--if

  while true do
    if (num37status~=0) then -- hopefully impossible
      break
    end--if
    vartmmp = string.find (strdinding, string.char(numseparator), numchrindexx, true) -- plain text search
    if (vartmmp==numchrindexx) then -- nothing to pick before separator char
      num37status = 40 -- E40 discovered an empty element
      break
    end--if
    if (vartmmp==numinlenin) then -- nothing to pick after separator char
      num37status = 40 -- E40 discovered an empty element
      break
    end--if
    if (vartmmp==nil) then
      vartmmp = numinlenin -- ONE-based last inclusive posi is end of string
    else
      vartmmp = vartmmp - 1 -- ONE-based last inclusive posi
    end--if
    stronesub = string.sub (strdinding,numchrindexx,vartmmp) -- at least ONE
    stronesub = lfgtrimspcm (stronesub) -- trim space only, risk of empty output
    if (stronesub=='') then
      num37status = 40 -- E40 empty element
      break
    end--if
    if (stronesub==strreservph) then
      num37status = 41 -- E41 reserved other placeholder
      break
    end--if
    tabyeahsplit[numsubstrinx] = stronesub -- store it
    numsubstrinx = numsubstrinx + 1 -- ZERO-based
    if (vartmmp==numinlenin) then
      break -- all stored, done
    end--if
    if (numsubstrinx>=(num37max+5)) then
      break -- abort now, max count elements exceeded and no end yet, soon E42
    end--if
    numchrindexx = vartmmp + 2 -- at least 1 octet left !!!
  end--while

  if ((num37status==0) and ((numsubstrinx<num37min) or (numsubstrinx>num37max))) then
    num37status = 42 -- E42 wrong number of elem -- do NOT override other err
  end--if

  if (num37status~=0) then
    if (num37status~=42) then
      numsubstrinx = 0 -- on E42 keep the count but still return empty table
    end--if
    tabyeahsplit = {} -- F**K
  end--if

  return num37status, numsubstrinx, tabyeahsplit

end--function lfhsplittotable

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

-- Local function LFHNILDASHTOEMPTY

local function lfhnildashtoempty (tabmasukkeluar, numfromin, numtoto)
  local varonepicked = 0
  while true do
    if (numfromin>numtoto) then -- upper limit is inclusive
      break
    end--if
    varonepicked = tabmasukkeluar[numfromin]
    if ((type(varonepicked)~='string') or (varonepicked=='-')) then
      tabmasukkeluar[numfromin] = ''
    end--if
    numfromin = numfromin + 1
  end--while
  return tabmasukkeluar
end--function lfhnildashtoempty

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

-- Local function LFHTRANSLATETRLI

-- Translate T ie translingual stuff (intended for signs and symbols)
-- ie phase 1. Caller must care that the lang code is "mul" only.

-- Input  : * strmytrli

-- Output : * vartbtrlitrli : 5 strings at [0]...[4] and 1 boolean at
--                            [33] (always false here), or type "number"
--                            error code on error

-- Depends on functions :
-- [H] lfhsplittotable lfhnildashtoempty

-- In table coming from "lfhsplittotable" values are either non-empty
-- strings or type "nil". Empty string is impossible in favor of either
-- "-" or type "nil"

-- possible errors:
-- * 2 trli not found (peeking "contabtt" fails)
-- * 3 trli malformed (split via "lfhsplittotable" fails)
-- * 4 control code used (it is prohibited here for "T")

local function lfhtranslatetrli (strmytrli)

  local vartbtrlitrli = 0
  local varpicktrli = 0
  local numgg45sta = 0
  local numgg45jum = 0
  local numlouklera = 0

  while true do -- fake loop
    varpicktrli = contabtt [strmytrli] -- risk of "nil"
    if (type(varpicktrli)~='string') then
      numlouklera = 2
      break
    end--if
    numgg45sta, numgg45jum, vartbtrlitrli = lfhsplittotable (varpicktrli,124,'',1,6)
    if (numgg45sta~=0) then
      numlouklera = 3
      break
    end--if
    vartbtrlitrli = lfhnildashtoempty (vartbtrlitrli,0,5)
    varpicktrli = vartbtrlitrli[5] -- low risk of bad content
    vartbtrlitrli[5] = nil -- remove it
    if (varpicktrli~='') then -- it is completely prohibited for "T"
      numlouklera = 4
      break
    end--if
    vartbtrlitrli[33] = false -- always false for group "T"
    if (vartbtrlitrli[1]=='') then
      vartbtrlitrli[1] = vartbtrlitrli[0] -- this will probably be the rule
    end--if
    break -- finally to join mark
  end--while -- fake loop -- join mark

  if (numlouklera~=0) then
    vartbtrlitrli = numlouklera
  end--if

  return vartbtrlitrli -- type "table" or "number"

end--function lfhtranslatetrli

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

-- Local function LFHPOSTPROCESSDIAL

-- Postprocess table for D ie dialect ie phase 2.

-- Input  : * tabdialdial : see below, 5 strings at [0]...[4],
--                          fool-proof ie non-table legal
--          * strbahanomo : lang name

-- Output : * numdialerror : error code
--          * tabdialdial : see below, 5 strings at [0]...[4]
--                          and one boolean at [33]

-- Depends on functions :
-- [H] lfhnildashtoempty

-- example of incoming table table picked by caller from
-- "loaddata-tbldialektoj" at index "en-ZA" :
-- [0] (visi)                    "Afrika Selatan
-- [1] (kato)                    "-"
-- [2]                           "-"
-- [3] (tooltip)                 "-"
-- [4] (image)                   "South_Africa_Flag.svg"
-- [5] (prohibited control code) nil

-- example of result based on table above and "strbahanomo" = "Inggris" :
-- [0] (visi)                    "<small>Inggris</small> Afrika Selatan"
-- [1] (kato)                    "Inggris Afrika Selatan"
-- [33]                          false

-- augmentation including "<small>" is applied only to the visible
-- text in [0] AFTER the raw content has been used to brew [1]

-- possible errors:
-- * 6 incoming table is not a table
-- * 7 control code used (it is prohibited here for "D")

local function lfhpostprocessdial (tabdialdial, strbahanomo)

  local numlocalerr = 0

  while true do -- fake loop
    if (type(tabdialdial)~='table') then
      tabdialdial = {}
      numlocalerr = 6
      break
    end--if
    tabdialdial = lfhnildashtoempty(tabdialdial,0,5) -- type "table" sure
    if (tabdialdial[5]~='') then -- residual risk of bad content
      tabdialdial = {} -- value here completely prohibited for "D"
      numlocalerr = 7
      break
    end--if
    tabdialdial[33] = false -- always false for group "D"
    if (tabdialdial[1]=='') then
      tabdialdial[1] = strbahanomo .. ' ' .. tabdialdial[0] -- the common case
    end--if
    tabdialdial[0] = '<small>' .. strbahanomo .. '</small> ' .. tabdialdial[0] -- special rule
    break -- finally to join mark
  end--while -- fake loop -- join mark

  return numlocalerr, tabdialdial

end--function lfhpostprocessdial

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

-- Local function LFHTRANSLATELAIN

-- Translate G F S C ie phase 3 to 6.

-- Input  : * strmylain
--          * numfazo : 3 ... 6 -- nope-T nope-D G F S C

-- Output : * vartblainlain : 5 strings at [0]...[4] and 1 boolean at
--                            [33], or type "number" error code on error

-- Depends on functions :
-- [H] lfhsplittotable lfhnildashtoempty

-- possible errors:
-- * 2 abbre not found (peeking "contab-gfsc" fails)
-- * 3 abbre malformed (split via "lfhsplittotable" fails)
-- * 4 optional control code faulty (different from "" or "1")

local function lfhtranslatelain (strmylain, numfazo)

  local vartblainlain = 0
  local varpicklain = 0
  local numgg46sta = 0
  local numgg46jum = 0
  local numlokalera = 0

  while true do -- fake loop
    if (numfazo==3) then
      varpicklain = contabgg [strmylain] -- risk of "nil"
    end--if
    if (numfazo==4) then
      varpicklain = contabf [strmylain] -- risk of "nil"
    end--if
    if (numfazo==5) then
      varpicklain = contabs [strmylain] -- risk of "nil"
    end--if
    if (numfazo==6) then
      varpicklain = contabc [strmylain] -- risk of "nil"
    end--if
    if (type(varpicklain)~='string') then
      numlokalera = 2
      break
    end--if
    numgg46sta, numgg46jum, vartblainlain = lfhsplittotable (varpicklain,124,'',1,6)
    if (numgg46sta~=0) then
      numlokalera = 3
      break
    end--if
    vartblainlain = lfhnildashtoempty (vartblainlain,0,5)
    varpicklain = vartblainlain[5] -- low risk of bad content
    vartblainlain[5] = nil -- remove it
    if ((varpicklain~='') and (varpicklain~="1")) then
      numlokalera = 4
      break
    end--if
    vartblainlain[33] = (varpicklain~="1") -- usually true, false possible
    if (vartblainlain[1]=='') then
      vartblainlain[1] = vartblainlain[0] -- this will probably be the rule
    end--if
    break -- finally to join mark
  end--while -- fake loop -- join mark

  if (numlokalera~=0) then
    vartblainlain = numlokalera
  end--if

  return vartblainlain -- type "table" or "number"

end--function lfhtranslatelain

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

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

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

function exporttable.ek (arxframent)

  -- general unknown type

  local vartymp = 0 -- variable without type multipurpose

  -- special type "args"

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

  -- general tab

  local tablg76yleft = {}  -- lang
  local tabdk78ysubt = {}  -- dial
  local tabx24x = {}       -- chain from arxsomons[2] ... arxsomons[25]
  local taberrdetail = {}  -- sent to "lfhbrewerrinsi"

  -- peeked stuff

  local strpiklangcode = '' -- "en" -- site language
  local strpikkatns    = '' -- "Category" -- namespace

  -- general str

  local strkodbah    = '' -- langcode (2 or 3 lowercase) from arxsomons[1]
  local strpagenam   = '' -- from "{{PAGENAME}}" or "pagenameoverridetestonly"
  local strnambah    = '' -- langname (without prefix "Bahasa")
  local strwarna     = '' -- raw 6 hex digits
  local strpikpareuf = '' -- name of templat no quot no NS prefix <<doit>>
  local strpikparent = '' -- name of templat long yes quot <<"Template:doit">>  !!!FIXME!!!

  local strelvisi  = ''  -- NOT yet UPPERcased (nurplurala, fiziko, vulgara)
  local strelkato  = ''  -- no "<small>" here !!!
  local strellink  = ''  -- "w:" permitted, wall NOT permitted
  local streltool  = ''  -- tooltip
  local strelimag  = ''  -- image name

  local strtymp    = ''  -- temp

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

  -- general num

  local numerr    = 0  -- 1 in 2 pa 3 sub 4 neva 5 neko 6 res 7 gene ...
  local numjumele = 0  -- number of elements (1...24) in chain
  local numerrpos = 0  -- posi of error in elements (1...24) ie NOT octet:s
  local numtamp   = 0
  local num2statcode = 0

  -- general boo

  local boonocat  = false  -- from "nocat=true"
  local boonokta  = false  -- from "R" or "M" global prefix

  local bootimp   = false
  local booismini = false  -- "false" for full or "R" | "true" for "M" minimal
  local booabbre  = false  -- is abbreviation element (colon-type or dot-type)
  local boobrewkt = false  -- brew 1 or 2 cat:s
  local boobrewld = false  -- brew language-dependent cat too
  local booshowvi = false  -- show visible text for element

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

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

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

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

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

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

  lfdtracemsg ('This is "mtagg", requested "detrc" report')
  lfdshowvar (conbookodlng,'conbookodlng')
  lfdshowvar (conboomiddig,'conboomiddig')
  lfdshowvar (numerr,'numerr','should be ZERO')

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

  -- this depends on "arxframent" if parent requested, but NEVER on "arx"

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

  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
    if ((type(strpiklangcode)~='string') or (type(strpikkatns)~='string')) then
      numerr = 1 -- #E01 internal
    end--if
  end--if

  lfdshowvar (strpiklangcode,'strpiklangcode','peeking from MW done')
  lfdshowvar (strpikkatns,'strpikkatns')
  lfdshowvar (numerr,'numerr','should be ZERO')

  ---- BOTHER WITH NAME OF INTENDED CALLER ----

  -- full "strpikparent" is used for error messages  !!!FIXME!!! peek it
  -- unfull "strpikpareuf" is used for tracking cat:s

  strpikpareuf = contabkoll [2] -- no quot no NS prefix <<doit>> -- unfull
  strpikparent = '"' .. contabkoll [1] .. contabkoll [2] .. '"' -- full

  lfdshowvar (strpikparent,'strpikparent')

  ---- PROCESS ERROR MESSAGES, FILL IN ALWAYS, CONVERT EO ONLY IF NEEDED ----

  -- placeholder "\@" "\\@" is replaced by augmented name of the caller
  -- from "strpikparent" in any case, for example <<"SXablono:test">> or
  -- <<"Templat:test">>, only for EO the X-substitution is subsequently done

  if (numerr==0) then
    contaberaroj = lfhfillsurrstrtab (contaberaroj, strpikparent, strpiklangcode)
    contabtt     = lfhfillsurrstrtab (contabtt, nil, strpiklangcode)
    contabgg     = lfhfillsurrstrtab (contabgg, nil, strpiklangcode)
    contabtrako  = lfhfillsurrstrtab (contabtrako, nil, strpiklangcode)
  end--if

  lfdtracemsg ('Conversion and filling done')

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

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

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

  ---- PICK TOTALLY TWO SUBTABLES FROM TWO SUBMODULES ----

  -- here risk of #E02 #E03 #E04 #E05

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

  while true do -- fake loop

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

    num2statcode, bootimp = lfhvali1status99code (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['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)

    num2statcode, bootimp = lfhvali1status99code (qldlektoj[2]) -- from "loaddata-tbldialektoj"
    if (num2statcode~=0) then
      if (bootimp) then
        numerr = 5 -- #E05 nombrigita
      else
        numerr = 4 -- #E04 malica
      end--if
      break -- to join mark
    end--if

    vartymp = qldlektoj['T78'] -- from "loaddata-tbldialektoj"
    if (type(vartymp)~='table') then -- important check
      numerr = 4 -- #E04 malica
      break -- to join mark
    end--if
    tabdk78ysubt = vartymp -- y-index -> sub-sub-table (one table per dialcode)

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

  lfdshowvar (numerr,'numerr','picked two subtables T76 ling and T78 dial')
  lfdshowvar (num2statcode,'num2statcode')

  ---- SEIZE 2...25 ANON PARAMETERS ----

  -- in arxsomons[1] expecting lang code
  -- in arxsomons[2]...arxsomons[25] expecting main control chain

  -- result in "strkodbah" will be later used as index in "tablg76yleft"
  -- chain goes into "tabx24x" with prevalidation
  -- #E13 wrong number of params or wrong length of string !!!FIXME!!!
  -- #E04 bad langcode !!!FIXME!!!
  -- #E06 reserved characters !!!FIXME!!!

  if (numerr==0) then
    if ((arxsomons[1]==nil) or (arxsomons[2]==nil) or (arxsomons[26])) then
      numerr = 13 -- #E13 -- need 2 ... 25 anon params, NOT more
    else
      strkodbah = arxsomons[1] -- y-index word (lang code)
      if (not lfivalidatelnkoadv(strkodbah,false,false,conbookodlng,conboomiddig,false)) then
        numerr = 4 -- #E04 -- evidente nevalida
      end--if
    end--if
  end--if

  if (numerr==0) then

    do -- scope
    local numindxxi = 0
    local numjjlenj = 0
    local varjjtmpj = ''

    while true do
      varjjtmpj = arxsomons[numindxxi+2] -- TWO-based
      if (type(varjjtmpj)~='string') then
        if (numindxxi==0) then
          numerr = 13 -- #E13 -- we need at least one more
        end--if
        break -- abort with success or failure
      end--if
      numjjlenj = string.len(varjjtmpj)
      if ((numjjlenj<2) or (numjjlenj>60)) then
        numerr = 13 -- #E13
        break
      end--if
      tabx24x [numindxxi] = varjjtmpj -- ZERO-based
      numindxxi = numindxxi + 1
    end--while
    if (numerr==0) then
      numjumele = numindxxi -- 1...24
    end--if

    end--do scope

  end--if

  lfdshowvar (strkodbah,'strkodbah','language code')
  lfdshowvar (numjumele,'numjumele','number of elements')
  lfdshowvar (tabx24x,'tabx24x',nil,26)
  lfdshowvar (numerr,'numerr','after seizure of anon params')

  ---- PROCESS 3 HIDDEN NAMED PARAMS INTO 1 STRING AND 2 BOOLEAN:S ----

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

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

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

  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 (lfgstringrange(vartymp,1,120)) then -- empty or too long NOT legal
        strpagenam = vartymp
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  if (arxsomons['nocat']=='true') then
    boonocat = true
  end--if

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

  lfdshowvar (numerr,'numerr','done with hidden params')
  lfdshowvar (boonocat,'boonocat')

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

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

  -- must be 1...120 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 (lfgstringrange(vartymp,1,120)) then -- empty or too long NOT legal
      strpagenam = vartymp -- cannot be left empty
    else
      numerr = 1 -- #E01 internal
    end--if
  end--if

  ---- STRICTLY CHECK THE PAGENAME ----

  -- for example "o'clock" is legal and "o'clock's" is legal too
  -- but "o''clock" is a crime

  if (numerr==0) then
    if (strpagenam=='') then
      numerr = 1 -- #E01 internal
    else
      if (lfibanmulti("'1[0]0{0}0",strpagenam)) then -- returns true if evil
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  lfdshowvar (numerr,'numerr','checked pagename')

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

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

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

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

  ---- PEEK THE LANGUAGE NAME ----

  -- for lang name in site language (/c0/) peeked from "tablg76yleft":
  -- * type "nil" can become #E05 (unknown code) if the site
  --   langcode works, otherwise #E03 (broken submodule)
  -- * "-" is unconditionally evil with #E03 (broken submodule)

  -- here we depend on "strpiklangcode" and submodule "loaddata-tbllingvoj"

  if (numerr==0) then

    while true do -- fake loop
      vartymp = tablg76yleft[strkodbah]
      if (type(vartymp)~='string') then
        if (type(tablg76yleft[strpiklangcode])=='string') then
          numerr = 5 -- #E05 unknown code given (since site code works)
        else
          numerr = 3 -- #E03 broken submodule (site code does NOT work either)
        end--if
        break
      end--if
      if (string.len(vartymp)<2) then -- less than 2 letters is not legal
        numerr = 3 -- #E03 broken submodule
        break
      end--if
      strnambah = vartymp -- got lang name :-)
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if

  lfdshowvar (strnambah,'strnambah','peeked lang name via T76')
  lfdshowvar (numerr,'numerr')

  ---- PARSE THE CONTROL CHAIN ----

  -- table "tabx24x" is guaranteed to contain at least one element at index 0
  -- the stipulated order is D:1 G:2 F:3 S:4 C:5 -- ZERO means no phase yet
  -- fill "strvisgud" and "strinvkat" here
  -- "%" show text do not categorize -- "&" categorize only do not show text
  -- combined prefix possible "D: & " in this order group -> element
  -- from "lfhanalpref" : 0 nope | 1..5 "D" ... "C" | 11 unk | 12 red | 13 emp
  --                      0 nope | 1 found "%" ("nopercatcent") vis only
  --                             | 2 found "&" cat only

  -- the 5 components are "strelvisi", "strelkato", "strellink",
  --                      "streltool", "strelimag"

  -- "[[File:Omong_Kosong.png|20px|link=]]" note that "link=" needs PD image

  -- there are 4 ways to suppress lemma categorization:
  -- * full suppression via "nocat=true" into "boonocat = true" (suppresses
  --   also tracking categories)
  -- * full suppression via "R" or "M" global prefix into "boonokta = true"
  -- * full suppression via prefix "%" AKA "nopercatcent" in a plain
  --   or abbreviation element (via numanlelpr==1) into "boobrewkt = false"
  -- * partial suppression via control code "1" in an abbreviation
  --   element into "boobrewld = false"

  if (numerr==0) then

    do -- scope
      local tabkompon = {}
      local strelemin = ''
      local strexxtra = '' -- autogenerated or explicit
      local strdialkey = ''
      local strspankolor = ''
      local numphaase = 0 -- group we are in, start from ZERO
      local numsrcpoi = 0 -- index
      local numanlgrpr = 0
      local numanlelpr = 0 -- 0 or 1 or 2 current
      local numprvelpr = 0 -- 0 or 1 or 2 previous
      local numoct    = 0
      local numerakod = 0 -- local error code
      local boojoinrekv = false -- join request copied into "boodojoin"
      local boodojoin = false

      while true do

      if (numsrcpoi==numjumele) then -- at least ONE iteration
        break
      end--if
      strelemin = tabx24x [numsrcpoi] -- type "nil" impossible

      lfdtracemsg ('Picked one element of the control chain')
      lfdshowvar (numsrcpoi,'numsrcpoi')
      lfdshowvar (strelemin,'strelemin')

      if (numsrcpoi==0) then

        numoct = string.byte(strelemin,1,1) -- length prevalidated 2...50
        if (numoct==82) then
          boonokta = true -- got "R"
          strelemin = string.sub(strelemin,2,-1)
        end--if
        if (numoct==77) then
          boonokta = true -- got "M"
          booismini = true -- !!!FIXME!!! implementation non-existent
          strelemin = string.sub(strelemin,2,-1)
        end--if

        lfdtracemsg ('Inspected and eaten possible special global ZERO-position prefix')
        lfdshowvar (strelemin,'strelemin','again')
        lfdshowvar (boonokta,'boonokta')
        lfdshowvar (booismini,'booismini')

      end--if

      strelemin, numanlgrpr, numanlelpr = lfhanalpref (strelemin)

      lfdtracemsg ('Chopped off possible prefixes')
      lfdshowvar (strelemin,'strelemin','again')
      lfdshowvar (numanlgrpr,'numanlgrpr')
      lfdshowvar (numanlelpr,'numanlelpr','tristate, ONE of ONE crucial for join')

      if (numanlgrpr==11) then
        numerr = 15 -- #E15 bad group code (message needs index too)
        break
      end--if
      if (numanlgrpr==12) then
        numerr = 16 -- #E16 redu or invalid group prefix
        break
      end--if
      if (numanlgrpr==13) then
        numerr = 7 -- #E07 generic (string became too short)
        break
      end--if
      if (numanlgrpr~=0) then
        if (numanlgrpr<numphaase) then
          numerr = 17 -- #E17 wrong order of group prefixes
          break
        end--if
        if (numanlgrpr==numphaase) then
          numerr = 19 -- #E19 repetitive group prefix
          break
        end--if
        numphaase = numanlgrpr -- OK, got a new group
        boodojoin = false -- reset join request !!!FIXME!!! should be an error ??
      end--if
      if (numphaase==0) then -- this must be below "numphaase = numanlgrpr" !!!
        numerr = 20 -- #E20 element outside of group
        break
      end--if

      numerakod, strelemin, boojoinrekv, booabbre, strexxtra = lfhanalsuff (strelemin,{"VRF","VRG","VRH"})

      lfdtracemsg ('Chopped off possible suffixes')
      lfdshowvar (numerakod,'numerakod')
      lfdshowvar (strelemin,'strelemin','again and maybe reduced')
      lfdshowvar (boojoinrekv,'boojoinrekv')
      lfdshowvar (booabbre,'booabbre')
      lfdshowvar (strexxtra,'strexxtra')

      if (boodojoin) then
        if (boojoinrekv) then
          lfdtracemsg ('Both are true, #E29')
          numerr = 29 -- #E29 cannot join more than 2 elements
          break
        end--if
        if (not (((numprvelpr==0) and (numanlelpr==1)) or ((numprvelpr==1) and (numanlelpr==0)))) then
          lfdtracemsg ('NOT exactly ONE "%", #E29')
          numerr = 29 -- #E29 exactly ONE of them must have "%", prohibit "&"
          break
        end--if
      end--if

      if ((numanlelpr~=0) and booabbre and (strexxtra~='')) then
        lfdtracemsg ('Element prefix and colon-type, #E21')
        numerr = 21 -- #E21 do not combine like that
        break
      end--if

      -- !!!FIXME!!! #E28 failed auto

      if ((numphaase==1) and (strkodbah~="mul")) then -- "T"
        numerr = 23 -- #E23 group "T" requires "mul"
        break
      end--if

      if (booabbre) then
        if (numphaase==1) then -- "T"
          lfdshowvar (strelemin,'strelemin','before T-translation')
          tabkompon = lfhtranslatetrli (strelemin)
          lfdshowvar (tabkompon,'tabkompon','after T-translation')
          if (type(tabkompon)~="table") then
            numerr = 24 -- #E24 unknown translang  !!!FIXME!!! use local error code returned
            break
          end--if
        end--if
        if (numphaase==2) then -- "D"
          strdialkey = strkodbah .. '-' .. strelemin
          lfdshowvar (strdialkey,'strdialkey','before D-translation')
          tabkompon = lfhrebuildtable (tabdk78ysubt [strdialkey]) -- risk of "nil"
          lfdshowvar (tabkompon,'tabkompon','after D-translation')
          numerakod, tabkompon = lfhpostprocessdial (tabkompon, strnambah)
          lfdshowvar (tabkompon,'tabkompon','after D-postprocessing')
          if (numerakod~=0) then
            numerr = 25 -- #E25 unknown dialect  !!!FIXME!!! local code 6 vs local code 7
            break
          end--if
        end--if
        if (numphaase>2) then -- G F S C
          lfdshowvar (strelemin,'strelemin','before GFSC-translation')
          tabkompon = lfhtranslatelain (strelemin,numphaase)
          lfdshowvar (tabkompon,'tabkompon','after GFSC-translation')
          if (type(tabkompon)~="table") then
            numerr = 26 -- #E26 unknown 4 group !!!FIXME!!! use local error code returned
            break
          end--if
        end--if
        strelvisi = tabkompon[0] -- translated abbreviation element
        strelkato = tabkompon[1] -- same or different here
        strellink = tabkompon[2]
        streltool = tabkompon[3]
        strelimag = tabkompon[4]
        boobrewkt = (numanlelpr~=1) -- false for "%" ("nopercatcent") vis only
        boobrewld = tabkompon[33]
        booshowvi = (numanlelpr~=2) -- false for "&" cat only
      else
        strelvisi = strelemin -- plain text element
        strelkato = strelemin -- always same here
        strellink = '' -- inherently impossible
        streltool = '' -- inherently impossible
        strelimag = '' -- inherently impossible
        boobrewkt = (numanlelpr~=1)
        boobrewld = true -- true here (false for T and D see below)
        booshowvi = (numanlelpr~=2)
      end--if

      if ((numphaase==1) or (numphaase==2)) then
        boobrewld = false -- always false for groups T and D
      end--if

      if (boobrewkt and (not boonocat) and (not boonokta)) then -- do 1 or 2 cats from "strelkato"
        strtymp = lfucaserest(strelkato,true,false,'eo') -- make upper, only one char
        strinvkat = strinvkat .. '[[' .. strpikkatns .. ':' .. strtymp .. ']]'
        if (boobrewld) then
          strinvkat = strinvkat .. '[[' .. strpikkatns .. ':' .. strtymp .. ' (' .. strnambah .. ')]]'
        end--if
      end--if

      if (booshowvi) then -- do show text from "strelvisi" from above and more
        strwarna = lfhgetwarna (numphaase,((strkodbah=="eo") and (numphaase==3))) -- only by index
        strspankolor = '<span style="background-color:#' .. strwarna .. '>'
        if (strellink~='') then
          strelvisi = '[[' .. strellink .. '|' .. strelvisi .. ']]'
        end--if
        strelvisi = strspankolor .. '<i>' .. strelvisi .. '</i></span>'
        if (strelimag~='') then
          strelvisi = '&nbsp;[[File:'.. strelimag ..'|20px]]&nbsp;&nbsp;' .. strelvisi
        end--if
        if (strvisgud~='') then
          if (boodojoin) then
            strvisgud = strvisgud .. strspankolor .. ' ' .. '</span>' -- separate them by coloured space only
          else
            strvisgud = strvisgud .. '; ' -- separate them harder and interrupt color
          end--if
        end--if
        strvisgud = strvisgud .. strelvisi
      end--if

        boodojoin = boojoinrekv -- join request applies to following element
        numprvelpr = numanlelpr -- and crucial check of "%" will be done there
        numsrcpoi = numsrcpoi + 1

      end--while

      numerrpos = numsrcpoi -- posi into more durable var, used on error only

    end--do scope

  end--if (numerr==0) then

  if ((numerr==0) and (strvisgud~='')) then
    strvisgud = '(' .. strvisgud .. ')'
  end--if

  ---- BREW TRACKING CAT:S #E02...#E99 ----

  -- no tracking cat:s for #E01
  -- "nocat=true" suppresses even tracking cat:s  !!!FIXME!!!
  -- special handling of #E04 evid and #E05 neko  !!!FIXME!!! error codes
  -- here we use "contabtrako" 0...5
  -- here we use "contabkoll" via "strpikpareuf" name of calling template
  -- here we use "strkodbah"

  if ((numerr>1) and (not boonocat)) then
    if ((numerr==4) or (numerr==5)) then
      if (numerr==4) then
        strtymp = contabtrako [1] -- #E04 evid  !!!FIXME!!! error codes
      else
        strtymp = contabtrako [2] -- #E05 neko  !!!FIXME!!! error codes
      end--if
      strtymp = '[[' .. strpikkatns .. ':' .. strtymp .. ' ' .. contabtrako [3] -- [3] is term for "language code"
      strtrakat = strtymp .. ']]' -- generic #E04 #E05  !!!FIXME!!! error codes
      strtrakat = strtrakat .. strtymp .. ' ' .. contabtrako [4] .. ' (' .. strpikpareuf .. ')]]' -- loke
      strtrakat = strtrakat .. strtymp .. ' ' .. contabtrako [5] .. ' (' .. strkodbah .. ')]]' -- nome
    else
      strtymp = '[[' .. strpikkatns .. ':' .. contabtrako [0] -- [0] is term for "bad use of template"
      strtrakat = strtymp .. ']]' -- generic other errors #E02 #E03 E#06 ...
      strtrakat = strtrakat .. strtymp .. ' (' .. strpikpareuf .. ')]]' -- specific
    end--if
  end--if

  ---- WHINE IF YOU MUST #E02...#E99 ---- !!!FIXME!!! from submodule

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

  -- some of them need "numerrpos" via "taberrdetail"

  if (numerr>1) then -- NOT for #E01
    if (numerr==15) then
      taberrdetail = {[0]=tostring(numerrpos+2)} -- #E15 message needs index too
    end--if
    strviserr = lfhbrewerrinsi (numerr,taberrdetail)
  end--if

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

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

  lfdtracemsg ('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
  else
    strret = strviserr .. strtrakat
  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 TABLE ----

return exporttable