Lompat ke isi

Modul:bigtable-tblbahasa

Dari Wikikamus bahasa Indonesia, kamus bebas

Dokumentasi untuk modul ini dapat dibuat di Modul:bigtable-tblbahasa/doc

--[===[

MODULE "MKOMTBLLIN" (kompleta tabelo kun lingvoj)

"eo.wiktionary.org/wiki/Modulo:bigtable-tbllingvoj" <!--2024-Dec-05-->
"id.wiktionary.org/wiki/Modul:bigtable-tbllingvoj"

Purpose: brews a (huge) complete table with all supported languages

Utilo: generas (egan) kompletan tabelon kun cxiuj subtenataj lingvoj

Manfaat: membuat tabel lengkap (dan sangat besar) dengan semua
         bahasa yang didukung

Syfte: skapar en (gigantisk) tabell med alla spraak som stoeds

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaent av mallar:
* pagxo "Aldono:Listo kun lingvoj" / halaman "Lampiran:Daftar bahasa"

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

Incoming: * optional anonymous
            * brew and show technical summary block on success (binary
              digit "0" or "1", default "0" ie false)
          * optional anonymous
            * string with 2 decimal digits "02"..."98" reducing probability
              to call expensive parser functions, default is 1 ie 100%
          * named obligatory parameter
            * "infsel=" column control -- string with 6 char:s (tristate
                        options: "0" do not show | "1" show no query |
                        "2" show and query)
              * "d07" "tln" top lng cat (0 or 1 only, no query)
              * "d07" "kap" cat
              * "d08" "vor" cat (0 or 1 only, no query)
              * "d08" "mal" cat
              * "d09" "apx" appendix (0 or 1 only, no query)
              * "d09" "ind" index
          * named optional parameter
            * "detrc=true" to create trace debug report

Returned: * one huge string with several HTML tables (one of them huge)

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

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 signocxenon "Grava eraro".

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.

The function of this module is governed by
the "Specification of the 2-dimensional table picking system".
* y-index markers are obligatory and
  lowercase (numbers and "-" not yet allowed)  !!!FIXME!!!
* x-index markers are disallowed

Note that the table T77 we use is NOT pluprocessed before, thus me must
strip RLLR ourselves, and both /cy/ and /c0/ are included (see below).

Status codes from submodule:
* #E00: OK
* #E01: internal
* #E02: template not found
* #E03 ... #E79: preprocessing error, range reserved by the specification

Column numbering in the source table:
* numbering starts from ZERO
* /cy/ does not count (it has theoretically index "-1")
* note that there is no comma between columns /cy/ and /c0/
* "loaddata-tbllingvoj" T77 returns the complete table line including
  the y-index marker /cy/, this means that unless the /cy/ part is
  trimmed off before, index ZERO fed into "lfhsplittotable" will
  return both /cy/ and /c0/, thus the indexes are NOT be OFF-by-ONE

Structure of a row in the source table with 1+11 columns:
- /cy/ : y-index marker (2 or 3 digits)  !!!FIXME!!! soon even more
- /c0/ : name of the language in the site language
         * EO: usually AJ lowercase, alternatively SB beginning
               with uppercase letter
         * ID: without "bahasa " and beginning with uppercase letter
- /c1/ : QID (for example "Q5432") "Q" + 2...8 digits, earliest digit
         only "1"..."9", further digits "0"..."9"
- /c2/ : name of the language in the language itself
         ("propralingve") (may be RLLR-enclosed)
- /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, base principle usually
       followed (there are some exceptions):
       - EO: core word usually AJ name with uppercased begin
             followed by " lingvo", no "la" article
       - 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 (may be RLLR-enclosed), can be absurdly long
       "Rummad:Wikeriadur:Distagaduriou' a vank en indonezeg" has 52
       octet:s and even worse 2 colons and one apo)
- 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 page:
* top either big fatal block or tiny success block, both with purge link
* huge destination table (not if fatal error)
* technical summary block (no links inside) (may be omitted in some cases)
* bottom either big fatal block or tiny success block, both with purge link

Structure of a row in the destination table with 14 columns:
- /d00/ : * index (in brackets, not bold)
          * space
          * language access code (bold) (literally peeked from /cy/
            y-index marker in the source table)
          # 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 "lfiultpl5repl"
            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 language code
          (literally peeked from "c3" in the source table)
- d06 : - "ISO 639-3:2007" 3-letter language code
          (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
            "lfiultpl5repl" 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
            "lfiultpl5repl" 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 "lfiultpl5repl" 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
          "lfiultpl5repl" 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 of the wiki 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 of the wiki 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 name constructed from /c8/, or alternatively if /c8/ is
          not available then it is guessed using "lfiultpl5repl" from /c0/
          and "contabpages[7]" and " (??)" is added)
        # adjust background to reddish if /c8/ is not available
        ! 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:
There are up to 6 expensive queries (2 cat:s plus appendix plus index, one
cat costs 2 bucks (separate "ifexist" and "pagesincategory") per page,
appendix and index cost one buck per page) per language and thus table row.
This is a big problem in "mkomtbllin" but in "msngtbllin" it is no problem
at all. The hard limit set by wiki software is 500. The totally fired number
of calls can be reduced via parameters "tabctl.tlnexp" "tabctl.kapexp"
"tabctl.vorexp" "tabctl.malexp" "tabctl.apxexp" "tabctl.indexp" and "numproba".

Error handling:
- #E13 #E14 if argument supplied from the caller is bad, output is minimal
  with huge error, no huge table and no technical summary block, fatal warning
  YES, position_err is NOT valid
- if early syntax check fails (line ZERO is not site lang) output is minimal
  with huge error "#E01 Grava eraro" then, no huge table and no summary,
  fatal warning YES, position_err is valid (can be ZERO or equal "numtblend")
- #E55 !!!FIXME!!! in a table row, the row is completed (risk
  for "-"), loop exited and table closed, huge error
  "#E37 Sintaksa eraro" is outside the table, summary YES, fatal warning YES
- "tabevalu.errtru" (no bool) incremented for every truncated line in source table
  having /cy/ and /c0/, minimal error "Nekompleta linio" is in the table
  element at column 0, do not exit the loop, fatal warning NO
- "tabevalu.errtlo" (no bool) incremented for every too long line in source table,
  lagom error "Tro longa (ele) linio" is in the table
  element at column 0, do not exit the loop, fatal warning YES
- "tabevalu.errkec" (no bool) incremented for every minor
  error (possible in line ZERO), do not exit the loop, fatal warning NO
- "tabevalu.miskap" (no bool) incremented
- "tabevalu.mismal" (no bool) incremented
- "tabevalu.misind" (no bool) incremented for every missing index page
  error (possible in line ZERO), do not exit the loop, fatal warning NO
- if "mw.title.getCurrentTitle().prefixedText" does not reveal our
  pagename (needed for purge link) then we don't whine but use a bad
  default "Special:Recent changes" instead

Errors codes in "numerr":
* #E01 -- internal
* #E02
* #E03
* #E08 -- number of anon param
* #E11 -- obviously invalid langcode from anon param {{{1}}} (only msng)
* #E12 -- unknown langcode from anon param {{{1}}} (only msng,
          only error showed inside table)
* #E13 -- anon param {{{1}}} boolean is bad (only big)
* #E14 -- anon param {{{2}}} integer is bad (only big)
* #E15 -- fatal (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 for lng 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 (only needed for LFIKODEOSG in turn LFHFILLSURRSTRTAB)

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) (used in sub and main)
contabwarna['reddi'] = 'FFD8D8' -- cell background color on minor error (reddish) (used in main)
contabwarna['merah'] = 'FFB0B0' -- cell background color on major error (red) (used in main)

-- 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['hugbeg'] = '<table style="' .. constrbord .. 'margin:0.5em auto 0.5em auto;border-collapse:collapse;text-align:center;"><tr>'
contabhtml['tdbgti'] = '<td style="' .. constrbord .. 'padding:0.4em;">'            -- colspan NO color NO (title line)
contabhtml['tdbgco'] = '<td style="' .. constrbord .. 'padding:0.4em;background:#'  -- colspan NO color YES (content)

local constrtdfd = ';">'                                                        -- part of HTML table code after HEX color
local constrtden = '</td>'

contabhtml['tekbeg'] = '<table style="margin:0.5em auto 0.5em auto;padding:0.5em;border:1px solid #000000;font-size:80%;"><tr><td>'
contabhtml['tekend'] = '</td></tr></table>'

-- constant strings err

  local constremibg = '<span class="error">'     -- mini whining begin
  local constremien = '</span>'                  -- mini whining end
  local constrelabg = '<span class="error"><b>'  -- lagom whining begin
  local constrelaen = '</b></span>'              -- lagom whining end
  local constrlaxhu = '&nbsp;&#91;&#93;&nbsp;'   -- lagom -> huge circumfix " [] "
  local constrexcla = '&nbsp;' .. constremibg .. '!!!' .. constremien  -- just " !!!" with some style (komp YES sng NO)

-- uncommentable error messages

-- #E01...#E37, note that #E00 is NOT supposed to be included here
-- submodule can give #E01 ... #E79 (#E03...#E79 from preprocessing) and we add 100 to those codes  !!!FIXME!!!

local contaberaroj = {}  -- !!!FIXME!!!
      -- contaberaroj[01] = 'Grava eraro'
        contaberaroj[01] = 'Galat jahat'                         -- #E01
      -- 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
      -- contaberaroj[32] = 'Sxablono "tbllingvoj" havas malbonan grandecon'
        contaberaroj[32] = 'Ukuran templat "tblbahasa" salah'              -- #E32

  -- contaberaroj[33] = 'Submodulo ne liveris du tabelojn T75 T77'            -- EO #E33
  contaberaroj[33] = 'Submodul tidak menyediakan dua tabel T75 T77'        -- ID #E33
  -- contaberaroj[35] = 'Nekompleta (ele) linio'                              -- EO (mini) neko #E35
  contaberaroj[35] = 'Garis tidak lengkap (ele)'                           -- ID (mini)
  -- contaberaroj[36] = 'Tro longa (ele) linio'                               -- EO (lagom) trol #E36
  contaberaroj[36] = 'Garis terlalu panjang (ele)'                         -- ID (lagom)
  -- contaberaroj[37] = 'Sintaksa eraro'                                      -- EO (huge) sexe #E37
  contaberaroj[37] = 'Kesalahan sintaks'                                   -- ID (huge)

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

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 (all bold)
  contabdestit[01] = 'Nama bahasa dalam ID dan halaman lema'                         -- ID (all 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

local contabevilitem = {} -- (komp YES sng NO)
  -- contabevilitem = {'erara kodo','erara lingvonomo','erara titolo','erara kategorio','erara Q-umo','erara kvanto'}   -- EO (index 1...6, no l&t spaces)
  contabevilitem = {'kode salah','nama bahasa salah','judul salah','kategori salah','butir Q salah','jumlah salah'}  -- ID (index 1...6, no l&t spaces)
  -- contabevilitem[0] = ' en kolumno'                                                                   -- EO (index 0, leading space needed)
  contabevilitem[0] = ' dalam kolom'                                                                  -- ID (index 0, leading space needed)

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

-- syntax of the request string:
-- * "@" followed by 2 uppercase letters and 2 hex digits,
--   otherwise the content is not expanded, but copied as-is instead
-- * 2 letters select the substitute string from table supplied by the
--   caller, see below
-- * 2 hex numbers control dropping left and right (0...15 char:s)

-- 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 "lfiultpl5repl" 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] = "@LU00 lingvo"           -- EO ipedia page guessing
  contabpages[7] = "Bahasa @LN00"           -- ID ipedia page guessing

  -- uncommentable EO vs ID

