Saltu al enhavo

Modulo:ind12dim

El Vikivortaro
 MODULO
Memtesto disponeblas sur la dokumentaĵa subpaĝo.
Ĉi tiu modulo estas vokata el {{eht}}, {{ind12dim}}, nur suba {{eht-kat-alfa}} (nerekte pere de "ind12dim"), {{eht-paĝo-alfa}} (nerekte pere de "ind12dim").
Ĉi tiu modulo estas uzata sur la ĉefpaĝo.
Se vi konas la eblajn sekvojn, tiam vi povas zorgeme ekredakti.
Se vi ne kuraĝas redakti tiam vi povas proponi la deziratan ŝanĝon en la diskutejo.

--[===[

MODULE "IND12DIM" (index 1 or 2 dimensions)

"eo.wiktionary.org/wiki/Modulo:ind12dim" <!--2025-Aug-13-->
"id.wiktionary.org/wiki/Modul:ind12dim"

Purpose: creates a 1-dimensional or 2-dimensional index list either inside
         table or without table, for a category or "Special:AllPages" or
         "Special:Prefixindex", with various types of links (with
         "from=" queries or to a section "#"... or with raw
         letter postfix)

Utilo: kreas 1-dimensian aux 2-dimensian enhavsuperrigardon aux ene de
       tabelo aux sen tabelo, por kategorio aux "Special:AllPages" aux
       "Special:Prefixindex", kun diversaj tipoj ...

Manfaat: membuat daftar indeks berdimensi 1 atau 2 baik dalam
         tabel atau tanpa tabel, untuk kategori atau ...

Syfte: skapar en 1-dimensionell eller 2-dimensionell indexlista antingen inom
       tabell eller utan tabell, foer ...

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaend av mallar:
* EO: "eht-kat-alfa" called from "eht" (both obsolete),
      "eht-pagxo-alfa", "ind12dim"

Required submodules / Bezonataj submoduloj: none / neniuj

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

Incoming: * 11 ordinary named and optional parameters (empty parameters are
            treated as exactly equivalent to non-existent ones, this allows
            empty forwarding like "tit={{{tit|}}}")
            * "fpn=" -- target fullpagename to link to from index items,
              either empty, or 5...100 octet:s, special value "fpn=-"
              for section links (same page), fullpagename read out from
              wiki alt taken from "fullpanaoverridetestonly=" if not
              in "fpn=", see below
            - "tit=" -- title of the table (empty or 5...120 octet:s, for
              example <<INDEKSO DE KATEGORIO "SVEDA">>, copy of parameter
              "fpn" (in turn received from caller or peeked fullpagename)
              taken if this parameter is missing or empty, or hardcoded
              text "Index" if "fpn=-", special value "-" to suppress the
              table in favor of a raw list, then the caller must
              provide <<class="plainlinks">>, no "<b>"
              and no "'''" (ie bolding) and no links tolerable
              in this parameter)
            - "dud=1" -- causes a 2-dimensional table to be
              created (default is 1-dimensional, "dud=1" is prohibited
              with "tit=-", "dud=1" is prohibited with base type
              (from "typ=") other than 0 or 1 or 5 or 6, conversely base
              types 5 and 6 require "dud=1")
            * "typ=" -- type of link, either empty, or control string with
              3 digits for base type and tuning, by default base type is
              auto-guessed from "fpn=" and no tuning, see below
            - "mwp=1" -- causes <<#mw-pages>> to be attached to the URL
              (default is false, "mwp=1" works and is tolerable only with
              categories, does NOT work with "Special:AllPages")
            - "ans=" -- namespace for "Special:AllPages" (empty or 1...5
              DEC digits 0...5'000, attached to the request as for example
              "namespace=108", default is empty resulting in main ie NS=0,
              tolerable only for "Special:", works only (??) for "AllPages")
            - "alf=" -- alphabet (empty or 5...400 octet:s, default is
              "ABCDEFGHIJKLMNOPQRSTUVWXYZ", may contain uppercase letters
              A...Z, numbers 0...9, dash "-", apo "'", and unicode, may NOT
              contain "~" nor other non-letters, lowercase is prohibited for
              categories, order is irrelevant but may NOT contain dupes,
              special syntax with brackets "ijkl(ll)mn" to define a
              "multi-char letter" for some silly languages or special cases,
              must contain 2...9 octet:s and 2...9 UTF8 char:s, at the
              end the alphabet must contain at least 3 elements AKA
              UTF8 char:s or "multi-char letter":s)
            - "fla=" -- two extra links "top" and "bottom" (for link type 0
                or 1 better named as "all" and remainder")
              - default if the parameter is not given is to auto-guess:
                - for base type (from "typ=") of 0 or 1: show them both with
                  visible texts "all" and "remainder" and link values "^"
                  and "~" (equivalent four fragments "^", "all", "~",
                  "remainder" ie parameter "fla=^,all,~,remainder")
                - for base type (from "typ=") of 2...6 : suppress them both
               - possible values:
                - "-" suppress them both
                - two fragments (totally 5...80 octet:s), string must contain
                  exactly one comma "," and none of the substrings must be
                  empty, two visible texts, for example ("cxio,restajxo" or
                  "semua,tertinggal" or "allt,rest"), link values will be:
                  - for base "typ=" of 0 or 1 : "^" (empty) and "~"
                  - for base "typ=" of 2...6 : copies of the visible texts
                - four fragments (totally 5...80 octet:s), string must contain
                  exactly three commas and none of the substrings must be
                  empty, links values and visible texts, intended for base
                  "typ=" of 1, the order is:
                  - link value top (special value "^" for empty)
                  - visible text top
                  - link value bottom
                  - visible text bottom
                note that "^" and "~" can be used for link types 0 and
                1 only, they are prohibited even for type 5 (combo of 1+3)
            - "ctb=" -- background colour theme of the table (values 0...5 for
              6 permutations of RGB, default is 0, prohibited with "tit=-")
            * "cos=" -- control string, 6 ext hex digits (tuning of text
              colour, (size), enclosing and separators, default "000-00",
              can be used even with "tit=-")
              * 6 positions
                * (pos 0) font colour ("0" ... "L", always, see below)
                * (pos 1) text background colour ("0" ... "L",
                  always, see below)
                * (pos 2) enclosing control ("0" or "1", always)
                  * "0" -- add [] brackets
                  * "1" -- do not add them
                * (pos 3) separator "-"
                * (pos 4) "0"..."6" type of separator in list (default
                  NBSP, not 2-dim see below)
                  * 0 -- NBSP
                  * 1...6 -- other types
                * (pos 5) enhancement of the separator in list (not valid for
                  separator type "0", default no enhancement, not 2-dim
                  see below)
                  * "0" no enhancement
                  * "1" enhance with NBSP on both sides
              * about positions 0 and 1
                * always applicable 2 colours
                  * 0 default
                  * 1...3 RGB, 4...6 CMY, 7 grey (range 1...7)
                  * add 7 for light (range 8...14/E)
                  * add 14 for dark (range 15/F...21/L)
                * those 2 colour values MUST NOT be both non-ZERO and same
              * about positions 4 and 5
                * applicable in 1-dimensional table or if table suppressed
                  with "tit=-", else (in 2-dimensional table) ignored
            * "pre=" -- prefix for items of the alphabet, 1...9 octet:s
              (9 octet:s can be as few as 2 UTF8 char:s + 1 ASCII char),
              long prefixes (more than 2 UTF8 char:s) are nonrecommended
          * notes:
            * parameters "dud=1" and "mwp=1" are boolean
            * parameters "typ=" and "cos=" are control strings
          * restrictions:
            * "mwp=1" and "ans=" can NEVER be used at same time
            * "ans=" requires base link type ZERO, "typ=000" can be
               used but is redundant, no other value than "000" is legal
            * "pre=" is prohibited together with "dud=1"
          * one hidden
            * "fullpanaoverridetestonly="

Returned: * large text with wikicode for the desired index list

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

This module is unbreakable (when called with correct module name
and function name).

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio).

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

Terminology:
- "http link": link brewed via "mw.uri.canonicalUrl" due to query part
  but here always remaining inside the same wiki, to other or the same
  already displayed page, inside single brackets, space as separator, can
  be a "selflink" at same time
- "selflink": link the the page itself with a query part making it useful,
  special type of "http link", there is no point with a "wiki link" to
  point to the displayed page itself
- "wiki link": link to a wiki page in same or other namespace without "http"
  stuff but with fullpagename, here we do not use project ("d:") or language
  ("id:") prefixes, inside double brackets, wall as separator, can be a "link
  to section" at same time but not "section link" according to the
  definition just below
- "section link": link beginning with "#" linking to a section on the
  displayed page itself, lacks fullpagename, inside double brackets, wall
  as separator
- "link to section": type of "wiki link" containing both fullpagename
  and a section part

Wikiprojects:
Calling this module from namespace ZERO can be useful on wikipedia but
probably NOT on wiktionary. The module itself works with namespace ZERO.
Whatever is said about appendix pages is based on the assumption that
we are on wiktionary, but applies equally to appendix pages on wikipedia,
and article pages in namespace ZERO on wikipedia.

But we need further separate 6 bools to mark presence of following
string and integer parameters, even if default value is supplied:
- "typ=" (empty means auto-guess and tuning options OFF, NOT
          the same as explicit ZERO by "000")
- "ans=" (empty is default ZERO, explicit ZERO may be illegal)
- "fla=" (empty means auto-guess)
- "ctb=" (empty is default ZERO, explicit ZERO may be illegal)
- "cos=" (empty is default ZERO, explicit ZERO by "000-00" may be illegal)
- "pre=" (default is empty, explicit empty is NOT possible)

Following 5 parameters (5 strings processed in different ways) are seized
elsewhere, auto-guessed, copied or filled with non-empty stuff rather
than kept empty if not supplied by the caller:
- "fpn=" (empty means read "fullpagename", separate bool "boohavfpn")
- "tit=" (empty means copy from "fpn=" that in turn can
          come from "fullpagename", no bool)
- "typ=" (empty means auto-guess, separate bool "boohavtyp")
- "alf=" (empty means use default "ABCDEFGHIJKLMNOPQRSTUVWXYZ", no bool)
- "fla=" (empty means auto-guess, 1 + 4 string
          variables and a separate bool)

Following 3 string parameters allow for special value "-" NOT
stored in the string but in a separate boolean:
* "fpn=-" (bool is "boofpns")
* "tit=-" (bool is "bootits")
* "fla=-" (bool is "booflasup")

Dependencies:
- "tit=" depends on "fpn="
- "typ=" depends on "fpn="
- "fla=" depends on "typ=" ("fpn=" -> "typ=" -> "fla=")

Single apo:s are permitted in wiki pagenames and some wiktionaries use them,
even as beginning or last char. Multiple consecutive apo:s are deprecated
and cause trouble, and only sysops may create such broken pages. This module
will replace a double apo coming from a 2-dimensional table by "N/A".

Wiki software allows for short section links formatted like "[[#Z|Z]]", the
effect is very same as with "[[{{FULLPAGENAME}}#Z|Z]]", a full URL is shown
in the browser's preview area, but nothing is loaded, the page just jumps
to the anchor when the link is clicked. This does NOT work with edit preview.

Listing of pages in categories is inherently case insensitive even on
wiktionary, but the order is dubious (maybe upper -> lower -> mixed, for
example "FAN" -> "fan" -> "Fan").

Listing in "Special:AllPages" (in EO called "Specialajxo:CXiuj_pagxoj") and
"Special:Prefixindex" (in EO called "Specialajxo:Indekso_de_prefiksoj") is
case sensitive on wiktionary, and case sensitive except earliest letter on
wikipedia.

There are two equivalent syntaxes for "Special:Prefixindex"
and "Special:AllPages":
- "Special:Prefixindex/a" and "Special:Prefixindex?from=a"
- "Special:AllPages/a" and "Special:AllPages?from=a"
The former one with a slash has the crucial benefit that it does NOT
need a URL query part and can thus be accessed via wiki links instead
of more bloated http links.

The URL is formed like this:
- https://en.wiktionary.org/w/index.php?title=Category:English_lemmas
- https://eo.wiktionary.org/w/index.php?title=Speciala%C4%B5o:%C4%88iuj_pa%C4%9Doj
- index.php?title=Category:English_lemmas
- index.php?title=Category:English_lemmas#mw-pages
- index.php?title=Category:English_lemmas&from=QQ#mw-pages
- index.php?title=Special:AllPages
- index.php?title=Special:AllPages?from=ZAKAR&namespace=108
- index.php?title=Speciala%C4%B5o:%C4%88iuj_pa%C4%9Doj&from=W
- index.php?title=Speciala%C4%B5o:%C4%88iuj_pa%C4%9Doj&from=~

Notes:
- Use parameter value "from=~" to point content after ASCII
  range (works with both categories and "Special:AllPages").
- Optional string "#mw-pages" can be attached to the URL to present
  the page scrolled down to the listing, this works with categories,
  but does NOT work with "Special:AllPages".
- Optional parameter "namespace=" can select namespace for
  "Special:AllPages", default is ZERO (NOT all namespaces), the
  possible namespace prefix is showed, the "from=" string is applied
  AFTER the namespace prefix.
- Note that for "Special:Prefixindex" the default namespace is
  all namespaces, NOT ZERO.
- Do NOT use "namespace=" with "Special:Prefixindex" because it gives
  messy results (indeed restricts to desired namespace, but switches
  behaviour to "Special:AllPages" (can list too much) and does NOT
  show the namespace prefix).

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

### UTF8

* $00'FFFF -> $EF,$BF,$BF
* $01'0000 -> $F0,$90,$80,$80
* $0F'FFFF -> $F3,$BF,$BF,$BF
* $10'0000 -> $F4,$80,$80,$80
* $10'FFFF -> $F4,$8F,$BF,$BF

* UTF8 is defined by "RFC 3629" from 2003-Nov (but already used to
  exist before, though)
* UTF8 sigi AKA BOM : HEX: $EF $BB $BF | DEC: 239 187 191 | ABS: $FEFF
* absolute unicode range has 17 (seventeen !!!) planes per 65'536 values
* totally 1'114'112 codepoints, most of them are unused, plane ZERO is
  somewhat full, other ones are almost or totally empty
* official notation: "U+0000" ... "U+10FFFF"
* codepoint range ZERO to 31 is valid by RFC but mostly useless, same for
  127, range 128 to 159, whereas 160 AKA "&nbsp;" does appear in wikitext
* range "U+D800" to "U+DFFF" is invalid by RFC
* UTF8 starting octet can be only $C2 to $DF , $E0 to $EF , $F0 to $F4
  giving a continuous range from $C2 to $F4 of size $33 = #51 values
* UTF8 subsequent octet:s (1 or 2 or 3) can be only $80 to $BF
  (6 bit:s, 64 possible values)
* octet values $C0, $C1 and $F5 to $FF may never appear in a UTF8 stream

Abs. char number range |      UTF8 octet sequence       | beginning octet
   (hexadecimal)       |            (binary)            |
-----------------------+--------------------------------+------------------
0000'0000 to 0000'007F | 0xxxxxxx                       | $00        to $7F
0000'0080 to 0000'07FF | 110xxxxx 10xxxxxx              | $C0 -> $C2 to $DF
0000'0800 to 0000'FFFF | 1110xxxx 10xxxxxx 10xxxxxx     | $E0        to $EF
0001'0000 to 0010'FFFF | 11110xxx 10xxxxxx 10xxxxxx ... | $F0 to $F7 -> $F4

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

### HTML TABLE

Base color for "border" is #2060A0 and for "background-color" #D0E0F0 with
permutation index ZERO.

Brew table (unless "tit=-" is used) with:
* '<table class="pla
   inlinks" style="margin:0.5em auto 0.5em auto;border:2px solid #'
* ';background-color:#'
* ';padding:5px;text-align:center;">'

For 1-dimensional table the content is:
- "tr" and "td" with title from "tit="
- "tr" and "td" with "br"
- "tr" and "td" with the list with links (see below) including the
  2 possible extra items "top" and "bottom", there is only
  one shared "td" for the complete list

For 2-dimensional table (link types 0 or 1 or 5 or 6 only) the content is:
- "tr" and "td" with title from "tit=" with appropriate colspan
- "tr" and "td" with "br" with appropriate colspan
- many "tr" rows and every of them containing many "td" cells with the
  elements of the lists with links (see below), every link has a private
  cell, the 2 extra items "all" and "remainder" NOT included here
- "tr" with the 2 extra items "all" and "remainder" are in 2 separate
  "td" cells in a separate row at the bottom, both have appropriate colspan

Structure of the list with links is:
* maybe extra "top" item (unless "fla=-" is used)
* alphabet items
* maybe extra "bottom" item (unless "fla=-" is used)

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

### FPN

Parameter "fpn=" assigns the target fullpagename to link to from index
items, either empty or 5...100 octet:s, special value "fpn=-" for
section links (same page), fullpagename read out from wiki alt taken
from "fullpanaoverridetestonly=" if not in "fpn=".

Examples: "Special:AllPages" or "Kategorio:Volapuko" or "Daftar unsur kimia"

Using this module from namespace ZERO can be useful on wikipedia but
probably NOT on wiktionary.

If fullpagename is given then it must contain either no or exactly one
colon ":", but not as beginning nor last char. No "<b>" and no "'''" (ie
bolding) and no links are tolerable in this parameter, singe inner spaces
and single apo:s are permitted anywhere. Note that wiki software trims away
leading and trailing spaces even if they are "encoded" with underscore "_".

If pagenames look like ("Dictionary a", "Dictionary b", etc) then submit
only "Dictionary" without final space here, and use link type 2 with
tuning options (add space).

Use special value "fpn=-" for section links (same page), base link type
guessed to 4 then and no other value is legal.

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

### TYP

Parameter "typ=" sets the type of link in index items, can be either empty,
or control string with 3 digits (ONE for base type and TWO for tuning), by
default base type is auto-guessed from "fpn=" and no tuning.

The values for base type of link are:
- 0 "from="               (http link, for categories and special pages, for
                          categories link to other page or selflink, for
                          special pages link to other page only, possible but
                          deprecated unless "ans=" is used)
- 1 wiki link + "/"       (for special pages, unless "ans=" is used,
                          prohibited for categories)
- 2 wiki link + raw postfix  (for appendixes, wiki link to other page only,
                             link to section not possible)
- 3 wiki link + "#"            (for appendixes, wiki link to other page
                               can be a link to section or not)
- 4 "#" section link           (for appendixes)
- 5 combo 2-part link "/"+"#"  (1+3, prohibited for categories)
- 6 combo 2-part link raw+"#"  (2+3, prohibited for categories)

Guessing defaults to type 4 section link if "fpn=-" is used, otherwise to 1
if the page is special and "ans=" is ZERO (default or explicit), otherwise to
0 if the page is category or special, otherwise to 3. The types 1 and 2 and 3
are very similar, the point with having them separate is that we do not have
to guess tuning with "#" or "/" that we otherwise would need to if we merged
those 3 types. Note that is is NOT possible to combine 0 "from=" with 3 "#".

There are several crucial differences between link type 0 (http
link) and types 1...3 (wiki link):
* http links are always blue, whereas wiki links can be red
* queries like "from=" are possible with http links only
* attempted wiki links to categories can cause malicious problems in the
  form of causing a cat insertion instead of showing a link, we have to
  auto-add ":" in such cases (note that "fpn=" cannot begin with a colon)
* http links are substantially more bloated, this can cause trouble
  especially with big 2-dimensional tables

The tuning options (last 2 of totally 3 digits) are:
* case tuning (only for base link types 1 "/" and 2 "raw"
  and 5 "combo" and 6 "combo"):
    - "0" - no tuning
    - "1" - change beginning letter to lowercase
    - "2" - change beginning letter to uppercase
    - "3" - change all letters to lowercase
    - "4" - change all letters to uppercase
* gap tuning (only for base link types 2 and 6):
    * "0" - no tuning
    * "1" - add a space (note that "fpn=" cannot end with a space)

Note that link type 4 ie section link duplicates the automatic creation
of an index by wiki software and HTML, thus "__NOTOC__" will be
needed. For alphabetic sections (A,B,C,...), even with a prefix
(YA,YB,YC,...), a horizontal index generated by this module is usually
preferable over the default vertical index. Flaw of this trick is that we
need an alphabet, nobody will automatically evaluate all "==" headings for us.

The "fla=" parameter must be used differently for link types 1...3 as opposed
to type 0 since there is no point with "#~" as opposed to "from=~". Instead of
"~" we will add the visible words or other specified words after the cross
"#", the two words will usually be something like "Index" (top) and
"Notes" (bottom).

For "typ=" link types 2...6 the default for "fla=" is "fla=-". Other
values are permitted, and even useful for type 3, but barely useful for 2.

Structure of a single http link (type 0 only) is:
- "["
- "canonicalurl" of: - "fpn=" as fullpagename
                     - possible "from=" thing as parameter
                     - possible "namespace=" as parameter if "ans=" has
                       a value different from ZERO
- possible <<#mw-pages>> if "mwp=1" (only categories)
- space
- visible link string (see below)
- "]"

The structure of a single wiki link (1,2,3) and
section link (4) and combo link (5,6) is:
- "[["
- ":" if it is a category for type 2 (crucial!!!)
- "fpn=" as fullpagename for type 1,2,3 and 5,6
- slash "/" for types 1 and 5
- possible optional gap char (space) and (optionally
  case-tuned) raw letter or string for type 1 or 2 or 5 or 6
- possible #-part with a letter for type 3...6
- wall
- visible link string (see below)
- "]]"

Structure of the visible link string is:
- for letters and other signs the single character enclosed in "["..."]"
  (unless enclosing deselected by subparameter in "cos=")
- for words from "fla=" ("all" and "remainder" by default for type 0) the
  word enclosed in "("...")"

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

Warning about bloat: This module can challenge the limits of the wiki
software. Given an alphabet with 35 letters, including lowercase 70, a
2-dimensional table will have 4'900 elements. If one element needs 150
octet:s (this is realistic) then the complete table will be only 720 KiO
in size, this is more than 1/3 of the limit of 2 MiO. To minimize the risk
of problems, you should prevent accumulation of following trouble factors:
* 2-dimensional table
* long fullpagenames with much non-ASCII -- little can be done, maybe move
  them, maybe use English terms for special pages and for namespace prefixes
* long alphabet -- little can be done, maybe split into several smaller tables
* link type 0 (http links) -- use other type whenever possible
* non-default colour of the text requested by "cos=" -- try to avoid
* enclosing brackets (on by default, but can be disabled by "cos=")

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

Error codes (continuous mapping from error code
to ONE-based index of parameter is NOT required):

-----  ----  -----------
error  para  description
-----  ----  -----------
#E00    --   OK
#E01    --   internal error (constant strings EO vs ID have
             not been uncommented)
#E02    --   undesirable anonymous parameter  !!!FIXME!!! should be #E06
#E03     1   "fpn=" bad
#E04     2   "tit=" bad generic except illegal stuff
#E05     2   "tit=" bad due to illegal links or transclusions or apo:s
#E06     3   "dud=" bad
#E07 (reserved)
#E08     4   "typ=" bad (3 digits) generic except bad base
#E09     4   "typ=" bad (3 digits) bad base link type
#E.. (reserved)
#E19     5   "mwp=" bad
#E20 (reserved)
#E21     6   "ans=" bad
#E22     7   "alf=" bad generic (except ASCII, lowercase, dupe, bracketing)
#E23     7   "alf=" bad due to bad ASCII non-letter char
#E24     7   "alf=" bad due to lowercase with category
#E25     7   "alf=" bad due to dupe
#E26     7   "alf=" bad due to bracketing
#E27     8   "fla=" bad
#E28     9   "ctb=" bad
#E29 (reserved)
#E30 (reserved)
#E31 (reserved)
#E32    10   "cos=" bad (6 ext hex digits) generic except equal colours
#E33    10   "cos=" bad (6 ext hex digits) both colours are non-ZERO and equal
#E34 (reserved)
#E35    11   "pre=" bad generic except illegal stuff
#E36    11   "pre=" bad due to illegal links or transclusions or apo:s
#E37 (reserved)
#E38    --   inconsistent explicitly specified parameters
             * "tit=-" and "dud=1"
             * "tit=-" and "ctb="
             * "dud=1" and "base link type" <> 0 1 5 6
             * "dud=0" and "base link type" is 5 or 6 (note that 5 or
                                                       6 is never guessed)
             * "fpn=-" and "base link type" <> 4
             * "ans="  and "base link type" <> 0
             * "fpn=" given (other than "-") and "base link type" = 4
             * "dud=1" and "pre="
             * "mwp=1" and "ans="
#E39 (reserved)
#E40 (reserved)
#E41 (reserved)
#E42    --   inconsistency with fullpagename and defaults and guesses other
             * "dud=1" and "base link type" <> 0 1 5 6
             * "fpn=-" and "base link type" <> 4
             * "ans="  and "base link type" <> 0
             * cat     and "base link type" <> 0 2
             * special and "base link type" <> 0 1
#E43    --   inconsistency with fullpagename and defaults and guesses cat
             * "mwp=1" and non-cat
#E44    --   inconsistency with fullpagename and defaults and guesses special
             * "ans=" and non-special
-----  ----  -----------
error  para  description
-----  ----  -----------

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

{{hr3}} <!-------------------------------->

* #T00 (no params, default table, peek fullpagename (fake "Category:Ido"), default colour ZERO)
* parameters: ""
* expected result: OK
* actual result:
<table style="margin:1em;padding:1em;border:1px solid #000000;"><tr><td>"{{#invoke:ind12dim|ek|fullpanaoverridetestonly=Category:Ido}}"</td></tr></table>

::* #T01 (illegal anonymous parameter)
::* parameters: "|stultulo"
::* expected result: #E02
::* actual result:
<table style="margin:1em;padding:1em;border:1px solid #000000;"><tr><td>"{{#invoke:ind12dim|ek|stultulo}}"</td></tr></table>

{{hr3}} <!-------------------------------->

* #T02 (explicit colour test, default table, peek fullpagename (fake "Category:Ido"), colour ZERO, same output as case #T00 above)
* parameters: "|ctb=0"
* expected result: OK
* actual result:
<table style="margin:1em;padding:1em;border:1px solid #000000;"><tr><td>"{{#invoke:ind12dim|ek|ctb=0|fullpanaoverridetestonly=Category:Ido}}"</td></tr></table>

{{hr3}} <!-------------------------------->

::* #T03 (explicit colour test, default table, peek fullpagename (fake "Category:Dana"), colour ONE)
::* parameters: "|ctb=1"
::* expected result: OK
::* actual result:
<table style="margin:1em;padding:1em;border:1px solid #000000;"><tr><td>"{{#invoke:ind12dim|ek|ctb=1|fullpanaoverridetestonly=Category:Dana}}"</td></tr></table>

{{hr3}} <!-------------------------------->

* #T04 (explicit colour test, default table, peek fullpagename (fake "Category:Dana"), colour TWO)
* parameters: "|ctb=2"
* expected result: OK
* actual result:
<table style="margin:1em;padding:1em;border:1px solid #000000;"><tr><td>"{{#invoke:ind12dim|ek|ctb=2|fullpanaoverridetestonly=Category:Dana}}"</td></tr></table>

{{hr3}} <!-------------------------------->

* #T05 (explicit colour test, default table, peek fullpagename, colour 3)
* "{{#invoke:ind12dim|ek|ctb=3}}"

{{hr3}} <!-------------------------------->

* #T06 (explicit colour test, default table, peek fullpagename, colour 4)
* "{{#invoke:ind12dim|ek|ctb=4}}"

{{hr3}} <!-------------------------------->

* #T07 (explicit colour test, default table, peek fullpagename, colour 5)
* "{{#invoke:ind12dim|ek|ctb=5}}"

{{hr3}} <!-------------------------------->

* #T08 (explicit colour test, default table, peek fullpagename, colour 6, invalid, error #E28)
* "{{#invoke:ind12dim|ek|ctb=6}}"

{{hr3}} <!-------------------------------->
{{hr3}} <!-------------------------------->

* #T10 (maximal parameter usage (8 of 11) with "mwp=1" whereas "ans=" is NOT possible, "dud=1" used while "pre=" is NOT possible)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|dud=1|mwp=1|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000-00}}"

{{hr3}} <!-------------------------------->

* #T11 (maximal parameter usage (8 of 11) with "ans=" whereas "mwp=1" is NOT possible, "dud=1" used while "pre=" is NOT possible)
* "{{#invoke:ind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|ans=12|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000-00}}"

{{hr3}} <!-------------------------------->

* #T12 (bad usage (9 of 11) due to both "mwp=1" and "ans=", error #E38)
* "{{#invoke:ind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|mwp=1|ans=12|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000-00}}"

{{hr3}} <!-------------------------------->

* #T13 (bad usage (9 of 11) due to both "dud=1" and "pre=", error #E38)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|dud=1|mwp=1|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000-00|pre=DATOR}}"

{{hr3}} <!-------------------------------->

* #T14 (maximal parameter usage (9 of 11) with "ans=" whereas "mwp=1" is NOT possible, "typ=000" is redundant, text colour test)
* "{{#invoke:ind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|typ=000|ans=12|alf=GHIJKLMNOP|fla=everything,leftover|ctb=5|cos=420-00}}"

{{hr3}} <!-------------------------------->

* #T15 (maximal parameter usage (9 of 11) with "ans=" whereas "mwp=1" is NOT possible, "typ=100" is criminal, text colour test, error #E38)
* "{{#invoke:ind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|typ=100|ans=12|alf=GHIJKLMNOP|fla=everything,leftover|ctb=5|cos=420-00}}"

{{hr3}} <!-------------------------------->

* #T16 (maximal parameter usage (9 of 11) with "ans=" whereas "mwp=1" is NOT possible, text colour test, invalid colours "33", error #E33)
* "{{#invoke:ind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|typ=000|ans=12|alf=EDCBA|fla=everything,leftover|ctb=5|cos=330-00}}"

{{hr3}} <!-------------------------------->
{{hr3}} <!-------------------------------->

* #T20 (no "dud=1", "typ=" guessed, text colour test here "EL", separator test here default)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=EL0-00}}"

{{hr3}} <!-------------------------------->

* #T21 (no "dud=1", "typ=" guessed, text colour test here "LE", separator test here "10")
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LE0-10}}"

