Saltu al enhavo

Modulo:msngtbllin

El Vikivortaro
 MODULO
Memtesto disponeblas sur la paĝo Ŝablono:montablng.

--[===[

MODULE "MSNGTBLLIN" (single tabelo kun lingvoj)

"eo.wiktionary.org/wiki/Modulo:msngtbllin" <!--2026-Feb-09-->
"id.wiktionary.org/wiki/Modul:msngtbllin"

Purpose: brews a table with info about one language

Utilo: generas tabelon kun informoj pri unu lingvo

Manfaat: membuat tabel dengan informasi tentang satu bahasa

Syfte: skapar en tabell med information kring ett spraak

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaend av mallar:
* "montablng" (EO) / "mpltabbah" (ID)

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

Incoming: * 2 ordinary parameters
            * anonymous obligatory parameter
              * string with 2 or 3 char:s -- language access code
            * named obligatory parameter
              * "infsel=" control string for lines -- 6 digits (boolean
                 options: "0" do not show | "1" show and query |
                 value "2" NOT available here)
                * "d07" "tln" top lng cat
                * "d07" "kap" cat
                * "d08" "vor" cat
                * "d08" "mal" cat
                * "d09" "apx" appendix
                * "d09" "ind" index

Returned: * one big string

This module is unbreakable (when called with correct module name
and function name). Every imaginable input from the parent and
from the imported module "loaddata-tbllingvoj" will output either
a useful result or at least a useful error message.

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 importata modulo "loaddata-tbllingvoj" eldonos aux
utilan rezulton aux almenaux utilan erarmesagxon.

In "msngtbllin" we walk down and for every d-item /d00/ ... /d13/ we brew a
row with two cells title and content. In "bigtable-tbllingvoj" "mkomtbllin" we
walk right and for every d-item /d00/ ... /d13/ we brew one cell in a column.

Note that the parameter "infsel=" (control string with 6 char:s) has almost
same function in both "bigtable-tbllingvoj" "mkomtbllin" and "msngtbllin", but
some of the values are tristate in "bigtable-tbllingvoj" "mkomtbllin", whereas
all are boolean in "msngtbllin", and querying is always on.

It is possible to control whether to show link to appendix page in "d09"
or not. This is provided for consistency with "mkomtbllin", but since
this module "msngtbllin" is supposed to be called from exactly those
appendix pages (via "montablng" or "mpltabbah"), this subparameter will
usually be false ie "0".

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

Numbering of elements in the source table (they become lines):
* numbering starts from ZERO
* /cy/ does not count (it has theoretically index "-1")
* note that there is no comma between /cy/ and /c0/

Structure of a line in the source table with 1+11 values
for 1+11 lines in the generated table:
- /cy/ : y-index marker (2 or 3 digits)
- /c0/ : name of the language in the site language
         * EO: usually AJ lowercase, alternatively SB uppercase begin
         * ID: without "bahasa " and begins with uppercase letter
- /c1/ : QID (for example "Q5432") "Q" + 2...8 digits, earliest digit
         "1"..."9", further digits "0"..."9"
- /c2/ : name of the language in the language itself
         ("propralingve")
- /c3/ : constructed and antique status (digit "0"..."3")
- /c4/ : ISO 639-1:2002 2-letter code (only lowercase)
- /c5/ : ISO 639-3:2007 3-letter code (only lowercase)
- /c6/ : wiki access code, itself usually same as /cy/, but may be different
         and longer up to 10 char:s, for example "zh-min-nan", leave it "-"
         here if code is same or unknown, use "??" if code belongs to
         other language ("als.pedia.org")
- /c7/ : wiki availability code (2 digits "0"..."2" ie "00"..."22"),
         "-" causes reddish background and no info about availability
- /c8/ : name of ipedia article, to be given here only if it CANNOT be
         constructed regularly:
         * EO: * if native form is AJ -> with uppercased begin
                 followed by " lingvo", no "la" article
               * if native form is SB -> unchanged
         * ID: prefixed by "Bahasa " and core word begins with uppercase letter
- /c9/ : category with our site language ("eo" or "id") in other
         tionary (with category NS prefix, but without language prefix,
         without "]]" or other nonsense, can be absurdly long such as
         "Rummad:Wikeriadur:Distagaduriou' a vank en indonezeg" having
         52 octet:s and even worse 2 apo:s)
- /ca/ : number of pages in that category, apo:s ("16'384") are
         tolerable (this cannot be peeked automatically)
Note that /cy/ and /c0/ are ultimately obligatory, the remaining
ones are only highly desirable.

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

Overall structure of the generated destination table:
* title cell "colspan=2" with fixed string (big, bold, no link)
* body part
  * 14 rows per 2 cells with info
     or
  * one single cell "colspan=2" with lagom error #E12 "Nekonata lingvokodo"
* bottom cell "colspan=2" with update/purge link (depends on pagename)
  and link to source table (fixed) and link to complete table (fixed)

Structure of the right column in the destination table with 14 rows:
- /d00/ : * incoming language access code (bold) only if valid and found
          # adjust background for "eo" only
- /d01/ : * name of the language in the site language with link to the
            lemma page (name literally peeked from /c0/ in the source table,
            must be available and valid, link augmented by "pripl5navare"
            according to control string "contabpages[0]")
- /d02/ : * QID (for example "Q5432")
- /d03/ : * name of the language in the language itself
            (literally peeked from /c1/ in the source table)
- /d04/ : * constructed and antique status (expanded from digit "0"..."3"
            peeked from "c2" in the source table)
          # adjust background to greenish or grey if applicable
- d05 : - "ISO 639-1:2002" 2-letter langcode
          (literally peeked from "c3" in the source table)
- d06 : - "ISO 639-3:2007" 3-letter langcode
          (literally peeked from /c4/ in the source table)
- d07 : - ? link to the "tln" top lng category in own tionary if
            desired, no page count here
        - ? EOL (only if both cat:s are desired)
        - ? link to the "kap" category in own tionary with all words in
            the language (constructed from constant string "strpikkatns"
            (category namespace prefix without ":") and /c0/ augmented by
            "pripl5navare" according to control string "contabpages[6]")
          - ? EOL (if complaint below exists)
          - ? complaint about not existing if applicable
          - ? EOL (if the "parent" link to the "kap" category is present)
          - ? number of pages (queried) or "??" if querying does not work
        # adjust background to reddish if the category (one of two)
          does not exist
        ! depends on variables "tabctl.tlnsow" and "tabctl.tlnexp" and "tabctl.kapsow"
          and "tabctl.kapexp" and includes expensive querying (2), column
          can be completely hidden by "tabkol.d07"
- d08 : - ? link to the "vor" category in own tionary with all dictionaries in
            the language (constructed from constant string "strpikkatns"
            (category namespace prefix without ":") and /c0/ augmented by
            "pripl5navare" according to control string "contabpages[3]"), no
            page count here
        - ? EOL (only if both cat:s are desired)
        - ? link to the "mal" template cat
          - ? EOL (if complaint below exists)
          - ? complaint about not existing if applicable
          - ? EOL (if the "parent" link to the "mal" template cat is present)
          - ? number of pages (queried) or "??" if querying does not work
        # adjust background to reddish if the category (one of two)
          does not exist
        ! depends on variables "tabctl.vorsow" and "tabctl.vorexp" and "tabctl.malsow"
          and "tabctl.malexp" and includes expensive querying (2), column
          can be completely hidden by "tabkol.d08"
- d09 : - link to the appendix page (constructed from constant string
          "strpikapxns" (appendix namespace prefix without ":") and /c0/
          augmented by "pripl5navare" according to control string
          "contabpages[1]") controlled by "tabctl.apxsow"
        - EOL (only if both pages are desired)
        - link to the index page in own tionary with
          the language (constructed from constant string "strpikindns"
          (index namespace prefix without ":") and /c0/ augmented by
          "pripl5navare" according to control string "contabpages[2]")
        # adjust background to reddish if at least one of them does not exist
        ! depends on variables "tabctl.apxsow" and "tabctl.apxexp" and "tabctl.indsow"
          and "tabctl.indexp" and includes expensive querying (1), column
          can be completely hidden by "tabkol.d09"
- d10 : - quasi-external link to wikipedia in the language concerned
          (constructed from /c5/ and /c6/ and /cy/, irrespective availability)
        - EOL (if note below exists)
        - note about bad availability if applicable
        # adjust background to grey according to bad availability
        ! no expensive querying here
- d11 : - quasi-external link to tionary in the language concerned
          (constructed from /c5/ and /c6/ and /cy/, irrespective availability)
        - EOL (if note below exists)
        - note about bad availability if applicable
        # adjust background to grey according to bad availability
        ! no expensive querying here
        & special rule ie blocking for own site language
- d12 : - quasi-external link to the article in own ipedia about the language
          (article title taken from /c8/, or alternatively
          constructed using "pripl5navare" from /c0/ and "contabpages[7]")
        # adjust background to reddish if given redundantly
        ! no expensive querying here
- d13 : - quasi-external link to EO or ID category in the tionary in the
          language concerned (peeked from "c8" in the source table)
        - EOL (if number of articles below exists)
        - number of articles (peeked from "c9" in the source table)
        # adjust background to reddish if "c8" or "c9" is not available
        ! no expensive querying here
        & special rule ie blocking for own site language

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

Expensive querying:  !!!FIXME!!!
There are 6 expensive queries (2 cat:s plus appendix plus index, one cat
costs 2 bucks ("ifexist" and "pagesincategory") per page, appendix and index
cost one buck per page) per language and thus per table row. This is a big
problem in "bigtable-tbllingvoj" "mkomtbllin" but here in "msngtbllin"
it is no problem at all.

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

Error handling:
* PRHBREWERR6SLNP used for severities ZERO and ONE
  * severities ZERO do NOT use "numerr"
  * severities ONE do use "numerr" only for #E12
* PRHBREWERR8SUBM used for severity TWO
  * anything except #E12
* here in "msngtbllin" we are much stricter than in "bigtable-tbllingvoj"
  "mkomtbllin", and do NOT show any broken stuff
* we do NOT use red background, but reddish background is still applied
  when appropriate
* #E12 unknown langcode (still show table with lagom error)
* fatal errors (output is minimal, huge error, no table, replace
  the output string if we already began to brew the table):
  * #E02
    * neither the requested language nor our own site language
      could be peeked
  * #E03
  * #E08
  * #E15:
    * lang-specific subtable is bad
    * /c0/ is bad or not available
    * any element other than /c0/ is available but bad
  * #E16 #E17 from "infsel="
* if there are too many elements then we ignore the superfluous ones
* if there are too few elements (but at least one ie /c0/) then we
  show "-" (not "=") and adjust the background color in cells not
  getting any content
* if "mw.title.getCurrentTitle().prefixedText" does not reveal our
  title (needed for purge link) then we don't whine, but use a bad
  default "Special:RecentChanges" instead

Errors codes in "numerr":
* #E01 -- internal
* #E02 -- submodule
* #E03 -- submodule
* #E08 -- wrong number of anon param
* #E11 -- obviously invalid langcode from anon param {{{1}}} (only msng)
* #E12 -- unknown langcode from anon param {{{1}}} (only msng,
          the only error showed inside table)
* #E13 -- anon param {{{1}}} (boolean) technical block is bad (only big)
* #E14 -- anon param {{{2}}} (integer) probability is bad (only big)
* #E15 -- fatal, one of many validations failed, coming
          from PRYDOONELANG (only msng)
* #E16 -- named param "infsel=" missing
* #E17 -- named param "infsel=" invalid

]===]

local exporttable = {}

require('strict')

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

-- uncommentable (imports)

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

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

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

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

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

-- uncommentable text

      local constrtop = '<big><b>Informoj pri lingvo</b></big>'  -- EO (komp no, sng yes)
        -- local constrtop = '<big><b>Informasi bahasa</b></big>'   -- ID (komp no, sng yes)

-- background colour translation for lang status /d04/ and
-- wiki availability /d10/ & /d11/ proc "lfhintegertocol6d"

local contabwarnlng = {}
contabwarnlng [0] = '?'      -- keep default (yellow) -> ordinary lang
contabwarnlng [1] = 'D0FFD8' -- greenish -> semiconlang
contabwarnlng [2] = 'A0FFC0' -- more greenish, but still not green -> conlang
contabwarnlng [3] = 'D0D0D0' -- grey -> dead lang

