Modulo:mkontrolupn

El Vikivortaro
Salti al navigilo Salti al serĉilo

Dokumentado por ĉi tiu modulo povas esti kreata ĉe Modulo:mkontrolupn/dokumentado

--[===[

MODULE "MKONTROLUPN" (kontrolu pagename)

"eo.wiktionary.org/wiki/Modulo:mkontrolupn" <!--2021-Jul-10-->
"id.wiktionary.org/wiki/Modul:mkontrolupn"

Purpose: checks pagename according to language code and name pattern
         and namespace given

Utilo: kontrolas pagxonomon laux lingvokodo kaj noma strukturo
       kaj nomspaco donitaj

Manfaat: mengecek nama halaman ...

Syfte: kontrollerar sidnamn enligt ...

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaent av mallar:
- "lidald" (??) "kat-vortaro" (EO) "kat-kapvorta" (EO)

Required submodules / Bezonataj submoduloj /
Submodul yang diperlukan / Behoevda submoduler:
Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
- "mpiktbllki" in turn requiring "mbllingvoj" (EO) or "mtblbahasa" (ID)
- "mtbllingvoj" in turn requiring template "tbllingvoj" (EO)
- "mtblbahasa" in turn requiring template "tblbahasa" (ID)

This module is special in that it takes parameters both those sent
to itself (own frame) and those sent to the caller (caller's frame).
This module needs parameters that are additional to or different from
parameters submitted to the calling template.

The 3 hidden parameters can be on our own or on the caller's frame. If a
parameter is not found on our own frame, the caller's one is peeked. This
way we can avoid forwarding that does not work for non-existent parameters
(non-existent ie nil becomes empty string, not appreciated).

Incoming: * 3 obligatory parameters (from our own frame only)
            * language code
            * desired pagename pattern (5...100 octet:s, NOT
              including namespace)
            * desired namespace number (1...3 digits, ns 0...999)
          * 3 hidden named parameters (from our own frame or caller's frame)
            * "pagenameoverridetestonly="
            * "nsnumberoverridetestonly="
            * "nocat="

Returned: * error numbers not reported here
            * empty string on success
            * big complaint + possibly cat on wrong pagename
            * other big complaint + possibly same cat on wrong namespace
            * "=" and no cat on internal error, submodule failure, unknown
              or obviously invalid language code, or faulty parameters

Following magic codes are available in the pattern for the
desired pagename via "lfinsertultim":
* "LK" language code ("io", "sv")
* "LN" language name ("Ido", "sveda")
* "LU" language name uppercased begin ("Ido", "Sveda")
For example:
* "eo|Nenio (@LN00)|102" gives "Aldono:Nenio (Esperanto)"  NS 102
* "da|@LU00|10"          gives "SXablono:Dana"             NS 10

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

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio). Cxiu imagebla enigo de la vokanto kaj
de la importata modulo "mtbllingvoj" eldonos aux utilan
rezulton aux almenaux signocxenon "=".

]===]

local kontrolupn = {}

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

---- CONSTANTS ----

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

  -- uncommentable EO vs ID constant strings (core site-related features)

  local constrpriv = "eo"                 -- EO (privileged site language)
  -- local constrpriv = "id"                 -- ID (privileged site language)
  local constrplki = "Modulo:mpiktbllki"  -- EO
  -- local constrplki = "Modul:mpiktbllki"   -- ID

  -- surrogate transcoding table (only needed for EO)

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

  -- constants related to submodules

  local constrtblc0 = "0" -- in site language

  -- uncommentable EO vs ID main whining table

  local conwhinetable = {}
  conwhinetable[0] = '&nbsp;#&nbsp;#&nbsp;<span class="error"><b>'
  conwhinetable[1] = 'Erara nomspaco'                  -- EO
  -- conwhinetable[1] = 'Ruang nama yang salah'           -- ID
  conwhinetable[2] = 'Erara pagxonomo'                 -- EO
  -- conwhinetable[2] = 'Nama halaman yang salah'         -- ID
  conwhinetable[3] = ', trovita '                      -- EO follows quoted string
  -- conwhinetable[3] = ', ditemukan '                    -- ID follows quoted string
  conwhinetable[4] = ', estu '                         -- EO follows quoted string
  -- conwhinetable[4] = ', sebaiknya '                    -- ID follows quoted string
  conwhinetable[5] = ' laux lingvokodo '               -- EO follows dashed string
  -- conwhinetable[5] = ' menurut kode bahasa '           -- ID follows dashed string
  conwhinetable[6] = '</b></span>&nbsp;#&nbsp;#&nbsp;'
  conwhinetable[7] = '[[Kategorio:Erara pagxonomo]]'   -- EO raw cat
  -- conwhinetable[7] = '[[Kategori:Nama halaman salah]]' -- ID raw cat

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

---- SPECIAL STUFF OUTSIDE MAIN FUNCTION ----

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

---- VAR:S ----

local qpiktbllki = {}       -- type "table" with type "function" inside
local qbooguard = false     -- only for the guard test, pass to other var ASAP

---- GUARD AGAINST INTERNAL ERROR & ONE IMPORT VIA REQUIRE ----

if ((type(constrpriv)~="string") or (type(constrplki)~="string") or (type(conwhinetable[7])~="string")) then
  qbooguard = true -- "true" on error
else
  qpiktbllki = require(constrplki) -- can crash here despite guarding ??
  if (type(qpiktbllki)~="table") then
    qbooguard = true -- "true" on error
  end--if
end--if

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

---- LOCAL MATH FUNCTIONS ----

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

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

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

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

-- Local function MATHBITWRIT

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

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"mathmod".

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

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

---- LOCAL CONVERSION FUNCTIONS ----

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

-- Local function LFDEC1DIGIT

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

local function lfdec1digit (num1digit)
  num1digit = num1digit - 48 -- may become invalid
  if ((num1digit<0) or (num1digit>9)) then
    num1digit = 255
  end--if
  return num1digit
end--function lfdec1digit

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

-- Local function LFDECINP

-- Convert string (1...13 octet:s) with decimal ASCII digits to a positive
-- (UINT32) integer number (0...4'000'000'000) ignoring possible inside apo:s.

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

-- Input  : * strin (string, empty tolerable, but "nil" type is NOT)

-- Output : * numaout (4'294'967'295 on error)

local function lfdecinp (strin)
  local numaout = 0
  local numleen = 0
  local numinxx = 1 -- ONE-based -- counts up
  local numokkt = 0
  numleen = string.len (strin)
  if ((numleen<1) or (numleen>13)) then
    numaout = 4294967295 -- damn
  else
    while (true) do
      numokkt = string.byte (strin,numinxx,numinxx)
      if (numokkt==39) then
        if ((numinxx==1) or (numinxx==numleen)) then
          numaout = 4294967295
          break -- damn (may not begin or end with apo)
        end--if
      else
        if ((numokkt<48) or (numokkt>57) or (numaout>400000000)) then
          numaout = 4294967295
          break -- damn (bad char or out of range)
        end--if
        numaout = numaout * 10 + (numokkt - 48)
      end--if
      if (numinxx==numleen) then
        break -- done (hopefully success, but not yet sure)
      end--if
      numinxx = numinxx + 1
    end--while
    if (numaout>4000000000) then
      numaout = 4294967295 -- damn (out of range)
    end--if
  end--if
  return numaout
end--function lfdecinp

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

-- Local function LFONEHEXTOINT

-- Convert 1 ASCII code of a hex digit to an UINT4 ie 0...15 (255 invalid).

-- Only uppercase accepted

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

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

---- LOCAL STRING FUNCTIONS ----

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

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

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

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

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

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

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

---- LOCAL UTF8 FUNCTIONS ----

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

-- Local function LFUTF8LENGTH

-- Evaluate length of a single UTF8 char, return ZERO if invalid.

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

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

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

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

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

-- Local function LFCASEREST

-- Adjust case of a single letter (restricted), only ASCII
-- and 6 extra EO letters.

-- Input  : * strucinrsut : single unicode letter (1 or 2 octet:s)
--          * booupcas    : for desired uppercase "true" and for
--                          lowercase "false"

-- Output : * strucinrsut : (same var, "ZZ" on failure)

-- This will not give useful results for any non-ASCII char:s other
-- than uppercase and lowercase EO letters (CX GX HX JX SX UX
-- cx gx hx jx sx ux). Invalid non-ASCII input generates "ZZ".

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "MATH FUNCTIONS"\"mathdiv" and "MATH FUNCTIONS"\"mathbitwrit" and
-- "STRING FUNCTIONS"\"lftestuc" and "STRING FUNCTIONS"\"lftestlc" and
-- "UTF8 FUNCTIONS"\"lfutf8length".

local function lfcaserest (strucinrsut, booupcas)

  local numlaengdn = 0 -- length from "string.len"
  local numchaerr = 0 -- UINT8 beginning char
  local numchaess = 0 -- UINT8 later char (BIG ENDIAN, lower value here up)
  local numchareel = 0 -- UINT8 code relative to beginning of block $00...$FF
  local numdeta = 0 -- SINT8 +32 or -32 or +1 or -1 or ZERO
  local numtheemp = 0

  local boowantlowr = false
  local booiisuppr = false
  local booiislowr = false
  local boovalid = false

  boowantlowr = (not booupcas)

  while (true) do -- fake loop

    numlaengdn = string.len (strucinrsut)
    if ((numlaengdn<1) or (numlaengdn>2)) then -- only 1 or 2 accepted
      break -- bad string length
    end--if
    numchaerr = string.byte (strucinrsut,1,1)
    if ((lfutf8length(numchaerr))~=numlaengdn) then
      break -- mismatch with length
    end--if

    if (numlaengdn==1) then
      booiisuppr = lftestuc(numchaerr)
      booiislowr = lftestlc(numchaerr)
      if (booiisuppr and boowantlowr) then
        numdeta = 32 -- ASCII UPPER->lower
      end--if
      if (booiislowr and booupcas) then
        numdeta = -32 -- ASCII lower->UPPER
      end--if
      boovalid = true
      break -- success, almost done
    end--if

    if ((numchaerr<196) or (numchaerr>197)) then -- only 196 or 197 accepted
      break -- unsupported UTF8 block
    end--if
    numchaess = string.byte (strucinrsut,2,2)
    numchareel = (numchaerr-196)*64 + (numchaess-128) -- begins at $C4
    numtheemp = mathbitwrit (numchareel,0,false) -- bad way to do AND $FE
    if ((numtheemp~=8) and (numtheemp~=28) and (numtheemp~=36) and (numtheemp~=52) and (numtheemp~=92) and (numtheemp~=108)) then
      break -- unsupported UTF8 char
    end--if

    booiislowr = ((mathmod(numchareel,2))==1) -- UC even (ordinary)
    booiisuppr = not booiislowr
    if (booiisuppr and boowantlowr) then
      numdeta = 1 -- UPPER->lower
    end--if
    if (booiislowr and booupcas) then
      numdeta = -1 -- lower->UPPER
    end--if
    boovalid = true -- success, almost done

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

  while (true) do -- fake loop

    if (boovalid==false) then
      strucinrsut = "ZZ" -- illegal non-ASCII char
      break -- helvete
    end--if

    if (numdeta==0) then
      break -- nothing to do
    end--if

    if (numlaengdn==1) then
      strucinrsut = string.char (numchaerr + numdeta) -- no risk of carry here
      break -- done
    end--if

    strucinrsut = string.char (numchaerr) .. string.char (numchaess + numdeta)

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

  return strucinrsut -- same var for input and output !!!

end--function lfcaserest

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

-- Local function LFXCASEULT

-- Adjust letter case of beginning letter or all letters in a word or group of
-- words to upper or lower, limited unicode support (restricted LFCASEREST).

-- Input  : * strenigo : word or group of words (may be empty)
--          * booupcas : "true" for uppercase and "false" for lowercase
--          * boodoall : "true" to adjust all letters, "false" only beginning

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "MATH FUNCTIONS"\"mathdiv" and "MATH FUNCTIONS"\"mathbitwrit" and
-- "STRING FUNCTIONS"\"lftestuc" and "STRING FUNCTIONS"\"lftestlc" and
-- "UTF8 FUNCTIONS"\"lfutf8length" and "UTF8 FUNCTIONS"\"lfcaserest"
-- (restricted LFCASEREST).

local function lfxcaseult (strenigo, booupcas, boodoall)

  local numlein = 0
  local numposi = 1 -- octet position ONE-based
  local numcut = 0 -- length of an UTF8 char
  local bootryadj = false -- try to adjust single char
  local strte7mp = ""
  local strelygo = ""

  numlein = string.len (strenigo)
  while (true) do
    if (numposi>numlein) then
      break -- done
    end--if
    bootryadj = (boodoall or (numposi==1))
    numcut = lfutf8length(string.byte(strenigo,numposi,numposi))
    if ((numcut==0) or ((numposi+numcut-1)>numlein)) then
      numcut = 1 -- skip ie copy one faulty octet
      bootryadj = false
    end--if
    strte7mp = string.sub (strenigo,numposi,(numposi+numcut-1)) -- 1...4 oct
    if (bootryadj) then
      strte7mp = lfcaserest(strte7mp,booupcas) -- (restricted LFCASEREST)
    end--if
    strelygo = strelygo .. strte7mp -- this can be slow
    numposi = numposi + numcut
  end--while
  return strelygo

end--function lfxcaseult

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

---- LOCAL HIGH LEVEL FUNCTIONS ----

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

-- Local function LFTESTLNGKOD

-- Test whether language code is valid depending on "boodigit".

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

local function lftestlngkod (strlikodo,boodigit)
  local boovalid   = true
  local numlencxcx = 0
  local numchaa    = 0
  numlencxcx = string.len (strlikodo)
  if ((numlencxcx<2) or (numlencxcx>3)) then
    boovalid = false
  else
    numchaar = string.byte (strlikodo,1,1)
    if (lftestlc(numchaar)==false) then
      boovalid = false
    end--if
    numchaar = string.byte (strlikodo,2,2)
    if (lftestlc(numchaar)==false) then
      if ((numlencxcx==3) and boodigit) then
        if (lfdec1digit(numchaar)>9) then
          boovalid = false -- neither lowercase nor digit
        end--if
      else
        boovalid = false -- digits NOT permitted in middle position
      end--if
    end--if
    if (numlencxcx==3) then
      numchaar = string.byte (strlikodo,3,3)
      if (lftestlc(numchaar)==false) then
        boovalid = false
      end--if
    end--if
  end--if
  return boovalid
end--function lftestlngkod

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

-- Local function LFINSERTULTIM

-- Insert selected extra strings into a given string at given positions
-- with optional discarding if the insertable item is empty. Discarding is
-- protected from access out of range by clamping the distances.

-- Input  : * strmdata -- main data string with control cod (syntax see below)
--          * tabinseert -- non-string in the table is safe and has same effect
--                          as empty string, still "nil" or empty
--                          string "" are preferred
-- Output : * strhazil

-- syntax of the insertion and discarding magic string:
-- "@" followed by 2 uppercase letters and 2 hex numbers
-- otherwise the hit is not expanded, but copied as-is instead
-- 2 letters select the insertable item from table supplied by the caller
-- 2 hex numbers control discarding left and right (0...15 char:s)

-- Empty item in "tabinseert" is legal and results in discarding if some of
-- the control numbers is non-ZERO. Left discarding is practically performed
-- on "strhazil" whereas right discarding on "strmdata" and "numdatainx".

-- If uppercasing or other adjustment is needed then the caller must take
-- care of it in the form of 2 or more separate items provided in the table
-- with separate names.

-- This sub depends on "STRING FUNCTIONS"\"lftestuc"
-- and "CONVERSION FUNCTIONS"\"lfonehextoint".

local function lfinsertultim (strmdata,tabinseert)

  local varduahuruf = 0
  local strhazil = ''
  local numdatalen = 0
  local numdatainx = 0 -- src index
  local numdataoct = 0 -- maybe @
  local numdataodt = 0 -- UC
  local numdataoet = 0 -- UC
  local numammlef = 0 -- hex and discard left
  local numammrig = 0 -- hex and discard right
  local boogotmagic = false

  numdatalen = string.len(strmdata)
  numdatainx = 1 -- ONE-based

  while (true) do -- genuine loop, "numdatainx" is the counter
    if (numdatainx>numdatalen) then -- beware of risk of overflow below
      break -- done (ZERO iterations possible)
    end--if
    boogotmagic = false
    numdataoct = string.byte(strmdata,numdatainx,numdatainx)
    numdatainx = numdatainx + 1
    while (true) do -- fake loop
      if ((numdataoct~=64) or ((numdatainx+3)>numdatalen)) then
        break -- no hit here
      end--if
      numdataodt = string.byte(strmdata, numdatainx   , numdatainx   )
      numdataoet = string.byte(strmdata,(numdatainx+1),(numdatainx+1))
      if ((lftestuc(numdataodt)==false) or (lftestuc(numdataoet)==false)) then
        break -- no hit here
      end--if
      numammlef = string.byte(strmdata,(numdatainx+2),(numdatainx+2))
      numammrig = string.byte(strmdata,(numdatainx+3),(numdatainx+3))
      numammlef = lfonehextoint (numammlef)
      numammrig = lfonehextoint (numammrig)
      boogotmagic = ((numammlef~=255) and (numammrig~=255))
      break
    end--while -- fake loop -- join mark
    if (boogotmagic) then
      numdatainx = numdatainx + 4 -- consumed 5 char:s, cannot overflow here
      varduahuruf = string.char (numdataodt,numdataoet)
      varduahuruf = tabinseert[varduahuruf] -- risk of type "nil"
      if (type(varduahuruf)~="string") then
        varduahuruf = '' -- type "nil" or invalid type gives empty string
      end--if
      if (varduahuruf=='') then
        numdataoct = string.len(strhazil) - numammlef -- this can underflow
        if (numdataoct<=0) then
          strhazil = ''
        else
          strhazil = string.sub(strhazil,1,numdataoct) -- discard left
        end--if
        numdatainx = numdatainx + numammrig -- discard right this can overflow
      else
        strhazil = strhazil .. varduahuruf -- insert / expand
      end--if
    else
      strhazil = strhazil .. string.char(numdataoct) -- copy char as-is
    end--if (boogotmagic) else
  end--while

  return strhazil

end--function lfinsertultim

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

-- Local function LFKODEOSG

-- Transcode eo X-surrogates (without "\", thus for example "kacxo",
-- NOT "ka\cxo") to cxapeloj in a single string (EO only).

-- Input  : * strsurr -- string (empty is useless but can't cause major harm)

-- Output : * strcxapeloj -- UTF8 string

-- We need const table "contabtransiltable".

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"mathmod".

local function lfkodeosg (strsurr)
  local varpeek = 0
  local strcxapeloj = ''
  local numinputl = 0
  local numininx = 0 -- ZERO-based source index
  local numknark = 0 -- current char (ZERO is NOT valid)
  local numknarp = 0 -- previous char (ZERO is NOT valid)
  local numlow = 0
  local numhaj = 0
  numinputl = string.len(strsurr)
  while (true) do
    if (numininx==numinputl) then
      break
    end--if
    numknark = string.byte(strsurr,(numininx+1),(numininx+1))
    numininx = numininx + 1
    numhaj = 0 -- pre-assume no translation
    if ((numknarp~=0) and ((numknark==88) or (numknark==120))) then -- got "x"
      varpeek = contabtransiltable[numknarp] -- UINT16 or nil
      if (varpeek~=nil) then
        numlow = mathmod (varpeek,256)
        numhaj = mathdiv (varpeek,256)
      end--if
    end--if
    if (numhaj~=0) then
      strcxapeloj = strcxapeloj .. string.char(numhaj,numlow) -- add UTF8 char
      numknark = 0 -- invalidade current char
    else
      if (numknarp~=0) then -- add previous char only if valid
        strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
      end--if
    end--if
    numknarp = numknark -- copy to previous even if invalid
  end--while
  if (numknarp~=0) then -- add previous and last char only if valid
    strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
  end--if
  return strcxapeloj
end--function lfkodeosg

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

---- MAIN EXPORTED FUNCTION ----

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

function kontrolupn.ek (arxframent)

  -- general unknown type ie "var"

  local vartmp = 0     -- variable without type multipurpose

  -- special type "args" AKA "arx"

  local arxourown = 0  -- metaized "args" from our own "frame"
  local arxcaller = 0  -- metaized "args" from caller's "frame"

  -- general "tab"

  local tabinsert2able = {} -- double-letter indexes for "lfinsertultim"

  -- general "str"

  local strkodbah  = "" -- processed to desired pagename
  local strpattern = "" -- processed to desired pagename as well as the result
  local strpagenam = "" -- "{{PAGENAME}}" o "pagenameoverridetestonly"
  local strnambah  = "" -- language name (without prefix "Bahasa")
  local strtmp     = "" -- temp
  local strruangna = "" -- used only late when brewing complaint
  local strnsdezir = "" -- used only late when brewing complaint
  local strret     = "" -- output string

  -- general "num"

  local numerr     = 0   -- 0 OK | 1 internal para | 2 namespace | 3 pagename
  local numtamp    = 0
  local numnsdezir = 0   -- desired NS 0...999
  local numruangna = 0   -- "{{NAMESPACENUMBER}}" o "nsnumberoverridetestonly"

  -- general boo

  local boonocat = false -- from "nocat=true"

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

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

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

  arxourown = arxframent.args -- "args" from our own "frame"
  arxcaller = arxframent:getParent().args -- "args" from caller's "frame"

  if (type(arxourown)~="table") then
    arxourown = {} -- guard against indexing error before any indexing
    numerr = 1 -- #E01 internal para
  end--if

  if (type(arxcaller)~="table") then
    arxcaller = {} -- guard against indexing error before any indexing
  end--if

  ---- PROCESS 3 HIDDEN NAMED PARAMS ----

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

  -- both overrides can give #E01, nocat cannot

  strpagenam = "" -- 1...120 octet:s fake-found pn -- preassume none
  if (numerr==0) then
    vartmp = arxourown['pagenameoverridetestonly']
    if (vartmp==nil) then
      vartmp = arxcaller['pagenameoverridetestonly']
    end--if
    if (type(vartmp)=="string") then
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartmp -- empty is not legal
      else
        numerr = 1 -- #E01 internal para
      end--if
    end--if
  end--if

  numruangna = 99999 -- 1...3 digits fake-detected ns -- preassume none
  if (numerr==0) then
    vartmp = arxourown['nsnumberoverridetestonly']
    if (vartmp==nil) then
      vartmp = arxcaller['nsnumberoverridetestonly']
    end--if
    if (type(vartmp)=="string") then
      numruangna = lfdecinp (vartmp) -- number of fake-detected ns
      if (numruangna>999) then
        numerr = 1 -- #E01 internal para
      end--if
    end--if
  end--if

  if (numerr==0) then
    vartmp = arxourown['nocat']
    if (vartmp==nil) then
      vartmp = arxcaller['nocat']
    end--if
    if (vartmp=='true') then
      boonocat = true -- YES we deprecate cat:s
    end--if
  end--if

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

  -- must be 1...120 octet:s keep consistent with "pagenameoverridetestonly="

  if ((numerr==0) and (strpagenam=='')) then
    vartmp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (type(vartmp)=="string") then -- this can leave bhnd "strpagenam" empty
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartmp -- pagename here (empty is NOT legal)
      else
        numerr = 1 -- #E01 internal para
      end--if
    end--if
  end--if

  if (numerr==0) then
    if (strpagenam=='') then
      numerr = 1 -- #E01 internal para
    end--if
  end--if

  ---- SEIZE THE NAMESPACE FROM MW ----

  if ((numerr==0) and (numruangna>999)) then
    vartmp = mw.title.getCurrentTitle().namespace -- this is a number
    if (type(vartmp)=="number") then -- this can leave bhnd "numruangna" empty
      if ((vartmp>=0) and (vartmp<=999)) then
        numruangna = vartmp -- number of detected ns
      else
        numerr = 1 -- #E01 internal para
      end--if
    end--if
  end--if

  ---- SEIZE 3 OBLIGATORY PARAMETERS ----

  while (true) do -- fake loop
    if (numerr~=0) then
      break -- to join mark
    end--if

    if ((arxourown[1]==nil) or (arxourown[2]==nil) or (arxourown[3]==nil) or (arxourown[4])) then -- weak
      numerr = 1 -- need 3 obligatory params, four are not appreciated
      break -- to join mark
    end--if

    strkodbah = arxourown[1]
    if (lftestlngkod(strkodbah,true)==false) then -- allow digit
      numerr = 1 -- #E01 bad code
      break -- to join mark
    end--if

    strpattern = arxourown[2]
    numtamp = string.len(strpattern)
    if ((numtamp<5) or (numtamp>100)) then
      numerr = 1 -- #E01 bad desired pn pattern
      break -- to join mark
    end--if

    strtmp = arxourown[3]
    numnsdezir = lfdecinp (strtmp) -- later we will compare integers
    if (numnsdezir>999) then
      numerr = 1 -- #E01 bad desired ns
    end--if

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

  ---- TRANSCODE EO IF NEEDED ----

  if (constrpriv=="eo") then
    conwhinetable[2] = lfkodeosg(conwhinetable[2]) -- erara pn
    conwhinetable[5] = lfkodeosg(conwhinetable[5])
    conwhinetable[7] = lfkodeosg(conwhinetable[7]) -- cat
  end--if

  ---- CHECK NS BY COMPARING INTEGERS ----

  -- got "numnsdezir" and "numruangna" (>999 is invalid but already checked)

  if ((numerr==0) and (numnsdezir~=numruangna)) then
    numerr = 2 -- #E02 used in wrong ns
  end--if

  ---- PEEK THE LANGUAGE NAME VIA SUBMODULE ----

  -- get lng name in site language ("c0" -- "constrtblc0")

  if (numerr==0) then
    strnambah = qpiktbllki.ek { args = { strkodbah , constrtblc0 } }
    if ((strnambah=="=") or (strnambah=="-")) then
      numerr = 1 -- #E01 internal err, submodule failure, bad lang code, etc
    end--if
  end--if

  ---- EXPAND PATTERN IE BREW THE DESIRED PAGENAME AND CHECK IT ----

  if (numerr==0) then
    strtmp = lfxcaseult(strnambah,true,false) -- short uppercased
    tabinsert2able = {} -- bugger all inside
    tabinsert2able["LK"] = strkodbah -- code
    tabinsert2able["LN"] = strnambah -- short name
    tabinsert2able["LU"] = strtmp -- uppercased name
    strpattern = lfinsertultim (strpattern,tabinsert2able) -- pattern -> name
    if (strpattern~=strpagenam) then
      numerr = 3 -- #E03 wrong pagename
    end--if
  end--if

  ---- BREW THE OUTPUT STRING ----

  -- "strret" was preassigned to empty ""

  -- 0 OK | 1 internal para ("=") | 2 namespace (stri 1) | 3 pagename (stri 2)

  if (numerr==1) then
    strret = "=" -- #E01
  end--if

  if (numerr>=2) then -- #E02 #E03
    strruangna = tostring (numruangna) -- weak -- detected ns
    strnsdezir = tostring (numnsdezir) -- weak -- desired ns
    strret = conwhinetable[0] -- both
    if (numerr==3) then
      strret = strret .. conwhinetable[2] -- pn #E03
    else
      strret = strret .. conwhinetable[1] -- ns #E02
    end--if
    strret = strret .. conwhinetable[3] .. '"'
    if (numerr==3) then
      strret = strret .. strpagenam -- #E03 detected pn
    else
      strret = strret .. strruangna -- #E02 detected ns
    end--if
    strret = strret .. '"' .. conwhinetable[4] .. '"'
    if (numerr==3) then
      strret = strret .. strpattern -- #E03 desired pn (now expanded)
    else
      strret = strret .. strnsdezir -- #E02 desired ns
    end--if
    strret = strret .. '"'
    if (numerr==3) then
      strret = strret .. conwhinetable[5] .. '-' .. strkodbah .. '-' -- c #E03
    end--if
    strret = strret .. conwhinetable[6] -- close whining
    if (boonocat==false) then
      strret = strret .. conwhinetable[7] -- cat
    end--if
  end--if

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

  return strret

end--function

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

return kontrolupn