{{hr3}} <!-------------------------------->

* #T22 (no "dud=1", "typ=" guessed, text colour test here "LE", separator test here "01", invalid, error #E32)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LE0-01}}"

{{hr3}} <!-------------------------------->

* #T23 (no "dud=1", "typ=" guessed, text colour test here "LE", separator test here "20")
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LE0-20}}"

{{hr3}} <!-------------------------------->

* #T24 (no "dud=1", "mwp=1", "typ=" guessed, text colour test here "LD", separator test here "60")
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LD0-60}}"

{{hr3}} <!-------------------------------->

* #T25 (no "dud=1", no "mwp=1", "typ=" guessed, text colour test here "LC", separator test here "61")
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LC0-61}}"

{{hr3}} <!-------------------------------->
{{hr3}} <!-------------------------------->

* #T30 (no "dud=1", "fpn=" explicit cat "Category:", "typ=" guessed, "alf=" explicit)
* "{{#invoke:ind12dim|ek|fpn=Category:Verbo|alf=HGFEDCBA}}"

{{hr3}} <!-------------------------------->

* #T31 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=HGFEDCBA}}"

{{hr3}} <!-------------------------------->

* #T32 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit, criminal due to lowercase, #E24)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=HGFEdCBA}}"

{{hr3}} <!-------------------------------->