local contabwarnwik = {}
contabwarnwik [0] = 'B0B0B0' -- darker grey -> dead domain
contabwarnwik [1] = 'D0D0D0' -- grey -> closed wiki
contabwarnwik [2] = '?'      -- keep default (yellow) -> active wiki

-- uncommentable text for lang status /d04/ and
-- wiki availability /d10/ & /d11/

local contabd04ap = {}
      contabd04ap = {'ne planita','parte planita','planita','antikva'}  -- EO (index 1...4, no spaces)
        -- contabd04ap = {'tidak buatan','agak buatan','buatan','kuno'}    -- ID (index 1...4, no spaces)
local contabd10d11w = {}
      contabd10d11w = {'fermita aux malplena','nedisponebla'}  -- EO
        -- contabd10d11w = {'tertutup atau kosong','tidak ada'}   -- ID

-- surrogate transcoding table

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

-- constant table (color)

local contabwarna = {}
contabwarna.frame = '2020E0' -- table frame color (blue) (used only below)
contabwarna.bkgud = 'FFFFD0' -- default cell background color (light yellow)
contabwarna.reddi = 'FFD8D8' -- cell background color on minor error (reddish)
-- contabwarna.merah = 'FFB0B0' -- cell background color on major error (red) (komp yes, sng commented out)

-- constant table (HTML)

local constrbord = 'border:0.25em solid #' .. contabwarna.frame .. ';' -- part of "style" element (used only here in this block)

local contabhtml = {} -- various <table> and <div>

contabhtml.bigbeg = '<table style="' .. constrbord .. 'float:right;margin:0 0 1em 1em;border-collapse:collapse;">'

contabhtml.tdbgti = '<td style="' .. constrbord .. 'text-align:center;padding:0.4em;">'             -- colspan NO color NO
contabhtml.tdcstw = '<td style="' .. constrbord .. 'text-align:center;padding:0.4em;" colspan="2">' -- colspan YES color NO (komp commented out, sng defined)
contabhtml.tdbgco = '<td style="' .. constrbord .. 'text-align:center;padding:0.4em;background:#'   -- colspan NO color YES

contabhtml.tdfd = ';">' -- part of HTML table code after HEX color
contabhtml.tden = '</td>'

-- constant table (error circumfixes)

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

-- uncommentable error messages

local contaberaroj = {}
      contaberaroj[02] = 'Malica eraro en submodulo uzata far %@'                      -- EO #E02
        -- contaberaroj[02] = 'Kesalahan jahat dalam submodul digunakan oleh %@'          -- ID #E02
      contaberaroj[03] = 'Nombrigita eraro en submodulo uzata far %@'                  -- EO #E03
        -- contaberaroj[03] = 'Kesalahan ternomor dalam submodul digunakan oleh %@'       -- ID #E03
      contaberaroj[11] = 'Nevalida lingvokodo'
        -- contaberaroj[11] = 'Kode bahasa tidak sah'               -- #E11
      contaberaroj[12] = 'Nekonata lingvokodo'
        -- contaberaroj[12] = 'Kode bahasa tidak dikenal'           -- #E12
      contaberaroj[15] = 'Grava eraro'
        -- contaberaroj[15] = 'Kesalahan jahat'                     -- #E15
      contaberaroj[16] = 'Parametro "infsel=" ne transdonita'
        -- contaberaroj[16] = 'Parameter "infsel=" tidak diterima'  -- #E16
      contaberaroj[17] = 'Parametro "infsel=" nevalida'
        -- contaberaroj[17] = 'Parameter "infsel=" tidak benar'     -- #E17

-- uncommentable EO vs ID constant table (titles for the table)

  -- * see "lfhbrewtitsel" and note that contabmisc[0] is needed too
  -- * note that we can have 2 items in one cell but the non-bold title part
  --   "kvanto" cannot be doubled and must be shared if needed !!!FIXME!!!

local contabdestit = {}
      contabdestit[00] = 'Alira lingva kodo'                                       -- EO
        -- contabdestit[00] = 'Kode akses bahasa'                                     -- ID
      contabdestit[01] = 'Nomo de la lingvo en EO kaj kapvorta pagxo'              -- EO (complete title bold)
        -- contabdestit[01] = 'Nama bahasa dalam ID dan halaman lema'                 -- ID (complete title bold)
      contabdestit[02] = 'Vikidatumeja pagxo'                                      -- EO
        -- contabdestit[02] = 'Halaman wikidata'                                      -- ID
      contabdestit[03] = 'Lingvonomo propralingve'                                 -- EO
        -- contabdestit[03] = 'Nama bahasa dalam bahasa itu'                          -- ID
      contabdestit[04] = 'Planlingva kaj antikva statuso'                          -- EO
        -- contabdestit[04] = 'Status buatan dan kuno'                                -- ID
      contabdestit[05] = 'ISO 639-1:2002 2-litera kodo'                -- EO
        -- contabdestit[05] = 'ISO 639-1:2002 kode 2 huruf'               -- ID
      contabdestit[06] = 'ISO 639-3:2007 3-litera kodo'                -- EO
        -- contabdestit[06] = 'ISO 639-3:2007 kode 3 huruf'               -- ID
      contabdestit[07] = 'Cxefa kategorio%Kapvorta kategorio#kvanto da pagxoj'             -- EO (colu can be hidden)
        -- contabdestit[07] = 'Kategori utama%Kategori lema#jumlah halaman'                   -- ID (colu can be hidden)
      contabdestit[08] = 'Vortara kategorio%Sxablona kategorio#kvanto da pagxoj'           -- EO (colu can be hidden)
        -- contabdestit[08] = 'Kategori kamus%Kategori templat#jumlah halaman'                -- ID (colu can be hidden)
      contabdestit[09] = 'Aldono%Indekso'                                                  -- EO (colu can be hidden)
        -- contabdestit[09] = 'Lampiran%Indeks'                                               -- ID (colu can be hidden)
      contabdestit[10] = 'Vikipedio en la lingvo'                                                 -- EO
        -- contabdestit[10] = 'Wikipedia dalam bahasa itu'                                           -- ID
      contabdestit[11] = 'Vikivortaro en la lingvo'                                               -- EO
        -- contabdestit[11] = 'Wikikamus dalam bahasa itu'                                           -- ID
      contabdestit[12] = 'Artikolo pri la lingvo en EO vikipedio'                                 -- EO
        -- contabdestit[12] = 'Artikel tentang bahasa itu dalam wikipedia ID'                        -- ID
      contabdestit[13] = 'Kategorio pri EO en vikivortaro en la lingvo#kvanto da pagxoj'          -- EO
        -- contabdestit[13] = 'Kategori tentang ID dalam wikikamus dalam bahasa itu#jumlah halaman'  -- ID

-- uncommentable EO vs ID constant strings and tables (misc)

local contabmisc = {}
      contabmisc[0] = "kaj"            -- EO coordinator "&"
        -- contabmisc[0] = "dan"          -- ID coordinator "&"
      contabmisc[1] = "ne ekzistas"    -- EO status of category pages lng kap vor
        -- contabmisc[1] = "tidak ada"    -- ID status of category pages lng kap vor

-- uncommentable EO vs ID constant table (titles to be constructed)

-- syntax of the named and variable placeholder:
-- * "@" followed by 2 uppercase letters and 2 hex digits
-- substitute strings defined:
-- * LK langcode (for example "da" or "io" or "grc")
-- * LN langname native case (for example "dana" or "Ido")
-- * LU langname uppercased (for example "Dana" or "Ido")
-- see "pripl5navare" and "tabstuff" and use space here and avoid "_"

local contabpages = {}
      contabpages[0] = "@LN00"                  -- EO lemma page in NS ZERO
        -- contabpages[0] = "bahasa @LN00"         -- ID lemma page in NS ZERO
      contabpages[1] = "@LU00"                  -- EO appendix-NS page
        -- contabpages[1] = "Bahasa @LN00"         -- ID appendix-NS page
      contabpages[2] = "@LU00"                  -- EO index-NS page
        -- contabpages[2] = "Bahasa @LN00"         -- ID index-NS page
      contabpages[3] = "Vortaro (@LN00)"        -- EO "vor" dict cat
        -- contabpages[3] = "Kamus bahasa @LN00"   -- ID "vor" dict cat
      contabpages[4] = "Sxablonaro -@LK00-"     -- EO "mal" template cat "Kategorio:Sxablonaro -ja-"
        -- contabpages[4] = "Templat bahasa @LN00" -- ID "mal" template cat "Kategori:Templat bahasa Jepang"
      contabpages[5] = "@LU00"                  -- EO "tln" top lng cat
        -- contabpages[5] = "Bahasa @LN00"         -- ID "tln" top lng cat
      contabpages[6] = "Kapvorto (@LN00)"       -- EO "kap" lemma cat
        -- contabpages[6] = "Kata bahasa @LN00"    -- ID "kap" lemma cat
      contabpages[7] = "@AT00"                  -- EO ipedia article -- needs help of PRIBREWARTICLETITLEEO
        -- contabpages[7] = "Bahasa @LN00"         -- ID ipedia article

-- uncommentable EO vs ID

local contabligiloj = {}
      contabligiloj[0] = 'gxisdatigo'                                    -- EO (no dot "." here)
        -- contabligiloj[0] = 'pemutakhiran'                                -- ID (no dot "." here)
      contabligiloj[1] = '[[SXablono:tbllingvoj|fonta tabelo]]'          -- EO (no dot "." here)
        -- contabligiloj[1] = '[[Templat:tblbahasa|tabel sumber]]'          -- ID (no dot "." here)
      contabligiloj[2] = '[[Aldono:Listo kun lingvoj|kompleta tabelo]]'  -- EO
        -- contabligiloj[2] = '[[Lampiran:Daftar bahasa|tabel lengkap]]'    -- ID

-- diverse tuning values in one table

local contabtunmisc = {}
contabtunmisc[0] = 44      -- limit, safe values 16 ...  96, eval "tabevalu.maxc0n"
contabtunmisc[1] = 108     -- limit, safe values 16 ...  120, eval "tabevalu.maxc2n" (for example "ae" "lad" are very long)
contabtunmisc.mkat = 100   -- limit, safe values 64 ... 256, eval "tabevalu.maxkal"
contabtunmisc.long = false -- "true" to allow long codes like "zh-min-nan" (komp commented out, sng defined)
contabtunmisc.digi = false -- "true" to allow middle digit "s7a" (komp commented out, sng defined)
contabtunmisc.tifi = false -- tight-fisted for "prwifexistfull" copied into qtcostquery[1] (komp true, sng false)
contabtunmisc.cats = 0     -- category style: 0 raw | 1 "Ka:" | 2 <br> (komp 1, sng 0)

-- uncommentable (override)

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

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

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

  -- SPECIAL VAR:S

local qldingvoj = {}     -- type "table" and nested
local qtcostquery = {}   -- for IfExists and PagesInCategory with 5 elements
local qtabbunch = {}     -- bunch strings here for "prhoptconcat"
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 & proc, debug report request by "detrc="
local qstrret = ''       -- declare here if "prhoptconcat" used

  -- GUARD AGAINST INTERNAL ERROR AND IMPORT ONE VIA LOADDATA

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

-- ******************************
-- *    DEBUG PROCEDURES [D]    * --------------------------------------
-- ******************************

-- Enhance upvalue "qstrtrace" with fixed text.

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

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

-- uses upvalue "qboodetrc"

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

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

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

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

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

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

---- SOME STUFF [?] ----

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

local function lfgcountchr (strqq,numascii) -- count occurrences of char in str
  local numrezalt = 0
  local numciar = 0
  local numukuran = 0
  local numindxe = 0 -- ZERO-based
  numukuran = string.len(strqq)
  while true do
    if (numindxe==numukuran) then
      break
    end--if
    numciar = string.byte(strqq,(numindxe+1),(numindxe+1))
    if (numciar==numascii) then
      numrezalt = numrezalt + 1
    end--if
    numindxe = numindxe + 1
  end--while
  return numrezalt
end--function lfgcountchr

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

-- Local function LFCOUNTNDG

-- Count occurrences of non-digits in string.

-- Tolerable char:s that do not count are:
-- - digits "0"..."9"
-- - space
-- - apo