local contabdoubled = {}
    -- contabdoubled[0] = 'Gravaj eraroj trovigxas en la tabelo'                              -- EO (no dot "." here) ERR
    contabdoubled[0] = 'Ada kesalahan jahat dalam tabel'                                   -- ID (no dot "." here) ERR
    -- contabdoubled[1] = 'Se la eraroj persistas tiam vi redaktu'                            -- EO (no dot "." here) ERR
    contabdoubled[1] = 'Kalau kesalahan masih ada jadi Anda harus menyunting'              -- ID (no dot "." here) ERR
    -- contabdoubled[2] = 'kaj sekve revenu cxi tien por reinspekti rezulton'                 -- EO (no dot "." here) ERR
    contabdoubled[2] = 'dan kemudian datang ke sini sekali lagi untuk memeriksa hasilnya'  -- ID (no dot "." here) ERR
    -- contabdoubled[3] = 'Gxustigu kaj reprovu gxis ke cxi tiu mesagxo malaperas'            -- EO (no dot "." here) ERR
    contabdoubled[3] = 'Silakan memperbaiki dan mencoba ulang sampai pesan ini hilang'     -- ID (no dot "." here) ERR
    -- contabdoubled[4] = 'Vi povas redakti'                                                  -- EO (no dot "." here) OK
    contabdoubled[4] = 'Anda bisa menyunting'                                              -- ID (no dot "." here) OK
    -- contabdoubled[5] = 'Klaku cxi tie (kaj sekve konsentu al "malplenigi kasxmemoron") por regeneri la tabelon.'            -- EO (dot "." needed here)
    contabdoubled[5] = 'Kliklah di sini (dan kemudian setujulah dengan "hapus singgahan") untuk menghasilkan ulang tabel.'  -- ID (dot "." needed here)
    -- contabdoubled[6] = '[[SXablono:tbllingvoj|la fontan tabelon]]'                                                          -- EO (no dot "." here)
    contabdoubled[6] = '[[Templat:tblbahasa|tabel sumber]]'                                                                 -- ID (no dot "." here)

local contabsum = {}
  -- contabsum[0] = 'Eniga kontrolparametro, probablo: '             -- EO
  contabsum[0] = 'Parameter pengontrol masuk, probabilitas: '     -- ID
  -- contabsum[1] = 'Amplekso de la fonta tabelo (bitokoj): '        -- EO
  contabsum[1] = 'Ukuran tabel sumber (oktet): '                  -- ID
  -- contabsum[2] = 'Kvanto da lingvoj: '                            -- EO
  contabsum[2] = 'Jumlah bahasa: '                                -- ID
  -- contabsum[3] = 'Kvanto da (gravaj) sortigaj eraroj: '           -- EO
  contabsum[3] = 'Jumlah kesalahan sort (jahat): '                -- ID
  -- contabsum[4] = 'Kvanto da nekompletaj (ele) linioj: '           -- EO
  contabsum[4] = 'Jumlah garis tidak lengkap (ele): '             -- ID
  -- contabsum[5] = 'Kvanto da tro longaj (ele) linioj: '            -- EO
  contabsum[5] = 'Jumlah garis terlalu panjang (ele): '           -- ID

-- diverse tuning values in one table

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

-- uncommentable (override)

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

local contabovrd = {}
  -- contabovrd['sitelang'] = 'eo'                                     -- "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'

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

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

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

---- SPECIAL VAR:S ----

local qldingvoj = {}     -- type "table" and nested
local qtcostquery = {}   -- for IfExists and PagesInCategory
local qtabbunch = {}     -- bunch strings here for "lfhoptconcat"
local qbooguard = false  -- only for the guard test, pass to other var ASAP
local qboodetrc = true   -- from "detrc=true" but default is "true" !!!
local qstrtrace = '<br>' -- for main & sub:s, debug report request by "detrc="
local qstrret = ''       -- declare here if "lfhoptconcat" used

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

qbooguard = (type(constringvoj)~='string')

---- SEIZE THE HUGE SOURCE TABLE VIA LOADDATA ----

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

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

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

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

-- Local function LFDTRACEMSG

-- Enhance upvalue "qstrtrace" with fixed text.

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

-- no size limit

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

-- uses upvalue "qboodetrc"

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

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

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

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

local function mathisintrange (numinpuut, numzjmin, numzjmax)
  local numclean = 0
  local booisclean = false
  if (type(numinpuut)=='number') then -- no non-numbers, thanks
    numclean = math.floor (numinpuut) -- no transcendental
    numclean = math.max (numclean,numzjmin) -- not below minimum
    numclean = math.min (numclean,numzjmax) -- no trillions
    booisclean = (numclean==numinpuut)
  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

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

-- Local function MATHBITWRIT

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

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

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

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

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

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

-- Local function 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 LFDEC1DIGIT

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

local function lfdec1digit (num1digit)
  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 lfdec1digit

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

-- Local function LFSTRTWDIG2INT

-- Convert string with always 2 decimal ASCII digit:s
-- to integer 0...99 (255 if invalid).

-- Depends on functions :
-- [N] lfdec1digit

local function lfstrtwdig2int (strmasuuk)
  local numleft = 255 -- preASSume guilt
  local numrayt = 0
  if (string.len(strmasuuk)==2) then
    numleft = string.byte (strmasuuk,1,1)
    numleft = lfdec1digit (numleft) -- 255 if invalid, ZERO would be valid
    numrayt = string.byte (strmasuuk,2,2)
    numrayt = lfdec1digit (numrayt) -- 255 if invalid, ZERO would be valid
    if (numrayt==255) then
      numleft = 255 -- 255 is invalid, note that ZERO would be valid
    else
      numleft = numleft * 10 + numrayt -- valid integer number 0...99 now
    end--if
  end--if
  return numleft
end--function lfstrtwdig2int

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

-- Local function LFNUMTO2DIGIT

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

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

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

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

-- Local function LFNUMTODECBUN

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

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

local function lfnumtodecbun (numnomoriin)
  local strnomorut = ''
  local numindeex = 0
  local numcaar = 0
  numnomoriin = math.floor (numnomoriin) -- transcendental numbers suck
  if (numnomoriin<0) then
    numnomoriin = 0 -- negative numbers suck
  end--if
  while true do
    numcaar = mathmod(numnomoriin,10) + 48 -- get digit moving right to left
    numnomoriin = mathdiv(numnomoriin,10)
    if (numindeex==3) then
      strnomorut = "'" .. strnomorut -- ueglstr apo
      numindeex = 0
    end--if
    strnomorut = string.char(numcaar) .. strnomorut -- ueglstr digit
    numindeex = numindeex + 1
    if (numnomoriin==0) then
      break
    end--if
  end--while
  return strnomorut
end--function lfnumtodecbun

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

-- Local function LFNONEHEXTOINT

-- Convert single quasi-digit (ASCII HEX "0"..."9" "A"..."F") to
-- integer (0...15, 255 invalid).

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

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

---- SOME FUNCTIONS [?] ----

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

-- convert integer to deciml string with digit bunching & warn unconditionally

-- need constant string "constrexcla" ie just " !!!" with some style

local function lftostwauc (numccrap)
  local strhaasil = ''
  strhaasil = lfnumtodecbun(numccrap) .. constrexcla
  return strhaasil
end--function lftostwauc

-- convert integer to deciml string with digit bunching & warn if non-ZERO

-- need constant string "constrexcla" ie just " !!!" with some style

local function lftostwaz (numcrap)
  local strhasil = ''
  strhasil = lfnumtodecbun(numcrap)
  if (numcrap~=0) then
    strhasil = strhasil .. constrexcla
  end--if
  return strhasil
end--function lftostwaz

-- convert integer to deciml string with digit bunching & warn if over limit

-- need constant string "constrexcla" ie just " !!!" with some style

local function lftostw2p (numcreap, numinklimit)
  local strhasiil = ''
  strhasiil = lfnumtodecbun(numcreap)
  if (numcreap>numinklimit) then
    strhasiil = strhasiil .. constrexcla
  end--if
  return strhasiil
end--function lftostw2p

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

-- Local function LFISPACUNDR

-- Spaces to underscores or vice-versa.

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

local function lfispacundr (strxxin, boounderlig)
  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 lfispacundr

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

-- Local function LFCOUNTCHR

-- Count occurrences of a char (given by ASCII code) in a string.

-- Input  : * strqq -- string (empty is useless but can't cause major harm)
--          * numascii -- code of char to be counted

-- Output : * numrezalt -- number of hits

local function lfcountchr (strqq, numascii)
  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 -- done -- ZERO iterations possible
    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 lfcountchr

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

-- Local function LFCOUNTNDG  !!!FIXME!!! dubious, one use

-- Count occurrences of non-digits (from a customizable set) in a string

-- Tolerable char:s that do not count are:
-- * digits "0"..."9"
-- * maybe apo for (trictl=1) or (trictl=2)
-- * maybe space for (trictl=2)

local function lfcountndg (strzzz,trictl)
  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==39) and (trictl~=0)) then
        numcair=48 -- apo OK
      end--if
      if ((numcair==32) and (trictl==2)) then
        numcair=48 -- space OK
      end--if
    if ((numcair<48) or (numcair>57)) 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

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

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

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

-- Local function LFGSTRINGRANGE

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

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

-- Local function LFGPOKESTRING

-- Input  : * strinpokeout -- empty legal
--          * numpokepoz   -- ZERO-based, out of range legal
--          * numpokeval   -- new value

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

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

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

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

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

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

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