* #T33 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit, criminal due to dupe, #E25)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=HGFEHCBA}}"

{{hr3}} <!-------------------------------->

* #T34 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, criminal due to dupe, #E25)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=(HO)GFE(HO)CBA}}"

{{hr3}} <!-------------------------------->

* #T35 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, do NOT convert X-system to unicode here)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CX)DEFG(GX)HIJKL(LL)MNO(NENIO)P}}"

{{hr3}} <!-------------------------------->

* #T36 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, reaching limit of 9)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(NENIOMULO)P}}"

{{hr3}} <!-------------------------------->

* #T37 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, using non-ASCII char:s, replace "CX" with unicode and "UXX" with some Amharic letter in unicode)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(ACX)HIJKL(LL)MNO(LUXX)P}}"

{{hr3}} <!-------------------------------->
{{hr3}} <!-------------------------------->

* #T40 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation inside brackets (9->10), #E26)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(NENIOMULOJ)P}}"

{{hr3}} <!-------------------------------->

* #T41 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation inside brackets (2->1), #E26)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(N)P}}"

{{hr3}} <!-------------------------------->

* #T42 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation inside brackets (2->1), replace "UX" with unicode, #E26)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(UX)P}}"

{{hr3}} <!-------------------------------->

* #T43 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation inside brackets (2->1), replace "UXX" with some Amharic letter in unicode, #E26)
* "{{#invoke:ind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(UXX)P}}"

{{hr3}} <!-------------------------------->
{{hr3}} <!-------------------------------->

* #T50 (no "dud=1", "fpn=" explicit "Special:AllPages", "tit=-" no table, "typ=" guessed to "100" (wiki links), "alf=" explicit)
* "{{#invoke:ind12dim|ek|fpn=Special:AllPages|tit=-|alf=KLMNOPQR}}"

{{hr3}} <!-------------------------------->

* #T51 (no "dud=1", "fpn=" explicit "Special:AllPages", "tit=-" no table and nobody provides "plainlinks", "typ=" guessed to "000" (http links), "alf=" explicit)
* "{{#invoke:ind12dim|ek|fpn=Special:AllPages|ans=102|tit=-|alf=KLMNOPQR}}"

{{hr3}} <!-------------------------------->

* #T52 (no "dud=1", "fpn=" explicit "Special:AllPages", "tit=-" no table and caller provides "plainlinks", "typ=" guessed to "000" (http links), "alf=" explicit)
* parameters: "|fpn=Special:AllPages|ans=102|tit=-|alf=KLMNOPQR"
* expected result: OK
* actual result:
<table style="margin:1em;padding:1em;border:1px solid #000000;"><tr><td>"<span class="plainlinks">{{#invoke:ind12dim|ek|fpn=Special:AllPages|ans=102|tit=-|alf=KLMNOPQR}}</span>"</td></tr></table>

{{hr3}} <!-------------------------------->

::* #T53 (no "dud=1", "fpn=" explicit "Special:AllPages", "alf=" explicit and lowercase is legal here)
::* parameters: "|fpn=Special:AllPages|alf=KLMNklmn"
::* expected result: OK
::* actual result:
<table style="margin:1em;padding:1em;border:1px solid #000000;"><tr><td>"{{#invoke:ind12dim|ek|fpn=Special:AllPages|alf=KLMNklmn}}"</td></tr></table>

{{hr3}} <!-------------------------------->

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

]===]

local exporttable = {}

require('strict')

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

-- HTML stuff for our insane table (unless "tit=-" is used)

local constrborder = '2060A0'  -- 6 digits no cross "#" here
local constrbakgnd = 'D0E0F0'  -- 6 digits no cross "#" here

local contabhtml = {}
contabhtml.matabg  = '<table class="plainlinks" style="margin:0.5em auto 0.5em auto;border:2px solid #'
local constrtabu8  = ';background-color:#'
local constrtabu9  = ';padding:5px;text-align:center;">'

-- very verbose error messages

  -- note that #E01 and "constrda5mn" and "contabevil5txt[ 1]" MUST
  -- remain in English and MUST NOT depend on uncommentable strings

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

local constrda5mn  = 'Error in "Module:ind12dim" code #E'

local contabevil5top = {} -- 1...44 AKA #E01...#E44 (use "-" for reserved codes, even holes possible)
contabevil5top = {'A', 'B', 'CaD', 'CbD', 'CbDI', 'CcD', '-', 'CdD', 'CdDE'} -- last is #E09
contabevil5top [19] = 'CeD' -- #E19
contabevil5top [21] = 'CfD' -- #E21
contabevil5top [22] = 'CgD' -- #E22 -- generic/other
contabevil5top [23] = 'CgDG' -- #E23
contabevil5top [24] = 'CgDH' -- #E24
contabevil5top [25] = 'CgDI' -- #E25
contabevil5top [26] = 'CgDJ' -- #E26
contabevil5top [27] = 'ChD' -- #E27
contabevil5top [28] = 'CiD' -- #E28
contabevil5top [32] = 'CjD' -- #E32
contabevil5top [33] = 'CjDM' -- #E33
contabevil5top [35] = 'CkD' -- #E35
contabevil5top [36] = 'CkDK' -- #E36
contabevil5top [38] = 'N' -- #E38
contabevil5top [42] = 'O' -- #E42 -- generic/other
contabevil5top [43] = 'OR' -- #E43
contabevil5top [44] = 'OS' -- #E44

local contabevil5par = {} -- 1...11 AKA "a"..."k" -- lowercase, holes possible
contabevil5par = {'fpn','tit','dud','typ','mwp','ans','alf','fla','ctb','cos','pre'}

local contabevil5txt = {} -- 1...19 AKA "A"..."S" -- UPPERCASE, holes possible
contabevil5txt[ 1] = 'Internal error'                            -- "A" this one MUST remain in English
contabevil5txt[ 2] = 'Undesirable anonymous parameter'           -- "B"
contabevil5txt[ 3] = 'Parameter "'                               -- "C"
contabevil5txt[ 4] = '=" is bad'                                 -- "D"
contabevil5txt[ 5] = ' (bad base link type, must be 0...6)'      -- "E"
contabevil5txt[ 7] = ' (bad ASCII non-letter char)'              -- "G"
contabevil5txt[ 8] = ' (lowercase char)'                         -- "H"
contabevil5txt[ 9] = ' (dupe char or group)'                     -- "I"
contabevil5txt[10] = ' (bracketing)'                             -- "J"
contabevil5txt[11] = ' (illegal dbl apo:s links transclusions)'  -- "K"
contabevil5txt[13] = ' (both colours are non-ZERO and same)'     -- "M"
contabevil5txt[14] = 'Inconsistent explicit parameters'          -- "N"
contabevil5txt[15] = 'Inconsistency with pagename'               -- "O"
contabevil5txt[18] = ' ("mwp=1" and non-cat)'                    -- "R"
contabevil5txt[19] = ' ("ans=" and non-special)'                 -- "S"

  -- text colours (none,1-dim,2-dim) and separators for "strapart" (none,1-dim) controlled by "cos="

  local contabwarn = {'F00000','00F000','0000F0','F0F000','00F0F0','F000F0','808080'} -- 1...7 RGB CMY grey (ZERO valid and default but separate)
  local contabsepa = {'-','--','*','|',string.char(0xC2,0xB7),string.char(0xE2,0x80,0xA2)} -- 1...6 (ZERO valid and default but separate)

-- uncommentable constant strings EO vs ID  !!!FIXME!!! use PRWGETNSOFTITLE

  local constrkatp5 = "Category:"             -- EO (obligatory)
  local constrkatp6 = "Kategorio:"            -- EO (assign to empty string if unused)

  -- local constrkatp5 = "Category:"             -- ID (obligatory)
  -- local constrkatp6 = "Kategori:"             -- ID (assign to empty string if unused)

  local constrspep5 = "Special:"                                    -- EO (obligatory)
  local constrspep6 = "Speciala" .. string.char(0xC4,0xB5) .. "o:"  -- EO (assign to empty string if unused) ($C4,$B5 = "jx")

  -- local constrspep5 = "Special:"              -- ID (obligatory)
  -- local constrspep6 = "Istimewa:"             -- ID (assign to empty string if unused)

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

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

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

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

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

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

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

-- Result has type "boolean".

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

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

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

-- Local function LFDEC1DIGLM

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

-- Use this for single-digit conversions with range and for pseudo-boolean
-- (0,1) and for genuine boolean (false,true) via "boobecool=(numheltal==1)".

local function lfdec1diglm (num1dygyt, num1lim)
  num1dygyt = num1dygyt - 48 -- may become invalid ie negative
  if ((num1dygyt<0) or (num1dygyt>num1lim)) then
    num1dygyt = 255 -- report ERROR on invalid input digit
  end--if
  return num1dygyt
end--function lfdec1diglm

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

local function prndecinp (stridn, booalwapo, booalwlez)