local function lfcountndg (strzzz)
  local numjrezalt = 0
  local numcair = 0
  local numjukuran = 0
  local numjindxe = 0 -- ZERO-based
  numjukuran = string.len(strzzz)
  while true do
    if (numjindxe==numjukuran) then
      break
    end--if
    numcair = string.byte(strzzz,(numjindxe+1),(numjindxe+1))
    if (((numcair<48) or (numcair>57)) and (numcair~=32) and (numcair~=39)) then
      numjrezalt = numjrezalt + 1
    end--if
    numjindxe = numjindxe + 1
  end--while
  return numjrezalt
end--function lfcountndg

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

-- Local function LFREMOVENONDI

-- Remove non-digits from a string, return "0" if no digits available.

local function lfremovenondi (strmess)
  local strnumautnum = ''
  local numcchhrr = 0
  local numwukur = 0
  local numwindxe = 0 -- ZERO-based
  numwukur = string.len(strmess)
  while true do
    if (numwindxe==numwukur) then
      break
    end--if
    numcchhrr = string.byte(strmess,(numwindxe+1),(numwindxe+1))
    if ((numcchhrr>47) and (numcchhrr<58)) then
      strnumautnum = strnumautnum .. string.char(numcchhrr) -- cpy digits only
    end--if
    numwindxe = numwindxe + 1
  end--while
  if (strnumautnum=='') then
    strnumautnum = '0' -- result CANNOT be empty, always a valid number
  end--if
  return strnumautnum
end--function lfremovenondi

-- ******************************************
-- *    NUMBER CONVERSION PROCEDURES [N]    * --------------------------
-- ******************************************

local function prndec1digit (num1digit)

-- Convert 1 decimal ASCII digit to integer 0...9 (255 if invalid).

  num1digit = num1digit - 48 -- may become invalid
  if ((num1digit<0) or (num1digit>9)) then
    num1digit = 255 -- report ERROR on invalid input digit
  end--if
  return num1digit
end--function prndec1digit

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

-- Local function LFDEC1DIGLM

-- Convert 1 digit decimal to UINT8 with inclusive upper limit.

local function lfdec1diglm (num1dygyt,num1lim)
  num1dygyt = num1dygyt - 48 -- may become invalid
  if ((num1dygyt<0) or (num1dygyt>num1lim)) then
    num1dygyt = 255
  end--if
  return num1dygyt
end--function lfdec1diglm

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

local function prnonehextoint (numdigit)
  local numresult = 255
  if ((numdigit>47) and (numdigit<58)) then
    numresult = numdigit-48
  end--if
  if ((numdigit>64) and (numdigit<71)) then
    numresult = numdigit-55
  end--if
  return numresult
end--function prnonehextoint

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

local function prnnumto2digit (numzerotoninetynine)

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

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

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

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

local function lfnnumtodecbun (numnomoriin)

-- Convert non-negative integer to decimal string with bunching.

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

local strnomorut = '??'
local numapoindeex = 0
local numcaar = 0

  if (mathisintrange(numnomoriin,0,4294967295)) then
    strnomorut = ''
    while true do
      numcaar = mathmod(numnomoriin,10) + 48 -- get digit moving right to left
      numnomoriin = mathdiv(numnomoriin,10)
      if (numapoindeex==3) then
        strnomorut = "'" .. strnomorut -- ueglstr apo
        numapoindeex = 0
      end--if
      strnomorut = string.char(numcaar) .. strnomorut -- ueglstr digit
      numapoindeex = numapoindeex + 1
      if (numnomoriin==0) then
        break
      end--if
    end--while
  end--if

return strnomorut

end--function lfnnumtodecbun

-- *****************************************
-- *    LOW LEVEL STRING PROCEDURES [G]    * ---------------------------
-- *****************************************

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

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

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

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

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

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

local function prgpokestring (strinpokeout, numpokepoz, numpokeval)

-- Replace single octet in a string.

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

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

local numpokelen = 0

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

return strinpokeout

end--function prgpokestring

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

local function prulnutf8char (numbgoctet)

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

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

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

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

local numlen1234x = 0

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

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

local function prusrcasepairrest (strmy4set, num16val4in)

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

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

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

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

-- This obeys "UTF8UT.TXT".

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

local num16upper = -1
local num16lower = -1

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

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

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

return num16upper, num16lower

end--function prusrcasepairrest

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

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

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

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

-- Output : * strinco5cs

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

-- This obeys "UTF8UT.TXT".

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

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

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

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

local numlong5bor = 0 -- expected length of single char

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

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

local boowan6tlowr = false

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

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

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

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

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

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

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

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

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

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

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

return strinco5cs

end--function prucaseaiorest

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

local function prispacundr (strxxin, boounderlig)

-- Spaces to underscores or vice-versa.

-- Incoming boolean "true" for output underscores and "false" for spaces.

  local numleang = 0
  local numandex = 0 -- ZERO-based
  local numcxarr = 0
  local strkatrol = ''
  numleang = string.len (strxxin)
  while true do -- genuine loop
    if (numandex==numleang) then
      break
    end--if
    numcxarr = string.byte (strxxin,(numandex+1),(numandex+1))
    if ((numcxarr==32) or (numcxarr==95)) then
      if (boounderlig) then
        numcxarr = 95
      else
        numcxarr = 32
      end--if
    end--if
    strkatrol = strkatrol .. string.char (numcxarr)
    numandex = numandex + 1
  end--while
  return strkatrol
end--function prispacundr

-- *******************************************    high-level validation
-- *    HIGH LEVEL STRING PROCEDURES [I3]    * ------------------------
-- *******************************************               of strings

local function privalidatestr (stryn,varalt,nummyn,nummex,numapo,numpoz)

-- Validate string according to a huge bunch of criteria.

-- Input  : - stryn : string
--          - varalt : alternative valid string (for example "??" or "-")
--                     that can "jump over" all other checks, note that empty
--                     string can be used here, use type "nil" if none defined
--          - nummyn : minimal length
--          - nummex : maximal length
--          - numapo : apo rules
--                     0 : no restrictions
--                     1 : leading and trailing and multiple apo:s prohibited
--                         (no-wiki-bolding-policy)
--                     2 : apo:s totally prohibited
--          - numpoz : positive list of tolerable char:s
--                     0 : none
--                     1 : only ASCII digits and maybe
--                         apo:s (see also "numapo" above)
--                     2 : only ASCII uppercase letters
--                     3 : only ASCII lowercase letters

-- Output : - booisvalid : true if string is valid

  local booisvalid = true -- for the caller
  local numlencx = 0
  local numynx = 0
  local numcxar = 0
  local numcxur = 0
  local vartnp = 0
  while true do -- fake loop
    if (type(varalt)=='string') then
      if (stryn==varalt) then
        break -- to join mark -- string is valid
      end--if
    end--if
    numlencx = string.len(stryn)
    if ((numlencx<nummyn) or (numlencx>nummex)) then
      booisvalid = false
      break -- to join mark -- string is faulty
    end--if
    if (numlencx==0) then
      break -- to join mark -- string is empty but valid
    end--if
    if (numapo==1) then
      numcxar = string.byte(stryn,1,1)
      numcxur = string.byte(stryn,numlencx,numlencx)
      vartnp = string.find(stryn, "''", 1, true) -- plain text search for apo
      if ((numcxar==39) or (numcxur==39) or vartnp) then
        booisvalid = false
        break -- to join mark -- string is faulty
      end--if
    end--if (numapo==1) then
    if ((numpoz~=0) or (numapo==2)) then
      numynx = 0 -- ZERO-based index
      while true do
        if (numynx==numlencx) then
          break -- done search, all OK
        end--if
        numcxar = string.byte(stryn,(numynx+1),(numynx+1))
        if ((numapo==2) and (numcxar==39)) then
          booisvalid = false
          break -- abort search, crime detected (apo)
        end--if
        if ((numpoz==1) and (numcxar~=39)) then
          if ((numcxar<48) or (numcxar>57)) then
            booisvalid = false
            break -- abort search, crime detected (non-digit)
          end--if
        end--if
        if (numpoz==2) then
          if ((numcxar<65) or (numcxar>90)) then
            booisvalid = false
            break -- abort search, crime detected (non-uppercase)
          end--if
        end--if
        if (numpoz==3) then
          if ((numcxar<97) or (numcxar>122)) then
            booisvalid = false
            break -- abort search, crime detected (non-lowercase)
          end--if
        end--if
        numynx = numynx + 1 -- ZERO-based index
      end--while
    end--if ((numpoz~=0) or (numapo==2)) then
    break -- finally to join mark
  end--while -- fake loop -- join mark
  return booisvalid
end--function privalidatestr

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

-- Local function LFIVALIDATEQUACK

-- Validate a Q-item.

-- Input  : * strqynq -- string

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

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

-- Criteria:
-- * length totally 3...9 chars ("Q" plus 2...8 digits)
-- * zeroth char "Q"
-- * then "1"..."9"
-- * subsequent chars (1...7 left) "0"..."9"

local function lfivalidatequack (strqynq)
  local booqisvalid = false -- preASSume guilt
  local numlendx = 0
  local numuinx = 0
  local numcyaar = 0
  while true do -- fake loop
    numlendx = string.len(strqynq)
    if ((numlendx<3) or (numlendx>9)) then
      break -- to join mark -- string is faulty, length
    end--if
    if (string.byte(strqynq,1,1)~=81) then
      break -- to join mark -- string is faulty, no "Q"
    end--if
    if (string.byte(strqynq,2,2)==48) then
      break -- to join mark -- string is faulty, ZERO "0" prohibited here
    end--if
    numuinx = 1 -- ZERO-based -- skip "Q"
    booqisvalid = true -- preASSume innocence
    while true do
      if (numuinx>=numlendx) then
        break -- done search, all OK
      end--if
      numcyaar = string.byte(strqynq,(numuinx+1),(numuinx+1))
      if (not prgtestnum(numcyaar)) then
        booqisvalid = false -- guilty, no number
        break -- abort search
      end--if
      numuinx = numuinx + 1 -- ZERO-based
    end--while
    break -- finally to join mark
  end--while -- fake loop -- join mark
  return booqisvalid
end--function lfivalidatequack

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

-- Local function LFIVALIDATELNKOADV

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

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

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

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

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

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

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

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

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

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

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

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

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

  return booisvaladv

end--function lfivalidatelnkoadv

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

local function lfivaliumdctlstr (strresdpat, strctldstr)

-- Validate control string against restrictive pattern (dec).

-- Input  : * strresdpat -- restrictive pattern (max 200 char:s)
--          * strctldstr -- incoming suspect

-- Output : * numbadpos -- bad position, or 254 wrong length, or 255 success

-- Depends on procedures :
-- [N] prndec1digit

-- Content of restrictive pattern:
-- * "."                           -- skip check
-- * "-" and "?"                   -- must match literally
-- * digit "1"..."9" ("0" invalid) -- inclusive upper limit (min ZERO)

  local numlenresdpat = 0
  local numldninkom = 0
  local numcomperindex = 0 -- ZERO-based
  local numead2 = 0
  local numead3 = 0
  local numbadpos = 254 -- preASSume guilt (len differ or too long or ...)
  local booddaan = false

  numlenresdpat = string.len(strresdpat)
  numldninkom = string.len(strctldstr)
  if ((numlenresdpat<=200) and (numlenresdpat==numldninkom)) then
    while true do
      if (numcomperindex==numlenresdpat) then
        numbadpos = 255
        break -- success
      end--if
      numead2 = string.byte(strresdpat,(numcomperindex+1),(numcomperindex+1)) -- rest
      numead3 = string.byte(strctldstr,(numcomperindex+1),(numcomperindex+1)) -- susp
      booddaan = false
      if ((numead2==45) or (numead2==63)) then
        if (numead2~=numead3) then
          numbadpos = numcomperindex
          break -- "-" and "?" must match literally
        end--if
        booddaan = true -- position OK
      end--if
      if (numead2==46) then -- skip for dot "."
        booddaan = true -- position OK
      end--if
      if (not booddaan) then
        numead2 = prndec1digit(numead2) -- rest
        if (numead2>9) then -- limit defined or bad ??
          numbadpos = 254
          break -- bad restrictive pattern
        else
          numead3 = prndec1digit(numead3) -- susp
          if (numead3>numead2) then
            numbadpos = numcomperindex
            break -- value limit violation
          end--if
        end--if (numead2>9) else
      end--if (not booddaan) then
      numcomperindex = numcomperindex + 1
    end--while
  end--if ((numlenresdpat<=200) and (numlenresdpat==numldninkom)) then

  return numbadpos