-- Local function LFGRESTRICTLEN

-- Restrict length of a string using ASCII tripledot "..." if needed.

-- Input  : * strmaxdot
--          * nummaxdotlen -- at least 8

-- Output : * strmaxdot -- NEVER longer than input

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

-- Worst cases:
-- * strmaxdot = "ABCDEFGHI" len = 9 and nummaxdotlen = 8
--   --> after sub nummaxdotlen = 3 --> after shr nummaxdotlen = 1
--   --> output "A ... I" len = 7
-- * strmaxdot = "ABCDEFGHIJ" len = 10 and nummaxdotlen = 9
--   --> after sub nummaxdotlen = 4 --> after shr nummaxdotlen = 2
--   --> output "AB ... IJ" len = 9

-- Can add 3 dot:s and 2 spaces, but NOT quotes.

local function lfgrestrictlen (strmaxdot,nummaxdotlen)
  local numinddlen = 0
  numinddlen = string.len(strmaxdot)
  if (nummaxdotlen<8) then
    nummaxdotlen = 8 -- limit must be >= 8 !!!
  end--if
  if (numinddlen>nummaxdotlen) then
    nummaxdotlen = mathdiv((nummaxdotlen-5),2) -- at least 1
    strmaxdot = string.sub(strmaxdot,1,nummaxdotlen) .. ' ... ' .. string.sub(strmaxdot,(-nummaxdotlen),-1)
  end--if
  return strmaxdot
end--function lfgrestrictlen

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

-- Local function LFGTRIMSPCM

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

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

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

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

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

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

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

-- Local function LFULNUTF8CHAR

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

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

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

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

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

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

-- Local function LFUCASEREST

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

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

-- Output : * strinco6cs

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

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

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

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

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

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

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

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

  local numtem6p = 0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return strinco6cs

end--function lfucaserest

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

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

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

-- Local function LFIVALIDATESTR

-- 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 parameter "numapo" above)
--                     2 : only ASCII uppercase letters
--                     3 : only ASCII lowercase letters

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

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

local function lfivalidatestr (stryn,varalt,nummyn,nummex,numapo,numpoz)
  local numlencx = 0
  local numynx = 0
  local numcxar = 0
  local numcxur = 0
  local bootnp = false
  local booisvalid = true -- preASSume innocence -- final verdict
  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)
      bootnp = (string.find(stryn, "''", 1, true)~=nil) -- pltxt search dblapo
      if ((numcxar==39) or (numcxur==39) or bootnp) 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 (not lfgtestnum(numcxar)) then
            booisvalid = false
            break -- abort search, crime detected (non-digit)
          end--if
        end--if
        if (numpoz==2) then
          if (not lfgtestuc(numcxar)) then
            booisvalid = false
            break -- abort search, crime detected (non-uppercase)
          end--if
        end--if
        if (numpoz==3) then
          if (not lfgtestlc(numcxar)) 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 lfivalidatestr

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

-- Local function LFIVALIDATEQUACK

-- Validate a Q-item.

-- Input  : * strqynq -- string

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

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

-- 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 numlendx = 0
  local numuinx = 0
  local numcyaar = 0
  local booqisvalid = false -- preASSume guilt -- final verdict
  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 lfgtestnum(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 language code) is valid
-- containing only 2 or 3 lowercase letters, or 2...10 char:s and with some
-- dashes, or maybe a digit in middle position or maybe instead equals to "-"
-- or "??" and maybe additionally is not included on the ban list.

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

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

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

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

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

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

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

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

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

  while true do -- fake (outer) loop

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

    numindeex = 0
    while true do -- 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 = lfgtestnum(numchiiar)
      booislclc = lfgtestlc(numchiiar)
      if (not (booislclc or booisdigi or booisdash)) then
        booisvaladv = false
        break -- to join mark -- inherently bad char
      end--if
      if (booislclc) then
        numadjlet = numadjlet + 1
      else
        numadjlet = 0
      end--if
      if (booisdigi and ((numukurran~=3) or (numindeex~=1) or (not boodigit))) then
        booisvaladv = false
        break -- to join mark -- illegal digit
      end--if
      if (booisdash) then
        if ((numukurran<4) or (numindeex==0) or ((numindeex+1)==numukurran)) then
          booisvaladv = false
          break -- to join mark -- illegal dash
        end--if
        numadjdsh = numadjdsh + 1
        numtotdsh = numtotdsh + 1 -- total
      else
        numadjdsh = 0 -- do NOT zeroize the total !!!
      end--if
      if ((numadjlet>3) or (numadjdsh>1) or (numtotdsh>2)) then
        booisvaladv = false
        break -- to join mark -- evil
      end--if
      numindeex = numindeex + 1 -- ZERO-based
    end--while -- 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

-- 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 functions :
-- [N] lfdec1digit

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