-- Convert string (1...13 octet:s) with decimal ASCII digits to a
-- positive (UINT32) integer number (0...4'000'000'000) optionally
-- skipping possible inner apo:s, optionally tolerating leading ZERO:s.

-- Input  : * stridn -- string, empty tolerable (gives -1), but type
--                      other than "string" is NOT
--          * booalwapo -- allow apo:s
--          * booalwlez -- allow leading ZERO:s

-- Output : * numaout -- -1 on error

-- 65'536 good and maybe accepted
-- 655'36 ugly but maybe accepted  --  6''''55''''36 ugly but maybe accepted
-- '65536 rejected                 --  65536' rejected

  local numaout = 0
  local numleen = 0
  local numinxx = 1 -- ONE-based -- counts up
  local numokkt = 0
  numleen = string.len (stridn)
  while true do
    if ((numleen<1) or (numleen>13)) then
      numaout = -1 -- damn
      break
    end--if
    if (stridn=='0') then
      break -- bypass check for leading ZERO:s
    end--if
    numokkt = string.byte (stridn,numinxx,numinxx)
    if (numokkt==39) then
      if ((not booalwapo) or (numinxx==1) or (numinxx==numleen)) then
        numaout = -1
        break -- damn (must NOT begin or end with apo)
      end--if
    else
      if ((numokkt<48) or (numokkt>57) or (numaout>400000000) or ((numokkt==48) and (numinxx==1) and (not booalwlez))) then
        numaout = -1
        break -- damn (bad char or out of range or bad leading)
      end--if
      numaout = numaout * 10 + (numokkt - 48)
    end--if
    if (numinxx==numleen) then
      break -- done (hopefully success, but not yet sure, risk 4'000'000'009)
    end--if
    numinxx = numinxx + 1
  end--while
  if (numaout>4000000000) then
    numaout = -1 -- damn (out of range)
  end--if
  return numaout
end--function prndecinp

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

-- Local function LFNEXTHEXTOINT

-- Convert single quasi-digit (ASCII extended HEX "0"..."9" "A"..."Z")
-- to integer (0...35, 255 invalid).

-- Only uppercase accepted.

local function lfnexthextoint (numdgiit)
  local numressult = 255 -- preASSume guilt
  if ((numdgiit>=48) and (numdgiit<=57)) then
    numressult = numdgiit-48
  end--if
  if ((numdgiit>=65) and (numdgiit<=90)) then
    numressult = numdgiit-55
  end--if
  return numressult
end--function lfnexthextoint

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

local function prnnumto2digit (numzerotoninetynine)

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

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

local strtwodig = '??' -- always 2 digits

  if (mathisintrange(numzerotoninetynine,0,99)) then
    strtwodig = tostring(mathdiv(numzerotoninetynine,10)) .. tostring(mathmod(numzerotoninetynine,10))
  end--if

return strtwodig

end--function prnnumto2digit

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

local function prgstringrange (varvictim, nummini, nummaxi)

local nummylengthofstr = 0
local booveryvalid = false -- preASSume guilt

  if (type(varvictim)=='string') then
    nummylengthofstr = string.len(varvictim)
    booveryvalid = ((nummylengthofstr>=nummini) and (nummylengthofstr<=nummaxi))
  end--if

return booveryvalid

end--function prgstringrange

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

local function prgstringnonempty (varkorban)

-- Useful if forwarding of template parameters can occur.

local nummylengthdecxeno = 0 -- preASSume guilt

  if (type(varkorban)=='string') then
    nummylengthdecxeno = string.len(varkorban)
  end--if

return nummylengthdecxeno

end--function prgstringnonempty

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

local function prgpokestring (strinpokeout, numpokepoz, numpokeval)

-- Replace single octet in a string.

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

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

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

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

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

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

-- Test whether incoming char is maybe illegal or belongs
-- to a useful ASCII range.

local function prgranges (num2askio)
  local numpentsta = 0 -- preASSume illegal
  if ((num2askio>=48) and (num2askio<=57)) then
    numpentsta = 1 -- innocent -- "0"..."9"
  end--if
  if ((num2askio>=65) and (num2askio<=90)) then
    numpentsta = 2 -- innocent -- "A"..."Z"
  end--if
  if ((num2askio>=97) and (num2askio<=122)) then
    numpentsta = 3 -- lowercase -- "a"..."z"
  end--if
  if ((num2askio==39) or (num2askio==45)) then
    numpentsta = 4 -- innocent -- apo "'" or dash "-"
  end--if
  return numpentsta
end--function prgranges

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

-- Local function LFGCOUNTCHR

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

-- Input  : * strqq -- string (empty is useless but cannot cause major harm)
--          * numasciic -- code of char to be counted

-- Output : * numre3zalt -- number of hits

local function lfgcountchr (strqq, numasciic)
  local numre3zalt = 0
  local numciar = 0
  local numukuran = 0
  local numind3xe = 0 -- ZERO-based
  numukuran = string.len (strqq)
  while true do
    if (numind3xe==numukuran) then
      break -- done -- ZERO iterations possible
    end--if
    numciar = string.byte (strqq,(numind3xe+1),(numind3xe+1))
    if (numciar==numasciic) then
      numre3zalt = numre3zalt + 1
    end--if
    numind3xe = numind3xe + 1
  end--while
  return numre3zalt
end--function lfgcountchr

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

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

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

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

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

-- Local function prulnutf8char

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

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

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

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

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

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

local function prucasefull (strinco7cs, booup7cas, boodo7all)

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

-- Input  : * strinco7cs : single unicode letter (1 or 2 octet:s) or
--                         longer string
--          * booup7cas  : for desired output uppercase "true" and for
--                         lowercase "false"
--          * boodo7all  : "true" to adjust all letters, "false"
--                         only beginning letter

-- Output : * strinco7cs

-- Depends on procedures : (this is GENE)
-- [U] prulnutf8char
-- [G] prgpokestring lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbittest

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

-- * lowercase is usually above uppercase, but not always, letters can be
--   only misaligned (UC even vs UC odd), and rarely even swapped (French "Y")
-- * case delta can be 1 or $20 or $50 other
-- * case pair distance can span $40-boundary or even $0100-boundary
-- * in the ASCII range lowercase is $20 above uppercase, b5 reveals
--   the case (1 is lower)
-- * the same is valid in $C3-block
-- * this is NOT valid in $C4-$C5-block, lowercase is usually 1 above
--   uppercase, but nothing reveals the case reliably

-- ## $C2-block $0080 $C2,$80 ... $00BF $C2,$BF no letters (OTOH NBSP mm)

-- ## $C3-block $00C0 $C3,$80 ... $00FF $C3,$BF (SV mm) delta $20 UC-LC-UC-LC
-- upper $00C0 $C3,$80 ... $00DF $C3,$9F
-- lower $00E0 $C3,$A0 ... $00FF $C3,$BF
-- AA AE EE NN OE UE mm
-- $D7 $DF $F7 excluded (not letters)
-- $FF excluded (here LC, UC is $0178)

-- ## $C4-$C5-block $0100 $C4,$80 ... $017F $C5,$BF (EO mm)
-- delta 1 and UC even, but messy with many exceptions
-- EO $0108 ... $016D case delta 1
-- for example SX upper $015C $C5,$9C -- lower $015D $C5,$9D
-- $0138 $0149 $017F excluded (not letters)
-- $0178 excluded (here UC, LC is $FF)
-- $0100 ... $0137 UC even
-- $0139 ... $0148 misaligned (UC odd) note that case delta is NOT reversed
-- $014A ... $0177 UC even again
-- $0179 ... $017E misaligned (UC odd) note that case delta is NOT reversed

-- ## $CC-$CF-block $0300 $CC,$80 ... $03FF $CF,$BF (EL mm) delta $20
-- EL $0370 ... $03FF (officially)
-- strict EL base range $0391 ... $03C9 case delta $20
-- $0391 $CE,$91 ... $03AB $CE,$AB upper
-- $03B1 $CE,$B1 ... $03CB $CD,$8B lower
-- for example "omega" upper $03A9 $CE,$A9 -- lower $03C9 $CF,$89

-- ## $D0-$D3-block $0400 $D0,$80 ... $04FF $D3,$BF (RU mm)
-- * delta $20 $50 1
-- * strict RU base range $0410 ... $044F case delta $20 but there
--   is 1 extra char outside !!!
--   * $0410 $D0,$90 ... $042F $D0,$AF upper
--   * $0430 $D0,$B0 ... $044F $D1,$8F lower
--   * for example "CCCP-gamma" upper $0413 $D0,$93 -- lower $0433 $D0,$B3
-- * extra base char and exception is special "E" with horizontal doubledot
--   case delta $50 (upper $0401 $D0,$81 -- lower $0451 $D1,$91)
-- * same applies for ranges $0400 $D0,$80 ... $040F $D0,$8F upper
--   and $0450 $D1,$90 ... $045F $D1,$9F lower
-- * range $0460 $D1,$A0 ... $04FF $D3,$BF (ancient RU, UK, RUE, ...) case
--   delta 1 and UC usually even, but messy with many exceptions $048x
--   $04Cx (combining decorations and misaligned)

-- Variables "numdel7abs" and "numdel7ta" must be at least 16-bit to avoid
-- misevaluation or wrong wrapping when fitting into the range 128...191,
-- even if no deltas exceeding +-127 are supported (there are very few pairs
-- of char:s exceeding this). Also both can be declared unsigned since only
-- addition and subtraction are performed on them.

-- 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 numlong7den = 0 -- actual length of input string
  local numokt7index = 0
  local numlong7bor = 0 -- expected length of single char

  local numdel7abs = 0 -- at least 16-bit, absolute posi delta
  local numdel7ta = 0 -- quasi-signed at least 16-bit, can be negative

  local numdel7car = 0 -- quasi-signed 8-bit, can be negative

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

  local boowan7tlowr = false
  local boois7uppr = false
  local boois7lowr = false

  local boomy7bit0x = false -- single relevant bits picked -- b0
  local boomy7bit5x = false -- single relevant bits picked -- b5
  local boopen7din = false -- only fake loop

  local boodo7adj = true -- preASSume innocence -- continue changing
  local boobotch7d = false -- preASSume innocence -- NOT yet botched

  local booc3block = false -- $C3 only $00C0...$00FF SV mm delta 32
  local booc4c5blk = false -- $C4 $C5  $0100...$017F EO mm delta 1
  local boocccfblk = false -- $CC $CF  $0300...$03FF EL mm delta 32
  local bood0d3blk = false -- $D0 $D3  $0400...$04FF RU mm delta 32 80

  booup7cas = not (not booup7cas)
  boowan7tlowr = (not booup7cas)
  numlong7den = string.len (strinco7cs)

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

    if (numokt7index>=numlong7den) then
      break -- done complete string
    end--if
    if ((not boodo7all) and (numokt7index~=0)) then -- loop can skip index ONE
      boodo7adj = false
    end--if
    boois7uppr  = false -- preASSume on every iteration
    boois7lowr  = false -- preASSume on every iteration
    numdel7ta   = 0 -- preASSume on every iteration
    numlong7bor = 1 -- preASSume on every iteration

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

      numcha7r = string.byte (strinco7cs,(numokt7index+1),(numokt7index+1))
      if (boobotch7d) then
        numdel7ta = 90 - numcha7r -- "Z" -- delta must be non-ZERO to write
        break -- fill with "Z" char:s
      end--if
      if (not boodo7adj) then
        break -- copy octet after octet
      end--if
      numlong7bor = prulnutf8char(numcha7r)
      if ((numlong7bor==0) or ((numokt7index+numlong7bor)>numlong7den)) then
        numlong7bor = 1 -- reassign to ONE !!!
        numdel7ta = 90 - numcha7r -- "Z" -- delta must be non-ZERO to write
        boobotch7d = true
        break -- truncated char or broken stream
      end--if
      if (numlong7bor>=3) then
        break -- copy UTF8 char, no chance for adjustment
      end--if

      if (numlong7bor==1) then
        boois7uppr = lfgtestuc(numcha7r)
        boois7lowr = lfgtestlc(numcha7r)
        if (boois7uppr and boowan7tlowr) then
          numdel7ta = 32 -- ASCII UPPER->lower
        end--if
        if (boois7lowr and booup7cas) then
          numdel7ta = -32 -- ASCII lower->UPPER
        end--if
        break -- success with ASCII and one char almost done
      end--if

      booc3block = (numcha7r==195) -- case delta is 32
      booc4c5blk = ((numcha7r==196) or (numcha7r==197)) -- case delta is 1
      boocccfblk = ((numcha7r>=204) and (numcha7r<=207)) -- case delta is 32
      bood0d3blk = ((numcha7r>=208) and (numcha7r<=211)) -- case delta is 32 80 1

      numcha7s = string.byte (strinco7cs,(numokt7index+2),(numokt7index+2)) -- only $80 to $BF
      numcxa7rel = (mathmod(numcha7r,4)*64) + (numcha7s-128) -- 4 times 64
      boomy7bit0x = ((mathmod(numcxa7rel,2))==1)
      boomy7bit5x = mathbittest(numcxa7rel,5)

    if (booc3block) then
      boopen7din = true -- pending flag
      if ((numcxa7rel==215) or (numcxa7rel==223) or (numcxa7rel==247)) then
        boopen7din = false -- not a letter, we are done
      end--if
      if (numcxa7rel==255) then
        boopen7din = false -- special LC silly "Y" with horizontal doubledot
        if (booup7cas) then
          numdel7ta = 121 -- lower->UPPER (distant and reversed order)
        end--if
      end--if
      if (boopen7din) then
        boois7lowr = boomy7bit5x -- mostly regular block, look at b5
        boois7uppr = not boois7lowr
        if (boois7uppr and boowan7tlowr) then
          numdel7ta = 32 -- UPPER->lower
        end--if
        if (boois7lowr and booup7cas) then
          numdel7ta = -32 -- lower->UPPER
        end--if
      end--if (boopen7din) then
      break -- to join mark
    end--if (booc3block) then

    if (booc4c5blk) then
      boopen7din = true -- pending flag
      if ((numcxa7rel==56) or (numcxa7rel==73) or (numcxa7rel==127)) then
        boopen7din = false -- not a letter, we are done
      end--if
      if (numcxa7rel==120) then
        boopen7din = false -- special UC silly "Y" with horizontal doubledot
        if (boowan7tlowr) then
          numdel7ta = -121 -- UPPER->lower (distant and reversed order)
        end--if
      end--if
      if (boopen7din) then
        if (((numcxa7rel>=57) and (numcxa7rel<=73)) or (numcxa7rel>=121)) then
          boois7lowr = not boomy7bit0x -- UC odd (misaligned)
        else
          boois7lowr = boomy7bit0x -- UC even (ordinary align)
        end--if
        boois7uppr = not boois7lowr
        if (boois7uppr and boowan7tlowr) then
          numdel7ta = 1 -- UPPER->lower
        end--if
        if (boois7lowr and booup7cas) then
          numdel7ta = -1 -- lower->UPPER
        end--if
      end--if (boopen7din) then
      break -- to join mark
    end--if (booc4c5blk) then

    if (boocccfblk) then
      boois7uppr = ((numcxa7rel>=145) and (numcxa7rel<=171))
      boois7lowr = ((numcxa7rel>=177) and (numcxa7rel<=203))
      if (boois7uppr and boowan7tlowr) then
        numdel7ta = 32 -- UPPER->lower
      end--if
      if (boois7lowr and booup7cas) then
        numdel7ta = -32 -- lower->UPPER
      end--if
      break -- to join mark
    end--if (boocccfblk) then

    if (bood0d3blk) then
      if (numcxa7rel<=95) then -- messy layout but no exceptions
        boois7lowr = (numcxa7rel>=48) -- delta $20 or $50
        boois7uppr = not boois7lowr
        numdel7abs = 32 -- $20
        if ((numcxa7rel<=15) or (numcxa7rel>=80)) then
          numdel7abs = 80 -- $50
        end--if
      end--if
      if ((numcxa7rel>=96) and (numcxa7rel<=129)) then -- no exceptions here
        boois7lowr = boomy7bit0x -- UC even (ordinary align)
        boois7uppr = not boois7lowr
        numdel7abs = 1
      end--if
      if (numcxa7rel>=138) then -- some misaligns here  !!!FIXME!!!
        boois7lowr = boomy7bit0x -- UC even (ordinary align)
        boois7uppr = not boois7lowr
        numdel7abs = 1
      end--if
      if (boois7uppr and boowan7tlowr) then
        numdel7ta = numdel7abs -- UPPER->lower
      end--if
      if (boois7lowr and booup7cas) then
        numdel7ta = -numdel7abs -- lower->UPPER
      end--if
      break -- to join mark
    end--if (bood0d3blk) then

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

    if ((numlong7bor==1) and (numdel7ta~=0)) then -- no risk of carry here
      strinco7cs = prgpokestring (strinco7cs,numokt7index,(numcha7r+numdel7ta))
    end--if
    if ((numlong7bor==2) and (numdel7ta~=0)) then -- no risk of carry here
      numdel7car = 0
      while true do -- inner genuine loop
        if ((numcha7s+numdel7ta)<192) then
          break
        end--if
        numdel7ta = numdel7ta - 64 -- get it down into range 128...191
        numdel7car = numdel7car + 1 -- BIG ENDIAN 6 bits with carry
      end--while
      while true do -- inner genuine loop
        if ((numcha7s+numdel7ta)>127) then
          break
        end--if
        numdel7ta = numdel7ta + 64 -- get it up into range 128...191
        numdel7car = numdel7car - 1 -- BIG ENDIAN 6 bits with carry
      end--while
      if (numdel7car~=0) then -- in-place change only if needed
        strinco7cs = prgpokestring (strinco7cs,numokt7index,(numcha7r+numdel7car))
      end--if
      if (numdel7ta~=0) then -- in-place change only if needed
        strinco7cs = prgpokestring (strinco7cs,(numokt7index+1),(numcha7s+numdel7ta))
      end--if
    end--if
    numokt7index = numokt7index + numlong7bor -- advance in incoming string

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

  return strinco7cs

end--function prucasefull

-- *****************************************
-- *    LOW LEVEL STRING PROCEDURES [G]    * ---------------------------  !!!FIXME!!! again
-- *****************************************

-- Check whether string begins with one of 2 supplied substrings.       !!!FIXME!!! move -- use LFWGETNSOFTITLE

-- Input  : * striinp
--          * strx4 (may be empty, partial result is false then)
--          * strx5 (may be empty, partial result is false then)

-- Output : * booequal ("true" if "striinp" begins with "strx4" or "strx5")

local function lfgdblcmp (striinp, strx4, strx5)
  local numleniin = 0
  local numlexxen = 0
  local booequal = false
  numleniin = string.len (striinp) -- length of input string to be tested
  numlexxen = string.len (strx4)
  if ((numlexxen~=0) and (numlexxen<=numleniin)) then
    booequal = (string.sub(striinp,1,numlexxen)==strx4) -- hit against "strx4"
  end--if
  if (not booequal) then
    numlexxen = string.len (strx5)
    if ((numlexxen~=0) and (numlexxen<=numleniin)) then
      booequal = (string.sub(striinp,1,numlexxen)==strx5) -- hit against "strx5"
    end--if
  end--if
  return booequal
end--function lfgdblcmp

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

-- Local function LFIPILLEGAL

-- Check whether a string contains illegal stuff such
-- as LF < > [ ] '' [[ ]] {{ }} UNIQ [http. Note that codes
-- 0...9 and 11...31 are unconditionally prohibited.

-- Input  : * strinpil  -- empty cannot cause major harm and returns "false"
--          * boonolflf -- disallow LF
--          * boonotags -- disallow single diamond brackets <> ie HTML tags
--          * boonobold -- disallow double apo "''" ie both italics and bold
--                         (<b> and <i> will still pass)
--          * numnorect -- 0 allow rect | 1 disallow double "[[" and "]]"
--                         but not single ones | 2 disallow even single [ ]
--          * boonotskl -- disallow "{{" and "}}" but not single ones
--          * boonostrp -- disallow strip
--          * boonohttp -- disallow "[http" (this is redundant
--                         for numnorect=2)

-- Output : * boocriminal ("true" if "strinpil" contains illegal stuff)

local function lfipillegal (strinpil, boonolflf, boonotags, boonobold, numnorect, boonotskl, boonostrp, boonohttp)

  local numleunin = 0
  local numindoxx = 1 -- ONE-based
  local numtecken = 0
  local numtecprv = 0 -- previous char
  local boocriminal = false

  while true do -- fake loop

    boocriminal = boonostrp and (string.find(strinpil,(string.char(127,39,34,96)..'UNIQ'),1,true)~=nil) -- striptease marker
    if (boocriminal) then
      break
    end--if
    boocriminal = boonohttp and (string.find(strinpil,"[http",1,true)~=nil) -- external link  !!!FIXME!!! this is weak
    if (boocriminal) then
      break
    end--if
    boocriminal = boonohttp and (string.find(strinpil,"[ http",1,true)~=nil) -- external link  !!!FIXME!!! this is weak
    if (boocriminal) then
      break
    end--if

    numleunin = string.len (strinpil) -- length of input string to be tested
    while true do
      if (numindoxx>numleunin) then
        break -- innocent now
      end--if
      numtecken = string.byte (strinpil,numindoxx,numindoxx)
      if ((numtecken<31) and (numtecken~=10)) then
        boocriminal = true -- unconditionally prohibited
        break
      end--if
      if (boonolflf and (numtecken==10)) then
        boocriminal = true -- LF
        break
      end--if
      if (boonotags and ((numtecken==60) or (numtecken==62))) then
        boocriminal = true -- single "<" or ">" is criminal
        break
      end--if
      if ((numnorect==2) and ((numtecken==91) or (numtecken==93))) then
        boocriminal = true -- catch even single [ ]
        break
      end--if
      if (numtecken==numtecprv) then
        if (boonobold and (numtecken==39)) then
          boocriminal = true -- "''" ie italics or bold
          break
        end--if
        if ((numnorect~=0) and ((numtecken==91) or (numtecken==93))) then
          boocriminal = true
          break
        end--if
        if (boonotskl and ((numtecken==123) or (numtecken==125))) then
          boocriminal = true
          break
        end--if
      end--if
      numtecprv = numtecken -- previous char
      numindoxx = numindoxx + 1
    end--while

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

  return boocriminal

end--function lfipillegal

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

-- Local function LFIPER6MUT

-- Permute RGB in a colour string.

-- Input  : * strhex6in (6 hex digits, no cross "#")
--          * numindepe (0...5)

-- Output : * strhex6out

local function lfiper6mut (strhex6in, numindepe)
  local strhexmm   = ""
  local strhexnn   = ""
  local strhexoo   = ""
  local strhex6out = ""
  if (string.len(strhex6in)==6) then
    strhexmm = string.sub (strhex6in,1,2)
    strhexnn = string.sub (strhex6in,3,4)
    strhexoo = string.sub (strhex6in,5,6)
    if (numindepe==1) then
      strhex6out = strhexmm .. strhexoo .. strhexnn
    end--if
    if (numindepe==2) then
      strhex6out = strhexnn .. strhexmm .. strhexoo
    end--if
    if (numindepe==3) then
      strhex6out = strhexnn .. strhexoo .. strhexmm
    end--if
    if (numindepe==4) then
      strhex6out = strhexoo .. strhexmm .. strhexnn
    end--if
    if (numindepe==5) then
      strhex6out = strhexoo .. strhexnn .. strhexmm
    end--if
  end--if
  if (strhex6out=='') then
    strhex6out = strhex6in -- unchanged if (numindepe==0) or invalid value
  end--if
  return strhex6out
end--function lfiper6mut

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

-- local function prhsplittotable  !!!FIXME!!! move to here

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

local function prhbrewerrfra (numerar5code)

-- Generate error message about predominantly module
-- parameters from fragments.

-- Input  : * numerar5code -- 1 ... 98

-- Output : * stryt5sux -- message

-- Depends on procedures :
-- [G] lfgtestuc lfgtestlc prgstringrange
-- [N] prnnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * 2 strings contabfel.hubg contabfel.huen
-- * string constrda5mn
-- * contabevil5top contabevil5par contabevil5txt (holes legal in all 3)

-- * single element in "contabevil5top" holds a list referring to 1...4
--   message fragments, index in the table is 1...98 #E01...#E98 error code, top
-- * single element in "contabevil5par" holds a message fragment, index
--   range is 1...26 "a"..."z" -- lowercase, parameter names
-- * single element in "contabevil5txt" holds a message fragment, index
--   range is 1...26 "A"..."Z" -- UPPERASE, texts

local var1to4err = 0 -- top 1...4 char:s from "contabevil5top"
local vartm5op = 0 -- from "contabevil5par" or "contabevil5txt"
local strt5frg = ''
local stryt5sux = ''
local numlo5ng = 0
local numtm5ix = 1 -- ONE-based index -- limit "numlo5ng"
local numoc5t = 0
local boogu5dd = false -- preASSume guilt about generating error message

  if (mathisintrange(numerar5code,1,98)) then
    var1to4err = contabevil5top[numerar5code] -- 1...98 -- pick top
    boogu5dd = prgstringrange(var1to4err,1,4)
  end--if

  if (boogu5dd) then
    stryt5sux = prnnumto2digit(numerar5code) .. " -- "
    numlo5ng = string.len (var1to4err)
    while true do -- walk through top 1...4 char:s in "var1to4err"
      if (numtm5ix>numlo5ng) then
        break -- done
      end--if
      numoc5t = string.byte (var1to4err,numtm5ix,numtm5ix) -- a...z or A...Z
      strt5frg = '' -- preASSume guilt
      if (lfgtestlc(numoc5t)) then -- "a"..."z"
        vartm5op = contabevil5par[numoc5t-96] -- pick fragment -- param name
        if (type(vartm5op)=='string') then
          strt5frg = vartm5op
        end--if
      end--if
      if (lfgtestuc(numoc5t)) then -- "A"..."Z"
        vartm5op = contabevil5txt[numoc5t-64] -- pick fragment -- text
        if (type(vartm5op)=='string') then -- holes possible
          strt5frg = vartm5op
        end--if
      end--if
      if (strt5frg=='') then
        boogu5dd = false -- failed to brew the error message
        break
      end--if
      stryt5sux = stryt5sux .. strt5frg -- add fragment
      numtm5ix = numtm5ix + 1 -- ONE-based index
    end--while
  end--if (boogu5dd) then

  if (not boogu5dd) then -- do NOT merge with above "if" stuff
    stryt5sux = "??" -- failed to brew a better error message
  end--if
  stryt5sux = contabfel.hubg .. constrda5mn .. stryt5sux .. contabfel.huen

return stryt5sux

end--function prhbrewerrfra

-- ***********************************
-- *    HIGH LEVEL PROCEDURES [H]    * ---------------------------------  !!!FIXME!!! again
-- ***********************************

-- Local function LFHWARNA

-- Convert index 1...21 (1...7 8...14 15...21) to 6 HEX digits.

-- Depends on const table "contabwarn"

local function lfhwarna (numwarnaidx)
  local strhexhex = '000000' -- this is HTML 3 x UINT8 RGB
  local strhexaut = ''
  local numin5ex = 1
  local numch5ar = 0
  local boolajt = false
  local boodark = false
  if ((numwarnaidx>=1) and (numwarnaidx<=21)) then
    boolajt = ((numwarnaidx>= 8) and (numwarnaidx<=14))
    boodark = ((numwarnaidx>=15) and (numwarnaidx<=21))
    if (boolajt) then
      numwarnaidx = numwarnaidx - 7
    end--if
    if (boodark) then
      numwarnaidx = numwarnaidx - 14
    end--if
  else
    numwarnaidx = 7 -- grey
  end--if
  strhexhex = contabwarn [numwarnaidx] -- 1...7
  while true do
    if (numin5ex==7) then -- ONE-based
      break -- done
    end--if
    numch5ar = string.byte (strhexhex,numin5ex,numin5ex)
    if ((numin5ex==1) or (numin5ex==3) or (numin5ex==5)) then
      if (numch5ar>57) then
        numch5ar = numch5ar - 7 -- now 48...63
      end--if
      if (boolajt) then
        numch5ar = numch5ar + 6
      end--if
      if (boodark) then
        numch5ar = numch5ar - 6
      end--if
      if (numch5ar<48) then
        numch5ar = 48 -- do NOT sink below ZERO
      end--if
      if (numch5ar>63) then
        numch5ar = 63 -- do NOT grow above F AKA 15
      end--if
      if (numch5ar>57) then
        numch5ar = numch5ar + 7 -- now 48...57 or 65...70
      end--if
    end--if
    strhexaut = strhexaut .. string.char (numch5ar)
    numin5ex = numin5ex + 1
  end--while
  return strhexaut
end--function lfhwarna

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

local function prhsplittotable (strdinding,numseparator,strreservph,num37min,num37max)  -- !!!FIXME!!! move from here

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

-- Input  : * strdinding -- incoming main string, must NOT contain codes < 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 procedures :
-- [G] prgtrimspcm

-- * empty elements must use dash "-" as a placeholder (applies to both
--   the incoming string and the returned table, but there is other
--   procedure "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 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 = prgtrimspcm (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 prhsplittotable

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

local function prhstringtotab (strinstring, boolegallow, strprefx)

-- Convert one UTF8 string to separate strings for single UTF8
-- char:s (1-octet...4-octet) or "cluster char:s" (2-octet...9 octet)
-- plus possible prefix (1-octet...9-octet) all stored in a LUA table,
-- length of entry 1-octet...18-octet.

-- Input  : * strinstring -- empty legal but useless
--          * boolegallow -- true to allow lowercase
--          * strprefx -- prefix to get added before all found char:s,
--                        must be valid string 0...9 octet:s

-- Output : * numsta44tus -- see below
--          * numqua44nt -- number of elements either at end or processed
--                          before error occurred, ZERO possible
--          * strvillain -- on S53 S54 S55 the villain, else empty string
--          * tabmychars -- ONE-based, empty possible

-- Depends on procedures :
-- [U] prulnutf8char
-- [G] prgranges

-- Out from PRGRANGES only ASCII : ZERO none | 1 "0"..."9" |
-- 2 "A"..."Z" | 3 "a"..."z" | 4 apo "'" or dash "-"

-- If lowercase is disallowed, then ASCII lowercase is checked,
-- whereas unicode lowercase is prohibited too but not checked.

-- ASCII range 0...127 is disallowed except:
-- # allowed range 48...57
-- # allowed range 65...90
-- # maybe allowed range 97...122 lowercase
-- # allowed single values 39 apo and 45 dash

-- Order of char:s is irrelevant ("ZYXCBAP" is fine) but dupes
-- are prohibited ("EDCBAD" is criminal).

-- Silly "cluster char:s" are allowed, they can be placed between
-- arc brackets "(" and ")", length inside must be both 2...9 octet:s
-- and 2...9 UTF8 char:s.

-- Status codes:
-- ZERO : success (even if empty)
-- 52 : S52 broken or truncated UTF8
-- 53 : S53 invalid ASCII value (0...127)
-- 54 : S54 illegal lowercase
-- 55 : S55 dupe
-- 56 : S56 broken bracketing

-- Source index is ONE-based and may grow faster than ZERO-based
-- destination index still serving a ONE-based table.

local tabmychars = {} -- returned

local strele44nt = ''
local strvillain = '' -- returned

local numsta44tus = 0 -- returned
local numqua44nt = 0 -- returned

local num44long = 0
local numsrcind = 1
local numo44t = 0
local numunilen = 0
local numranger = 0
local numtm44oc = 0 -- in single UTF8 char or "cluster char"
local numtm44ut = 0 -- in single UTF8 char or "cluster char"
local numtm44x = 0

local booklastr = false

  num44long = string.len (strinstring)

  while true do -- outer genuine loop -- walk through "strinstring"

    if (numsrcind>num44long) then -- ONE-based
      break -- done
    end--if
    numo44t = string.byte (strinstring,numsrcind,numsrcind)
    booklastr = (numo44t==40) -- "("
    if (booklastr) then
      numsrcind = numsrcind + 1 -- ONE-based
    end--if

    numtm44oc = 0 -- number of octet:s
    numtm44ut = 0 -- number of UTF8 char:s
    strele44nt = ''

    while true do -- upper inner loop -- copy element
      if (booklastr) then
        if (numsrcind>num44long) then -- ONE-based
          numsta44tus = 56 -- S56 broken bracketing (unclosed, overrun)
          break -- give up -- exits upper inner loop only
        end--if
        numo44t = string.byte (strinstring,numsrcind,numsrcind) -- eat another
        if (numo44t==41) then -- ")"
          numsrcind = numsrcind + 1 -- ONE-based
          break -- exits upper inner loop only -- do NOT exit outer genuine loop
        end--if
      end--if (booklastr) then
      if ((numo44t==40) or (numo44t==41)) then
        numsta44tus = 56 -- S56 broken bracketing (level two or minus one)
        break -- give up -- exits upper inner loop only
      end--if
      numunilen = prulnutf8char (numo44t) -- ZERO is invalid
      if ((numunilen==0) or ((numsrcind+numunilen-1)>num44long)) then
        numsta44tus = 52 -- S52 broken or truncated UTF8
        break -- give up -- exits upper inner loop only
      end--if
      if ((numtm44oc+numunilen)>9) then -- no need to check "numtm44ut" here
        numsta44tus = 56 -- S56 broken bracketing (too long inside)
        break -- give up -- exits upper inner loop only
      end--if
      if (numunilen==1) then -- check ranges for ASCII only
        numranger = prgranges (numo44t) -- 0 none | 1...4 ranges
        if ((numranger==0) or ((numranger==3) and (not boolegallow))) then
          if (numranger==3) then
            numsta44tus = 54 -- S54 illegal lowercase
            strvillain = string.char(numo44t) -- villain guilty of lowercase
          else
            numsta44tus = 53 -- S53 other error (invalid char)
            strvillain = '#' .. tostring(numo44t) -- villain guilty of invalid
          end--if
          break -- give up -- exits upper inner loop only
        end--if
      end--if (numunilen==1) then
      strele44nt = strele44nt .. string.sub (strinstring,numsrcind,(numsrcind+numunilen-1)) -- 1...4 octet:s
      numsrcind = numsrcind + numunilen -- ONE-based -- for "(" ")" done above
      numtm44oc = numtm44oc + numunilen -- number of octet:s inside
      numtm44ut = numtm44ut + 1 -- number of UTF8 char:s inside
      if (not booklastr) then -- only ONE UTF8 char, only ONE iteration here
        break -- exits upper inner loop -- do NOT exit outer genuine loop
      end--if
    end--while upper inner loop -- copy element

    if (numsta44tus~=0) then
      break -- crucial to exit outer genuine loop too on error
    end--if
    if (booklastr and ((numtm44oc<2) or (numtm44ut<2))) then
      numsta44tus = 56 -- S56 broken bracketing (too short inside)
      break -- give up
    end--if

    numtm44x = 1 -- ONE-based search index for dupe investigation

    while true do -- lower inner loop -- walk through "tabmychars"
      if (numtm44x>numqua44nt) then
        break -- lower inner loop -- done -- ZERO iterations are possible
      end--if
      if (strele44nt==tabmychars[numtm44x]) then
        numsta44tus = 55 -- S55 dupe
        strvillain = strele44nt -- villain guilty of dupe
        break -- lower inner loop -- give up
      end--if
      numtm44x = numtm44x + 1
    end--while lower inner loop -- walk through "tabmychars"

    if (numsta44tus~=0) then
      break -- crucial to exit outer genuine loop too on error
    end--if

    numqua44nt = numqua44nt + 1 -- index variable ZERO-based -- table ONE-based
    tabmychars[numqua44nt] = strprefx .. strele44nt -- totally 1...18 octet:s

  end--while -- outer genuine loop

  if (numsta44tus~=0) then
    tabmychars = {} -- nothing here
  end--if

return numsta44tus, numqua44nt, strvillain, tabmychars

end--function prhstringtotab

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

-- Local function LFWLUKSALIGILO

-- Enhance string, namely the visible part of the link.

-- Input  : * one string and 3 numbers (one of them tristate)

-- Called only from LFWBREWLINK.

local function lfwluksaligilo (strvysy, numenklo, numwarnanya, numwarnabkg)
  local strstilo = ''
  strvysy = "<b>" .. strvysy .. "</b>" -- text is BOLD, enclosement is NOT
  if (numenklo==1) then
    strvysy = "<small>&#91;</small>" .. strvysy .. "<small>&#93;</small>" -- "[" and "]"
  end--if
  if (numenklo==2) then
    strvysy = "<small>(</small>" .. strvysy .. "<small>)</small>"
  end--if
  if (numwarnanya~=0) then
    strstilo = 'color:#' .. lfhwarna(numwarnanya) .. ';'
  end--if
  if (numwarnabkg~=0) then
    strstilo = strstilo .. 'background-color:#' .. lfhwarna(numwarnabkg) .. ';'
  end--if
  if (strstilo~="") then
    strvysy = '<span style="' .. strstilo .. '">' .. strvysy .. '</span>'
  end--if
  return strvysy
end--function lfwluksaligilo

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

-- Local function LFWBREWLINK

-- Brew a single link (maybe quasi-external http link, but actually
-- always internal not leaving the wiki)

-- Input  : * numtyplnk -- base type of link 0...6
--          * numtuncas -- case tuning 0...4 (1: lower | 2: upper | 3 | 4)
--                         (for ba types 1 2 5 6 only -- NOT for "^" and "~")
--          * numtungap -- gap tuning 0 or 1 (1: add space) (for ba types
--                         2 and 6 only -- NOT for "^" and "~")
--          * strfull -- fullpagename (category name must be already prefixed
--                       by a dot like ":Category:Piracy", here we add wall,
--                       visible string and brackets, ignored for type 4)
--          * strpostf -- postfix string (one letter, two letters, maybe
--                        other type of char or short string, special values
--                        "^" for "all" and "~" for "remainder" are permitted
--                        for types 0 and 1 only, may NOT be empty, may not be
--                        "''" (double apo), can be both "y" and "x" parts
--                        concatenated in this order for 2-dim table
--                        (caller has the responsibility)
--          * strvisi -- visible link string (may NOT be empty)
--          * numencl -- enclosement of the visible link string (0 none |
--                       1 "[" and "]" as "&#91;" and "&#93;" | 2 "(" and ")")
--                       forwarded to "lfwluksaligilo"
--          * numw8, numw9 -- colour
--          * boomowop -- true if <<#mw-pages>> desired (only type 0
--                        and only category, caller has the responsibility)
--          * numruangnama -- namespace (required, use ZERO if no better
--                            value available, ZERO is the default of the
--                            wiki software, ZERO will NOT be passed to
--                            the wiki, caller has the responsibility)
--          * strpos2pa -- for base link types 5 and 6 the "y" row part

-- Output : * strlaenken (always begins with "[" and ends with "]")

-- Depends on procedures :
-- [W] lfwluksaligilo
-- [U] prucasefull (in turn depends on "prulnutf8char" some [G] some [E])

-- Depends on "mw.uri.canonicalUrl".

-- BEWARE: It is the caller's responsibility to make sure that
-- the 12 parameters are valid and consistent.

-- The "strfull" fullpagename may contain plain spaces, no encoding
-- to underscore "_" or "%20" is needed since for base type 0 it goes
-- through "mw.uri.canonicalUrl". Gap tuning is possible only for base
-- link types 2 and 6 where we use wiki links thus spaces cannot cause
-- any major harm either.

-- "numtyplnk" values: 0 "from=" | 1 wiki link + "/"
--                     2 wiki link + raw postfix
--                     3 wiki link + "#" | 4 "#" section link
--                     5 combo 2-part link "/"+"#" 1+3
--                     6 combo 2-part link raw+"#" 2+3

-- With 2-part links (types 5 and 6) we use "strpos2pa" (only "y") and
-- "strpostf" ("y" and "x") in this order, otherwise we use only "strpostf"
-- and ignore "strpos2pa".

-- Incoming "strpos2pa" and "strpostf" are NOT used anymore after "stradrq"
-- and "strkrys" have been assigned from them. Only "stradrq" can be victim
-- of tuning. "stradrq" is needed even for bottom link "~" but NOT for
-- top link.

local function lfwbrewlink (numtyplnk, numtuncas, numtungap, strfull, strpostf, strvisi, numencl, numw8, numw9, boomowop, numruangnama, strpos2pa)
  local strlaenken = ""
  local stradrq = "" -- part of address or http query
  local strkrys = "" -- part after the cross "#"
  local strquery = "" -- can have ZERO or 1 or 2 items
  local booalltop = false -- all AKA top AKA "^"
  local boorembot = false -- bottom AKA remainder AKA "~"
  if (numtyplnk<=1) then
    booalltop = (strpostf=="^") -- do not use "strpostf" "strpos2pa" "strkrys"
    boorembot = (strpostf=="~") -- do not use "strpostf" "strpos2pa" "strkrys"
  end--if
  strvisi = lfwluksaligilo (strvisi, numencl, numw8, numw9)
  if ((not booalltop) and (not boorembot)) then
    if (numtyplnk<=2) then
      stradrq = strpostf -- "krys" NOT needed for 0 1 2
    end--if
    if (numtyplnk>=3) then
      strkrys = strpostf -- "adre" maybe needed (for 5 and 6 only)
    end--if
    if (numtyplnk>=5) then
      stradrq = strpos2pa -- "krys" needed and comes from "strpostf"
    end--if
    if (numtuncas==1) then
      stradrq = prucasefull (stradrq, false, false) -- force lowercase beg
    end--if
    if (numtuncas==2) then
      stradrq = prucasefull (stradrq, true, false) -- force uppercase beg
    end--if
    if (numtuncas==3) then
      stradrq = prucasefull (stradrq, false, true) -- force lowercase all
    end--if
    if (numtuncas==4) then
      stradrq = prucasefull (stradrq, true, true) -- force uppercase all
    end--if
    if (numtungap==1) then
      stradrq = "_" .. stradrq -- add space encoded as underscore "_" (!!!)
    end--if
  end--if
  if (boorembot) then
    stradrq = "~" -- CRUCIAL & needed several times below ("strkrys" NOT used)
  end--if
  if ((numtyplnk==0) and (not booalltop)) then
    strquery = "from=" .. stradrq -- even for bottom AKA remainder but NOT top
  end--if
  if (numruangnama~=0) then
    if (strquery~="") then
      strquery = strquery .. "&" -- need "and" separator (NOT wall separator)
    end--if
    strquery = strquery .. "namespace=" .. tostring (numruangnama)
  end--if
  if (numtyplnk==0) then
    if (strquery=='') then
      strlaenken = tostring ( mw.uri.canonicalUrl ( strfull ) )
    else
      strlaenken = tostring ( mw.uri.canonicalUrl ( strfull , strquery ) )
    end--if
  end--if
  if ((numtyplnk~=0) and (numtyplnk~=4)) then
    strlaenken = strfull -- via "canonicalUrl" for 0 and not needed for 4
  end--if
  if (((numtyplnk==1) or (numtyplnk==5)) and (not booalltop)) then
    strlaenken = strlaenken .. "/" -- link type 1 or 5 (but NOT for "^")
  end--if
  if ((numtyplnk~=0) and (not booalltop)) then
    strlaenken = strlaenken .. stradrq -- type 1...6 -- tuning already done
  end--if
  if ((numtyplnk>=3) and (not booalltop)) then
    strlaenken = strlaenken .. "#" .. strkrys -- link type 3 or 4 or 5 or 6
  end--if
  if (boomowop) then
    strlaenken = strlaenken .. "#mw-pages" -- only legal for type 0 some cases
  end--if
  if (numtyplnk==0) then
    strlaenken = strlaenken .. " " -- separation space (NOT wall "|" in http)
  else
    strlaenken = strlaenken .. "|" -- wiki link
  end--if
  strlaenken = "[" .. strlaenken .. strvisi .. "]"
  if (numtyplnk~=0) then
    strlaenken = "[" .. strlaenken .. "]"
  end--if
  return strlaenken
end--function lfwbrewlink

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

-- Local function LFWGETNSOFTITLE

local function lfwgetnsoftitle (strnstitle)
  local tabtitlensobject = {}
  local numnsoftheobjk = 65535 -- preASSume guilt
  if (type(strnstitle)=='string') then
    tabtitlensobject = mw.title.new(strnstitle) -- type "nil" for invalid
    if (type(tabtitlensobject)=='table') then
      numnsoftheobjk = tabtitlensobject.namespace
    end--if
  end--if
  return numnsoftheobjk
end--function lfwgetnsoftitle

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

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

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

function exporttable.ek (arxframent)

  -- various types 11 parameters (fpn,tit,dud,typ,mwp,ans,alf,fla,ctb,cos,pre)

  local strfpn    = ''     -- fullpagename AKA title
  local boohavfpn = false  -- true even if "-" was supplied
  local boofpns   = false  -- no fullpagename and link type 3 due to "fpn=-"

  local strtit    = ''     -- title of the table
  local bootits   = false  -- suppress table due to "tit=-"

  local boodud    = false  -- berdimensi dua

  local strtyp    = ''     -- base type & 2 tuning digits (guess "000" or ...)
  local boohavtyp = false  -- true even if "000" was supplied
  local numtypb   = 0      -- (0...6) ba type of link -- 0 "from=" 1 "/" 2 raw
  local numtycas  = 0      -- case tuning 0 or 1 2 3 4 (1 lower | 2 upper)
  local numtygap  = 0      -- gap tuning 0 or 1

  local boomwp    = false  -- add <<#mw-pages>>

  local numans    = 0      -- namespace parameter for "Special:AllPages"
  local boohavans = false  -- true even if ZERO was supplied

  local stralf    = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -- alph (later to "tababjad")

  local strfla    = ''
  local boohavfla = false -- true even if "-" was supplied
  local tabduaempat = {}  -- from "fla="
  local strflb4lk = ''    -- "top" link value ("all" "top" "^")
  local strflb5vi = ''    -- "top" visible text
  local strflb6lk = ''    -- "bottom" link value ("remainder" "bottom" "~")
  local strflb7vi = ''    -- "bottom" visible text
  local booflasup = false -- suppress 2 extra links due to "fla=-"

  local numctb    = 0       -- (0...5) permutation of colour table background
  local boohavctb = false   -- true even if ZERO was supplied

  local strcos    = ''      -- control string tuning (default "000-00")
  local boohavcos = false   -- true even if "000-00" was supplied
  local numscw8   = 0
  local numscw9   = 0
  local numenkli  = 0       -- forwarded to some calls, can be 0 or 1 only, []
  local numspix   = 0       -- (0...6) 1...6 indx "contabsepa" or ZERO -> NBSP
  local boosepenh = false   -- enhance separator with 2 NBSP:s if "true"

  local strpre    = ''      -- default is empty "", empty value is even valid
  local boohavpre = false   -- true if param used (explicit empty impossible)

  -- general unknown type

  local vartymp = 0 -- variable without type

  -- special type "args" AKA "arx"

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

-- misc tab

  local tababjad = {} -- fill in useful default content later if needed

-- misc str

  local strfupagen = ''
  local strret     = ''  -- output string
  local strapart   = ''  -- separator for 1-dim or no table from "contabsepa"
  local strtimp    = ''
  local strtpm2dim = ''
  local stryonl    = ''  -- for 2-dimensional -- only "y"
  local stryyxx    = ''  -- for 2-dimensional -- "y" and "x" concatenated
  local strrow     = ''  -- for 2-dimensional
  local strminbov  = ''  -- from PRHSTRINGTOTAB

-- misc num

  local numerr    = 0  -- error code (0...39 -- 0 OK 1 inter 2 anon 3 ...)
  local numabjad  = 0  -- hard minimum 3 -- maximum ca 220

  local numoct    = 0
  local numtmpx   = 0
  local numtmpy   = 0

  local numclspl  = 0  -- colspan left
  local numclspr  = 0  -- colspan right

  -- general "boo"

  local booiskat  = false  -- true for categories
  local booisspe  = false  -- true for special pages

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

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

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

  -- constrkatp5,constrkatp6,constrspep5,constrspep6 must be assigned
  -- constrkatp5,constrspep5 must be non-empty

  if ((type(constrkatp5)~='string') or (type(constrspep5)~='string')) then
    numerr = 1 -- #E01 -- internal
  else
    if ((constrkatp5=='') or (constrspep5=='')) then
      numerr = 1 -- #E01 -- internal
    end--if
  end--if
  if ((type(constrkatp6)~='string') or (type(constrspep6)~='string')) then
    numerr = 1 -- #E01 -- internal
  end--if

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

  if (numerr==0) then
    arxsomons = arxframent.args -- "args" from our own "frame"
    if (arxsomons[1]) then
      numerr = 2 -- #E02 -- anonymous params NOT appreciated
    else
      vartymp = arxsomons['parentframe']
      if (vartymp=='true') then
        arxsomons = arxframent:getParent().args -- "args" frm parent's "frame"
        if (arxsomons[1]) then
          numerr = 2 -- #E02 -- anonymous params NOT appreciated
        end--if
      end--if
    end--if
  end--if

  ---- SEIZE 11 OPTIONAL NAMED PARAMETERS ----  !!!FIXME!!! use LFICHKPARAM

  while true do -- fake loop

    if (numerr~=0) then
      break -- to join mark -- we already suck :-(
    end--if

    do -- scope
      local vartpm = 0
      local numlo3ng = 0

      vartpm = arxsomons['fpn'] -- 5...100 octet:s zero or one colon ":" code 58
      numlo3ng = prgstringnonempty(vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if (vartpm=='-') then
          boofpns = true -- suppress the fullpagename
          boohavfpn = true -- "strfpn" was preassigned to empty "" far above
        else
          if ((numlo3ng<5) or (numlo3ng>100)) then
            numerr = 3 -- bad "fpn=" #E03 (length)
            break -- to join mark
          end--if
          strfpn = vartpm
          boohavfpn = true -- "strfpn" was preassigned to empty "" far above
          if (lfipillegal(strfpn,true,true,true,1,true,true,true)) then
            numerr = 3 -- bad "fpn=" #E03 (illegal char:s)
            break -- to join mark
          end--if
          numtmpx = lfgcountchr(strfpn,58)
          if (numtmpx>1) then
            numerr = 3 -- bad "fpn=" #E03 (number of colon:s)
            break -- to join mark
          end--if
          numtmpx = string.byte (strfpn,1,1)
          numtmpy = string.byte (strfpn,numlo3ng,numlo3ng)
          if ((numtmpx==58) or (numtmpy==58)) then
            numerr = 3 -- bad "fpn=" #E03 (colon at begin or end)
            break -- to join mark
          end--if
        end--if
      end--if (numlo3ng~=0) then

    vartpm = arxsomons["tit"] -- 5...120 octet:s but no [[]] {{}} or similar
    numlo3ng = prgstringnonempty(vartpm)
    if (numlo3ng~=0) then -- ignore empty string
        if (vartpm=="-") then
          bootits = true -- suppress the table
        else
          if ((numlo3ng<5) or (numlo3ng>120)) then
            numerr = 4 -- #E04 bad "tit" generic
            break -- to join mark
          end--if
          strtit = vartpm -- there is no "boohavtit"
          if (lfipillegal(strtit,true,true,true,1,true,true,true)) then
            numerr = 5 -- #E05 bad "tit" illegal content apo/brackets
            break -- to join mark
          end--if
        end--if (vartpm=="-") else
    end--if (numlo3ng~=0) then

    vartpm = arxsomons["dud"] -- bool must be "0" or "1"
    if (type(vartpm)=='string') then
      numlo3ng = string.len (vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if (numlo3ng~=1) then
          numerr = 6 -- bad "dud=" #E06
          break -- to join mark
        end--if
        numoct = lfdec1diglm(string.byte(vartpm,1,1),1) -- 255 if invalid
        if (numoct==255) then
          numerr = 6 -- bad "dud=" #E06
          break -- to join mark
        end--if
        boodud = (numoct==1) -- was preassigned to "false" ie 1-dim far above
      end--if (numlo3ng~=0) then
    end--if

    vartpm = arxsomons["typ"] -- need 3 digit:s base link type + 2 tuning digi
    if (type(vartpm)=='string') then
      numlo3ng = string.len (vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if (numlo3ng~=3) then
          numerr = 8 -- bad "typ=" #E08
          break -- to join mark
        end--if
        strtyp = vartpm -- further validation below
        boohavtyp = true -- "strtyp" was preassigned to empty "" far above
      end--if (numlo3ng~=0) then
    end--if

    vartpm = arxsomons["mwp"] -- bool must be "0" or "1"
    if (type(vartpm)=='string') then
      numlo3ng = string.len (vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if (numlo3ng~=1) then
          numerr = 19 -- bad "mwp=" #E19
          break -- to join mark
        end--if
        numoct = lfdec1diglm(string.byte(vartpm,1,1),1) -- 255 if invalid
        if (numoct==255) then
          numerr = 19 -- bad "mwp=" #E19
          break -- to join mark
        end--if
        boomwp = (numoct==1) -- was preassigned to "false" far above
      end--if (numlo3ng~=0) then
    end--if

    vartpm = arxsomons["ans"] -- 1...5 octet:s DEC number 0...5'000
    if (type(vartpm)=='string') then
      numlo3ng = string.len (vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if ((numlo3ng<1) or (numlo3ng>5)) then
          numerr = 21 -- #E21 bad "ans="
          break -- to join mark
        end--if
        numans = prndecinp (vartpm,true,true) -- -1 on error
        if ((numans<0) or (numans>5000)) then
          numerr = 21 -- #E21 bad "ans="
          break -- to join mark
        end--if
        boohavans = true
      end--if (numlo3ng~=0) then
    end--if

    vartpm = arxsomons["alf"] -- 5...400 octet:s with further restrictions
    if (type(vartpm)=='string') then
      numlo3ng = string.len (vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if ((numlo3ng<5) or (numlo3ng>400)) then
          numerr = 22 -- #E22 bad "alf" generic
          break -- to join mark
        end--if
        stralf = vartpm -- later converted to "tababjad" with further checks
      end--if (numlo3ng~=0) then
    end--if

    vartpm = arxsomons["fla"] -- 5...80 octet:s (exactly 1 or 3 comma "," 44)
    if (type(vartpm)=='string') then
      numlo3ng = string.len (vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if ((vartpm~="-") and ((numlo3ng<5) or (numlo3ng>80))) then
          numerr = 27 -- bad "fla" #E27
          break -- to join mark
        end--if
        strfla = vartpm -- "-" or 5...80 octet:s, further validation below
        boohavfla = true -- "strfla" was preassigned to empty "" far above
      end--if (numlo3ng~=0) then
    end--if

    vartpm = arxsomons["ctb"] -- need 1 digit "0"..."5" -- colour permutation
    if (type(vartpm)=='string') then
      numlo3ng = string.len (vartpm)
      if (numlo3ng~=0) then -- ignore empty string
        if (numlo3ng~=1) then
          numerr = 28 -- bad "ctb=" #E28
          break -- to join mark
        end--if
        numctb = lfdec1diglm(string.byte(vartpm,1,1),5) -- 255 if invalid
        if (numctb==255) then
          numerr = 28 -- bad "ctb=" #E28
          break -- to join mark
        end--if
        boohavctb = true -- "numctb" was preassigned to 0 far above
      end--if (numlo3ng~=0) then
    end--if

      vartpm = arxsomons['cos'] -- ctl string 6 ext hex digi (up to "L" 21)
      if (prgstringnonempty(vartpm)~=0) then -- ignore empty string
        if (not prgstringrange(vartpm,6,6)) then
          numerr = 32 -- bad "cos=" #E32 (we have also separate #E33 below)
          break -- to join mark
        end--if
        strcos = vartpm -- was preassigned to empty "" far above
        boohavcos = true -- further validation below
      end--if (prgstringnonempty(vartpm)~=0) then

      vartpm = arxsomons['pre'] -- need 1...9 oct, not even single [ ] legal
      if (prgstringnonempty(vartpm)~=0) then -- ignore empty string
        if (not prgstringrange(vartpm,1,9)) then
          numerr = 35 -- bad "pre=" #E35 (length)
          break -- to join mark
        end--if
        if (lfipillegal(vartpm,true,true,true,2,true,true,true)) then
          numerr = 36 -- bad "pre=" #E36 (illegal char:s) extra code
          break -- to join mark
        end--if
        strpre = vartpm -- was preassigned to empty "" far above
        boohavpre = true
      end--if (prgstringnonempty(vartpm)~=0) then

    end--do scope

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

  ---- PROCESS ONE HIDDEN NAMED PARAM ----

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

  strfupagen = '' -- using vartymp here
  if ((numerr==0) and (not boohavfpn)) then -- get fullpagename (error if bad, silent if absent)
    vartymp = arxsomons['fullpanaoverridetestonly']
    if (type(vartymp)=='string') then -- do NOT merge if:s
      if (prgstringrange(vartymp,1,200)) then -- empty or too long NOT legal
        strfupagen = vartymp
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  ---- SEIZE FULLPAGENAME FROM MW IF NEEDED ----

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

  if ((numerr==0) and (strfupagen=='') and (not boohavfpn)) then -- get fullpagename (error if bad)
    vartymp = mw.title.getCurrentTitle().prefixedText -- "{{FULLPAGENAME}}"
    if (prgstringrange(vartymp,1,200)) then -- empty or too long NOT legal
      strfupagen = vartymp -- cannot be left empty
    else
      numerr = 1 -- #E01 internal
    end--if
  end--if

  ---- FULL ---

  -- there is "boohavfpn" and "boofpns", do NOT seize fullpagename
  -- anywhere for "fpn=-"

  if ((numerr==0) and (not boohavfpn)) then
    strfpn = strfupagen
  end--if

  ---- FIGURE OUT WHETHER THE PAGE IS A CATEGORY OR SPECIAL PAGE ----

  -- assign 2 bool:s based on "strfpn" (can't be empty anymore)  !!!FIXME!!! use LFWGETNSOFTITLE

  -- "booiskat" has several effects:
  -- * affects type guessing
  -- * used for inconsistency evaluation
  -- * prohibits lowercase
  -- * adds extra colon for link type 2 -> "[[:Category:Piracy]]"

  if (numerr==0) then
    booiskat = lfgdblcmp(strfpn,constrkatp5,constrkatp6)
    booisspe = lfgdblcmp(strfpn,constrspep5,constrspep6)
  end--if

  ---- GUESS "TIT=" IF WE DO NOT HAVE ANY YET ----

  -- "tit=" depends on "fpn="
  -- there is no "boohavtit", check if (strtit=='') instead

  if ((numerr==0) and (strtit=='')) then
    if (boofpns) then
      strtit = 'Index' -- this was rocket science
    else
      strtit = strfpn -- this was rocket surgery
    end--if
  end--if

  ---- GUESS "TYP=" IF WE DO NOT HAVE ANY YET ----

  -- "typ=" depends on "fpn=" (via "boofpns" and "booiskat" and "booisspe")
  -- based on "boohavtyp"

  -- 0 "from=" | 1 wiki link + "/" | 2 wiki link + raw postfix
  -- 3 wiki link + "#" | 4 "#" section link | 5 and 6 "combo" never guessed

  -- guessing type 4 for "fpn=-" (and NO other type is tolerable), otherwise
  -- type 1 if the page is special and "ans=" is ZERO, otherwise type 0
  -- if the page is category or special, otherwise type 3

  if ((numerr==0) and (boohavtyp==false)) then
    while true do -- fake loop

      if (boofpns) then
        strtyp = "400" -- "fpn=-" -> base type 4 and no tuning
        break
      end--if
      if (booisspe and (numans==0)) then
        strtyp = "100" -- special and "ans=" is ZERO
        break
      end--if
      if (booiskat or booisspe) then
        strtyp = "000" -- base type 0 and no tuning
        break
      end--if
      strtyp = "300" -- last: maybe some appendix needing the crossy "#"-type

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

  ---- PROCESS "TYP=" ----

  -- from above "strtyp" has 3 octet:s (empty or invalid length is
  -- NOT possible) and possible guess done, but validation incomplete,
  -- and we have "boohavtyp" (needed below)

  -- # base link type 0...6
  -- # case tuning 0...4 (0 none | 1 beg low | 2 beg upp | 3 all
  --   low | 4 all upp) (types 1 2 5 6 only)
  -- # gap tuning 0 or 1 pseudo-bool (types 2 and 6 only)

  if (numerr==0) then

    while true do -- fake loop
      numtypb = lfdec1diglm(string.byte(strtyp,1,1),6) -- 255 if invalid
      if (numtypb==255) then
        numerr = 9 -- bad base type in "typ=" #E09 -- separate error code
        break -- to join mark
      end--if
      numtycas = lfdec1diglm(string.byte(strtyp,2,2),4) -- 255 if invalid
      if (numtycas==255) then
        numerr = 8 -- bad case tuning value in "typ=" #E08
        break -- to join mark
      end--if
      numtygap = lfdec1diglm(string.byte(strtyp,3,3),1) -- 255 if invalid
      if (numtygap==255) then
        numerr = 8 -- bad gap tuning value in "typ=" #E08
        break -- to join mark
      end--if
      if ((numtycas~=0) and (numtypb~=1) and (numtypb~=2) and (numtypb~=5) and (numtypb~=6)) then
        numerr = 8 -- illegal use of case tuning in "typ=" #E08
        break -- to join mark
      end--if
      if ((numtygap~=0) and (numtypb~=2) and (numtypb~=6)) then
        numerr = 8 -- illegal use of gap tuning in "typ=" #E08
        break -- to join mark
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if (numerr==0) then

  ---- GUESS "FLA" IF WE DO NOT HAVE ANY YET ----

  -- "fla=" depends on "typ="

  -- we need "numtypb" to be already assigned (separate rules
  -- apply for types 0 and 1)

  -- based on "boohavfla"

  if ((numerr==0) and (boohavfla==false)) then
    if (numtypb<=1) then
      strfla = "^,all,~,remainder" -- types 0 1 ("^" is placeholder for empty)
    else
      strfla = "-" -- types 2...6 (will assign "booflasup" to "true" soon)
    end--if
  end--if

  ---- PROCESS "FLA=" ----

  -- from above "strfla" either is "-" or has 5...80 octet:s (empty or
  -- invalid length is NOT possible), but validation incomplete (need
  -- 2 or 4 CSV fragments), "boohavfla" exists but not needed anymore

  -- we need "numtypb" to be already assigned (separate
  -- rules apply for types 0 and 1)

  -- assign strflb4lk, strflb5vi, strflb6lk, strflb7vi and booflasup

  -- here we can throw #E27 bad "fla="

  while true do -- fake loop
    if (numerr~=0) then
      break -- to join mark
    end--if
    if (strfla=='-') then
      booflasup = true -- suppress those 2 extra links
      break -- to join mark
    end--if
    numtmpx, numtmpy, tabduaempat = prhsplittotable (strfla,44,nil,1,4) -- CSV
    if ((numtmpx~=0) or ((numtmpy~=2) and (numtmpy~=4))) then
      numerr = 27 -- #E27 bad "fla"
      break -- to join mark
    end--if
    if (numtmpy==2) then
      strflb5vi = tabduaempat[0] -- 1 comma 2 words (visible, visible)
      strflb7vi = tabduaempat[1]
      if (numtypb<=1) then
        strflb4lk = '^' -- special placeholder for empty
        strflb6lk = '~'
      else
        strflb4lk = strflb5vi -- copy from visible text to link value
        strflb6lk = strflb7vi
      end--if
    else
      strflb4lk = tabduaempat[0] -- 3 commas 4 words (link, visi, link, visi)
      strflb5vi = tabduaempat[1]
      strflb6lk = tabduaempat[2]
      strflb7vi = tabduaempat[3]
    end--if
    break -- finally
  end--while -- fake loop -- join mark

  ---- ASSIGN "COS=" TO DEFAULT IF WE DO NOT HAVE ANY YET ----

  -- based on "boohavcos"

  if ((numerr==0) and (not boohavcos)) then
    strcos = '000-00' -- control string
  end--if

  ---- PROCESS "COS=" ----

  -- from above "strcos" has always 6 octet:s (empty or invalid length
  -- is NOT possible) but validation is incomplete, and we have
  -- "boohavcos" (needed below)

  -- "numspix" was preassigned to ZERO far above
  -- "boosepenh" was preassigned to "false" far above

  -- always assign (colours):
  -- numscw8, numscw9 -- font, backgound (may NOT be non-ZERO and equal)

  -- always assign (enclosing):
  -- # numenkli -- non-default 0 suppresses "[","]" enclosing (value 2 for
  --   enclosing exists but cannot occur is this variable)

  -- for 1-dim or no table assign also (type of separator):
  -- # "numspix" must be "0"..."6" -- default is 0 ie NBSP
  -- # "boosepenh" default is "false"
  -- later we brew "strapart" from them

  if (numerr==0) then

    while true do -- fake loop
      numscw8 = lfnexthextoint(string.byte(strcos,1,1)) -- 255 if invalid
      if (numscw8>21) then
        numerr = 32 -- bad "cos=" #E32 generic -- text colour
        break -- to join mark
      end--if
      numscw9 = lfnexthextoint(string.byte(strcos,2,2)) -- 255 if invalid
      if (numscw9>21) then
        numerr = 32 -- bad "cos=" #E32 generic -- text colour
        break -- to join mark
      end--if
      if ((numscw8~=0) and (numscw8==numscw9)) then
        numerr = 33 -- #E33 bad "cos=" both colours equal
        break -- to join mark
      end--if
      numoct = lfdec1diglm(string.byte(strcos,3,3),1) -- bool -- 255 if invali
      if (numoct==255) then
        numerr = 32 -- bad "cos=" #E32 generic -- enclosing
        break -- to join mark
      end--if
      numenkli = 1 - numoct -- invert it
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if (numerr==0) then

  if ((numerr==0) and (not boodud)) then

    while true do -- fake loop
      numspix = lfdec1diglm(string.byte(strcos,5,5),6) -- 255 if invalid
      if (numspix==255) then
        numerr = 32 -- bad "cos=" #E32 generic -- type of separator
        break -- to join mark
      end--if
      numoct = lfdec1diglm(string.byte(strcos,6,6),1) -- 255 if invalid
      if (numoct==255) then
        numerr = 32 -- bad "cos=" #E32 generic -- type of separator
        break -- to join mark
      end--if
      boosepenh = (numoct==1)
      if ((numspix==0) and boosepenh) then
        numerr = 32 -- bad "cos=" #E32 -- do NOT enclose NBSP with 2 NBSP:s
        break -- to join mark
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if ((numerr==0) and (not boodud)) then

  ---- CHECK FOR INCONSISTENT PARAMETERS ----

  -- criminal conditions:
  -- # "tit=-" and "dud=1"
  -- # "tit=-" and "ctb="
  -- # "dud=1" and "base link type" <> 0 1 5 6 and "typ=" explicitly specified
  -- # "dud=0" and "base link type" is 5 or 6 (not that 5 or 6 is nvr guessed)
  -- # "fpn=-" and "base link type" <> 4 and "typ=" explicitly specified
  -- # "ans="  and "base link type" <> 0 and "typ=" explicitly specified
  -- # "fpn=" given (not "-") and "base link type" = 4
  -- # "dud=1" and "pre="
  -- # "mwp=1" and "ans="
  -- error code #E38
  -- rely on bool:s:  - "boodud", "boomwp"
  --                  - "bootits" (OTOH "booflasup" not used here), "boofpns"
  --                  - "boohavfpn", "boohavtyp", "boohavans", "boohavctb"
  -- rely on integer: - "numtypb"

  if (numerr==0) then

    if (bootits and boodud) then
      numerr = 38 -- "tit=-" and "dud=1"
    end--if
    if (bootits and boohavctb) then
      numerr = 38 -- "tit=-" and "ctb="
    end--if
    if (boodud and boohavtyp and (numtypb~=0) and (numtypb~=1) and (numtypb~=5) and (numtypb~=6)) then
      numerr = 38 -- "dud=1" and bad type
    end--if
    if ((not boodud) and ((numtypb==5) or (numtypb==6))) then
      numerr = 38 -- "dud=0" and bad type
    end--if
    if (boofpns and boohavtyp and (numtypb~=4)) then
      numerr = 38 -- "fpn=-" and bad type
    end--if
    if (boohavans and boohavtyp and (numtypb~=0)) then
      numerr = 38 -- "ans=" and "base link type" <> 0
    end--if
    if (boohavfpn and (boofpns==false) and (numtypb==4)) then
      numerr = 38 -- "fpn=" given (not "-") and bad type
    end--if
    if (boodud and boohavpre) then
      numerr = 38 -- "dud=1" and "pre="
    end--if
    if (boomwp and boohavans) then
      numerr = 38 -- "mwp=1" and "ans="
    end--if

  end--if

  ---- CHECK FOR INCONSISTENCY WITH FULLPAGENAME ----

  -- criminal conditions:
  -- # "mwp=1" and non-cat #E43
  -- # "ans="  and non-special #E44
  -- # "dud=1" and "base link type" <> 0 1 5 6 and that type auto-guessed
  -- # "fpn=-" and "base link type" <> 4 and that type auto-guessed
  -- # "ans="  and "base link type" <> 0 and that type auto-guessed
  -- # cat     and "base link type" <> 0 2 and that type auto-guessed
  -- # special and "base link type" <> 0 1 and that type auto-guessed
  -- error codes #E42 generic/other -- #E43 #E44
  -- rely on booleans "booiskat", "booisspe", "boofpns"

  if (numerr==0) then

    if (boomwp and (booiskat==false)) then
      numerr = 43 -- #E43 "mwp=1" and non-cat
    end--if
    if (boohavans and (booisspe==false)) then
      numerr = 44 -- #E44 "ans=" and non-special
    end--if
    if (boodud and (numtypb~=0) and (numtypb~=1) and (numtypb~=5) and (numtypb~=6)) then
      numerr = 42 -- "dud=1" and bad type
    end--if
    if (boofpns and (numtypb~=4)) then
      numerr = 42 -- "fpn=-" and bad type
    end--if
    if (boohavans and (numtypb~=0)) then
      numerr = 42 -- "ans=" and "base link type" <> 0
    end--if
    if (booiskat and (numtypb~=0) and (numtypb~=2)) then
      numerr = 42 -- cat and bad type
    end--if
    if (booisspe and (numtypb~=0) and (numtypb~=1)) then
      numerr = 42 -- special and bad type
    end--if

  end--if

  ---- CONVERT THE ALPHABET ----

  -- convert UTF8 string "stralf" to elements for single char:s stored
  -- in "tababjad" and assign "numabjad"

  -- "stralf" is guaranteed to be nonempty (5...400) either
  -- from default or from "alf=" but further checks are needed

  -- "strpre" contains the prefix string to be attached to all letters,
  -- default is empty, allows to creates sublevel index, for example a
  -- dictionary has many words "abstract" to "zone" divided on 26 subpages
  -- "A"..."Z", and then we further divide the subpage "A" as "AA" to "AZ"
  -- and index the subsections via #-type section links

  -- we depend on "strfpn" only indirectly via "booiskat" in order to
  -- decide whether lowercase is tolerable or not (only ASCII checked)

  -- order of char:s is irrelevant ("alf=EDCBA" is fine), dupes illegal
  -- ("alf=EDCBAD" is bad), "cluster char:s" are allowed (2...9 octet:s
  -- and 2...9 UTF8 char:s between brackets "(" and ")")

  -- ensure that at least 3 alphabet elements are available at the end

  -- possible error codes: S52->#E22 generic | S53->#E23 ASCII | S54->#E24
  -- lowercase with cat S55->#E25 dupe | S56->#E26 broken bracketing

  if (numerr==0) then
    numerr, numabjad, strminbov, tababjad = prhstringtotab (stralf, (not booiskat), strpre)
    if (numerr~=0) then
      numerr = (({[53]=23,[54]=24,[55]=25,[56]=26})[numerr]) or 22 -- LUT in LUA
    end--if
    if ((numerr==0) and (numabjad<3)) then
      numerr = 22 -- #E22 bad "alf" too -- proc tolerates this, here we don't
    end--if
  end--if (numerr==0) then

  ---- GENERATE ANGRY MESSAGE IF THERE IS AN ERROR ----

  if (numerr~=0) then
    strret = prhbrewerrfra(numerr)
    if ((numerr>=23) and (numerr<=25)) then -- #E23 #E24 #E25
      strret = strret .. '<br>Botched at element position ' .. tostring(numabjad) .. ' with value "' .. strminbov .. '".'
    end--if
  end--if

  ---- BEGIN TO BREW THE TABLE IF APPROPRIATE ----

  -- colours are "constrborder" and "constrbakgnd" and we
  -- permute them by "numctb" (0...5) in "lfiper6mut"

  -- "contabhtml.matabg" "constrtabu8" "constrtabu9" build the "<table>" element

  -- the title is made big and bold through "<big><b>"

  -- the title is obligatory, if you hate it then suppress the complete
  -- table (bootits==true) through syntax "tit=-"

  -- note that a 1-dimensional table has the complete list in one cell
  -- but a 2-dimensional table has 17'000'000 (maybe only "numabjad") cells
  -- in a row and thus we need "colspan" for the title line

  -- below the title line we brew a narrow separation line

  if ((numerr==0) and (not bootits)) then
    if (boodud) then
      strtimp = '<td colspan="' .. tostring (numabjad) .. '">'
    else
      strtimp = '<td>' -- no silly span in 1-dimensional table
    end--if
    strret = contabhtml.matabg .. lfiper6mut (constrborder,numctb) .. constrtabu8
    strret = strret .. lfiper6mut (constrbakgnd,numctb) .. constrtabu9
    strret = strret .. '<tr>' .. strtimp .. '<big><b>' .. strtit .. '</b></big></td></tr>'
    strret = strret .. '<tr style="font-size:20%;">' .. strtimp .. '&nbsp;</td></tr>' -- empty line
  end--if

  ---- APPLY THE SILLY DOT RULE IF NEEDED ----

  -- category pages can have type 0 or 2 only, and only the latter
  -- needs this silly hack

  if (booiskat and (numtypb==2)) then
    strfpn = ":" .. strfpn -- "[[:Category:Piracy]]"
  end--if

  ---- PREPARE THE SEPARATOR FOR THE 1-DIMENSIONAL LIST FROM "COS=" ----

  -- separator string will be in "strapart"
  -- (preassigned to empty "" far above, we must fix it here)

  -- this is useful for 1-dimensional table and no table only

  -- "numspix" is 1...6 index in "contabsepa" or ZERO for NBSP
  -- 1...6 -> dash, dbldash, star, wall, small central dot, huge central dot

  -- if "boosepenh" is "true" then enclose separator in 2 NBSP:s ie "&nbsp;"
  -- (NOT valid for separator type in "numspix" ZERO)

  if (numerr==0) then
    strapart = "&nbsp;"
    if (numspix~=0) then
      vartymp = contabsepa[numspix] -- 1...6
      if (type(vartymp)=='string') then
        strapart = vartymp
      end--if
    end--if
    if (boosepenh) then
      strapart = '&nbsp;' .. strapart .. '&nbsp;' -- NOT for "numspix" = 0
    end--if
  end--if

  ---- CARRY OUT THE HARD WORK TO BREW THE 1-DIMENSIONAL LIST ----

  -- here we can have a table or not (check "bootits")

  -- a single "<tr><td>" and "</td></tr>" is needed if we have a table

  -- relevant parameters forwarded to "lfwbrewlink":
  -- # numtypb (0...6)
  -- # numtycas (0...2)
  -- # numtygap (0 or 1)
  -- # strfpn (fullpagename)
  -- # strflb4lk & strflb5vi ("top"), strflb6lk & strflb7vi ("bottom")
  -- # numscw8, numscw9
  -- # boomwp
  -- # numans
  -- some of the 12 params (only 2 -- namely enclosing and "y"-part) are
  -- maybe hardcoded here rather than forwarded

  -- other relevant parameters:
  -- # "bootits"
  -- # "booflasup" (suppress those 2 extra links if "true")
  -- # "strapart"

  -- the core content is "tababjad" and "numabjad" (they MUST be valid)
  -- the alphabet is in "tababjad" and number of letters is
  -- in "numabjad" (MUST be >=3)

  if ((numerr==0) and (not boodud)) then

    if (not bootits) then
      strret = strret .. "<tr><td>" -- have a table
    end--if

    if (not booflasup) then -- have "top" (here) and "bottom" (below)
      strret = strret .. lfwbrewlink (numtypb, numtycas, numtygap, strfpn, strflb4lk, strflb5vi, 2, numscw8, numscw9, boomwp, numans, "") .. strapart
    end--if

    numtmpx = 1 -- ONE-based index
    while true do -- walk through "tababjad"
      strtimp = tababjad[numtmpx] -- this CANNOT fail
      strret = strret .. lfwbrewlink (numtypb, numtycas, numtygap, strfpn, strtimp, strtimp, numenkli, numscw8, numscw9, boomwp, numans, "")
      if (numtmpx==numabjad) then
        break -- done -- ZERO iterations NOT possible
      end--if
      strret = strret .. strapart
      numtmpx = numtmpx + 1 -- complete list is in 1 table cell
    end--while

    if (not booflasup) then -- have "top" (above) and "bottom" (here)
      strret = strret .. strapart .. lfwbrewlink (numtypb, numtycas, numtygap, strfpn, strflb6lk, strflb7vi, 2, numscw8, numscw9, boomwp, numans, "")
    end--if

    if (not bootits) then
      strret = strret .. "</td></tr>" -- have a table
    end--if

  end--if

  ---- CARRY OUT THE VERY HARD WORK TO BREW THE 2-DIMENSIONAL LIST ----

  -- here we always do have a table due to "dud=1" -> (boodud==true)

  -- every link gets a separate table cell with "<td>" and "</td>"

  -- "top" and "bottom" land in a separate table row with "colspan" below the
  -- alphabet matrix, note that the table layout may be suboptimal with long
  -- visible strings for "top" and "bottom" and few elements in the alphabet

  -- relevant parameters forwarded to "lfwbrewlink":
  -- # numtypb (0...6)
  -- # numtycas (0...2)
  -- # numtygap (0 or 1)
  -- # strfpn (fullpagename)
  -- # strflb4lk & strflb5vi ("top"), strflb6lk & strflb7vi ("bottom")
  -- # numscw8, numscw9
  -- # boomwp
  -- # numans
  -- some of the 12 params (only 2 -- namely enclosing and "y"-part) are
  -- maybe hardcoded here rather than forwarded

  -- other relevant parameters (no "bootits" no "strapart" here):
  -- # "booflasup" (suppress those 2 extra links if "true")

  -- the core content is "tababjad" and "numabjad" (they MUST be valid)
  -- the alphabet is in "tababjad" and number of letters is
  -- in "numabjad" (MUST be >=3)

  -- An absurd problem can occur here that we have to guard against. Apo ("'")
  -- is and must be permitted in the alphabet, but a double apo ("''") is
  -- prohibited in pagenames as it would cause italic style. Thus if the
  -- 2-dimensional processing happens to generate a double apo ("''") (note
  -- that "pre=" is prohibited together with "dud=1") then we give F**K in
  -- a link and display plain "N/A" instead.

  if ((numerr==0) and (boodud==true)) then

    numtmpy = 1 -- ONE-based index -- move down
    while true do -- walk through "tababjad"
      strrow = "" -- bunch "td" elements here
      numtmpx = 1 -- ONE-based index -- move right
      while true do -- inner loop -- walk through "tababjad"
        stryonl = tababjad[numtmpy] -- this CANNOT fail
        stryyxx = stryonl .. tababjad[numtmpx] -- this CANNOT fail
        if (stryyxx=="''") then
          strtpm2dim = "N/A" -- guard against italics
        else
          strtpm2dim = lfwbrewlink (numtypb, numtycas, numtygap, strfpn, stryyxx, stryyxx, numenkli, numscw8, numscw9, boomwp, numans, stryonl)
        end--if
        strrow = strrow .. '<td>' .. strtpm2dim .. '</td>'
        if (numtmpx==numabjad) then
          break -- done -- ZERO iterations NOT possible -- inner loop
        end--if
        numtmpx = numtmpx + 1 -- move right
      end--while -- inner loop
      strret = strret .. '<tr>' .. strrow .. '</tr>'
      if (numtmpy==numabjad) then
        break -- done -- ZERO iterations NOT possible -- outer loop
      end--if
      numtmpy = numtmpy + 1 --  move down
    end--while

    if (not booflasup) then -- have those 2 extra links "top" and "bottom"
      numclspl = numabjad -- at least 3 (hard rule) but more is preferred
      numclspr = 0
      while true do -- this is integer division in LUA :-D
        if (numclspl<=(numclspr+1)) then -- 4 -> 2+2 | 3 -> 2+1 (NOT 1+2)
          break
        end--if
        numclspl = numclspl - 1 -- left
        numclspr = numclspr + 1 -- right
      end--while
      if ((numclspl>numclspr) and ((string.len(strflb5vi))<(string.len(strflb7vi)))) then
        numclspl = numclspl - 1 -- left -- change L=R+1 to L=R-1
        numclspr = numclspr + 1 -- right
      end--if
      strret = strret .. '<tr><td colspan="' .. tostring (numclspl) .. '">'
      strret = strret .. lfwbrewlink (numtypb, numtycas, numtygap, strfpn, strflb4lk, strflb5vi, 2, numscw8, numscw9, boomwp, numans, "")
      strret = strret .. '</td><td colspan="' .. tostring (numclspr) .. '">'
      strret = strret .. lfwbrewlink (numtypb, numtycas, numtygap, strfpn, strflb6lk, strflb7vi, 2, numscw8, numscw9, boomwp, numans, "")
      strret = strret .. '</td></tr>'
    end--if

  end--if

  ---- CLOSE THE TABLE IF APPROPRIATE ----

  if ((numerr==0) and (not bootits)) then
    strret = strret .. '</table>'
  end--if

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

  return strret

end--function

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

return exporttable