end--function lfivaliumdctlstr

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

local function prikodeosg (streosurr)

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

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

-- Output : * strutf8eo -- UTF8 string

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

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

-- To be called ONLY from "PRHRECUSURRSTRTAB".

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

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

  numeoinplen = string.len(streosurr)

  while true do

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

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

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

  end--while

  return strutf8eo

end--function prikodeosg

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

local function pritest5plejs (stroneplejs, boowantstryg, tabsubtitu)

-- Test one placeholder against spec and against table of known placeholders.

-- Input  : * stroneplejs -- 5 octet:s
--          * boowantstryg -- return also string if available
--          * tabsubtitu -- optional collection of substitute strings
--                          (LUA table) using two-letter codes
--                          as keys, type "nil" for none

-- Output : * numtriplejs -- main status: ZERO placeholder not valid | ONE
--                           placeholder valid but either no value or empty
--                           value | TWO found also valid non-empty value
--          * numpl5left, numpl5right -- only for status non-ZERO
--          * strkeyval -- * if optional table given then contains value
--                           only for status TWO and boowantstryg
--                         * if no table then contains the key (substring of
--                           stroneplejs) only for status ONE and boowantstryg
--                         * in all other cases empty string

-- This obeys PLRISM.TXT .

-- Depends on procedures :
-- [G] prgtestuc prgstringrange
-- [N] prnonehextoint

local varduavaluehur = 0

local strmajky  = ''
local strkeyval = '' -- final key or value

local numtriplejs = 0 -- preASSume guilt
local numpl5left  = 0 -- hex and drop left
local numpl5right = 0 -- hex and drop right

local numplejodt = 0 -- UC
local numplejoet = 0 -- UC
local numplejx5u = 0 -- maybe hex
local numplejx5v = 0 -- maybe hex

  while true do -- fake loop
    if (not prgstringrange(stroneplejs,5,5)) then
      break -- should be impossible
    end--if
    if (string.byte(stroneplejs,1,1)~=64) then
      break -- not valid per spec
    end--if
    numplejodt = string.byte(stroneplejs,2,2)
    numplejoet = string.byte(stroneplejs,3,3)
    if ((not prgtestuc(numplejodt)) or (not prgtestuc(numplejoet))) then
      break -- not valid per spec
    end--if
    numplejx5u = prnonehextoint(string.byte(stroneplejs,4,4))
    numplejx5v = prnonehextoint(string.byte(stroneplejs,5,5))
    if ((numplejx5u==255) or (numplejx5v==255)) then
      break -- not valid per spec
    end--if
    numtriplejs = 1 -- valid now
    numpl5left  = numplejx5u -- do NOT copy in invalid value
    numpl5right = numplejx5v -- do NOT copy in invalid value
    strmajky = string.char (numplejodt,numplejoet) -- key
    if (type(tabsubtitu)=='table') then -- got optional table ??
      varduavaluehur = tabsubtitu[strmajky] -- risk of type "nil"
      if (prgstringrange(varduavaluehur,1,255)) then -- MUST be non-empty
        if (boowantstryg) then -- else keep empty string
          strkeyval = varduavaluehur -- table here and found value, return it
        end--if
        numtriplejs = 2 -- table here and found value
      end--if
    else
      if (boowantstryg) then -- else keep empty string
        strkeyval = strmajky -- no table but placeholder valid, return key
      end--if
    end--if (type(tabsubtitu)=='table') else
    break -- finally to join mark
  end--while -- fake loop -- join mark

return numtriplejs, numpl5left, numpl5right, strkeyval

end--function pritest5plejs

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

local function pripl5navare (strrekvest, tabsubstut)

-- Insert selected substitute strings into request string at given
-- positions with optional dropping if a valid non-empty substitute
-- string is unavailable.

-- Input  : * strrekvest -- request string (maybe containing placeholders,
--                          syntax see below, empty string legal, other
--                          data type NOT legal)
--          * tabsubstut -- obligatory collection of substitute strings
--                          (LUA table) using two-letter codes as keys

-- Output : * strhazil

-- This obeys PLRISM.TXT .

-- Depends on procedures :
-- [I7] pritest5plejs
-- [G] prgtestuc prgstringrange
-- [N] prnonehextoint

-- Syntax of the named and variable placeholder:
-- * fixed "@"
-- * 2 uppercase ASCII letters select the substitute string from
--   collection supplied by the caller
-- * 2 hex digits control dropping left and right (0...15 char:s)

-- Empty item in "tabsubstut" (key does not exist, empty string "", or
-- non-string value, the latter discouraged) is legal and safe and results
-- in dropping if some of the control numbers is non-ZERO. Left dropping
-- is practically performed on "strhazil" whereas right dropping on
-- "strrekvest" and "numrekvinx". Dropping is protected from access
-- out of range.

-- ZERO iterations of the loop over request string are possible,
-- namely if request string "strrekvest" is empty.

-- If uppercasing or other adjustment is needed, then the caller must
-- take care of it by providing several separate substitute strings with
-- separate names in the collection ("tabsubstut"), such is NOT done here.

local strhazil = ''
local stronevalue = ''

local numrekvlen = 0
local numrekvinx = 0 -- src index ZERO-based
local numremainder = 0

local numpltristeta = 0
local numnonhitoct = 0
local numammlef = 0 -- drop left
local numammrig = 0 -- drop right

  numrekvlen = string.len(strrekvest)

  while true do -- genuine loop over request string, "numrekvinx" is counter
    if (numrekvinx>=numrekvlen) then
      break -- done
    end--if
    numnonhitoct = string.byte(strrekvest,(numrekvinx+1),(numrekvinx+1))
    numpltristeta = 0 --  preASSume no valid placeholder here
    if ((numrekvinx+5)<=numrekvlen) then -- beware of risk of overflow
      numpltristeta, numammlef, numammrig, stronevalue = pritest5plejs (string.sub(strrekvest,(numrekvinx+1),(numrekvinx+5)), true, tabsubstut)
    end--if
    if (numpltristeta==2) then -- found
      strhazil = strhazil .. stronevalue -- insert non-empty substitute
      numrekvinx = numrekvinx + 5 -- consumed 5 char:s, cannot overflow here
    end--if
    if (numpltristeta==1) then -- placeholder valid per spec but empty unknown
      numremainder = string.len(strhazil) - numammlef -- this can underflow
      if (numremainder<=0) then
        strhazil = '' -- drop left -- leaving nothing behind
      else
        strhazil = string.sub(strhazil,1,numremainder) -- drop left
      end--if
      numrekvinx = numrekvinx + 5 + numammrig -- drop right this can overflow
    end--if
    if (numpltristeta==0) then -- no valid placeholder here
      strhazil = strhazil .. string.char(numnonhitoct) -- copy octet as-is
      numrekvinx = numrekvinx + 1 -- consumed 1 char
    end--if
  end--while -- genuine loop over request string, "numrekvinx" is counter

return strhazil

end--function pripl5navare

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

local function pribrewurl (straxk,bootip,varpage)

-- Brew an external link [] to a different wiki.

-- Input  : * straxk : wiki access code (2...10 char:s), "??" for invalid
--          * bootip : type (false -> ipedia | true -> tionary)
--          * varpage : pagename string (article, or category with
--                      prefix) or type "nil"

-- Depends on procedures :
-- [I] prispacundr

-- Here we replace spaces in URL by underscores, no further encoding
-- is performed.

-- No link if wiki access code is "??", only plain text instead.

  local strurlkita = ''
  local strsite = ''
  local boolinkvalid = false
  local boohavepage = false

  if (bootip) then
    strsite = 'tionary'
  else
    strsite = 'ipedia'
  end--if
  boolinkvalid = (straxk~='??')
  boohavepage = (type(varpage)=='string')

  if (boolinkvalid) then
    strurlkita = '[http://' .. straxk .. '.wik' .. strsite .. '.org'
    if (boohavepage) then -- string (article or category with prefix) or nil
      strurlkita = strurlkita .. '/wiki/' .. prispacundr(varpage,true) -- replace in URL
    end--if
    strurlkita = strurlkita .. ' ' -- separation space
  end--if
  strurlkita = strurlkita .. straxk .. '.' .. strsite .. '...' -- visible text
  if (boohavepage) then -- string (article or category with prefix) or nil
    strurlkita = strurlkita .. '<br>/' .. varpage -- do NOT replace here
  end--if
  if (boolinkvalid) then
    strurlkita = strurlkita .. ']' -- done
  end--if

  return strurlkita

end--function pribrewurl

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

local function prhoptconcat (varincom)

-- Optimizing concatenator as workaround for poor memory management in LUA
-- during string concatenations. Add incoming string to upvalue "qstrret".

-- Input  : * "varincom" either string (add) or type "nil" (flush)
--          * "qtabbunch" either table or type "nil" (ignore "varincom")

-- Depends on upvalues :
-- * qtabbunch qstrret

-- CHG : qtabbunch (table), qstrret (string)

-- NOTE: to be inited by <<local qstrret = ''>> and <<local qtabbunch = {}>>

-- NOTE: to be flushed by "prhoptconcat (nil)" before using "qstrret"

