Modulo:mpiktbllki

El Vikivortaro
Salti al navigilo Salti al serĉilo
StopsignIndonesia.png Ĉi tiu modulo estas multfoje bindita.
Se vi konas la eblajn sekvojn, tiam vi povas zorgeme ekredakti.
Se vi ne kuraĝas redakti tiam vi povas proponi la deziratan ŝanĝon en la diskutejo.
Memtesto disponeblas sur la dokumentaĵa subpaĝo

--[===[

MODULE "MPIKTBLLKI" (pick from table lingvokodo al info)

"eo.wiktionary.org/wiki/Modulo:mpiktbllki" <!--2022-Jul-22-->
"id.wiktionary.org/wiki/Modul:mpiktbllki"

Purpose: picks an element from a raw two-dimensional table, or more
         precisely from "tbllingvoj" (EO) or "tblbahasa" (ID)

Utilo: ellegas elementon el kruda dudimensia tabelo, aux pli
       precize el "tbllingvoj" (EO) aux "tblbahasa" (ID)

Manfaat: membaca elemen dari tabel berdimensi dua, ...

Syfte: laeser ut ett element fraan en tvaadimensionell tabell, ...

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaent av mallar:
- "Lingvo" "auxdo" "deveno3" "t" "ligald" ...
- "Modulo:mlili" "mfarado" ...
- "Modulo:mlawc" siavice uzata far sxablono "livs" (EO) /
  "Modul:mlawc" digunakan oleh templat "bakk" (ID)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
- "mbllingvoj" in turn requiring template "tbllingvoj" (EO)
- "mtblbahasa" in turn requiring template "tblbahasa" (ID)
We do NOT need "mpiksubstrind" since we contain a copy of a part of its code.

Incoming: * 1 or 2 obligatory anonymous parameters :
            * requested y-index (access code of the
              language, 2 or 3 lowercase chars) (string indexing),
              or "c0" for reverse query, or "c1" for extra query
            * requested x-index (0...10) (numerical indexing),
              or special value "L" (line) or "R" (reverse) or "X" (extra),
              required for default "bin=0", optional for "bin=1" (only "R"
              and "X" legal then)
          * 5 optional named parameters :
            * "bin=1" for binary output, "0" is error and "1" is success,
              works for forward (only by y-index) and reverse and extra
              query, default is "bin=0"
            * "rel=0" for no removal of rl-lr-enclosement (for
              non-latin scripts), or "1" to remove it, default is "rel=1",
              only for ordinary x-indexes, not for "L" "R" "X", nor "bin=1"
            * "alt=" alternative output string to replace "-" coming literally
              from the source table (1...64 char:s, default is "-")
            * "err=" alternative output string to replace the "=" error
              message (1...64 char:s, default is "=") (this works even
              for errors coming from anonymous parameters)
            * "detrc=true" to create trace debug report

Returned: * one string
            * for index number 0...10 the found element or "=" if
              the element was not found (due to y or x) or an error occurred
            * for special input value "L" the complete table line including
              the y-index marker (but excessive whitespace reduced), or "="
              if the y-index marker was not found or an error occurred
            * for "bin=1" binary output either "1" if the
              y-index marker was found (not checking the table line), or
              "0" if the requested y-index was not found or an error occurred
            * for special input value "R" or "X" the "cy" found from "c0" or
              "c1" by a reverse or extra query

This module is unbreakable (when called with correct module name
and function name). Every imaginable input will output either a useful
result or the string "=" (not found or error).

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio). Cxiu imagebla enigo eldonas aux utilan
rezulton aux signocxenon "=" (ne trovita aux eraro).

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

Column numbering in the source table:
- numbering starts from ZERO
- "cy" does not count (it has theoretically index "-1")
- note that there is no comma between "cy" and "c0"
- "mpiktbllki" with special x-index "L" returns the complete table line
  including the y-index marker "cy", this means that unless the "cy" part
  is trimmed off, index ZERO fed into "mpiksubstrind" will return both
  "cy" and "c0", and the indexes used by "mpiksubstrind" will NOT be
  OFF-by-ONE in any case

y-index marker (row index marker) placed in the
table content area (here lowercase)

x-index marker (column index marker) placed in the
table content area (here prohibitied)

digit index marker (only digits) (here NOT supported)
lowercase index marker (lowercase letters and maybe a bit more)

incoming requested index (per spec may be evaluated to string indexing
                          or numerical indexing, but here hardcoded)

string indexing     <-->  lowercase index marker
numerical indexing  <-->  digit index marker or no index
                          marker and counting (here only counting)

General rules and specific limits (see spec):

- double square brackets "[[" and "]]" are reserved for y-index markers
  and such may occur only at the beginning of line (here we assume this)
- usage of single square brackets "[" and "]" is not limited
- double round brackets "((" and "))" are reserved for x-index markers
  (here we don't support x-index markers at all)
- usage of single round brackets "(" and ")" is not limited
- more than two subsequent brackets ie for
  example "[[[" or ")))" are prohibited
- no comma "," following [[...]] and ((...)), inline whitespace
  is recommended but not required
- index markers are optional, it's possible (and potentially useful) to mix
  elements with index markers and elements without index markers, and it's
  possible (but probaly less useful) to mix lines with index markers and
  lines without index markers (here we require index markers for lines
  and disallow them for elements)
- index markers must be 2 or 3 or 4 chars long and consist of either
  only lowercase letters (lowercase index marker) or only digits
  (digit index marker), it's possible (but maybe barely useful) to
  mix the 2 types (here we don't support digit index markers at all,
  and limit lowercase index markers to 2 or 3 chars)
- if digit index markers are used then they must match the index numbers for
  both lines and columns, ie a digit index marker inside [[...]] and ((...))
  is optional but cannot hold a value different from what it would be
  anyway based on ZERO-based counting (here we don't support digit
  index markers at all)
- if y-index markers are used then they must be sorted, with a privileged role
  for 1 entry at the beginning and 1 entry at the end of the table that
  are guaranteed to be found even if they are misplaced (here we
  require y-index markers, sorting rule holds)
- redundant whitespace is not a problem as long as "maxwidth" is not exceeded
  (this applies also to the beginning of the table) and no inline whitespace
  (TAB or SPACE) follows EOL whitespace (ASCII 0...8 and 10...31),
  this applies both to table lines and empty lines, ie an EOL
  must be followed by either EOL or a table line (ie [[...]] or ((...))
  or raw table element data)
- an empty table not containing any lines is valid as long as whitespace
  rules are observed, this means EOL's are tolerable while inline whitespace
  is not (here we require at least 1 line)
- a table line may not be empty, there must be at least 1 element
  (note that allowing empty lines would cause inconsistencies between
  lines with [[...]] and those without it)
- truncated table lines, ie lines shorter than other lines are tolerable, they
  can end with element data or inline whitespace following element data, but
  not with a comma ","
- a table element may not empty, dash "-" is the recommended filler for
  dummy elements, or ((...)) can be used to "jump" over columns, even an
  x-index marker ((...)) must be followed by at least one char of table
  data (here we don't support x-index markers at all)
- a table element may not contain the string "=", if this string is found it
  is translated to "-", the string "=" is reserved to indicate "not found or
  error" (this rule holds even if alternative output string is requested)

Strategy:

- we support only lowercase y-indexing, not digit, the incoming
  requested y-index string must be a valid language code (checked
  by "lfivalidatelnkoadv", note that the table that we read from is
  checked too, namely separately by module "mbllingvoj" or "mtblbahasa")
- the incoming requested x-index number must be 0 ... 10 (checked)
- we support only index marker for "y" and expect [[...]] in every line
- we support only counting for "x" and expect ((...)) not to occur
- we give a f**k in UTF8 and consider the table to be an array of octets
- for "y" indexing we apply a mixed strategy of linear forward and backward
  search with step 2 and binary search
- for "y" indexing we begin with checking 0th entry, then last entry, then
  continue with binary search, and switch back to linear search as soon as
  the remaining size becomes dangerously low
- for "x" indexing we perform a linear forward search with step 1
- we give a f**k in closing "]]" and compare only opening "[["
  and 2 or 3 chars

Special rule about enclosing of elements written with
non-latin scripts (rl-lr-enclosement):

- non-ASCII, non-latin and even RTL (right-to-left) scripts are permitted
  in the table
- a table element longer than 2 Octet:s may be enclosed in "rl " and " lr"
  strings on both sides, this is useful since many editors would otherwise
  break the element order if an RTL script is used
- by default the enclosing strings are stripped off, but an optional
  parameter "rel=0" can disable this for forward requests with ordinary
  x-indexes (0...10), not valid for special value "L"

Usage examples with results / Ekzemploj de uzo
kun rezultoj / Contoh penggunaan dengan hasil:

(assuming that the huge table seized via "mw.loadData" contains the
line "[[id]]  indonezia       , Q9240 , bahasa Indonesia , 1 , id , i
nd , - , 22 , Indonezia lingvo , Kategori:Bahasa Esperanto ,   100" at
the top, as well as lines for y-index "hy" with "rl ......... lr " and for
y-index "sv" with "[[sv]]sveda" (missing space, but works nevertheless),
but no line for "zzz", x-indexes: c0:nama c1:Q c2:autonym c3:sta c4:id
c5:ind c6:subdomain c7:avail c8:pedia-art c9:ali-kat c10:jumlah)

: ---------------------------------------

* #T00 {mpiktbllki|ek} (bad use: obligatory params missing, 0 given)
* expected result: "="
* actual result: "{{#invoke:mpiktbllki|ek}}"

::* #T01 {mpiktbllki|ek|eo} (bad use: obligatory params missing, only 1 given and no "bin=1")
::* expected result: "="
::* actual result: "{{#invoke:mpiktbllki|ek|eo}}"

* #T02 {mpiktbllki|ek|err=fatal error} (bad use: obligatory params missing, 0 given, "err=" param used)
* expected result: "fatal error"
* actual result: "{{#invoke:mpiktbllki|ek|err=fatal error}}"

::* #T03 {mpiktbllki|ek|id|0} (simplest example, "c0", ZERO'th entry by y-index)
::* expected result: "indonezia"
::* actual result: "{{#invoke:mpiktbllki|ek|id|0}}"

* #T04 {mpiktbllki|ek|sv|0} (simplest example, "c0", later entry by y-index)
* expected result: "sveda"
* actual result: "{{#invoke:mpiktbllki|ek|sv|0}}"

: ---------------------------------------

* #T10 {mpiktbllki|ek|hy|2} ("c2" autonym, strip rl lr by default)
* expected result: .......
* actual result: "{{#invoke:mpiktbllki|ek|hy|2}}"

::* #T11 {mpiktbllki|ek|hy|2|rel=0} ("c2" autonym, keep rl lr per "rel=0")
::* expected result: "rl "......." lr"
::* actual result: "{{#invoke:mpiktbllki|ek|hy|2|rel=0}}"

* #T12 {mpiktbllki|ek|hy|2|rel=1} ("c2" autonym, strip rl lr per redundant "rel=1")
* expected result: .......
* actual result: "{{#invoke:mpiktbllki|ek|hy|2|rel=1}}"

::* #T13 {mpiktbllki|ek|hy|9} ("c9" ali-kat, strip rl lr by default)
::* expected result: .......
::* actual result: "{{#invoke:mpiktbllki|ek|hy|9}}"

* #T14 {mpiktbllki|ek|hy|9|rel=0} ("c9" ali-kat, keep rl lr per "rel=0")
* expected result: "rl "............" lr"
* actual result: "{{#invoke:mpiktbllki|ek|hy|9|rel=0}}"

::* #T15 {mpiktbllki|ek|hy|9|rel=1} ("c9" ali-kat, strip rl lr per redundant "rel=1")
::* expected result: ............
::* actual result: "{{#invoke:mpiktbllki|ek|hy|9|rel=1}}"

: ---------------------------------------

* #T20 {mpiktbllki|ek|id|3} ("c3" status)
* expected result: "1"
* actual result: "{{#invoke:mpiktbllki|ek|id|3}}"

::* #T21 {mpiktbllki|ek|id|5} ("c5" ind)
::* expected result: "ind"
::* actual result: "{{#invoke:mpiktbllki|ek|id|5}}"

* #T22 {mpiktbllki|ek|id|6} ("c6" subdomain, keep found "-")
* expected result: "-"
* actual result: "{{#invoke:mpiktbllki|ek|id|6}}"

::* #T23 {mpiktbllki|ek|id|6|rel=1|alt=N/A} ("c6" subdomain, replace found "-" by "N/A")
::* expected result: "N/A"
::* actual result: "{{#invoke:mpiktbllki|ek|id|6|rel=1|alt=N/A}}"

* #T24 {mpiktbllki|ek|id|10} ("c10" jumlah)
* expected result: "100" (likely to deviate in practice)
* actual result: "{{#invoke:mpiktbllki|ek|id|10}}"

::* #T25 {#invoke:mpiktbllki|ek|zzz|3} ("c3" but unknown y-index "zzz")
::* expected result: "="
::* actual result: "{{#invoke:mpiktbllki|ek|zzz|3}}"

* #T26 {#invoke:mpiktbllki|ek|zzz|3|err=bugger} ("c3" but unknown y-index "zzz", "err=" param used)
* expected result: "bugger"
* actual result: "{{#invoke:mpiktbllki|ek|zzz|3|err=bugger}}"

: ---------------------------------------

* #T30 {#invoke:mpiktbllki|ek|id|11} (invalid x-index "11", NOT found, max is decimal "10")
* expected result: "="
* actual result: "{{#invoke:mpiktbllki|ek|id|11}}"

::* #T31 {#invoke:mpiktbllki|ek|id|A} (invalid x-index "A", NOT found, max is decimal "10")
::* expected result: "="
::* actual result: "{{#invoke:mpiktbllki|ek|id|A}}"

* #T32 {#invoke:mpiktbllki|ek|id|A|rel=1} (invalid x-index "A", "rel=" has no effect)
* expected result: "="
* actual result: "{{#invoke:mpiktbllki|ek|id|A|rel=1}}"

::* #T33 {#invoke:mpiktbllki|ek|id|A|err=bugger} (invalid x-index "A", "err=" param)
::* expected result: "bugger"
::* actual result: "{{#invoke:mpiktbllki|ek|id|A|err=bugger}}"

: ---------------------------------------

* #T40 {mpiktbllki|ek|grc|bin=1} (binary request, only y-index, assuming that "grc" exists)
* expected result: "1"
* actual result: "{{#invoke:mpiktbllki|ek|grc|bin=1}}"

::* #T41 {mpiktbllki|ek|xxx|bin=1} (binary request, only y-index, assuming that "xxx" does not exist)
::* expected result: "0"
::* actual result: "{{#invoke:mpiktbllki|ek|xxx|bin=1}}"

* #T42 {mpiktbllki|ek|id|L} (complete line, remove excessive whitespace)
* expected result: <nowiki> "[[id]] indonezia , Q9240 , rl bahasa Indo
                             nesia lr , 1 , id , ind , - , - , Ind
                             onezia lingvo , Kategori:Bahasa Esperanto , 100" </nowiki> (likely to deviate in practice)
* actual result via debu: "{{debu|{{#invoke:mpiktbllki|ek|id|L}}|0020}}"

::* #T43 {mpiktbllki|ek|xxx|L} (complete line, assuming that "xxx" does not exist)
::* expected result: "="
::* actual result: "{{#invoke:mpiktbllki|ek|xxx|L}}"

* #T44 {mpiktbllki|ek|xxx|L|detrc=true} (complete line, assuming that "xxx" does not exist, "detrc=true")
* expected result: long text followed by the short shameful verdict "="
* actual result: "{{#invoke:mpiktbllki|ek|xxx|L|detrc=true}}"

: ---------------------------------------

* #T50 {mpiktbllki|ek|sveda|R} (reverse query, assuming that "sv" <-> "sveda" exists)
* expected result: "sv"
* actual result: "{{#invoke:mpiktbllki|ek|sveda|R}}"

::* #T51 {mpiktbllki|ek|Sveda|R|err=nope} (reverse query, assuming that "sv" <-> "sveda" exists, but wrong case, NOT found)
::* expected result: "nope"
::* actual result: "{{#invoke:mpiktbllki|ek|Sveda|R|err=nope}}"

* #T52 {mpiktbllki|ek|sveda|R|bin=1} (reverse query, binary, assuming that "sv" <-> "sveda" exists, found)
* expected result: "1"
* actual result: "{{#invoke:mpiktbllki|ek|sveda|R|bin=1}}"

::* #T53 {mpiktbllki|ek|Sveda|R|bin=1} (reverse query, binary, assuming that "sv" <-> "sveda" exists, but wrong case, NOT found)
::* expected result: "0"
::* actual result: "{{#invoke:mpiktbllki|ek|Sveda|R|bin=1}}"

: ---------------------------------------

* #T60 {mpiktbllki|ek|Q9240|X} (extra query, assuming that "id" <-> "Q9240" exists, found)
* expected result: "id"
* actual result: "{{#invoke:mpiktbllki|ek|Q9240|X}}"

: ---------------------------------------

* note that test #T42 depends on "debu"
* note that test #T42 cannot be reasonably executed on the docs subpage without help of "pate" or "debu"

: ---------------------------------------

Note that output from this module when sent to screen directly as wikitext
will be subject to partial wiki parsing and full HTML parsing. This will
result in "[[id]" turned into a wikilink and in whitespace reduction. It
is not possible to call a module between "<pre>". In order to test this
module more thoroughly the output can be fed into another module ("debu"
or "pate") that analyzes the string and diplays ASCII codes or similar.

]===]

local piktbllki = {}

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

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

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

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

  local construsmo = "Modulo:mtbllingvoj"  -- EO
  -- local construsmo = "Modul:mtblbahasa"    -- ID

  -- constant table translating requested x-indexes and defining maximum
  -- valid indexes 0...10 covering totally 10 columns "c0"..."c10"

  local contabrxi = {[0]=0,1,2,3,4,5,6,7,8,9,10}

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

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

  -- "en.wiktionary.org/wiki/Wiktionary:Language_treatment" excluded languages
  -- "en.wikipedia.org/wiki/Spurious_languages"
  -- "iso639-3.sil.org/code/art" only valid in ISO 639-2
  -- "iso639-3.sil.org/code/zxx" "No linguistic content"

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

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

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

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

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

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

  -- SPECIAL VAR:S --

local qtbllingbaha = {} -- from submodule type "table" but metaized up to 11 e
local qbooguard = false -- only for the guard test, pass to other var ASAP
local qboodetrc = true  -- from "detrc=true" but default is "true" !!!
local qstrtrace = ""    -- for main & sub:s, debug report request by "detrc="

  -- GUARD AGAINST INTERNAL ERROR & ONE IMPOR VIA LOADDATA HUGE SRC TABLE --

if (type(construsmo)~="string") then
  qbooguard = true
else
  qtbllingbaha = mw.loadData(construsmo) -- can crash here despite guarding ??
  if (type(qtbllingbaha)~="table") then
    qbooguard = true
  end--if
end--if

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

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

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

-- Local function LFTRACEMSG

-- for variables the other sub "lfshowvar" is preferable but in exceptional
-- cases it can be justified to send text containing variables to this sub

-- enhances upvalue "qstrtrace" (may NOT be type "nil" on entry)
-- uses upvalue "qboodetrc"

local function lftracemsg (strbigcrap)
  if (qboodetrc) then
    qstrtrace = qstrtrace .. "<br>" .. strbigcrap .. '.'
  end--if
end--function lftracemsg

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

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

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

-- Local function LFDEC1DIGIT

-- Convert 1 decimal 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 LFSTR12DIG2INT

-- Convert 1 or 2 decimal digits to integer 0...99 (255 if invalid).

local function lfstr12dig2int (strmasuk)
  local numleen = 0
  local numaa   = 255 -- preassign to "error"
  local numbb   = 0
  numleen = string.len (strmasuk)
  if ((numleen==1) or (numleen==2)) then
    numaa = string.byte (strmasuk,1,1)
    numaa = lfdec1digit (numaa) -- 255 if invalid, ZERO would be valid
  end--if
  if ((numaa~=255) and (numleen==2)) then
    numbb = string.byte (strmasuk,2,2)
    numbb = lfdec1digit (numbb) -- 255 if invalid, ZERO would be valid
    if (numbb==255) then
      numaa = 255 -- 255 is invalid, note that ZERO would be valid
    else
      numaa = numaa * 10 + numbb -- valid integer number 0...99 now
    end--if
  end--if
  return numaa
end--function lfstr12dig2int

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

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

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

-- test whether char is an ASCII digit "0"..."9", return boolean

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

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

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

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

-- Local function LFCOUNTNDG

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

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

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

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

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

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

-- Local function LFIVALIDATELNKOADV

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

-- Input  : * strqooq -- string (empty is useless and returns
--                       "true" ie "bad" but cannot cause any major harm)
--          * numnokod -- "0" ... "3" how special codes "-" "??" should pass
--          * boolonkg -- "true" to allow long codes such as "zh-min-nan"
--          * boodigit -- "true" to allow digit in middle position
--          * boonoban -- "true" to skip test against ban table

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

-- Depends on functions :
-- [G] lftestnum lftestlc

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

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

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

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

local function lfivalidatelnkoadv (strqooq, numnokod, boolonkg, boodigit, boonoban)

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

  while (true) do -- fake (outer) loop

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

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

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

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

  return booisvaladv

end--function lfivalidatelnkoadv

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

-- Local function LFHRLSTRIP

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

local function lfhrlstrip (strrlinut)
  local numsoct = 0
  local numsodt = 0
  local numsoet = 0
  local numsoft = 0
  local numsogt = 0
  local numsoht = 0
  local numlaengd = 0
  numlaengd = string.len(strrlinut)
  if (numlaengd>=8) then -- at least 2 Octet:s length "rl .. lr" after strip
    numsoct = string.byte(strrlinut,1,1)
    numsodt = string.byte(strrlinut,2,2)
    numsoet = string.byte(strrlinut,3,3)
    numsoft = string.byte(strrlinut,(numlaengd-2),(numlaengd-2))
    numsogt = string.byte(strrlinut,(numlaengd-1),(numlaengd-1))
    numsoht = string.byte(strrlinut,(numlaengd  ),(numlaengd  ))
    if ((numsoct==114) and (numsodt==108) and (numsoet==32) and (numsoft==32) and (numsogt==108) and (numsoht==114)) then
      strrlinut = string.sub(strrlinut,4,(numlaengd-3)) -- stri off 3+3 char:s
    end--if
  end--if
  return strrlinut
end--function lfhrlstrip

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

-- Local function LFHPICKCSV

  -- This is done in 3 steps:
  -- * skip possible unsuitable elements
  -- * skip possible whitespace preceding the searched element
  -- * copy the searched element (takes a char from previous step)

  -- this is a copy of module "mpiksubstrind" with minimal modifications

local function lfhpickcsv (strcsvline, numxindex)

  local strelemento = ''

  local numypos  = 1 -- ONE-based
  local numlymyt = 0
  local numocct  = 0

  local bootymp  = false
  local boospc   = false -- flag for whitespace reduction feature
  local boouch   = false -- flag for found useful char when skipping element

  numlymyt = string.len(strcsvline)

  while (true) do -- iterate through elements in order to skip them
        if (numxindex==0) then -- nothing to skip anymore
          bootymp = true
          break -- exit the outer loop, nothing to skip anymore
        end--if
        numxindex = numxindex - 1
        bootymp = false
        boouch = false
        while (true) do -- iterate through whspc & chars mix looking for comma
          if (numypos>=numlymyt) then
            break -- exit the inner loop, NOT found, give up
          end--if
          numypos = numypos + 1
          numocct = string.byte (strcsvline,numypos,numypos)
          if ((numocct<32) and (numocct~=9)) then
            break -- EOL: exit the inner loop, NOT found, give up
          end--if
          if ((numocct>32) and (numocct~=44)) then
            boouch = true -- found a valid char (empty field is an error)
          end--if
          if (numocct==44) then
            if (boouch) then
              bootymp = true -- field was not empty
            end--if
            break -- exit the inner loop, found a comma, this is good or bad
          end--if
        end--while
        if (not bootymp) then
          break -- exit the outer loop, NOT found or empty field, give up
        end--if
  end--while

  if (bootymp) then
        bootymp = false
        while (true) do -- iterate through chars skipping whitespace
          if (numypos>=numlymyt) then
            break -- exit the loop, NOT found, give up
          end--if
          numypos = numypos + 1
          numocct = string.byte (strcsvline,numypos,numypos)
          if ((numocct<32) and (numocct~=9)) then
            break -- EOL: exit the loop, NOT found, give up
          end--if
          if (numocct>32) then
            if (numocct==44) then
              break -- exit loop, found comma, give up according to rules
            end--if
            bootymp = true
            break -- exit the loop, found valid char, start copying now
          end--if
        end--while
  end--if

  if (bootymp) then
        while (true) do -- iterate through chars copying the found element
          if ((numocct==32) or (numocct==9)) then
            boospc=true
          else
            if (boospc) then
              strelemento = strelemento .. string.char (32,numocct)
              boospc = false
            else
              strelemento = strelemento .. string.char (numocct)
            end--if
          end--if
          if (numypos>=numlymyt) then -- "strelemento" was preassigned to empty
            break -- done
          end--if
          numypos = numypos + 1
          numocct = string.byte (strcsvline,numypos,numypos)
          if ((numocct<32) and (numocct~=9)) then
            break -- EOL: exit the loop, done
          end--if
          if (numocct==44) then
            break -- comma: exit the loop, done
          end--if
        end--while
  end--if

  if (not bootymp) then
    strelemento = ''
  end--if

  return strelemento

end--function lfhpickcsv

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

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

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

function piktbllki.ek (arxframent)

  -- general unknown type

  local vartmp = 0     -- variable without type

  -- special type "args"

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

  -- general str (cool "qstrtrace" declared separately outside main)

  local strcy      = '' -- for reverse query only, is NOT equal "str1anon"
  local strkomline = ''
  local strret     = '' -- output string
  local strtmp     = ''

  -- stuff from "args" (cool "qboodetrc" declared separately outside main)

  local str1anon = ''    -- y-index from args[1]
  local num2anon = 255   -- x-index from args[2] (0...10 111 177 233 237)

  local boobin   = false
  local boorel   = true  -- flag from "rel=1" for stripping rl-lr-enclosement
  local stralt   = "-"   -- alternative string to replace "-" literal fr table
  local sterr    = "="   -- default or alterna string to replace "=" on error

  -- stuff from incoming table

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

  -- local str5errorstr = ''
  -- local str6errorstr = ''

  -- local tab7list     = {} -- obligatory but we don't use it here
  local tab8forward  = {} -- obligatory
  local tab9reverse  = {} -- optional
  local tab10extra   = {} -- optional

  -- general num

  local numoct  = 0 -- temp
  local numlong = 0 -- temp for parameter evaluation

  -- general boo

  local booerr  = false -- overall error flag
  local bootimp = false -- temp

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

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

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

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

  booerr = qbooguard
  lftracemsg ('This is "mpiktbllki", requested "detrc" report')
  lftracemsg ('Const "conbookodlng" is "' .. tostring(conbookodlng) .. '"')
  lftracemsg ('Const "conboomiddig" is "' .. tostring(conboomiddig) .. '"')
  lftracemsg ('Var "booerr" is "' .. tostring(booerr) .. '" so far')

  ---- GATHER LESS THAN 11 ITEMS FROM THE HUGE TABLE AND PRECHECK ----

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

  while (true) do -- fake loop
    if (booerr) then
      break -- to join mark
    end--if
    num0statcode = qtbllingbaha[0]
    if (num0statcode~=0) then
      lftracemsg ("Submodule failed with error code " .. tostring (num0statcode))
      booerr = true
      break -- to join mark
    end--if
    num1lenfullo = qtbllingbaha[1]
    if ((num1lenfullo>=320) and (num1lenfullo<=1000000)) then
      lftracemsg ("Passed 320...1'000'000 check with length " .. tostring (num1lenfullo))
    else
      lftracemsg ("Failed 320...1'000'000 check with length " .. tostring (num1lenfullo))
      booerr = true
      break -- to join mark
    end--if
    tab8forward = qtbllingbaha[8] -- ultimately obligatory
    if (type(tab8forward)~="table") then -- important check
      lftracemsg ("Submodule failed to deliver obligatory [8]")
      booerr = true
      break -- to join mark
    end--if
    tab9reverse = qtbllingbaha[9] -- optional
    if (type(tab9reverse)~="table") then -- important check
      tab9reverse = {} -- replace by empty table
      lftracemsg ("Submodule failed to deliver optional [9]")
    end--if
    tab10extra = qtbllingbaha[10] -- optional
    if (type(tab10extra)~="table") then -- important check
      tab10extra = {} -- replace by empty table
      lftracemsg ("Submodule failed to deliver optional [10]")
    end--if
    break -- finally to join mark
  end--while -- fake loop -- join mark

  ---- SEIZE 1 OR 2 ANONYMOUS PARAMS (1 OBLIGATORY PLUS 1 OPTINOAL) ----

  -- y-index string from arxourown[1] is "str1anon"
  -- here y-index is checked very preliminarily only

  -- x-index string or special value from arxourown[2] is integer "num2anon"
  -- (0...10, special 111, 177, 233, 237, invalid 255)
  -- here we access "contabrxi" (x-translation)

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

  str1anon = '' -- preassume not given (but it's obligatory)
  num2anon = 255 -- preassume not given (maybe can live without it)

  if (not booerr) then
    vartmp = arxourown[1] -- requested y-index
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if ((numlong>0) and (numlong<100)) then
        str1anon = vartmp -- preliminarily accepted
      else
        lftracemsg ('Requested y-index is blatantly invalid')
        booerr = true
      end--if
    else
      lftracemsg ('No y-index')
      booerr = true
    end--if
  end--if

  if (not booerr) then
    vartmp = arxourown[2] -- x-index or special
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if ((numlong>0) and (numlong<100)) then
        strtmp = vartmp -- preliminarily accepted
        if (numlong==1) then
          numoct = string.byte (strtmp,1,1)
          if (numoct==76) then
            num2anon = 177 -- special "L" (complete line)
          end--if
          if (numoct==82) then
            num2anon = 233 -- special "R" (reverse query)
          end--if
          if (numoct==88) then
            num2anon = 237 -- special "X" (extra query)
          end--if
        end--if
        if ((num2anon==255) and ((numlong==1) or (numlong==2))) then
          numoct = lfstr12dig2int (strtmp) -- 255 if invalid
          vartmp = contabrxi [numoct] -- risk of type "nil"
          lftracemsg ('Translated requested x-index from "' .. strtmp .. '" to "' .. tostring(vartmp) .. '"')
          if (type(vartmp)=="number") then
            num2anon = vartmp -- otherwise keep the preassigned 255
          end--if
        end--if
        if (num2anon==255) then
          lftracemsg ('Requested x-index "' .. strtmp .. '" is invalid') -- NOT "vartmp" !!!
          booerr = true
        end--if
      else
        lftracemsg ('Requested x-index is blatantly invalid')
        booerr = true
      end--if
    else
      lftracemsg ('No x-index')
    end--if
  end--if

  ---- SEIZE 3 OF 5 NAMED AND OPTIONAL PARAMETERS ----

  if (not booerr) then
    vartmp = arxourown["bin"] or "0" -- default is "false"
    boobin = (vartmp=="1")
    bootimp = (vartmp=="0")
    if (not (boobin or bootimp)) then
      lftracemsg ('Invalid parameter "bin="')
      booerr = true
    end--if
    if (boobin) then
      lftracemsg ('Parameter "bin=1" seized') -- silent for "bin=0"
    end--if
  end--if

  if (not booerr) then
    vartmp = arxourown["rel"] or "1" -- default is "true"
    boorel = (vartmp=="1")
    bootimp = (vartmp=="0")
    if (not (boorel or bootimp)) then
      lftracemsg ('Invalid parameter "rel="')
      booerr = true
    end--if
    if (not boorel) then
      lftracemsg ('Parameter "rel=0" seized') -- silent for "rel=1"
    end--if
  end--if

  if (not booerr) then
    vartmp = arxourown["alt"] -- 1...64 char:s alt string no default
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if ((numlong<1) or (numlong>64)) then
        lftracemsg ('Invalid parameter "alt="')
        booerr = true
      else
        lftracemsg ('Parameter "alt=" seized')
        stralt = vartmp -- default was "-"
      end--if
    end--if
  end--if

  ---- CHECK PARAM CONSISTENCY ----

  -- here we access "conbookodlng" and "conboomiddig"

  if ((not booerr) and boobin) then -- "bin=1" here
    if (num2anon==255) then
      num2anon = 111 -- y-index only, parameter x-index not given
    end--if
    if ((num2anon~=111) and (num2anon~=233) and (num2anon~=237)) then
      lftracemsg ('Forward search and "bin=1" exclude ordinary x-index and "L"')
      booerr = true
    end--if
  end--if

  if (not booerr) then
    if ((not boobin) and (num2anon==255)) then
      lftracemsg ('Without "bin=1" an x-index is required')
      booerr = true
    end--if
  end--if

  if (not booerr) then
    if (num2anon>200) then -- must be done after 255->111 conversion
      numlong = string.len (str1anon) -- reverse or extra query
      if ((numlong<2) or (numlong>48)) then
        lftracemsg ("Requested y-index has invalid length for reverse or extra query")
        booerr = true -- not valid
      end--if
    else
      if (not lfivalidatelnkoadv(str1anon,0,conbookodlng,conboomiddig,false)) then
        lftracemsg ("Requested y-index is invalid for forward query, need language code")
        booerr = true -- not valid language code
      end--if
    end--if
  end--if

  ---- SEIZE NAMED PARAMETER "ERR" EVEN IF WE ALREADY SUCK ----

  -- try to pick named param "err=" even if we already have "booerr==true"
  -- 1...64 char:s alternative string to replace "="

  vartmp = arxourown["err"] -- can be type "nil"
  if (type(vartmp)=="string") then
    numlong = string.len (vartmp)
    if ((numlong<1) or (numlong>64)) then
      lftracemsg ('Alterna string "err=" has invalid length')
      booerr = true -- damn -- maybe redundant -- keep "sterr" as "="
    else
      sterr = vartmp -- was preassigned to "="
      lftracemsg ('Alterna string "err=" seized')
    end--if
  end--if

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

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

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

  lftracemsg ('All params done, "booerr" is "' .. tostring(booerr) .. '" so far')

  ---- PERFORM FORWARD OR REVERSE QUERY AS REQUESTED ----

  -- this delivers either "strkomline" (forward, even "L") or
  -- "strcy" (types "R" and "X")

  if (not booerr) then
    if (num2anon<200) then -- even for "L"
      strkomline = tab8forward [str1anon] or "" -- protect against type "nil"
      if (strkomline=='') then
        lftracemsg ('Forward search for "' .. str1anon .. '" resulted in no hit')
        booerr = true
      else
        lftracemsg ('Forward search for "' .. str1anon .. '" resulted in len=' .. tostring(string.len(strkomline)))
      end--if
    end--if
    if (num2anon==233) then
      strcy = tab9reverse [str1anon] or "" -- protect against type "nil"
      if (strcy=='') then
        lftracemsg ('Reverse search for "' .. str1anon .. '" resulted in no hit')
        booerr = true
      else
        lftracemsg ('Reverse search for "' .. str1anon .. '" resulted in len=' .. tostring(string.len(strcy)))
      end--if
    end--if
    if (num2anon==237) then
      strcy = tab10extra [str1anon] or "" -- protect against type "nil"
      if (strcy=='') then
        lftracemsg ('Extra search for "' .. str1anon .. '" resulted in no hit')
        booerr = true
      else
        lftracemsg ('Extra search for "' .. str1anon .. '" resulted in len=' .. tostring(string.len(strcy)))
      end--if
    end--if
  end--if

  ---- RETURN ONE ELEMENT PICKED ACCORDING TO X-INDEX NUMBER ----

  -- excessive whitespace from forward query already reduced in submodule

  -- "num2anon" can be 0...10 whereas values > 100 are reserved:
  -- * 111 forward search y-only query per "bin=1" while no "R" no "X"
  -- * 177 complete line per "L"

  if (num2anon<100) then

    if (not booerr) then
      do
        local numa = 0
        local numb = 0
        numa = string.len(str1anon) + 5 -- ONE-based position after "]]"
        numb = string.len(strkomline)
        if (numa>numb) then
          lftracemsg ('Failed to discard "cy"')
          booerr = true -- should impossible to occur unless submodule broken
        else
          strkomline = string.sub(strkomline,numa,numb) -- discard "cy" part
          lftracemsg ('Discarded "cy" before x-pick')
          strret = lfhpickcsv (strkomline,num2anon) -- pick
          lftracemsg ('After x-pick got "' .. strret .. '"')
          booerr = (strret=='')
        end--if
      end--do
    end--if

    if (not booerr) then
      if (boorel) then -- default is "true"
        strret = lfhrlstrip (strret) -- strip
      end--if
      if (strret=='-') then
        strret = stralt -- do this BEFORE "=" -> "-"
      end--if
      if (strret=='=') then
        strret = '-' -- string "=" is prohibited, punishment is conversion to "-"
      end--if
    end--if

  end--if

  ---- RETURN COMPLETE LINE OR RESULT OF REVERSE OR EXTRA QUERY ----

  -- excessive whitespace from forward query already reduced in submodule

  if ((not booerr) and (num2anon==177)) then -- x-index was "L"
    strret = strkomline
  end--if
  if ((not booerr) and (num2anon>200)) then -- x-index was "R" or "X"
    strret = strcy
  end--if

  ---- RETURN BINARY DIGIT OR CARE ABOUT ERRORS ----

  if (boobin) then -- "bin=1"
    if (booerr) then
      strret = "0" -- y-index NOT found or error
    else
      strret = "1" -- y-index found
    end--if
  else
    if (booerr) then
      strret = sterr -- string "strret" was preassigned to empty
    end--if
  end--if

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

  if (qboodetrc) then
    strret = "<br>" .. qstrtrace .. "<br><br>" .. strret
  end--if
  return strret

end--function piktbllki.ek

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

return piktbllki