local function lfivaliumdctlstr (strresdpat, strctldstr)

  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 = lfdec1digit(numead2) -- rest
        if (numead2>9) then -- limit defined or bad ??
          numbadpos = 254
          break -- bad restrictive pattern
        else
          numead3 = lfdec1digit(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

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

-- Local function LFIKODEOSG

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

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

-- Output : * strutf8eo -- UTF8 string

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

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

-- To be called ONLY from "lfhfillsurrstrtab".

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

local function lfikodeosg (streosurr)

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

  numeoinplen = string.len(streosurr)

  while true do

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

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

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

  end--while

  return strutf8eo

end--function lfikodeosg

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

-- Local function LFITEST5PLEJS

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

-- Input  : * stroneplejs -- 5 octet:s
--          * tabsubtitu -- optional table, list with substitute strings using
--                          two-letter codes as keys, type "nil" for none

-- Output : * numtriplejs -- main status: ZERO not valid | ONE valid unknown |
--                                        TWO found
--          * numpl5left, numpl5right -- only for non-ZERO
--          * strvaljuk -- * if optional table given then contains value
--                           only for status TWO
--                         * if no table then contains the key
--                           only for status ONE
--                         * in all other cases empty string

-- Depends on functions :
-- [G] lfgstringrange lfgtestuc
-- [N] lfnonehextoint

local function lfitest5plejs (stroneplejs, tabsubtitu)

  local varduahuruf = 0

  local strmajky  = ''
  local strvaljuk = '' -- preASSume guilt

  local numtriplejs = 0 -- preASSume guilt
  local numplejoct  = 0 -- maybe "@"
  local numpl5left  = 0 -- hex and discard left
  local numpl5right = 0 -- hex and discard 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 lfgstringrange(stroneplejs,5,5)) then
      break -- internal error
    end--if
    numplejoct = string.byte(stroneplejs,1,1) -- also returned
    if (numplejoct~=64) then
      break -- not valid per spec
    end--if
    numplejodt = string.byte(stroneplejs,2,2)
    numplejoet = string.byte(stroneplejs,3,3)
    if ((not lfgtestuc(numplejodt)) or (not lfgtestuc(numplejoet))) then
      break -- not valid per spec
    end--if
    numplejx5u = lfnonehextoint(string.byte(stroneplejs,4,4))
    numplejx5v = lfnonehextoint(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 ??
      varduahuruf = tabsubtitu[strmajky] -- risk of type "nil"
      if (lfgstringrange(varduahuruf,1,255)) then
        strvaljuk = varduahuruf -- else keep empty string and status ONE
        numtriplejs = 2 -- table here and placeholder found, return value
      end--if
    else
      strvaljuk = strmajky -- no table but placeholder valid, return key
    end--if (type(tabsubtitu)=='table') else
    break -- finally to join mark
  end--while -- fake loop -- join mark

  return numtriplejs, numpl5left, numpl5right, strvaljuk

end--function lfitest5plejs

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

-- Local function LFIULTPL5REPL

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

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

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

-- Local function LFHOPTCONCAT

-- 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 "lfhoptconcat (nil)" before using "qstrret"

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

local function lfhoptconcat (varincom)
  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 lfhoptconcat

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

-- Local function LFHVALI1STATUS99CODE

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

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

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

-- Local function LFHCONSTRUCTERAR

-- Construct error message maybe peeking description.

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

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

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

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

local function lfhconstructerar (numerar3code, boopeek3it)
  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,99)) then
    stryt3sux = stryt3sux .. lfnumto2digit(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 lfhconstructerar

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

-- Local function LFHBREWERRSVR

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

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

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

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

local function lfhbrewerrsvr (numerar8code, numseverity)
  local stryt8sux = ''
  stryt8sux = lfhconstructerar (numerar8code,true)
  if (numseverity==0) then
    stryt8sux = constremibg .. stryt8sux .. constremien -- 0
  else
    stryt8sux = constrelabg .. stryt8sux .. constrelaen -- 1 and 2
  end--if
  if (numseverity==2) then
    stryt8sux = constrlaxhu .. stryt8sux .. constrlaxhu -- 2
  end--if
  return stryt8sux
end--function lfhbrewerrsvr

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

-- Local function LFIULTPL5REPL  !!!FIXME!!! move

-- Insert selected substitute strings into request string at given positions
-- with optional dropping if the substitute string is empty.

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

-- Output : * strhazil

-- Depends on functions :
-- [I] lfitest5plejs
-- [G] lfgstringrange lfgtestuc
-- [N] lfnonehextoint

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

-- Anything not valid as placeholder is kept ie literally copied
-- as-is instead.

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

local function lfiultpl5repl (strrekvest, tabsubstut)

  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 -- discard left
  local numammrig = 0 -- discard 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 = lfitest5plejs (string.sub(strrekvest,(numrekvinx+1),(numrekvinx+5)), 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 = '' -- discard left -- leaving nothing behind
      else
        strhazil = string.sub(strhazil,1,numremainder) -- discard left
      end--if
      numrekvinx = numrekvinx + 5 + numammrig -- discard 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 lfiultpl5repl

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

-- Local function LFHFILLSURRSTRTAB

-- Process (fill in, 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 value are kept as-is. Optional NOPE filling in own name "\@",
-- and optional transcoding of eo and NOPE sv surrogates (via 3 separate
-- functions). Optionally even string keys/indexes are transcoded but
-- NOT filled in.

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

-- Depends on functions :
-- [I] lfikodeosg (only if conv of eo X-surrogates desired)
-- [I] NOPE lfikodsvsg
-- [I] NOPE lfipl2altwre (only if filling-in desired)
-- [G] NOPE lfgstringrange (via "lfipl2altwre")
-- [E] mathdiv mathmod (via "lfikodeosg" and NOPE "lfikodsvsg")

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

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

local function lfhfillsurrstrtab (varinkommen, varfyllo, strlingkod, bookeys)

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

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

  if (type(varinkommen)=='table') then
    varutmatning = {} -- brew new table from scratch
    for k4k,v4v in pairs(varinkommen) do -- nothing done if table empty
      if ((bookeys==true) and (type(k4k)=='string')) then
        varnky = lfhfillsurrstrtab (k4k, false, strlingkod, nil) -- RECURSION
      else
        varnky = k4k
      end--if
      if ((type(v4v)=='string') or (type(v4v)=='table')) then
        v4v = lfhfillsurrstrtab (v4v, varfyllo, 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 lfhfillsurrstrtab

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

-- nope LFHTABLETOHTMLLISTA

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

-- Local function LFHTABLETOHTMLLISNT

-- Convert LUA table into a HTML list.

-- Input  : * tabsomeitemsnt -- ZERO-based LUA table, empty legal

-- Output : * strhotomool -- * empty string if incoming table is empty
--                           * raw string if incoming table has ONE element
--                           * else HTML list

-- <li></li> encloses single items in list
-- <ul></ul> encloses complete list

local function lfhtabletohtmllisnt (tabsomeitemsnt)

  local var4elemento = 0
  local strhotomool = ''
  local numindekso = 0 -- ZERO-based

  while true do -- genuine loop over incoming table
    var4elemento = tabsomeitemsnt [numindekso]
    if (type(var4elemento)~='string') then
      break -- done
    end--if
    if (numindekso==0) then
      strhotomool = var4elemento
    end--if
    if (numindekso==1) then
      strhotomool = '<li>' .. strhotomool .. '</li>' -- postponed work for #0
    end--if
    if (numindekso>=1) then
      strhotomool = strhotomool .. '<li>' .. var4elemento .. '</li>'
    end--if
    numindekso = numindekso + 1
  end--while

  if (numindekso>=2) then
    strhotomool = '<ul>' .. strhotomool .. '</ul>'
  end--if

  return strhotomool

end--function lfhtabletohtmllisnt

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

-- Local function LFHSPLITTOTABLE

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return num37status, numsubstrinx, tabyeahsplit

end--function lfhsplittotable

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

-- Local function LFIRLLRSTRIP  !!!FIXME!!! move

-- Strip "rl " ... " lr" if it is present and string
-- length is at least 8 octet:s.

local function lfirllrstrip (strorlinut)
  if (string.len(strorlinut)>=8) then -- at least 2 octet:s length after strip
    if ((string.sub(strorlinut,1,3)=='rl ') and (string.sub(strorlinut,-3,-1)==' lr')) then
      strorlinut = string.sub(strorlinut,4,-4) -- strip off 3+3 char:s
    end--if
  end--if
  return strorlinut
end--function lfirllrstrip

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

-- Local function LFINUMBERINKAT  !!!FIXME!!! move

local function lfinumberinkat (numjumlahorminone)
  local strnothing = '' -- "(16'384 pages)" or "(?? pages)"
  if (numjumlahorminone<0) then -- expected type integer, value -1 for unknown
    strnothing = '(??'
  else
    strnothing = '(' .. lfnumtodecbun(numjumlahorminone) -- ZERO is valid
  end--if
  strnothing = strnothing .. ' pages)'
  return strnothing
end--function lfinumberinkat

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

-- Local function LFAVAILCO

local function lfavailco (numava) -- translates number 2...0 to color (Y,grey)
  local strcolor = contabwarna['bkgud'] -- preASSume light yellow (default color)
  if (numava==1) then
    strcolor = 'D0D0D0' -- grey
  end--if
  if (numava==0) then
    strcolor = 'B0B0B0' -- darker grey
  end--if
  return strcolor
end--function lfavailco

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

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

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

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

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

-- Local function LFEVILITEM

-- Brew complaint about faulty item in the source table.

-- Input  : * numtpitem -- type (0:code 1:langname 2:title 3:category
--                               4:Q-item 5:number)
--          * numcol  -- source column index (0...10) or special value 67
--          * strxx   -- the faulty string already suitably clamped

-- Output : * 2 lines separated by "<br>"

-- We need table "contabevilitem" (1...6) OFF-by-ONE with special
-- content at index ZERO.

-- We need "constrelabg" and "constrelaen" (lagom error).

local function lfevilitem (numtpitem,numcol,strxx)
  local strharang = ''
  local numquot = 0
  strharang = constrelabg .. contabevilitem[numtpitem+1] .. contabevilitem[0] .. constrelaen .. ' "c'
  if (numcol==67) then
    strharang = strharang .. '6" "c7' -- special case
  else
    strharang = strharang .. tostring(numcol)
  end--if
  strharang = strharang .. '"&nbsp;:<br>'
  numquot = string.byte(strxx,1,1)
  if (numquot==34) then
    strharang = strharang .. strxx -- truncated string like <<"sh"..."ck">>
  else
    strharang = strharang .. '"' .. strxx .. '"' -- add quotes if not yet pres
  end--if
  return strharang
end--function lfevilitem

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

-- Local function LFIBREWURL

-- Brew [] external link to a wiki.

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

-- Depends on functions :
-- [I] lfispacundr

-- Here we replace spaces by underscore, no further encoding is performed

local function lfibrewurl (straxk,bootip,varpage)
  local strurlkita = ''
  local strsite = ''
  if (bootip) then
    strsite = 'tionary'
  else
    strsite = 'ipedia'
  end--if
  strurlkita = '[http://' .. straxk .. '.wik' .. strsite .. '.org'
  if (varpage) then -- string (article or category with prefix) or nil
    strurlkita = strurlkita .. '/wiki/' .. lfispacundr(varpage,true) -- replace in URL
  end--if
  strurlkita = strurlkita .. ' ' -- separation space
  strurlkita = strurlkita .. straxk .. '.' .. strsite .. '...'
  if (varpage) then -- string (article or category with prefix) or nil
    strurlkita = strurlkita .. '<br>/' .. varpage -- do NOT replace here
  end--if
  strurlkita = strurlkita .. ']' -- done
  return strurlkita
end--function lfibrewurl

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

-- Local function LFHBREWTITSEL

-- 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 functions :
-- [N] lfnumto2digit

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

-- We need const table "contabdestit" and
-- contabmisc[0] (translated word "and").

local function lfhbrewtitsel (numindxe, bootopp, boobotm)

  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
    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' .. lfnumto2digit (numindxe) .. ')'

  return strdesc

end--function lfhbrewtitsel

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

---- MEDIAWIKI INTERACTION FUNCTIONS [W] ----

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

-- Local function LFWBREW3URL

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

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

-- Local function LFWIFEXIST

-- 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 lfwifexist (strpgname)
  local boomemangada = false
  local arxxx = 0
  local metaa = 0
  qtcostquery[6] = qtcostquery[6] + 1 -- total counter
  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 -- tight-fisted counter
  else
    metaa = mw.title.new (strpgname) -- 1 param
    boomemangada = metaa.exists -- expensive here
    qtcostquery[7] = qtcostquery[7] + 1 -- cost
  end--if
  return boomemangada
end--function lfwifexist

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

-- Local function LFWKATROCL

-- 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 lfwkatrocl (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 -- total counter
  qtcostquery[4] = qtcostquery[4] + 1 -- cost
  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 lfwkatrocl

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

---- VERY HIGH LEVEL FUNCTIONS [Y]

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

-- Local function LFYKATTLAENK

-- 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 functions :
-- [W] lfwifexist lfwkatrocl
-- [I] lfinumberinkat

-- 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 = lfwifexist (strfullcatname)
    if (not booisbiru) then -- incidents reported here but counted by caller
      strlikatter = strlikatter .. '<br>(' .. contabmisc[1] .. ')' -- "does not exist"
    end--if
    numjumlah = lfwkatrocl (strkattnamn) -- discard 1 of 2 results, both can be -1
    strlikatter = strlikatter .. '<br>' .. lfinumberinkat(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 1 maybe expensive check

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

-- Depends on functions :
-- [W] lfwifexist

-- 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 = lfwifexist (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 LFYDOONELANG

-- Input  : * strsitelangcode -- site langcode
--          * strpykkatns strpykindns strpykapxns
--          * stronecy -- processed langcode /cy/
--          * tabonelang -- ZERO-based
--          * tabxctl
--          * tabxkol -- "d01" ... "d13"
--          * numrowindex
--          * numjumlahel -- number or available columns / elements
--          * boorandom
--          * tabxevalu -- updated and returned

-- Modif  : * qtabbunch qstrret (no flush)

-- Depends on functions :
-- [Y] lfykattlaenk
-- [H] lfhoptconcat lfhbrewerrsvr
-- [I] lfivalidatestr lfivalidatequack

local function lfydoonelang (strsitelangcode, strpykkatns, strpykindns, strpykapxns, stronecy, tabonelang, tabxctl, tabxkol, numrowindex, numjumlahel, boorandom, tabxevalu)

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

  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
  local numtamp = 0
  local numoct = 0

  local booc0bad   = false  -- true if len /c0/ out of rng, even "-" NOT valid
  local booc1bad   = false  -- Q-item
  local booc2bad   = false  -- propralingve
  local booc3bad   = false  -- 2-litera -- true if bad
  local booc4bad   = false  -- 3-litera -- true if bad
  local booc6c7bad = false  -- true if /c6/ or /c7/ is bad
  local booc8bad   = false  -- /c8/ ipedia article -- true if bad

  local boolocerr = false
  local bootjmp = false

      -- begin /d00/ -- index number and lang code
      strbkn = contabwarna['bkgud'] -- color code without "#" cross
      if (stronecy=='eo') then
        strbkn = '90FF90' -- green decoration
      end--if
      strdeste = '(' .. tostring (numrowindex) .. ') <b>' .. stronecy .. '</b>'
        if (numjumlahel==0) then
          numlokera = 55 -- this criminal, abort table generation after row
        else
          if (numjumlahel<11) then -- expect all /c0/ to /ca/
            strbkn = contabwarna['reddi']
            strdeste = strdeste .. '<br>' .. lfhbrewerrsvr(35,0) -- err (#E35 mini)
            tabxevalu.errtru = tabxevalu.errtru + 1 -- this is NOT fatal (not "kec" either)
          end--if
          if (numjumlahel>11) then
            strbkn = contabwarna['merah']
            strdeste = strdeste .. '<br>' .. lfhbrewerrsvr(36,1) -- err (#E36 lagom)
            tabxevalu.errtlo = tabxevalu.errtlo + 1 -- this is NOT fatal (but lagom)
          end--if
          if (numjumlahel~=11) then
            strdeste = strdeste .. ' (' .. tostring(numjumlahel) .. ')'
          end--if
        end--if (numjumlahel==0) else
      if (numlokera~=0) then
        strbkn = contabwarna['merah']
      end--if
      strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme with color
      lfhoptconcat (strt3d .. strdeste .. constrtden)
      -- done /d00/

        -- begin /c0/ -- lingvonomo in site language -- no rl-enclos possible
        strtblc0 = tabonelang[0] or '-' -- both /cy/ and /c0/
        strtmp = '[[' .. stronecy .. ']] ' -- add 5 char:s
        numtamp = string.len(strtmp) -- 7 or 8
        booc0bad = (string.len(strtblc0)<(numtamp+1))
        if (not booc0bad) then
          booc0bad = (string.sub(strtblc0,1,numtamp)~=strtmp)
        end--if
        if (not booc0bad) then
          strtblc0 = string.sub(strtblc0,(numtamp+1),-1)
          booc0bad = (not lfivalidatestr(strtblc0,nil,2,contabtunmisc[0],0,0))
        end--if
        if (booc0bad) then
          strtblc0 = '??'
        end--if
        numafstbfcl = string.len (strtblc0) -- no stripping here len befor cli
        strtblc0 = lfgrestrictlen(strtblc0,(contabtunmisc[0]+4)) -- even "-" is bad
        if (numafstbfcl>tabxevalu.maxc0n) then
          tabxevalu.maxc0n = numafstbfcl -- new record established
        end--if
        if (booc0bad) then
          numlokera = 55 -- this criminal, abort table generation after row
        end--if
        -- done /c0/

        tabstuff["LK"] = stronecy -- for "lfiultpl5repl"
        tabstuff["LN"] = strtblc0 -- for "lfiultpl5repl"
        tabstuff["LU"] = lfucaserest(strtblc0,true,false,strsitelangcode) -- langname uppercase

        -- begin "d01" -- lemma page
    if (tabxkol.d01) then
        strbkn = contabwarna['bkgud'] -- preASSume -- color code without "#" cross
        if (booc0bad) then
          strbkn = contabwarna['merah'] -- red
          strdeste = lfevilitem(1,0,strtblc0) -- show the evil -- bad langname
        else
          strtmp = lfiultpl5repl(contabpages[0],tabstuff) -- augment and maybe uc
          strdeste = '[[' .. strtmp .. '|' .. strtblc0 .. ']]' -- wl to lem pg
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d01"

        -- begin /c1/ -- Q-item -- no rl-enclos possible
        strtblc1 = tabonelang[1] or '-'
        strtblc1 = lfgrestrictlen(strtblc1,14) -- valid len range 3...9 including "Q"
        booc1bad = false
        if (strtblc1~="-") then
          booc1bad = not lfivalidatequack(strtblc1) -- sub gives true if valid
        end--if
        -- done /c1/

        -- begin "d02" -- Q-item
    if (tabxkol.d02) then
        strbkn = contabwarna['bkgud'] -- preASSume -- color code without "#" cross
        strdeste = strtblc1 -- preASSume -- already clamped
        if (booc1bad) then
          strbkn = contabwarna['merah'] -- red
          strdeste = lfevilitem(4,1,strtblc1) -- show the evil -- bad Q-item
          numlokera = 55 -- this criminal, abort table generation after row
        else
          if (strtblc1=='-') then -- nice to have, do not abort
            strbkn = contabwarna['reddi'] -- reddish if not available
            tabxevalu.errkec = tabxevalu.errkec + 1
          else
            strdeste = '[[d:' .. strtblc1 .. '|' .. strtblc1 .. ']]' -- link
          end--if
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d02"

        -- begin /c2/ -- propralingve -- rl-enclos possible
        strtblc2 = lfirllrstrip(tabonelang[2] or '-')
        numafstbfcl = string.len (strtblc2) -- len after strip before clamp
        if (numafstbfcl>tabxevalu.maxc2n) then
          tabxevalu.maxc2n = numafstbfcl -- new record established
        end--if
        strtblc2 = lfgrestrictlen(strtblc2,(contabtunmisc[1]+4))
        booc2bad = (not lfivalidatestr(strtblc2,"-",2,contabtunmisc[1],0,0))
        -- done /c2/

        -- begin "d03" -- propralingve
    if (tabxkol.d03) then
        strbkn = contabwarna['bkgud'] -- preASSume -- color code without "#" cross
        strdeste = strtblc2 -- preASSume -- already clamped
        if (booc2bad) then
          strbkn = contabwarna['merah'] -- red
          strdeste = lfevilitem(1,2,strtblc2) -- show the evil -- bad langname
          numlokera = 55 -- this criminal, abort table generation after row
        else
          if (strtblc2=='-') then -- nice to have, do not abort
            strbkn = contabwarna['reddi'] -- reddish if not available
            tabxevalu.errkec = tabxevalu.errkec + 1
          end--if
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d03"

        -- begin "c3" -- constructed and antique status
        strtblc3 = tabonelang[3] or '-'
        strtblc3 = lfgrestrictlen(strtblc3,10) -- valid length 1 only
        -- done "c3"

        -- begin "d04" -- constructed and antique status
    if (tabxkol.d04) then
        strbkn = contabwarna['bkgud'] -- color code without "#" cross
        boolocerr = false
        if (strtblc3=='-') then -- nice to have, do not abort
          strbkn = contabwarna['reddi'] -- reddish
          strdeste = "-"
          tabxevalu.errkec = tabxevalu.errkec + 1
        else
          numtamp = string.len(strtblc3) -- length after clamping
          if (numtamp~=1) then -- length after clamping
            boolocerr = true
          else
            numoct = string.byte (strtblc3,1,1) -- must be "0" ... "3"
            if ((numoct<48) or (numoct>51)) then
              boolocerr = true -- this will become "numlokera = 55"
            else
              if (numoct==49) then
                strbkn = 'D0FFD8' -- greenish
              end--if
              if (numoct==50) then
                strbkn = 'A0FFC0' -- more greenish (but not green)
              end--if
              if (numoct==51) then
                strbkn = 'D0D0D0' -- grey
              end--if
              strdeste = contabd04ap[numoct-47] -- "contabd04ap" is ONE-based
              strdeste = strdeste .. ' (' .. string.char(numoct) .. ')'
            end--if
          end--if (numtamp~=1) else
        end--if (strtblc3=='-') else
        if (boolocerr) then
          strbkn = contabwarna['merah']
          strdeste = lfevilitem(0,3,strtblc3) -- show the evil -- "bad code"
          numlokera = 55 -- this criminal, abort table generation after row
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d04"

        -- begin /c4/ -- 2-letter code -- no rl-enclos possible
        strtblc4 = tabonelang[4] or '-'
        strtblc4 = lfgrestrictlen(strtblc4,10) -- 2-letter code or "--" expected
        if (strtblc4=="--") then
          booc3bad = false -- 2 valid alternative values "-" and "--" exist
        else
          booc3bad = (not lfivalidatestr(strtblc4,"-",2,2,2,3))
        end--if
        -- done /c4/

        -- begin "d05" -- 2-letter code
    if (tabxkol.d05) then
        strbkn = contabwarna['bkgud'] -- color code without "#" cross
        if (booc3bad) then
          strbkn = contabwarna['merah'] -- red
          strdeste = lfevilitem(0,4,strtblc4) -- show the evil -- "bad code"
          numlokera = 55 -- this criminal, abort table generation after row
        else
          strdeste = strtblc4
          if (strtblc4=='-') then -- nice to have
            strbkn = contabwarna['reddi'] -- reddish if no ISO 639-1:2002 code
            tabxevalu.errkec = tabxevalu.errkec + 1
          end--if
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d05"

        -- begin /c5/
        strtblc5 = tabonelang[5] or '-'
        strtblc5 = lfgrestrictlen(strtblc5,10) -- 3-letter code or "---" expected
        if (strtblc5=="---") then
          booc4bad = false -- 2 valid alternative values "-" and "---" exist
        else
          booc4bad = (not lfivalidatestr(strtblc5,"-",3,3,2,3))
        end--if -- nice to have
        -- done /c5/

        -- begin "d06" -- 3-letter code
    if (tabxkol.d06) then
        strbkn = contabwarna['bkgud'] -- color code without "#" cross
        if (booc4bad) then
          strbkn = contabwarna['merah'] -- red
          strdeste = lfevilitem(0,5,strtblc5) -- show the evil -- "bad code"
          numlokera = 55 -- this criminal, abort table generation after row
        else
          strdeste = strtblc5
          if (strtblc5=='-') then -- nice to have
            strbkn = contabwarna['reddi'] -- reddish if no ISO 639-3:2007 code
            tabxevalu.errkec = tabxevalu.errkec + 1
          end--if
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d06"

        -- begin "d07" -- "tln" + "kap" (!!! can be hidden !!!)
    if (tabxkol.d07) then
          strbkn = contabwarna['bkgud'] -- color code without "#" cross
          if (booc0bad) then
            strbkn = contabwarna['merah'] -- red
            strdeste = "??" -- kat (we have already seen bad /c0/)
          else
            strdeste = '' -- crucial
            if (tabxctl.tlnsow) then
              strtpm = lfiultpl5repl(contabpages[5],tabstuff) -- augm maybe uc
              bootjmp,strtpm = lfykattlaenk (strtpm,strpykkatns,false,contabtunmisc['cats'])
              strdeste = strtpm -- "tln"
            end--if (tabxctl.tlnsow) then
            if (tabxctl.tlnsow and tabxctl.kapsow) then
              strdeste = strdeste .. '<br>'
            end--if
            if (tabxctl.kapsow) then
              strtpm = lfiultpl5repl(contabpages[6],tabstuff) -- augm maybe uc
              bootjmp,strtpm = lfykattlaenk (strtpm,strpykkatns,(tabxctl.kapexp and boorandom),contabtunmisc['cats'])
              strdeste = strdeste .. strtpm -- "kap"
              if (not bootjmp) then
                strbkn = contabwarna['reddi'] -- reddish if "kap" kat does not exist
                tabxevalu.miskap = tabxevalu.miskap + 1
              end--if
            end--if (tabxctl.kapsow) then
          end--if
          strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
          lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if (tabxkol.d07) then
        -- done "d07"

        -- begin "d08" -- "vor" + "mal" (!!! can be hidden !!!)
    if (tabxkol.d08) then
          strbkn = contabwarna['bkgud'] -- color code without "#" cross
          if (booc0bad) then
            strbkn = contabwarna['merah'] -- red
            strdeste = "??" -- kat (we have already seen bad /c0/)
          else
            strdeste = '' -- crucial
            if (tabxctl.vorsow) then
              strtpm = lfiultpl5repl(contabpages[3],tabstuff) -- augm maybe uc
              bootjmp,strtpm = lfykattlaenk (strtpm,strpykkatns,false,contabtunmisc['cats'])
              strdeste = strtpm -- "vor"
            end--if (tabxctl.vorsow) then
            if (tabxctl.vorsow and tabxctl.malsow) then
              strdeste = strdeste .. '<br>'
            end--if
            if (tabxctl.malsow) then
              strtpm = lfiultpl5repl(contabpages[4],tabstuff) -- augm maybe uc
              bootjmp,strtpm = lfykattlaenk (strtpm,strpykkatns,(tabxctl.malexp and boorandom),contabtunmisc['cats'])
              strdeste = strdeste .. strtpm -- "mal"
              if (not bootjmp) then
                strbkn = contabwarna['reddi'] -- reddish if "mal" kat does not exist
                tabxevalu.mismal = tabxevalu.mismal + 1
              end--if
            end--if (tabxctl.malsow) then
          end--if
          strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
          lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if (tabxkol.d08) then
        -- done "d08"

        -- begin "d09" -- Appendix + Index (!!! can be hidden !!!)
    if (tabxkol.d09) then
          strbkn = contabwarna['bkgud'] -- color code without "#" cross
          if (booc0bad) then
            strbkn = contabwarna['merah'] -- red
            strdeste = "??" -- non-kat (we have already seen bad /c0/)
          else
            strdeste = '' -- crucial
            if (tabxctl.apxsow) then
              strtpm = lfiultpl5repl(contabpages[1],tabstuff) -- augm maybe uc
              bootjmp,strtpm = lfyapxindlaenk (strtpm,strpykapxns,false)
              strdeste = strtpm -- "apx"
            end--if (tabxctl.apxsow) then
            if (tabxctl.apxsow and tabxctl.indsow) then
              strdeste = strdeste .. '<br>'
            end--if
            if (tabxctl.indsow) then
              strtpm = lfiultpl5repl(contabpages[2],tabstuff) -- augm maybe uc
              bootjmp,strtpm = lfyapxindlaenk (strtpm,strpykindns,(tabxctl.indexp and boorandom))
              strdeste = strdeste .. strtpm -- "ind"
              if (not bootjmp) then
                strbkn = contabwarna['reddi'] -- reddish if "ind" page does not exist
                tabxevalu.misind = tabxevalu.misind + 1
              end--if
            end--if (tabxctl.indsow) then
          end--if
          strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
          lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if (tabxkol.d09) then
        -- done "d09"

        booc6c7bad = false -- common for /c6/ and /c7/

        -- begin /c6/ -- wiki access code -- no rl-enclos possib
        strtblc6 = tabonelang[6] or '-'
        strtblc6 = lfgrestrictlen(strtblc6,16) -- wiki access code
        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
            booc6c7bad = true -- this will become "numlokera = 55"
          end--if
        end--if
        -- done /c6/

        -- begin /c7/ -- wiki availability code -- no rl-enclos possib
        strtblc7 = tabonelang[7] or '-'
        strtblc7 = lfgrestrictlen(strtblc7,10) -- availability 2 digits
        numtamp = string.len(strtblc7) -- length after clamping
        numc7pedi = 3
        numc7tion = 3
        if (strtblc7~="-") then -- nice to have (otherwise reddish !!!)
          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
              booc6c7bad = true -- this will become "numlokera = 55"
            end--if
          else
            booc6c7bad = true -- this will become "numlokera = 55"
          end--if
        end--if
        -- done /c7/

        -- begin "d10" -- other ipedia main page
    if (tabxkol.d10) then
        strbkn = contabwarna['bkgud'] -- color code without "#" cross
        if (booc6c7bad) then
          strbkn = contabwarna['merah']
          strdeste = lfevilitem(0,67,strtblc6) -- show the evil -- "bad code"
          numlokera = 55 -- this criminal, abort table generation after row
        else
          strdeste = lfibrewurl (strtblc6,false,nil) -- other ipedia main page
          if (numc7pedi==3) then
            strbkn = contabwarna['reddi'] -- reddish if "c6" was empty or "-"
          else
            strbkn = lfavailco(numc7pedi)
            strdeste = strdeste .. lfiavailnt(numc7pedi) -- <br>+notice or empty
          end--if (numc7pedi==3) else
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d10"

        -- begin "d11" -- other tionary main page
    if (tabxkol.d11) then
        strbkn = contabwarna['bkgud'] -- color code without "#" cross
        if (booc6c7bad) then
          strbkn = contabwarna['merah']
          strdeste = lfevilitem(0,67,strtblc7) -- show the evil -- "bad code"
          numlokera = 55 -- this criminal, abort table generation after row
        else
          if (stronecy==strsitelangcode) then
           strdeste = ':-)' -- only for "d11" cell "other tionary" and /d13/
          else
            strdeste = lfibrewurl (strtblc6,true,nil) -- other tionary main page
            if (numc7tion==3) then
              strbkn = contabwarna['reddi'] -- reddish if "c6" was empty or "-"
            else
              strbkn = lfavailco(numc7tion)
              strdeste = strdeste .. lfiavailnt(numc7tion) -- <br>+notice or empt
            end--if (numc7tion==3) else
          end--if
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done "d11"

        -- begin /c8/ -- article in own wikipedia
        strtblc8 = tabonelang[8] or '-' -- no rl-enclos possib
        booc8bad = (not lfivalidatestr(strtblc8,"-",2,32,0,0))
        strtblc8 = lfgrestrictlen(strtblc8,36) -- name of ipedia article
        -- done /c8/

        -- begin /d12/ -- article in own wikipedia
    if (tabxkol.d12) then
        strbkn = contabwarna['bkgud'] -- color code without "#" cross
        if (booc8bad) then -- /c8/ is bad
          strbkn = contabwarna['merah'] -- red
          strdeste = lfevilitem(2,8,strtblc8) -- show the evil -- "bad title"
          numlokera = 55 -- this criminal, abort table generation after row
        else
          if ((strtblc8=='-') and booc0bad) then -- nice to have /c8/
            strbkn = contabwarna['merah'] -- red if we have neither /c0/ nor /c8/
            strdeste = '-'
          else
            strtpm = strtblc8 -- name of wp article, BEWARE: may contain space
            bootjmp = false
            if (strtpm=='-') then
              bootjmp = true -- this is semi-evil, we have to guess
              strtpm = lfiultpl5repl(contabpages[7],tabstuff) -- augment and maybe uc
            end--if
            strdeste = lfibrewurl (strsitelangcode,false,strtpm) -- own ipedia article
            if (bootjmp) then
              strbkn = contabwarna['reddi'] -- reddish if we had to guess
              strdeste = strdeste .. ' (??)'
              tabxevalu.errkec = tabxevalu.errkec + 1
            end--if
          end--if
        end--if
        strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "<td>" eleme
        lfhoptconcat (strt3d .. strdeste .. constrtden)
    end--if
        -- done /d12/

        -- begin /c9/ -- cat in other tionary
        strtblc9 = lfirllrstrip(tabonelang[9] or '-') -- rl-enclos is possib
        numafstbfcl = string.len(strtblc9) -- length after strip before clamp
        if (numafstbfcl>tabxevalu.maxkal) then
          tabxevalu.maxkal = numafstbfcl -- new record establishd, beware of contabtunmisc[2]
        end--if
        strtblc9 = lfgrestrictlen(strtblc9,(contabtunmisc[2]+4))
        -- done /c9/

        -- begin /ca/ -- number of lemmas in that cat -- no rl-enclos possib
        strtblca = tabonelang[10] or '-'
        numafstbfkp = string.len(strtblca) -- length before clamping
        strtblca = lfgrestrictlen(strtblca,12)
        -- done /ca/

        -- begin /d13/ -- cat in other tionary with number of lemmas
    if (tabxkol.d13) then
        do -- scope
          local numtaamp  = 0
          local numtuump  = 0
          local boolocerx = false -- local error flag
          local boolocery = false -- other local error flag
          strbkn = contabwarna['bkgud'] -- color code without "#" cross
        if (strtblc9~="-") then -- do NOT evaluate evilness of "-" in "c9"
          if ((numafstbfcl<8) or (numafstbfcl>contabtunmisc[2])) then -- "c9" bad
            boolocerx = true -- length out of range
          else
            numtaamp = lfcountchr(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,2)~=0) then -- /ca/ bad  !!!FIXME!!!
              boolocery = true -- faulty char:s, a number is expected
            end--if
          end--if
        end--if
        if (boolocerx or boolocery) then
          strbkn = contabwarna['merah'] -- red if crime in "c9" or /ca/
          strdeste = '' -- preassign to empty, may be needed below
          if (boolocerx) then
            strdeste = lfevilitem(3,9,strtblc9) -- show evil "bad category"
          end--if
          if (boolocerx and boolocery) then
            strdeste = strdeste .. '<br>and<br>' -- damn: 2 crimi err in 1 row
          end--if
          if (boolocery) then
            strdeste = strdeste .. lfevilitem(5,10,strtblca) -- ev "bad number"
          end--if
          numlokera = 55 -- this criminal, abort table generation after row
        else
          if (stronecy==strsitelangcode) then
            strdeste = ':-)' -- only for "d11" cell "other tionary" and /d13/
          else
            if (strtblc9=='-') then
              strbkn = contabwarna['reddi'] -- reddish if no category
              strdeste = '-'
              tabxevalu.errkec = tabxevalu.errkec + 1
            else
              strdeste = lfibrewurl (strtblc6,true,strtblc9) -- other tionary cat
              strdeste = strdeste .. '<br>'
              if (strtblca=='-') then
                strdeste = strdeste .. lfinumberinkat(-1) -- NOT count as error
              else
                numtuump = tonumber(lfremovenondi(strtblca)) -- apo:s tolerable
                strdeste = strdeste .. lfinumberinkat(numtuump) -- apo:s added
              end--if (strtblca=='-') else
            end--if (strtblc9=='-') else
          end--if (stronecy==strsitelangcode) else
        end--if (boolocerx or boolocery) else
          strt3d = contabhtml['tdbgco'] .. strbkn .. constrtdfd -- long "td" eleme
          lfhoptconcat (strt3d .. strdeste .. constrtden)
        end--do scope
    end--if
        -- done /d13/

  return numlokera, tabxevalu

end--function lfydoonelang

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

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

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

function exporttable.ek (arxframent)

  -- general unknown type ie "var"

  local vartomp = 0

  -- special type "args" AKA "arx"

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

  -- stuff from incoming table (komp YES sng NO)

  local num0statcode = 0
  local num1lenfullo = 0
  local num2lenstrip = 0
  local num3errposit = 0
  local num4lineslin = 0

  local str5errorstr = ''
  local str6errorstr = ''
  local str7errorstr = ''

  local tablg75enume  = {}
  local tablg77comlin = {}

  -- general "tab"

  local tabctl = {} -- tlnsow tlnexp kapsow kapexp vorsow vorexp malsow malexp apxsow apxexp indsow indexp
  local tabkol = {} -- columns "d01" ... "d13"
  local tabevalu = {} -- evaluation min max nonfatal (komp YES sng NO)
  local tabtempi  = {} -- temp (komp YES sng NO)

  -- peeked stuff

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

  -- general "str"

  local strinfsel    = '' -- incoming parameter with 6 digi, reported at the end
  local strpurge     = '' -- purge quasi-external link complete with "[" and "]"
  local strtblcy     = '' -- y-index marker in main loop
  local strblogudbad = '' -- good or bad block, but always 2 times
  local strblosuma   = '' -- technical summary block
  local strtopm      = ''

  -- general "num"

  local numerr       = 0    -- 0 OK | 1 inter | 2 sub fail | 3 sub statuscode
  local num2statcode = 0
  local numproba     = 100  -- probability from arxourown[3] (komp YES sng NO)
  local numminwidth  = 10   -- !!!FIXME!!!
  local nummaxwidth  = 1000 -- !!!FIXME!!!
  local numofft      = 0
  local numrowinx    = 0

  -- general "boo"

  local boosummar  = false  -- from arxourown[2] do show on success
  local boodofatal = false
  local boohavtbl  = false
  local boorussia  = false  -- toss a coin for every row from "numproba"
  local bootimp    = false

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

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

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

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

  lfdtracemsg ('This is module "bigtable-tbllingvoj", 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 (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

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

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

  ---- PICK T75 T77 ----

  -- we do NOT have "nummaxwidth" yet  !!!FIXME!!!
  -- spec allows 32 ... 8'000'000 but here we restrict to 320...1'000'000

  while true do -- fake loop

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

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

    if (num2statcode~=0) then
      lfdtracemsg ("Submodule failed with error code " .. tostring (num0statcode))
    end--if

    num1lenfullo = qldingvoj[3]
    if ((num1lenfullo>=320) and (num1lenfullo<=1000000)) then
      lfdtracemsg ("Passed 320...1'000'000 check with length " .. tostring (num1lenfullo))
    else
      lfdtracemsg ("Failed 320...1'000'000 check with length " .. tostring (num1lenfullo))
      numerr = 32 -- #E32
      break -- to join mark
    end--if

    num2lenstrip = qldingvoj[4]
    lfdtracemsg ('FYI: num2lenstrip=' .. tostring (num2lenstrip))
    num3errposit = qldingvoj[5]
    lfdtracemsg ('FYI: num3errposit=' .. tostring (num3errposit))
    num4lineslin = qldingvoj[6]
    lfdtracemsg ('FYI: num4lineslin=' .. tostring (num4lineslin))

    str5errorstr = qldingvoj[7]
    lfdtracemsg ('FYI: str5errorstr="' .. tostring (str5errorstr) .. '"')
    str6errorstr = qldingvoj[8]
    lfdtracemsg ('FYI: str6errorstr="' .. tostring (str6errorstr) .. '"')
    str7errorstr = qldingvoj[9]
    lfdtracemsg ('FYI: str7errorstr="' .. tostring (str7errorstr) .. '"')

    tablg75enume  = qldingvoj['T75'] -- enumeration
    tablg77comlin = qldingvoj['T77'] -- complete line NOT pluprocessed
    if ((type(tablg75enume)~='table') or (type(tablg77comlin)~='table')) then -- important check
      lfdtracemsg ('Submodule failed to deliver T75 T77')
      numerr = 33 -- #E33
      break -- to join mark
    end--if

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

  lfdtracemsg ('Picking of T75 T77 done, numerr=' .. tostring(numerr) .. ', num2statcode=' .. tostring(num2statcode))

  ---- SEIZE 0 ... 2 PARAMETERS ----

  -- optional 1 digit into 1 boolean (default "false")
  -- optional 2 digits into 1 num (default 100 ie 100% ie 1) (komp YES sng NO)

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

  if (arxourown[3]) then
    numerr = 8 -- #E08 number of anon param
  end--if

  if (numerr==0) then
    vartomp = arxourown[1] -- optional, one binary digit expected "0" or "1"
    if (type(vartomp)=='string') then
      if (string.len(vartomp)==1) then
        numofft = string.byte (vartomp,1,1) -- "boosummar" preassigned to false
        boosummar = (numofft==49) -- show technical summary block
        if ((not boosummar) and (numofft~=48)) then
          numerr = 13 -- #E13 anon param {{{1}}}
        end--if
      else
        numerr = 13 -- #E13 anon param {{{1}}}
      end--if
    end--if
  end--if

  if (numerr==0) then
    vartomp = arxourown[2] -- optional, 2 decimal digits "02"..."98" expected
    if (type(vartomp)=='string') then
      numproba = lfstrtwdig2int(vartomp)
      if ((numproba<2) or (numproba>98)) then
        numerr = 14 -- #E14 anon param {{{2}}}
      end--if
    end--if
  end--if

  lfdtracemsg ('Small seizure done, numproba=' .. tostring(numproba) .. ', numerr=' .. tostring(numerr))

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

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

  -- in "infsel=" expecting 6 decimal digits, value "2" accepted in some posi

  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 -- param missing
        end--if
        strinfsel = varselinf
        numsilur = lfivaliumdctlstr ('121212',strinfsel)
        if (numsilur~=255) then
          numerr = 17 -- #E17 named param "infsel=" invalid
          break -- less than 255 is bad -- param invalid
        end--if
        numsilur = lfdec1digit(string.byte(strinfsel,1,1)) -- "tln"
        tabctl.tlnsow = (numsilur~=0) -- show tln in /d07/
        tabctl.tlnexp = (numsilur==2) -- query tln in /d07/  !!!FIXME!!! no query
        numsilur = lfdec1digit(string.byte(strinfsel,2,2)) -- "kap"
        tabctl.kapsow = (numsilur~=0) -- show kap in /d07/
        tabctl.kapexp = (numsilur==2) -- query kap in /d07/
        numsilur = lfdec1digit(string.byte(strinfsel,3,3)) -- "vor"
        tabctl.vorsow = (numsilur~=0) -- show vor in d08
        tabctl.vorexp = (numsilur==2) -- query vor in d08  !!!FIXME!!! no query
        numsilur = lfdec1digit(string.byte(strinfsel,4,4)) -- "mal"
        tabctl.malsow = (numsilur~=0) -- show mal in d08
        tabctl.malexp = (numsilur==2) -- query mal in d08
        numsilur = lfdec1digit(string.byte(strinfsel,5,5)) -- "apx"
        tabctl.apxsow = (numsilur~=0) -- show apx
        tabctl.apxexp = (numsilur==2) -- query apx  !!!FIXME!!! no query
        numsilur = lfdec1digit(string.byte(strinfsel,6,6)) -- "ind"
        tabctl.indsow = (numsilur~=0) -- show ind
        tabctl.indexp = (numsilur==2) -- query ind
        break -- finally to join mark
      end--while -- fake loop -- join mark
    end--do scope
  end--if

  lfdtracemsg ('Big seizure done, strinfsel=' .. strinfsel .. ', numerr=' .. tostring(numerr))

  ---- 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
    lfdtracemsg ('Seized "detrc=true"')
  else
    qboodetrc = false -- was preassigned to "true"
    qstrtrace = '' -- shut up now
  end--if

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

  ---- PROCESS MESSAGES ----

  contaberaroj  = lfhfillsurrstrtab(contaberaroj, nil, strpiklangcode, nil)
  contabd10d11w = lfhfillsurrstrtab(contabd10d11w, nil, strpiklangcode, nil) -- 1,2 -- "fermita aux malplena"
  contabdestit  = lfhfillsurrstrtab(contabdestit, nil, strpiklangcode, nil)  -- 0...13 -- table headings
  contabpages   = lfhfillsurrstrtab(contabpages, nil, strpiklangcode, nil)   -- 0...7 -- "Sxablonaro -@LK00-"
  contabdoubled = lfhfillsurrstrtab(contabdoubled, nil, strpiklangcode, nil) -- 0...6 -- for doubled block

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

  -- we use contabdoubled[5] with one complete sentence

  strtopm = 'Special:Recent changes' -- bad default
  vartomp = mw.title.getCurrentTitle().prefixedText -- with namespace prefix !!
  if (lfgstringrange(vartomp,1,255)) then
    strtopm = vartomp -- fullpagename here (otherwise "Special:Recent changes")
  end--if
  strpurge = lfwbrew3url (arxframent,strtopm,'action=purge',contabdoubled[5],true)

  ---- CHECK THE 0:TH INDEX ----

  -- check index ZERO against "strpiklangcode" -- must be equal site lang

  if ((numerr==0) and (tablg75enume[0]~=strpiklangcode)) then
    numerr = 1 -- #E01 internal  !!!FIXME!!!
  end--if

  lfdtracemsg ('ZERO:th index 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

    numrowinx = 0 -- main index and final count

    tabevalu.minwyd = nummaxwidth -- evaluated min width, record will sink
    tabevalu.maxwyd = 0 -- evaluated max width, record will grow
    tabevalu.maxc0n = 0 -- evaluated max bloat of language name site (c0)
    tabevalu.maxc2n = 0 -- evaluated max bloat of language name prop (c2)
    tabevalu.maxkal = 0 -- evaluated max bloat of category link (limit hard)

    tabevalu.errtru = 0 -- valid truncated lines in source table (minor)
    tabevalu.errtlo = 0 -- too long lines in source table
    tabevalu.errkec = 0 -- minor error (not fatal)

    tabevalu.mistln = 0 -- missing "tln" top lng cat (not fatal) !!!FIXME!!!
    tabevalu.miskap = 0 -- missing "kap" (not fatal)
    tabevalu.misvor = 0 -- missing "vor" (not fatal) !!!FIXME!!!
    tabevalu.mismal = 0 -- missing "mal" (not fatal)
    tabevalu.misapx = 0 -- missing "apx" appendix page (not fatal) !!!FIXME!!!
    tabevalu.misind = 0 -- missing "ind" index page (not fatal)

  end--if

  ---- BEGIN TO GENERATE THE OUTPUT TABLE (14 COLUMNS) ----

  -- using "contabhtml['tdbgti']" and "constrtden" for "<td"...">" and "</td>"

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

  -- here using 14 times function "lfhbrewtitsel" NOT
  -- adding "<td"...">" "</td>"

  if (numerr==0) then
    boohavtbl = true
    lfhoptconcat (contabhtml['hugbeg']) -- "<table"...">" and "<tr>"
    do -- scope
      local numtaymp = 0 -- ZERO-based index
      local booxxtop = false
      local booxxbtm = false
      while true do -- iterate over table columns
        if (numtaymp==14) then -- valid indexes 0...13
          break
        end--if
        booxxtop = true -- preASSume
        booxxbtm = true -- preASSume
        if (numtaymp==7) then -- column "d07" "tln" "kap" can be hidden
          booxxtop = tabctl.tlnsow
          booxxbtm = tabctl.kapsow
        end--if
        if (numtaymp==8) then -- column "d08" "vor" "mal" can be hidden
          booxxtop = tabctl.vorsow
          booxxbtm = tabctl.malsow
        end--if
        if (numtaymp==9) then -- column "d09" "apx" "ind" can be hidden
          booxxtop = tabctl.apxsow
          booxxbtm = tabctl.indsow
        end--if
        if ((numtaymp==0) or (tabkol['d'..lfnumto2digit(numtaymp)])) then
          lfhoptconcat (contabhtml['tdbgti'] .. lfhbrewtitsel (numtaymp,booxxtop,booxxbtm) .. constrtden) -- submit table cell
        end--if
        numtaymp = numtaymp + 1 -- count even hidden columns
      end--while
      lfhoptconcat ('</tr>') -- close row but keep table open
    end--do scope
  end--if

  ---- MAIN BLOCK OF TABLE DATA AND MAIN LOOP ----

  -- from above:
  -- * numrowinx = 0
  -- * strpiklangcode ...
  -- * tablg75enume tablg77comlin
  -- * tabctl (from "infsel=")
  -- * tabkol
  -- * tabevalu (komp YES sng NO)

  if (numerr==0) then

    do -- scope

      local tabsplout = {} -- out from "lfhsplittotable", short-lived
      local strsrcrow = '' -- complete row from the source table
      local numsplsta = 0 -- out from "lfhsplittotable", short-lived
      local numspljum = 0 -- out from "lfhsplittotable", fed into "lfydoonelang"

      while true do -- iterate over all table rows

        strtblcy = tablg75enume[numrowinx] -- get langcode
        strsrcrow = tablg77comlin[strtblcy] -- get complete line
        if ((type(strtblcy)~='string') or (type(strsrcrow)~='string')) then
          lfdtracemsg ('End of table reached, numrowinx=' .. tostring (numrowinx))
          break
        end--if
        lfdtracemsg ('About to begin a new row, numrowinx=' .. tostring(numrowinx) .. ', strtblcy=' .. strtblcy)
        numsplsta,numspljum,tabsplout = lfhsplittotable (strsrcrow,44,'=',2,12)
        lfdtracemsg ('Split of complete line done: numsplsta=' .. tostring (numsplsta) .. ' , numspljum=' .. tostring (numspljum))
        if (numsplsta~=0) then
          numerr = 55 -- this criminal, abort table generation after row
        end--if

        boorussia = (math.random(1,100)<=numproba) -- Russian roulette, limit 2...98 or 100
        if (numerr~=0) then
          tabkol = {} -- /d00/ is obligatory, remove all other columns
        end--if
        lfhoptconcat ('<tr>')
        numerr, tabevalu = lfydoonelang (strpiklangcode, strpikkatns, strpikindns, strpikapxns, strtblcy, tabsplout, tabctl, tabkol, numrowinx, numspljum, boorussia, tabevalu)
        lfhoptconcat ('</tr>')
        if (numerr~=0) then
          break -- something is evil, abort now that table row is finished
        end--if

        numrowinx = numrowinx + 1 -- main ZERO-based counter

      end--while

    end--do scope

  end--if

  ---- CLOSE THE OUTPUT TABLE IF IT EXISTS AND WHINE IF NEEDED ----

  if (numerr~=0) then
    qtabbunch = {} -- crucial
    if (num0statcode==0) then
      lfhoptconcat (lfhbrewerrsvr(numerr,2)) -- huge error
    else
      lfhoptconcat (lfhbrewerrsvr((num0statcode+100),2)) -- #E02...#E79 from submodule, huge  !!!FIXME!!!
    end--if
  else
    if (boohavtbl) then
      lfhoptconcat ('</table>')
    end--if
    if (numerr~=0) then
      lfhoptconcat (lfhbrewerrsvr(37,2)) -- #E37 outside the table
    end--if
  end--if

  lfhoptconcat (nil) -- crucial flush to "qstrret"
  boodofatal = (numerr~=0)

  ---- BREW DOUBLED BLOCK ----

  -- * block is displayed 2 times (above and below) for
  --   both cases (fatal and success)
  -- * block is enclosed in a "div" box for both cases (fatal and success)
  -- * text align is center for both cases (fatal and success)
  -- * style heavily depends on "boodofatal" fatality
  --   * font size 200% (fatal) or 100% (success) (default)
  --   * border and text color red (fatal) or black (success) (default)
  --   * border thick (fatal) or minimal (success)

  if (boodofatal) then
    strblogudbad = '<div style="margin:0.5em auto 0.5em auto;padding:0.5em;border:0.5em solid #FF0060;text-align:center;font-size:200%;color:#FF0060;">'
    strblogudbad = strblogudbad .. contabdoubled[0] .. '. ' .. strpurge .. ' ' .. contabdoubled[1]
    strblogudbad = strblogudbad .. ' ' .. contabdoubled[6] .. ' ' .. contabdoubled[2] .. '. ' .. contabdoubled[3] .. '.</div>'
  else
    strblogudbad = '<div style="margin:0.5em auto 0.5em auto;padding:0.5em;border:1px solid #000000;text-align:center;">'
    strblogudbad = strblogudbad .. strpurge .. ' ' .. contabdoubled[4] .. ' ' .. contabdoubled[6] .. '.</div>'
  end--if

  ---- BREW TECHNICAL SUMMARY BLOCK ----

  -- * technical summary block is enclosed in a single-cell table since
  --   a "div" box would fill the complete screen width
  -- * using a "ul/li" type list (we use "lfhtabletohtmllisnt", note
  --   that wikicode "*" would NOT work here)
  -- * font size 80%, text color black (default), text align left (default)

  -- * we use "lfnumtodecbun" for numbers with risk
  --   of >= 1'000 and partially "lftostwauc" and "lftostwaz" and "lftostw2p"

  if (boodofatal or boosummar) then

    tabtempi[ 0] = contabsum[0] .. '"' .. strinfsel .. '", ' .. tostring(numproba) -- 2 parameters (one of them is optional)
    tabtempi[ 1] = contabsum[1] .. lfnumtodecbun(num1lenfullo) .. ' -> ' .. lfnumtodecbun(num2lenstrip) -- source
    tabtempi[ 2] = contabsum[2] .. lfnumtodecbun(numrowinx) -- lang
    tabtempi[ 3] = contabsum[3] .. lftostwaz(0) -- sort !!!FIXME!!!
    tabtempi[ 4] = contabsum[4] .. lfnumtodecbun(tabevalu.errtru) -- trunc (ele)
    tabtempi[ 5] = contabsum[5] .. lftostwaz(tabevalu.errtlo) -- too long (ele)
    tabtempi[ 6] = 'errkec : ' .. lfnumtodecbun(tabevalu.errkec) -- minor
    tabtempi[ 7] = 'errkap : ' .. lfnumtodecbun(tabevalu.miskap) -- missing "kap" cat
    tabtempi[ 8] = 'errmal : ' .. lfnumtodecbun(tabevalu.mismal) -- missing "mal" cat
    tabtempi[ 9] = 'errind : ' .. lfnumtodecbun(tabevalu.misind) -- missing "ind" page
    tabtempi[10] = 'srctblbeg : ' .. lfnumtodecbun(0) -- bloat !!!FIXME!!! area
    tabtempi[11] = 'srctblend : ' .. lfnumtodecbun(0) -- bloat !!!FIXME!!! area

    strtopm = 'srctblper : ' -- earliest abort syntax err
    if (numerr~=0) then
      strtopm = strtopm .. lftostwauc(0) -- !!!FIXME!!! position
    else
      strtopm = strtopm .. 'N/A'
    end--if
    tabtempi[12] = strtopm

    tabtempi[13] = 'eval minwid, limit minwid : ' .. tostring(tabevalu.minwyd) .. ', ' .. tostring(numminwidth) -- minwidth
    tabtempi[14] = 'eval maxwid, limit maxwid : ' .. tostring(tabevalu.maxwyd) .. ', ' .. tostring(nummaxwidth) -- maxwidth
    tabtempi[15] = 'eval maxc0lngname, limit maxc0lngname : ' .. tostring(tabevalu.maxc0n) .. ', ' .. tostring(contabtunmisc[0])
    tabtempi[16] = 'eval maxc2lngname, limit maxc2lngname : ' .. tostring(tabevalu.maxc2n) .. ', ' .. tostring(contabtunmisc[1])
    tabtempi[17] = 'eval maxkatlink, limit maxkatlink : ' .. tostring(tabevalu.maxkal) .. ', ' .. tostring(contabtunmisc[2])

    tabtempi[18] = 'total-pik : ' .. lftostw2p(qtcostquery[3],499) -- total "pagesincategory"
    tabtempi[19] = 'total-ife : ' .. lftostw2p(qtcostquery[6],499) -- total "ifexist"
    tabtempi[20] = 'tight-fisted-ife : ' .. lftostw2p(qtcostquery[8],499) -- tight-fisted "ifexist"
    tabtempi[21] = 'dsttbl-siz : ' .. lfnumtodecbun(string.len(qstrret)) -- dst bloat

    strblosuma = contabhtml['tekbeg'] .. lfhtabletohtmllisnt(tabtempi) .. contabhtml['tekend']
    tabtempi = {}

  end--if

  ---- BREW THE FINAL HUGE STRING ----

  if (boodofatal or boosummar) then
    qstrret = qstrret .. strblosuma -- add technical summary block one time below
  end--if
  qstrret = strblogudbad .. qstrret .. strblogudbad -- add big fatal or tiny success block 2 times

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

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

end--function

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

return exporttable