-- NOTE: if the "qstrret" string has to be changed directly
--       (by concatenation or replace), then either "prhoptconcat (nil)"
--       (flush) or "qtabbunch = {}" (discard) must be done before !!!

  if (type(qtabbunch)=='table') then
    if (type(varincom)=='string') then
      if (#qtabbunch>20) then
        qstrret = qstrret .. table.concat (qtabbunch) .. varincom
        qtabbunch = {}
      else
        table.insert (qtabbunch, varincom) -- ONE-based numbering
      end--if
    end--if
    if ((varincom==nil) and (#qtabbunch>0)) then
      qstrret = qstrret .. table.concat (qtabbunch)
      qtabbunch = {}
    end--if
  end--if
end--function prhoptconcat

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

local function prhrecusurrstrtab (varinkommen, strlingkod, bookeys)

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

-- Input  : * varinkommen -- type "string" or "table"
--          * strlingkod -- "eo" or NOPE "sv" to transcode surrogates,
--                          anything else (preferably type "nil") to skip this
--          * bookeys -- transcode keys too (preferably either "true"
--                       or type "nil")

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

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

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

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

  if (type(varinkommen)=='string') then
    if (strlingkod=='eo') then
      varinkommen = prikodeosg (varinkommen) -- surr
    end--if
    varutmatning = varinkommen -- copy, change or no change
    boodone = true
  end--if

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

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

  return varutmatning

end--function prhrecusurrstrtab

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

local function prhvali1status98code (varvalue)

-- Check error code from submodule.

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

local boovalid = false -- preASSume guilt

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

return varvalue, boovalid

end--function prhvali1status98code

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

local function prhconstructerar (numerar3code, boopeek3it)

-- Construct partial error message maybe peeking description.

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

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

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

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

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

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

return stryt3sux

end--function prhconstructerar

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

local function prhbrewerr6slnp (numeror6code, numsever6ity)

-- Brew error 3 sev levels, one line, no parent.

-- Input  : * numeror6code -- TWO-based error code 2 ... 98 (resistant
--                            against invalid data type, giving "??" on such)
--          * numsever6ity  -- severity level can be 0 mini 1 lagom 2 huge

-- Output : * stryt6sux -- message with HTML

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

-- Depends on constants :
-- * table contabfel with 6 elements .labg .laen .mibg .mien .hubg .huen
-- * table contaberaroj TWO-based (holes permitted)

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

local stryt6sux = ''

  stryt6sux = prhconstructerar(numeror6code,true)
  if (numsever6ity==0) then
    stryt6sux = contabfel.labg .. stryt6sux .. contabfel.laen
  end--if
  if (numsever6ity==1) then
    stryt6sux = contabfel.mibg .. stryt6sux .. contabfel.mien
  end--if
  if (numsever6ity==2) then
    stryt6sux = contabfel.hubg .. stryt6sux .. contabfel.huen
  end--if

return stryt6sux

end--function prhbrewerr6slnp

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

local function prhbrewerr8subm (numeror8code, strparent8nm, numsub8kodo)

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

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

-- Output : * stryt8sux -- message with HTML

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

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

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

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

local stryt8sux = ''

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

return stryt8sux

end--function prhbrewerr8subm

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

---- SOME STUFF [?] ----

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

local function prinumberinkat (numjumlahorminone)  -- !!!FIXME!!! move from here
  local strnothing = '' -- "(16'384 pages)" or "(?? pages)"
  if (numjumlahorminone<0) then -- expected type integer, value -1 for unknown
    strnothing = '(??'
  else
    strnothing = '(' .. lfnnumtodecbun(numjumlahorminone) -- ZERO is valid
  end--if
  strnothing = strnothing .. ' pages)'
  return strnothing
end--function prinumberinkat

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

-- nope LFAVAILCO

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

-- Local function LFIAVAILNT  !!!FIXME!!! move to [I]

-- Translate number 2...0 to text with "<br>".

-- Depends on constants :
-- * table "contabd10d11w" with 2 strings at indexes ONE and TWO

local function lfiavailnt (numalb)
  local strnote = '' -- preASSume no extra complaint
  if (numalb==1) then
    strnote = '<br>(' .. contabd10d11w[1] .. ')' -- closed or empty
  end--if
  if (numalb==0) then
    strnote = '<br>(' .. contabd10d11w[2] .. ')' -- inaccessible
  end--if
  return strnote
end--function lfiavailnt

-- ************************************
-- *    HIGH LEVEL PROCEDURES [H6]    * --------------------------------
-- ************************************

local function lfhbrewtitsel (numindxe, bootopp, boobotm)

-- Brew content of title cell (for column or row) from index
-- number and two booleans.

-- Input  : * numindxe -- index number of column (ZERO-based)
--          * bootopp, boobotm

-- Depends on procedures :
-- [N] prnnumto2digit

-- Depends on constants :
-- * table "contabdestit"
-- * contabmisc[0] (translated coordinator "and")

-- * (begin of bold) is always added at the very beginning
-- * (end of bold) is added at the very end if no "#" is present
-- * "#" is expanded to (end of bold) + EOL + word "and" + EOL
-- * maximally ONE "#" is permitted
-- * "%" is border between 2 selectable items, it is expanded to
--   (end of bold) + EOL + word "and" + EOL + (begin of bold) only
--   if both items are active
-- * maximally ONE "%" is permitted

-- An entry in the table "contabdestit" is NOT supposed to contain
-- "<b>" (even less triple-apo "'''"), nor "</b>" at end nor "<br>".

  local strraw = ''
  local strdesc = ''
  local strandkajdan = ''
  local numlimiit = 0
  local numoindex = 1 -- ONE-based
  local numpycker = 0
  local numfragmm = 0 -- NOT relevant if both "bootopp" and "boobotm" are true
  local booslutbold = false
  local bootakeit = false

  strandkajdan = '<br>' .. contabmisc[0] .. '<br>' -- for both % and #
  strraw = contabdestit[numindxe]
  strdesc = '<b>'
  numlimiit = string.len (strraw)

  while true do -- genuine loop processing char after char
    if (numoindex>numlimiit) then
      break
    end--if
    bootakeit = true
    numpycker = string.byte(strraw,numoindex,numoindex)
    numoindex = numoindex + 1
    if (numpycker==35) then -- 35 is "#"
      strdesc = strdesc .. "</b>" .. strandkajdan -- no (begin of bold) !!!
      booslutbold = true
      bootakeit = false
      numfragmm = 2 -- following text taken unconditionally
    end--if
    if (numpycker==37) then -- 37 is "%"
      if (bootopp and boobotm) then
        strdesc = strdesc .. "</b>" .. strandkajdan .. "<b>"
      end--if
      numfragmm = numfragmm + 1
      bootakeit = false
    end--if
    if ((numfragmm==0) and (not bootopp)) then
      bootakeit = false
    end--if
    if ((numfragmm==1) and (not boobotm)) then
      bootakeit = false
    end--if
    if (bootakeit) then
      strdesc = strdesc .. string.char(numpycker)
    end--if
  end--while

  if (not booslutbold) then
    strdesc = strdesc .. '</b>' -- add "</b>" only if it is not yet in
  end--if
  strdesc = strdesc .. '<br>(d' .. prnnumto2digit (numindxe) .. ')'

  return strdesc

end--function lfhbrewtitsel

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

-- Local function LFHINTEGERTOCOL6D

-- Output : * varfromtable -- 6 hex quasi-digits, maybe copied from
--                            incoming "strdefault", or "=" if index bad

-- Called only from prydoonelang.

-- Depends on constants :
-- * tables "contabwarnlng" and "contabwarnwik" both ZERO-based

local function lfhintegertocol6d (numkoloindex,bootype,strdefault)
  local varfromtable = 0
  if (bootype) then
    varfromtable = contabwarnwik[numkoloindex]
  else
    varfromtable = contabwarnlng[numkoloindex]
  end--if
  if (type(varfromtable)~='string') then
    varfromtable = '=' -- index bad
  end--if
  if (varfromtable=='?') then
    varfromtable = strdefault
  end--if
  return varfromtable
end--function lfhintegertocol6d

-- **********************************************
-- *    MEDIAWIKI INTERACTION PROCEDURES [W]    * ----------------------
-- **********************************************

-- Brew httplink to current project from title + http parameters + visible
-- text, making it look like an ordinary wikilink.

-- Input  : * arxinp4
--          * strwikipage -- title ie {{FULLPAGENAME}} or
--                           for example "Special:WhatLinksHere"
--          * strhttpparam -- for example "hidetrans=1&hidelinks=1" or empty
--          * strvisible -- for example "do NOT click here" or empty
--          * boozordinary -- "true" to look like an ordinary wikilink,
--                            "false" if this is undesirable or done outside

-- URL structure:
-- * https://en.wiktionary.org/wiki/Category:Vulgarities
-- * https://en.wiktionary.org/wiki/Category:Vulgarities?action=purge
-- * https://en.wiktionary.org/w/index.php?title=Category:Vulgarities
-- * https://en.wiktionary.org/w/index.php?title=Category:Vulgarities&action=purge
-- * https://en.wiktionary.org/wiki/Special:AllPages
-- * https://en.wiktionary.org/wiki/Special:AllPages?namespace=5
-- * https://en.wiktionary.org/wiki/Special:WhatLinksHere/Template:Bugger
-- * https://en.wiktionary.org/wiki/Special:WhatLinksHere/Template:Bugger?hidetrans=1&hidelinks=1
-- * https://en.wiktionary.org/w/index.php?title=Special:WhatLinksHere/Template:Bugger&hidetrans=1&hidelinks=1
-- * https://en.wiktionary.org/w/index.php?title=Special:WhatLinksHere&target=Template:Bugger&hidetrans=1&hidelinks=1

-- Only 4 ways to brew a URL:
-- * {{canonicalurl:{{FULLPAGENAME}}|action=purge}}
--       we use this one via ":preprocess", will work on any project
-- * {{SERVER}}{{SCRIPTPATH}}/index.php?title={{urlencode:{{FULLPAGENAME}}}}&action=purge
--       unnecessarily complicated
-- * LUA field "canonicalUrl" or "fullUrl" on "mw.uri" or "title object"
--       complicated and dubious
-- * manually
--       where to take project URL from ?? plus routine would land in [I] then

-- <span class="plainlinks">...</span>

local function lfwbrew3url (arxinp4, strwikipage, strhttpparam, strvisible, boozordinary)

  local strourresult = ''

  if (strhttpparam~='') then
    strwikipage = strwikipage .. '|' .. strhttpparam
  end--if
  strourresult = arxinp4:preprocess ('{{canonicalurl:' .. strwikipage .. '}}')
  if (strvisible=='') then
    strourresult = '[' .. strourresult .. ' ' .. strourresult .. ']'
  else
    strourresult = '[' .. strourresult .. ' ' .. strvisible .. ']'
  end--if
  if (boozordinary) then
    strourresult = '<span class="plainlinks">' .. strourresult .. '</span>'
  end--if
  return strourresult

end--function lfwbrew3url

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

-- Optionally tight-fisted check whether a wiki page exists, counting cost.

-- Input  : * strpgname    -- fullpagename (default NS 0, not template NS 10)

-- Output : * boomemangada -- "true" on success

-- Depends on upvalues :
-- * heterogeneous table "qtcostquery"

-- Extra colon is tolerable for non-NS-ZERO and non-template-NS-10 pages
-- such as "{{msgnw::Help:TOC}}" and returns ...":Help:TOC"... if the
-- page does not exist.

-- Maybe expensive, the caller must use "pcall".

local function prwifexistfull (strpgname)
  local boomemangada = false
  local arxxx = 0
  local metaa = 0
  qtcostquery[6] = qtcostquery[6] + 1 -- counter requests IfExists total
  if (qtcostquery[1]) then
    arxxx = qtcostquery[0] -- trick below does NOT work with substitution
    boomemangada = ( '&#91;&#91;:' .. strpgname .. '&#93;&#93;' ~= arxxx:preprocess ('{{msgnw::' .. strpgname .. '}}') )
    qtcostquery[8] = qtcostquery[8] + 1 -- counter requests IfExists tight-fisted
  else
    metaa = mw.title.new (strpgname) -- one param
    boomemangada = metaa.exists -- expensive here
    qtcostquery[7] = qtcostquery[7] + 1 -- cost IfExists
  end--if
  return boomemangada
end--function prwifexistfull

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

-- Peek summary of a category, counting cost.

-- Input  : * strcat9name          -- without prefix

-- Output : * numpages, numsubcats -- 2 values, value -1 for unknown

-- Depends on upvalues :
-- * heterogeneous table "qtcostquery"

-- Always expensive, the caller must use "pcall".

local function prwkatrolfull (strcat9name)
  local metab = 0
  local numpages = -1 -- preASSume unknown
  local numsubcats = -1 -- preASSume unknown
  metab = mw.site.stats.pagesInCategory ( strcat9name, '*' ) -- expensive here
  qtcostquery[3] = qtcostquery[3] + 1 -- counter requests pagesInCategory total
  qtcostquery[4] = qtcostquery[4] + 1 -- cost pagesInCategory
  if (type(metab)=='table') then -- risk of type "number" or "nil"
    numpages = metab.pages
    numsubcats = metab.subcats
    if (numpages<0) then
      numpages = -1 -- YES MediaWiki is stupid
    end--if
    if (numsubcats<0) then
      numsubcats = -1 -- YES MediaWiki is stupid
    end--if
  end--if
  return numpages, numsubcats
end--function prwkatrolfull

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

-- nope PRWFIREBATCHED

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

---- SOME STUFF [?] ----

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

local function pribrewarticletitleeo (strnativetotitle)

  if (string.byte(strnativetotitle,-1,-1)~=111) then -- if NOT ends with "o"
    strnativetotitle = prucaseaiorest('eo',strnativetotitle,true,false) .. ' lingvo'
  end--if

return strnativetotitle

end--function pribrewarticletitleeo

-- ****************************************
-- *    VERY HIGH LEVEL PROCEDURES [Y]    * ----------------------------
-- ****************************************

-- Brew link to category possibly saving screen space and
-- optionally check existence and content.

-- Input  : * strkattnamn  -- without ns prefix
--          * strkattnspr  -- ns prefix NOT ending with colon
--          * boocheckdabl -- enable 2 maybe expensive checks
--          * num012style  -- 0 raw | 1 "Ka:" | 2 <br>

-- Output : * booisbiru
--          * strlikatter -- main string

-- Depends on procedures :
-- [W] prwifexistfull prwkatrolfull
-- [I] prinumberinkat

-- Depends on upvalues :
-- * heterogeneous table "qtcostquery"

-- Depends on constants :
-- * contabmisc[1] (statement "does not exist")

local function lfykattlaenk (strkattnamn,strkattnspr,boocheckdabl,num012style)
  local strfullcatname = ''
  local strlikatter = ''
  local straltprefix = ''
  local numjumlah = 0
  local booisbiru = true -- preASSume innocence
  strfullcatname = strkattnspr .. ':' .. strkattnamn
  if (num012style==0) then
    strlikatter = '[[:' .. strfullcatname .. ']]' -- raw
  else
    if (num012style==1) then
      straltprefix = string.sub (strkattnspr,1,2) .. ':' -- "Ca:" or "Ka:"
    else
      straltprefix = strkattnspr .. ':<br>' -- "Category:" + EOL
    end--if
    strlikatter = "[[:" .. strfullcatname .. "|" .. straltprefix .. strkattnamn.. "]]"
  end--if
  if (boocheckdabl) then
    booisbiru = prwifexistfull (strfullcatname)
    if (not booisbiru) then -- incidents reported here but counted by caller
      strlikatter = strlikatter .. '<br>(' .. contabmisc[1] .. ')' -- "does not exist"
    end--if
    numjumlah = prwkatrolfull (strkattnamn) -- discard 1 of 2 results, both can be -1
    strlikatter = strlikatter .. '<br>' .. prinumberinkat(numjumlah) -- can be -1
  end--if
  return booisbiru,strlikatter
end--function lfykattlaenk

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

-- Local function LFYAPXINDLAENK

-- Brew link to appendix or index page and optionally check existence.

-- Input  : * strapxindnamn -- without ns prefix
--          * strapxindnspr -- ns prefix NOT ending with colon
--          * boocekexx     -- enable one maybe expensive check

-- Output : * booitisblue
--          * strailiink -- main string

-- Depends on procedures :
-- [W] prwifexistfull

-- Depends on upvalues :
-- * heterogeneous table "qtcostquery"

-- Depends on constants :
-- * contabmisc[1] (statement "does not exist")

local function lfyapxindlaenk (strapxindnamn,strapxindnspr,boocekexx)
  local strfullainame = ''
  local strailiink = ''
  local booitisblue = true -- preASSume innocence
  strfullainame = strapxindnspr .. ':' .. strapxindnamn -- needed two times
  strailiink = '[[' .. strfullainame .. ']]' -- "Appendix:" "Index:"
  if (boocekexx) then
    booitisblue = prwifexistfull (strfullainame)
    if (not booitisblue) then -- incidents reported here but counted by caller
      strailiink = strailiink .. '<br>(' .. contabmisc[1] .. ')' -- "does not exist"
    end--if
  end--if
  return booitisblue,strailiink
end--function lfyapxindlaenk

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

local function prydoonelang (strsitelangcode, strpykkatns, strpykindns, strpykapxns, stronecy, tabonelang, tabxctl, tabxkol)

-- Do one lang and send it to the output buffer via prhoptconcat.

-- Input  : * strsitelangcode -- site langcode
--          * strpykkatns strpykindns strpykapxns
--          * stronecy -- processed langcode /cy/
--          * tabonelang -- ZERO-based
--          * tabxctl -- booleans
--          * tabxkol -- booleans /d01/ ... /d13/ (beware no /d0/ exists)

-- Output : * numlokera (komp return two, sng return one)

-- Modif  : * qtabbunch qstrret (flush NOT done here)

-- Depends on procedures :
-- [Y] lfykattlaenk
-- [H] prhoptconcat lfhbrewtitsel
-- [I] privalidatestr lfivalidatequack pribrewarticletitleeo
-- [U] prulnutf8char prusrcasepairrest  prucaseaiorest
-- [G] prgtestuc prgtestlc prgpokestring

  local tabstuff = {} -- double-letter indexes for "pripl5navare"

  local strbkn    = '' -- HTML HEX color (6 digits)
  local strt3d    = '' -- HTML "td" element concatenated from 3 parts
  local strdeste  = '' -- destinat table element with both "td" HTML elements

  local strtblc0  = '' -- src column 0 (both /cy/ and /c0/) (obligatory)
  local strtblc1  = '' -- src column 1 (nice to have) Q-item
  local strtblc2  = '' -- src column 2 (nice to have) propralingve
  local strtblc3  = '' -- src column 3 (nice to have) 1 digit
  local strtblc4  = '' -- src column 4 (nice to have) 2 letters ISO
  local strtblc5  = '' -- src column 5 (nice to have) 3 letters ISO
  local strtblc6  = ''
  local strtblc7  = ''
  local strtblc8  = '' -- src column 8
  local strtblc9  = '' -- src column 9
  local strtblca  = '' -- src column 10

local strtmp    = ''
local strtpm    = ''

local numc7pedi = 0 -- ipedia code "0"..."2" ("3" for "-" and reddish)
local numc7tion = 0 -- tionary code "0"..."2" ("3" for "-" and reddish)
local numafstbfcl = 0 -- string length aftr stripping rl lr before clamping
local numafstbfkp = 0 -- string length aftr stripping rl lr before clamping

local numlokera = 0 -- local error code, returned, only #E15
local numtamp = 0

local booc8expl = false  -- /c8/ wp article -- true if other than "-" or "??"

local boolocerr = false
local bootjmp = false

  while true do -- fake loop

    -- begin /d00/ -- our access code
    strtpm = '<tr>' .. contabhtml.tdbgti .. lfhbrewtitsel (0,true,true) .. contabhtml.tden
    prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
    strbkn = contabwarna.bkgud -- color code without cross
    if (stronecy=='eo') then
      strbkn = '90FF90' -- green decoration
    end--if
    strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
    prhoptconcat (strt3d .. '<b>' .. stronecy .. '</b>' .. contabhtml.tden .. '</tr>')
    -- done /d00/

    -- begin /c0/ -- lingvonomo in site language
    strtblc0 = tabonelang[0] or ''
    if (not privalidatestr(strtblc0,nil,2,contabtunmisc[0],0,0)) then
      numlokera = 15 -- #E15 sev huge, /c0/ is faulty
      break -- to join mark -- no table and huge error
    end--if
    -- done /c0/ -- ultimately obligatory -- even "-" is bad here

    -- crucial task done just afer /c0/ prepare for "pripl5navare"
    tabstuff['LK'] = stronecy -- this is /cy/ from parent ie langcode
    tabstuff['LN'] = strtblc0 -- peeked above ie langname
    tabstuff['LU'] = prucaseaiorest(strsitelangcode,strtblc0,true,false) -- langname uppercase
    if (strsitelangcode=='eo') then
      tabstuff['AT'] = pribrewarticletitleeo(strtblc0) -- we must prebrew it
    end--if

    -- begin /d01/ -- lemma page
    if (tabxkol.d01) then
      prhoptconcat ('<tr>')
      strtpm = contabhtml.tdbgti .. lfhbrewtitsel (1,true,true) .. contabhtml.tden
      prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
      strtpm = pripl5navare(contabpages[0],tabstuff) -- augment and maybe uc
      strdeste = '[[' .. strtpm .. '|' .. strtblc0 .. ']]' -- wl to lem page
      strt3d = contabhtml.tdbgco .. contabwarna.bkgud .. contabhtml.tdfd -- long "td" eleme
      prhoptconcat (strt3d .. strdeste .. contabhtml.tden) -- here "strdeste" used
      prhoptconcat ('</tr>')
    end--if
    -- done /d01/

    -- begin /c1/ -- Q-item
    strtblc1 = tabonelang[1] or ''
    if (strtblc1~='-') then
      if (not lfivalidatequack(strtblc1)) then
        numlokera = 15 -- #E15 sev huge, /c1/ is faulty
        break -- to join mark -- no table and huge error
      end--if
    end--if
    -- done /c1/

    -- begin /d02/ -- Q-item
    if (tabxkol.d02) then
      prhoptconcat ('<tr>')
      strtpm = contabhtml.tdbgti .. lfhbrewtitsel (2,true,true) .. contabhtml.tden
      prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
      strbkn = contabwarna.bkgud -- color code without cross
      if (strtblc1=="-") then -- nice to have, do not abort
        strbkn = contabwarna.reddi -- reddish if no "lang in the lang"
      else
        strtblc1 = '[[d:' .. strtblc1 .. '|' .. strtblc1 .. ']]'
      end--if
      strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
      prhoptconcat (strt3d .. strtblc1 .. contabhtml.tden) -- no "strdeste" here c1
      prhoptconcat ('</tr>')
    end--if
    -- done /d02/

        -- begin "c2" -- name of lang propralingve -- nice to have
        strtblc2 = tabonelang[2] or '' -- submodule cares about RLLR
        if (not privalidatestr(strtblc2,"-",2,contabtunmisc[1],0,0)) then
          numlokera = 15 -- #E15 sev huge, /c2/ is faulty ("-" tolerable here)
          break -- to join mark -- no table and huge error
        end--if
        -- done "c2"

        -- begin dest "d03" -- name of lang propralingve
    if (tabxkol.d03) then
        prhoptconcat ('<tr>')
        strtpm = contabhtml.tdbgti .. lfhbrewtitsel (3,true,true) .. contabhtml.tden
        prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
        strbkn = contabwarna.bkgud -- color code without cross
        if (strtblc2=="-") then -- nice to have, do not abort
          strbkn = contabwarna.reddi -- reddish if no propralingve
        end--if
        strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
        prhoptconcat (strt3d .. strtblc2 .. contabhtml.tden) -- no "strdeste" here c1
        prhoptconcat ('</tr>') -- done "d03"
    end--if

    -- begin /c3/ -- constructed and antique status
    strtblc3 = tabonelang[3] or ''
    -- done /c3/

    -- begin /d04/ -- constructed and antique status
    if (tabxkol.d04) then
      strtpm = '<tr>' .. contabhtml.tdbgti .. lfhbrewtitsel (4,true,true) .. contabhtml.tden
      prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
      boolocerr = false
      while true do -- inner fake loop
        if (strtblc3=='-') then -- nice to have, do not fire error
          strbkn = contabwarna.reddi -- reddish if missing
          strdeste = '-'
          break -- to inner join mark
        end--if
        boolocerr = (string.len(strtblc3)~=1) -- must be one char "0" ... "3"
        if (boolocerr) then
          break -- to inner join mark
        end--if
        numtamp = prndec1digit(string.byte(strtblc3,1,1)) -- 0...3 or 255
        boolocerr = (numtamp > 3)
        if (boolocerr) then
          break -- to inner join mark
        end--if
        strbkn = lfhintegertocol6d(numtamp,false,contabwarna.bkgud) -- preASSume light yellow (default color)
        boolocerr = (strbkn=='=')
        if (boolocerr) then
          break -- to inner join mark
        end--if
        strdeste = contabd04ap[numtamp+1] .. ' (' .. string.char(numtamp+48) .. ')' -- "contabd04ap" with texts is ONE-based
        break -- finally to inner join mark
      end--while -- inner join mark
      if (boolocerr) then
        if (numlokera==0) then
          numlokera = 15 -- #E15 sev huge, /c3/ is faulty
        end--if
        break -- to join mark
      end--if
      strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
      prhoptconcat (strt3d .. strdeste .. contabhtml.tden .. '</tr>')
    end--if
    -- done /d04/

    -- begin /c4/
    strtblc4 = tabonelang[4] or ''
    if (strtblc4=='--') then
      bootjmp = false -- 2 valid alternative values "-" and "--" exist
    else
      bootjmp = (not privalidatestr(strtblc4,"-",2,2,2,3))
    end--if
    if (bootjmp) then
      numlokera = 15 -- #E15 sev huge, /c4/ is faulty
      break -- to join mark -- no table and huge error
    end--if
    -- done /c4/

    -- begin /d05/ -- 2-letter code
    if (tabxkol.d05) then
      prhoptconcat ('<tr>')
        strtpm = contabhtml.tdbgti .. lfhbrewtitsel (5,true,true) .. contabhtml.tden
        prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
        strbkn = contabwarna.bkgud -- color code without cross
        if (strtblc4=="-") then -- nice to have ("-" is reddish, "--" is NOT)
          strbkn = contabwarna.reddi -- reddish if no ISO 639-1:2002 code
        end--if
        strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
        prhoptconcat (strt3d .. strtblc4 .. contabhtml.tden) -- no "strdeste" here c3
      prhoptconcat ('</tr>')
    end--if
    -- done /d05/

    -- begin /c5/
    strtblc5 = tabonelang[5] or ''
    if (strtblc5=='---') then
      bootjmp = false -- 2 valid alternative values "-" and "---" exist
    else
      bootjmp = (not privalidatestr(strtblc5,"-",3,3,2,3))
    end--if
    if (bootjmp) then
      numlokera = 15 -- #E15 sev huge, /c5/ is faulty
      break -- to join mark -- no table and huge error
    end--if
    -- done /c5/

    -- begin "d06" -- 3-letter code
    if (tabxkol.d06) then
        prhoptconcat ('<tr>')
        strtpm = contabhtml.tdbgti .. lfhbrewtitsel (6,true,true) .. contabhtml.tden
        prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
        strbkn = contabwarna.bkgud -- color code without cross
        if (strtblc5=="-") then -- nice to have ("-" is reddish, "---" is NOT)
          strbkn = contabwarna.reddi -- reddish if no ISO 639-3:2007 code
        end--if
        strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
        prhoptconcat (strt3d .. strtblc5 .. contabhtml.tden) -- no "strdeste" here c4
        prhoptconcat ('</tr>')
    end--if
    -- done "d06"

    -- begin "d07" -- "tln" + "kap" (!!! can be hidden !!!)
    if (tabxkol.d07) then
          prhoptconcat ('<tr>')
          strtpm = contabhtml.tdbgti .. lfhbrewtitsel (7,tabxctl.tlnsow,tabxctl.kapsow) .. contabhtml.tden
          prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
          strbkn = contabwarna.bkgud -- color code without cross
            strdeste = '' -- crucial
            if (tabxctl.tlnsow) then
              strtmp = pripl5navare(contabpages[5],tabstuff) -- augm maybe uc
              bootjmp,strdeste = lfykattlaenk (strtmp,strpykkatns,true,contabtunmisc.cats) -- "tln"
            end--if (tabxctl.tlnsow) then
            if (tabxctl.tlnsow and tabxctl.kapsow) then
              strdeste = strdeste .. '<br>'
            end--if
            if (tabxctl.kapsow) then
              strtmp = pripl5navare(contabpages[6],tabstuff) -- augm maybe uc
              bootjmp,strtmp = lfykattlaenk (strtmp,strpykkatns,true,contabtunmisc.cats) -- "kap"
              strdeste = strdeste .. strtmp
              if (not bootjmp) then
                strbkn = contabwarna.reddi -- reddish if "kap" kat does not exist
              end--if
            end--if (tabxctl.kapsow) then
          strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
          prhoptconcat (strt3d .. strdeste .. contabhtml.tden)
          prhoptconcat ('</tr>')
    end--if (tabxkol.d07) then
    -- done "d07"

    -- begin "d08" -- "vor" + "mal" (!!! can be hidden !!!)
    if (tabxkol.d08) then
          prhoptconcat ('<tr>')
          strtpm = contabhtml.tdbgti .. lfhbrewtitsel (8,tabxctl.vorsow,tabxctl.malsow) .. contabhtml.tden
          prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
          strbkn = contabwarna.bkgud -- color code without cross
            strdeste = '' -- crucial
            if (tabxctl.vorsow) then
              strtmp = pripl5navare(contabpages[3],tabstuff) -- augm maybe uc
              bootjmp,strdeste = lfykattlaenk (strtmp,strpykkatns,true,contabtunmisc.cats) -- "vor"
            end--if (tabxctl.vorsow) then
            if (tabxctl.vorsow and tabxctl.malsow) then
              strdeste = strdeste .. '<br>'
            end--if
            if (tabxctl.malsow) then
              strtmp = pripl5navare(contabpages[4],tabstuff) -- augm maybe uc
              bootjmp,strtmp = lfykattlaenk (strtmp,strpykkatns,true,contabtunmisc.cats) -- "mal"
              strdeste = strdeste .. strtmp
              if (not bootjmp) then
                strbkn = contabwarna.reddi -- reddish if "mal" kat does not exist
              end--if
            end--if (tabxctl.malsow) then
          strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
          prhoptconcat (strt3d .. strdeste .. contabhtml.tden)
          prhoptconcat ('</tr>')
    end--if (tabxkol.d08) then
    -- done "d08"

    -- begin "d09" -- Appendix + Index (!!! can be hidden !!!)
    if (tabxkol.d09) then
          prhoptconcat ('<tr>')
          strtpm = contabhtml.tdbgti .. lfhbrewtitsel (9,tabxctl.apxsow,tabxctl.indsow) .. contabhtml.tden
          prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
          strbkn = contabwarna.bkgud -- color code without cross
            strdeste = '' -- crucial
            if (tabxctl.apxsow) then
              strtmp = pripl5navare(contabpages[1],tabstuff) -- augm maybe uc
              bootjmp,strdeste = lfyapxindlaenk (strtmp,strpykapxns,false,true) -- "apx"
            end--if (tabxctl.apxsow) then
            if (tabxctl.apxsow and tabxctl.indsow) then
              strdeste = strdeste .. '<br>'
            end--if
            if (tabxctl.indsow) then
              strtmp = pripl5navare(contabpages[2],tabstuff) -- augm maybe uc
              bootjmp,strtmp = lfyapxindlaenk (strtmp,strpykindns,true,true) -- "ind"
              strdeste = strdeste .. strtmp
              if (not bootjmp) then
                strbkn = contabwarna.reddi -- reddish if "ind" page does not exist
              end--if
            end--if (tabxctl.indsow) then
          strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
          prhoptconcat (strt3d .. strdeste .. contabhtml.tden)
          prhoptconcat ('</tr>')
    end--if (tabxkol.d09) then
    -- done "d09"

    -- begin /c6/ -- wiki access code
        strtblc6 = tabonelang[6] or ''
        if (strtblc6=="-") then -- nice to have, long codes permitted, "??" permitted
          strtblc6 = stronecy -- copy access code from /cy/
        else
          if (not lfivalidatelnkoadv(strtblc6,false,true,true,false,false)) then -- function returns true if valid
            numlokera = 15 -- #E15 sev huge, /c6/ is faulty
            break -- to join mark -- no table and huge error
          end--if
        end--if
    -- done /c6/

    -- begin "c7" -- wiki availability code
        strtblc7 = tabonelang[7] or ''
        if (strtblc7~="-") then -- nice to have
          numtamp = string.len(strtblc7) -- length
          if (numtamp==2) then
            numc7pedi = string.byte(strtblc7,1,1) -- availability code
            numc7tion = string.byte(strtblc7,2,2) -- availability code
            numc7pedi = lfdec1diglm (numc7pedi,2) -- 0...2 or 255 if invalid
            numc7tion = lfdec1diglm (numc7tion,2) -- 0...2 or 255 if invalid
            if ((numc7pedi==255) or (numc7tion==255)) then
              numlokera = 15 -- #E15 sev huge, /c7/ is faulty
              break -- to join mark -- no table and huge error
            end--if
          else
            numlokera = 15 -- #E15 sev huge, /c7/ is faulty
            break -- to join mark -- no table and huge error
          end--if
        end--if
    -- done "c7"

    -- begin "d10" -- other ipedia main page
    if (tabxkol.d10) then
        strtpm = '<tr>' .. contabhtml.tdbgti .. lfhbrewtitsel (10,true,true) .. contabhtml.tden
        prhoptconcat (strtpm) -- "lfhbrewtitsel" does not add "<td"...">" "</td>"
        strbkn = lfhintegertocol6d(numc7pedi,true,contabwarna.bkgud) -- preASSume light yellow (default color)
        strdeste = pribrewurl(strtblc6,false,nil) .. lfiavailnt(numc7pedi)
        strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
        prhoptconcat (strt3d .. strdeste .. contabhtml.tden .. '</tr>')
    end--if
    -- done "d10"

    -- begin /d11/ -- other tionary main page from /c6/ and /c7/
    if (tabxkol.d11) then
      if (strtblc6==strsitelangcode) then -- !!!FIXME!!! we assume everywhere that for strsitelangcode /c6/ = /c0/
        strbkn = contabwarna.bkgud -- color code without cross -- light yellow (default color)
        strdeste = ':-)' -- reserved for /d11/ cell
      else
        strbkn = lfhintegertocol6d(numc7tion,true,contabwarna.bkgud) -- preASSume light yellow (default color)
        strdeste = pribrewurl (strtblc6,true,nil) .. lfiavailnt(numc7tion)
      end--if
      prhoptconcat ('<tr>' .. contabhtml.tdbgti .. lfhbrewtitsel (11,true,true) .. contabhtml.tden .. contabhtml.tdbgco .. strbkn .. contabhtml.tdfd .. strdeste .. contabhtml.tden .. '</tr>')
    end--if
    -- done /d11/

    -- begin /c8/ -- article in own wp
    strtblc8 = tabonelang[8] or '' -- this is sng
    booc8expl = (strtblc8~='-') and (strtblc8~='??')
    if (booc8expl and (not (privalidatestr(strtblc8,nil,2,32,0,0)))) then
      numlokera = 15 -- #E15 sev huge, /c8/ is faulty
      break -- to join mark -- no table and huge error
    end--if
    -- done /c8/

    -- begin /d12/ -- article in own wp from /c8/ literally or from /c0/ via contabpages[7]
    if (tabxkol.d12) then
      strbkn = contabwarna.bkgud -- color code without cross -- preASSume inno
      strtmp = pripl5navare(contabpages[7],tabstuff) -- always prebrewed
      if (booc8expl) then
        if (strtmp==strtblc8) then -- title wp article, BEWARE: may contain spc
          strbkn = contabwarna.reddi -- reddish if explicit title equal regularly constru
        else
          strtmp = strtblc8 -- given explicit title overrides regular
        end--if
      end--if
      strdeste = pribrewurl (strsitelangcode,false,strtmp) -- link wp article
      if (strtblc8=='??') then -- page does not exist now, but could in future
        strdeste = strdeste .. ' (??)'
      end--if
      prhoptconcat ('<tr>' .. contabhtml.tdbgti .. lfhbrewtitsel (12,true,true) .. contabhtml.tden .. contabhtml.tdbgco .. strbkn .. contabhtml.tdfd .. strdeste .. contabhtml.tden .. '</tr>')
    end--if
    -- done /d12/

    -- begin /c9/ -- cat in other tionary
    strtblc9 = tabonelang[9] or '' -- submodule cares about RLLR
    numafstbfcl = string.len(strtblc9)
    -- done /c9/

    -- begin /ca/ -- number of lemmas in that cat
    strtblca = tabonelang[10] or ''
    numafstbfkp = string.len(strtblca)
    -- done /ca/

    -- begin /d13/ -- cat in other tionary with number of lemmas
    if (tabxkol.d13) then
      do -- scope
        local strta13mp = ''
        local numtaamp  = 0
        local numtuump  = 0
        local boolocerx = false -- local error flag
        local boolocery = false -- other local error flag
        prhoptconcat ('<tr>')
        strta13mp = contabhtml.tdbgti .. lfhbrewtitsel (13,true,true) .. contabhtml.tden
        prhoptconcat (strta13mp) -- "lfhbrewtitsel" not add "<td"...">" "</td>"
        strbkn = contabwarna.bkgud -- color code without cross
        boolocerx = false
        boolocery = false
        if (strtblc9~="-") then -- do NOT evaluate evilness of "-" in "c9"
          if ((numafstbfcl<8) or (numafstbfcl>contabtunmisc.mkat)) then -- "c9" bad
            boolocerx = true -- length out of range
          else
            numtaamp = lfgcountchr(strtblc9,58)
            if ((numtaamp==0) or (numtaamp>2)) then -- "c9" bad
              boolocerx = true -- bad number of colons, ONE or TWO required
            end--if
          end--if
        end--if
        if (strtblca~="-") then -- do NOT evaluate evilness of "-" in /ca/
          if (numafstbfkp>8) then -- /ca/ bad
            boolocery = true -- length out of range (no minimum)
          else
            if (lfcountndg(strtblca)~=0) then -- /ca/ bad
              boolocery = true -- faulty char:s, a number is expected
            end--if
          end--if
        end--if
        if (boolocerx or boolocery) then
          numlokera = 15 -- #E15 sev huge, /c9/ or /ca/ is faulty
          break -- to join mark -- no table and huge error
        else
          if (strtblc9=='-') then
            strbkn = contabwarna.reddi -- reddish if no category
            strdeste = '-'
          else
            strdeste = pribrewurl (strtblc6,true,strtblc9) .. '<br>' -- other tionary cat
            if (strtblca=="-") then
              strdeste = strdeste .. prinumberinkat(-1) -- NOT count as error
            else
              numtuump = tonumber(lfremovenondi(strtblca)) -- apo:s tolerable
              strdeste = strdeste .. prinumberinkat(numtuump) -- apo:s added
            end--if (strtblca=='-') else
          end--if (strtblc9=='-') else
        end--if (boolocerx or boolocery) else
        strt3d = contabhtml.tdbgco .. strbkn .. contabhtml.tdfd -- long "td" eleme
        prhoptconcat (strt3d .. strdeste .. contabhtml.tden) -- here "strdeste" used
        prhoptconcat ('</tr>')
      end--do scope
    end--if
    -- done /d13/

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

return numlokera -- (komp return two, sng return one)

end--function prydoonelang

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

function exporttable.ek (arxframent)

-- misc unknown type

local vartymp = 0

-- special type "args" AKA "arx"

local arxourown = 0 -- metaized "args" from our own "frame" (NOT parent's)

  -- general "tab"

  local tablg78ysubt = {} -- all langcodes (komp no, sng yes)
  local tablgc0toca  = {} -- one langcode (komp no, sng yes)
  local tabctl = {} -- tlnsow tlnexp kapsow kapexp vorsow vorexp malsow malexp apxsow apxexp indsow indexp
  local tabkol = {} -- columns /d01/ ... /d13/

  -- peeked stuff

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

-- general "str"

  local striywocy = '' -- incoming parameter with langcode is the y-index word
  local strinfsel = '' -- incoming parameter with 6 digi, converted into 9 boo
  local strpurge  = '' -- purge quasi-external link complete with "[" and "]"
  local strtopm   = ''

-- general "num"

local numerr       = 0 -- #E01 internal #E12 unknown #E15 grava ...
local num2statcode = 0

-- general "boo"

  local boohavtbl = false -- have table on OK or #E12
  local bootimp = false

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

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

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

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

  qtcostquery[0] = arxframent         -- quasi-constant
  qtcostquery[1] = contabtunmisc.tifi -- control constant (be tight-fisted)
  qtcostquery[2] = 0          -- pass number (ZERO single | ONE collect | TWO retrieve)
  qtcostquery[3] = 0          -- total counter PagesInCategory
  qtcostquery[4] = 0          -- cost PagesInCategory (batched is cheaper, currently impossible)
  qtcostquery[5] = 0          -- counter tight-fisted PagesInCategory (no cost added, currently impossible)
  qtcostquery[6] = 0          -- total counter IfExists
  qtcostquery[7] = 0          -- cost IfExists (batched is cheaper, soon possible)
  qtcostquery[8] = 0          -- counter tight-fisted IfExists (no cost added, works but unofficial)

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

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

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

  -- no "strpikparent" and no "strpikpareuf" here

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

  prdtracemsg ('Peeking done, strpiklangcode=' .. tostring(strpiklangcode))
  prdtracemsg ('Further strpikkatns=' .. tostring(strpikkatns) .. ', strpikindns=' .. tostring(strpikindns) .. ', strpikapxns=' .. tostring(strpikapxns))

  ---- GET THE ARX:ES ----

  arxourown = arxframent.args -- "args" from our own "frame"

  ---- PROCESS MESSAGES ----

  contaberaroj  = prhrecusurrstrtab(contaberaroj, strpiklangcode, nil)
  contabd10d11w = prhrecusurrstrtab(contabd10d11w, strpiklangcode, nil) -- 1,2 -- "fermita aux malplena"
  contabdestit  = prhrecusurrstrtab(contabdestit, strpiklangcode, nil)  -- 0...13 -- table headings
  contabpages   = prhrecusurrstrtab(contabpages, strpiklangcode, nil)   -- 0...7 -- "Sxablonaro -@LK00-"
  contabligiloj = prhrecusurrstrtab(contabligiloj, strpiklangcode, nil) -- 0,1,2

  ---- SEIZE ONE ANON AND OBLIGATORY PARAMETER ----

  -- in arxourown[1] expecting lang code (2 or 3 lowercase letters,
  -- everythig else is criminal but this can be changed easily)

  -- result in "striywocy" will be later used as index for T78 but
  -- also displayed in right column in row /d00/

  if ((arxourown[1]==nil) or (arxourown[2])) then
    numerr = 8 -- #E08 need one obligatory anon param, NOT more
  else
    striywocy = arxourown[1] -- y-index word (langcode)
    if (not lfivalidatelnkoadv(striywocy,false,false,contabtunmisc.long,contabtunmisc.digi,false)) then
      numerr = 11 -- #E11 obviously invalid langcode
    end--if
  end--if

  ---- SEIZE ONE NAMED AND OBLIGATORY PARAMETER ----

  -- obligatory 6 digits about 6 items in 3 columns into 9
  -- tabbed boolean:s

  -- in "infsel=" expecting 6 decimal digits, value "2" NOT accepted here

  if (numerr==0) then
    do -- scope
      local varselinf = 0
      local numsilur = 0
      while true do
        varselinf = arxourown['infsel'] -- 6 digits: tln kap vor mal apx ind
        if (type(varselinf)~='string') then
          numerr = 16 -- #E16 named param "infsel=" missing
          break
        end--if
        strinfsel = varselinf
        numsilur = lfivaliumdctlstr ('111111',strinfsel) -- less than 255 bad
        if (numsilur~=255) then
          numerr = 17 -- #E17 named param "infsel=" invalid
          break
        end--if
        numsilur = prndec1digit(string.byte(strinfsel,1,1)) -- "tln"
        tabctl.tlnsow = (numsilur~=0) -- show tln in d07
        tabctl.tlnexp = false
        numsilur = prndec1digit(string.byte(strinfsel,2,2)) -- "kap"
        tabctl.kapsow = (numsilur~=0) -- show kap in d07
        tabctl.kapexp = false
        numsilur = prndec1digit(string.byte(strinfsel,3,3)) -- "vor"
        tabctl.vorsow = (numsilur~=0) -- show vor in d08
        tabctl.vorexp = false
        numsilur = prndec1digit(string.byte(strinfsel,4,4)) -- "mal"
        tabctl.malsow = (numsilur~=0) -- show mal in d08
        tabctl.malexp = false
        numsilur = prndec1digit(string.byte(strinfsel,5,5)) -- "apx"
        tabctl.apxsow = (numsilur~=0) -- show apx
        tabctl.apxexp = false
        numsilur = prndec1digit(string.byte(strinfsel,6,6)) -- "ind"
        tabctl.indsow = (numsilur~=0) -- show ind
        tabctl.indexp = false
        break -- finally to join mark
      end--while -- fake loop -- join mark
    end--do scope
  end--if

  ---- SEIZE NAMED AND OPTIONAL PARAMETER "DETRC" EVEN IF WE ALREADY SUCK ----

  -- try to pick named param "detrc=" even if we already have an error, so
  -- far we have collected to "qstrtrace" unconditionally, maybe shut up now

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

  prdtracemsg ('All params done, numerr=' .. tostring(numerr))

  ---- PICK ONE SUBTABLE T78 ----

  -- here risk of #E02 #E03

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

  while true do -- fake loop

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

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

    vartymp = qldingvoj['T78']
    if (type(vartymp)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if
    tablg78ysubt = vartymp -- has sub-sub-tables for langcodes

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

  prdtracemsg ('Picking of T78 done, numerr=' .. tostring(numerr) .. ', num2statcode=' .. tostring(num2statcode))

  ---- SEIZE THE PAGENAME AND BREW THE PURGE LINK FROM IT ----

  -- we use contabligiloj[0] with one word

  strtopm = 'Special:RecentChanges' -- bad default
  vartymp = mw.title.getCurrentTitle().prefixedText -- with namespace prefix !!
  if (prgstringrange(vartymp,1,255)) then
    strtopm = vartymp -- fullpagename here (otherwise "Special:RecentChanges")
  end--if
  strpurge = lfwbrew3url (arxframent,strtopm,'action=purge',contabligiloj[0],true)

  ---- CHECK LANGCODE AND GET ONE SUBTABLE ----

  -- * obviously invalid already ruled out above
  -- * here we check but do NOT pass further langname from /c0/
  -- * here we require string 1...400, more strict checks later  !!!FIXME!!!
  -- * here we depend on "strpiklangcode" and submodule "loaddata-tbllingvoj"

  while true do -- fake loop
    if (numerr~=0) then
      break
    end--if
    tablgc0toca = tablg78ysubt[striywocy] -- subtable for given langcode
    if (type(tablgc0toca)~='table') then
      if (type(tablg78ysubt[strpiklangcode])=='table') then -- site langcode
        numerr = 12 -- #E12 given langcode is unknown (site langcode works)
      else
        numerr = 2 -- #E02 broken submo (site langcode does NOT work either)
      end--if
      break
    end--if
    vartymp = tablgc0toca[0] -- pick langname from /c0/
    if (not prgstringrange (vartymp,1,400)) then -- bad type or len out range
      numerr = 2 -- #E02 broken submodule
    end--if
    break -- finally
  end--while -- fake loop -- join mark

  prdtracemsg ('Langcode checked, numerr=' .. tostring(numerr))

  ---- ASSIGN MANY ----

  if (numerr==0) then

    tabkol.d01 = true
    tabkol.d02 = true
    tabkol.d03 = true
    tabkol.d04 = true
    tabkol.d05 = true
    tabkol.d06 = true
    tabkol.d07 = tabctl.tlnsow or tabctl.kapsow
    tabkol.d08 = tabctl.vorsow or tabctl.malsow
    tabkol.d09 = tabctl.apxsow or tabctl.indsow
    tabkol.d10 = true
    tabkol.d11 = true
    tabkol.d12 = true
    tabkol.d13 = true

  end--if

  ---- BEGIN OUTPUT TABLE (1+14+1 ROWS) AND BREW TOP CELL "COLSPAN=2" ----

  -- using constants "contabhtml.tdcstw" and "contabhtml.tden" for
  -- "<td"...">" (with <<colspan="2">>) and "</td>"

  -- table "sng" must use <<float:right;>> and margins different from "komp"

  -- here we start using "prhoptconcat", the thing has been initialized
  -- by <<local qstrret = ''>> and <<local qtabbunch = {}>>

  boohavtbl = (numerr==0) or (numerr==12) -- OK or #E12

  if (boohavtbl) then -- OK or #E12
    prhoptconcat (contabhtml.bigbeg) -- "<table"... and "<tr>"
    prhoptconcat ('<tr>' .. contabhtml.tdcstw .. constrtop .. contabhtml.tden .. '</tr>')
  end--if

  ---- BOTHER WITH UNKNOWN LANGCODE ----

  -- using constants "contabhtml.tdcstw" and "contabhtml.tden" for
  -- "<td"...">" (with <<colspan="2">>) and "</td>", lagom error

  if (numerr==12) then -- #E12 unknown langcode
    prhoptconcat ('<tr>' .. contabhtml.tdcstw .. prhbrewerr6slnp(12,1) .. contabhtml.tden .. '</tr>')
  end--if

  ---- MAIN BLOCK OF TABLE DATA BUT NO LOOP ----

  -- from above:
  -- * strpiklangcode ...
  -- * striywocy (from parent, this is /cy/)
  -- * tablgc0toca (from tablg78ysubt)
  -- * tabctl (from "infsel=")
  -- * tabkol (booleans)

  -- inside "prydoonelang" using function "lfhbrewtitsel" (itself
  -- NOT adding "<td"...">" "</td>") for every table row /d00/ ... /d13/

  if (numerr==0) then -- skip also for #E12
    numerr = prydoonelang (strpiklangcode, strpikkatns, strpikindns, strpikapxns, striywocy, tablgc0toca, tabctl, tabkol)
  end--if

  --- BREW THE BOTTOM CELL "COLSPAN=2" ----

  -- using constants "contabhtml.tdcstw" and "contabhtml.tden" for
  -- "<td"...">" (with <<colspan="2">>) and "</td>"

  -- update/purge link (depends on pagename)
  -- link to source table (fixed) (template namespace)
  -- link to complete table (fixed) (appendix namespace)

  if (boohavtbl) then -- OK or #E12
    prhoptconcat ('<tr>' .. contabhtml.tdcstw .. strpurge .. '<br>' .. contabligiloj[1] .. '<br>' .. contabligiloj[2] .. contabhtml.tden .. '</tr>')
  end--if

  ---- CLOSE OUTPUT TABLE IF IT EXISTS, FINALIZE STRING, WHINE IF NEEDED ----

  -- both table and error report here is NOT possible

  if (boohavtbl) then -- OK or #E12, latter inside table
    prhoptconcat ('</table>')
    prhoptconcat (nil) -- crucial flush to "qstrret"
  else
    qtabbunch = {} -- discard, NOT used anymore
    if ((numerr==3) or (numerr==5)) then -- #E03 #E05, huge error, replace
      qstrret = prhbrewerr8subm(numerr,nil,num2statcode) -- 2 lines
    else
      qstrret = prhbrewerr8subm(numerr,nil,nil) -- no code from submo, one line only
    end--if
  end--if

  ---- RETURN THE STRING ----

  if (qboodetrc) then
    qstrret = '<br>' .. qstrtrace .. '<br><br>' .. qstrret
  end--if
  return qstrret

end--function exporttable.ek

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

return exporttable