Modul:Vorlage:Defekter Weblink

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen
Vorlagenprogrammierung Diskussionen Lua Test Unterseiten
Modul Deutsch English


local Serial  =  "2023-08-14"
--[=[
{{Defekter Weblink}}
]=]



local Ausnahmen        =  "Modul:Vorlage:Defekter Weblink/Ignorieren"
local Domains          =  "Modul:Vorlage:Defekter Weblink/Domains"
local Fehler           =  false
local KategorieBeginn  =  "Kategorie:Wikipedia:Defekte Weblinks/Bot"
local Kategorien       =
             { Bot            =  { s = "" },
               Archiviert     =  { s = "/Archiviert" },
               Deaktiviert    =  { s = "/Deaktiviert" },
               Domain         =  { s = "/Domains" },
               FalschPositiv  =  { s = "/Funktioniert vermutlich" },
               Intern         =  { s = "/Interner Fehler" },
               Klammer        =  { s = "/Eckige Klammer in URL" },
               Leer           =  { s = "/Keine URL mehr" },
               Namensraum     =  { s = "/Falscher Namensraum" },
               NonASCII       =  { s = "/Zeichen jenseits ASCII" },
               ResourceURL    =  { s = "/URL keine URL" },
               SchreibR       =  { s = "/Schreibrichtung in URL" },
               Solidus        =  { s = "/Lemma im Unterseitenformat" },
               Sonderzeichen  =  { s = "/URL endet auf Sonderzeichen" },
               Spam           =  { s = "/Spam" },
               SyntaxPipe     =  { s = "/Pipe-Symbol in URL" },
               Unauffindbar   =  { s = "/Keine auffindbaren URL" },
               Unicode        =  { s = "/Unicode in URL" },
               Unterseite     =  { s = "/Einbindung auf Unterseite" },
               Vorlage        =  { s = "/Vorlagensyntax" },
               Waise          =  { s = "/Artikel fehlt" }
             }
local Vorlage   =  "Vorlage:Defekter Weblink"
local Same      =  "Artikel mit gleicher URL:"
local Satzzch   =  "URL endet auf Satzzeichen; ggf. besser ohne"
local Schemes   =  ":http:https:ftp:sftp:mailto:telnet:gopher:"
local Schonmal  =  "Im Jahr %s bereits defekt gewesen."
local SchreibR  =  "Links-Rechts-Schreibrichtungszeichen in URL"
local Secure    =  "Wechsel zwischen http und https erforderlich"
local Selbst    =  "Modul:" .. Vorlage
local Serie     =  "2015-10"
local Silent    =  "%s'''%d''' deaktivierte URL vorhanden"
local Shrinked  =  "Leere oder vermutlich abgearbeitete URL-Liste; Einbindung kann dann entfernt werden"
local Skip      =  "''Vermutlich im Browser funktionierend. Ausprobieren; falls okay, dann diesen Eintrag löschen.''"
local Solved    =  "''Dieser Link ist vermutlich nicht mehr im Quelltext des Artikels vorhanden; falls insgesamt weg, dann diesen Eintrag löschen.''"
local Sonderz   =  "Eckige Klammer [ in der URL – siehe [[Hilfe:Links #Sonderzeichen]]."
local Source    =  "Details im Quelltext nachlesbar."
local Spam      =  "Spam-Link aus dem Artikel entfernen"
local Stat_1_2  =  "Seite hat Besonderheit"
local Stat_3nn  =  "Seite wurde umgeleitet; anpassen?"
local Stat_4nn  =  "Problem mit Ressource"
local Stat_502  =  "Server / ganze Domain anscheinend unerreichbar"
local Stat_5nn  =  "Serverproblem"
local Stat_9nn  =  "Netzwerk-Fehler (%d)"
local Steuer    =  "|Archiv|Bot|Lauf|Unterseite|"
local Stored    =  "Vielleicht ist eine archivierte Version geeignet:"
local Strange   =  "Unicode in URL"
local Sub       =  Vorlage .. "/Anleitung"
local Suche500  =  "andere Artikel, gleiche Domain"
local Suffix    =  "URL ohne Sonderzeichen am Ende funktioniert"
local Superz    =  "Zeichen jenseits von [[ASCII]] gefunden"
local SyntPipe  =  "Pipe-Symbol in URL; mal ohne Anhang probieren"
local SyntURL   =  "URL-Syntax: Keine Domain/IP, nicht im www erreichbar"
local Class     =  "deadlink-bot"
local HelpLink  =  { HTTPstat  =  1378713,
                     NonASCII  =  9063042,
                     Pipe      =  9063048,
                     Spec      =  9063045 }
local DomainFamilies, ExceptionCount, Exceptions, ExtLinks, Frame
local PageText, Refs, TemplUtl, URLutil



------------------------------------------------------------------------
------------------------------------------------------------------------
--  Privatfunktionen Benutzer:Boshomi
--  Ohne offiziell zugesicherte Wirkung
    local HideURLs          =  "%s<span style=\"display:none;\">%s</span>"
    local PraefixedURLsAll  =  ""
    local function addpraefixURL(url,praefix)
    	-- Add a praefix for finding URL of tht template table in special:LinkSearch
    	-- default is http://giftbot.dwl.invalid/
    	-- return space separated string of urls
    	local r =""
    	if praefix == nil then
    	   praefix="http://giftbot.dwl.invalid/"
    	end
    	if url==nil then
    	   r=""
    	else
    	   r =praefix .. url .. " "
    	end

    	return r
    end
------------------------------------------------------------------------
------------------------------------------------------------------------



local function fair( access )
    -- URL egalisieren
    -- Parameter:
    --     access  -- string, mit URL
    -- Returns:   1. string, mit egalisierter authority
    --            2. string, mit egalisiertem Pfad+query nach "/"
    local r1 = URLutil.getNormalized( access ):sub( 3 )
    local i, r2
    if r1:match( "[,%.;:!%?%)]$" ) then
        r1  =  r1:sub( 1,  #r1 - 1 )
    end
    i  =  r1:find( "#", 5, true )
    if i then
        r1  =  r1:sub( 1,  i - 1 )
    end
    i  =  r1:find( "/", 4, true )
    if i then
        r2  =  r1:sub( i + 1 )
        r1  =  r1:sub( 1,  i - 1 ):lower()
    else
        r2  =  ""
    end
    return r1, r2
end -- fair()



local function fallback( about, archive )
    -- Archivversion zum Weblink darstellen, falls archiviert
    -- Parameter:
    --     about    -- table mit aktivem Weblink
    --                  .url
    --                  .cit
    --                  .wba
    --     archive  -- Kennung ("cit", "wba")
    -- Returns: string mit betiteltem Archivlink, oder false
    local sign = about[ archive ]
    local r
    if sign then
        local seek  =  about.url
        local less, repo, repos, show, sub
        if sign:sub( 1, 1 ) == "-" then
            less  =  true
            sign  =  sign:sub( 2 )
            if about.lastChSz then
                sub  =  seek:match( "^(.+)%A$" )
                if sub then
                    seek  =  sub
                    show  =  Satzzch
                end
            elseif about.syntaxPipe then
--              sub  =  seek:match( "^(.+)%%7C$" )
                if sub then
                    seek  =  sub
                    show  =  SyntPipe
                end
            end
        else
            less  =  false
        end
        repos  =
             { cit  =  { scheme = "http://www.webcitation.org/%s",
                         show   = "webcitation.org",
                         params = { sign } },
               wba  =  { scheme = "http://wayback.archive.org/web/%s/%s",
                         show   = "archive.org",
                         params = { sign, seek } }
             }
        repo  =  repos[ archive ]
        if show then
            repo.show  =  string.format( "%s (%s)", repo.show, show )
        end
        r  =  string.format( repo.scheme,  unpack( repo.params ) )
        r  =  string.format( "[%s %s]",  r, repo.show )
    else
        r  =  false
    end
    return r
end -- fallback()



local function falsified( attempt )
    -- Check URL for exception
    -- Returns: true, if probably false positive
    local r
    if Exceptions then
        local s
        for i = 1, ExceptionCount do
            s  =  Exceptions[ i ]
            r  =  ( s == string.sub( attempt, 1, #s ) )
            if r then
                break -- for i
            end
        end -- for i
    end
    return r
end -- falsified()



local function families( achieve )
    -- Domain-Liste abarbeiten
    -- Parameter:
    --     achieve  -- nil: Initialisierung
    --                 1:   Kategorie
    --                 2:   Wikitext-Ausgabe
    -- Returns: string mit Liste, oder ""
    local r  =  ""
    if type( Domains ) == "string" then
        local lucky
        lucky, Domains  =  pcall( mw.loadData, Domains )
        if type( Domains ) ~= "table" then
            Domains  =  false
        end
    end
    if not DomainFamilies  and  Domains then
        DomainFamilies  =  { }
        for k, v in pairs( Domains ) do
            table.insert( DomainFamilies,
                          { seek = v } )
        end -- for k, v
    end
    if DomainFamilies then
        local n  =  table.maxn( DomainFamilies )
        local d
        for i = 1, n do
            d  =  DomainFamilies[ i ]
            d.suite  =  d.seek
        end -- for i
        if achieve == 1 then
            local s
            for i = 1, n do
                d  =  DomainFamilies[ i ]
                s  =  d.spot
                if s then
                    r  =  string.format( "%s[[%s/Domains/%s|%s]]",
                                         r, KategorieBeginn, d.suite, s )
                end
            end -- for i
        elseif achieve == 2 then
            for i = 1, n do
                d  =  DomainFamilies[ i ]
                r  =  string.format( "%s\n{{%s/DomainCat|%s}}",
                                     r, Vorlage, d.suite )
            end -- for i
        end
    end
    return r
end -- families()



local function family( address )
    -- URL eine der interessanten Domains?
    -- Parameter:
    --     address  -- string mit URL
    if DomainFamilies then
        local n  =  table.maxn( DomainFamilies )
        local d, k, s
        for i = 1, n do
            d  =  DomainFamilies[ i ]
            k  =  mw.ustring.find( address, d.seek, 2, true )
            if k then
                s  =  mw.ustring.sub( address,  k - 2,  k - 1 )
                if s == "//"  or
                   mw.ustring.codepoint( s, 2, 2 ) == 46 then
                    local m  =  mw.ustring.len( d.seek )
                    local j
                    s  =  mw.ustring.sub( address,  k + m )
                    if s == "" then
                        s  =  "/"
                        j  =  47
                    else
                        j  =  mw.ustring.codepoint( s, 1, 1 )
                    end
                    if j == 47  or  j == 58 then
                        d.spot   =  s:sub( 2 ) .. " "
                        break -- for i
                    end
                end
            end
        end -- for i
    end
end -- family()



local function fault( a )
    -- Formatiere Fehler mit class=error
    -- Parameter:
    --     a  -- string mit Text
    return string.format( "<span class=\"error\">%s</span>", a )
end -- fault()



local function favour( a )
    -- Generiere Link auf Hilfe
    -- Parameter:
    --     a  -- string mit Text
    return string.format( "([[Special:Redirect/page/%d|Details]])",
                          HelpLink[ a ] )
end -- favour()



local function fehler( art, anzeige )
    -- Ein Fehler ist aufgetreten
    -- Parameter:
    --     art      -- string mit Schlagwort zum Typ
    --     anzeige  -- string mit Einzelheiten
    local t
    if not Fehler then
        Fehler  =  { Intern       =  { s = "Interner Fehler",
                                       k = "Intern" },
                     Kennung      =  { s = "Unerlaubte Syntax",
                                       k = "Vorlage" },
                     Lauf         =  { s = "Botlauf fehlt",
                                       k = "Vorlage" },
                     Modul        =  { s = "Modul-Seite fehlt",
                                       k = "Intern" },
                     Namensraum   =  { s = "Falscher Namensraum",
                                       k = "Namensraum" },
                     Ohne         =  { s = "URL fehlt, Parameter ",
                                       k = "Vorlage" },
                     Seite        =  { s = "Fehlerhafte Seiten-ID ",
                                       k = "Vorlage" },
                     Spam         =  { s = "Spam-URL kaputt",
                                       k = "Vorlage" },
                     Unbekannt    =  { s = "Unbekannter Parameter",
                                       k = "Vorlage" },
                     Unterseite   =  { s = "Archivseite",
                                       k = "Unterseite" },
                     Waise        =  { s = "Kein Artikel vorhanden",
                                       k = "Waise" },
                     Zuviel       =  { s = "Unerlaubte Daten",
                                       k = "Vorlage" }
                   }
    end
    t  =  Fehler[ art ]
    if t then
        if t.e then
            t.e  =  string.format( "%s; %s", t.e, anzeige )
        elseif anzeige then
            t.e  =  anzeige
        else
            t.e  =  ""
        end
        if t.k then
            local wk  =  Kategorien[ t.k ]
            if wk then
                wk.e  =  true
            else
                Fehler.Intern.e      =  string.format( "Wartungskat %s",
                                                       wk )
                Kategorien.Intern.e  =  true
            end
        end
    else
        Fehler.Intern.e      =  string.format( "fehler(%s) %s",
                                               art, anzeige )
        Kategorien.Intern.e  =  true
    end
end -- fehler()



local function fehlerliste()
    -- Gesamt-Fehlermeldung
    -- Returns:
    --     string mit formatiertem Ergebnis, oder false
    local r
    if Fehler then
        r  =  ""
        for k, v in pairs( Fehler ) do
            if v.e and v.s then
                r  =  string.format( "%s*** %s: %s ", r, v.s, v.e )
            end
        end -- for k, v
        r  =  "<br />" .. fault( r )
    else
        r  =  false
    end
    return r
end -- fehlerliste()



local function fertig()
    -- Artikeltext herbeischaffen, so gut es geht
    -- Returns:
    --     string mit Artikeltext, oder nil
    local r
    if Frame then
        if type( PageText ) == "string" then
            local page  =  mw.title.new( PageText, 0 )
            r  =  page:getContent()
            if r then
                if #r < 185000 then
                    local lucky
                    lucky, TemplUtl = pcall( require, "Modul:TemplUtl" )
                    if type( TemplUtl ) == "table" then
                        TemplUtl  =  TemplUtl.TemplUtl()
                    else
                        fehler( "Modul", TemplUtl )
                    end
                    r  =  TemplUtl.flat( r ):gsub( "<", " " )
                    r  =  r:gsub( "%{%{#tag", "{{NULL|" )
                           :gsub( "%{%{Exzellent", "{{NULL|" )
                           :gsub( "%{%{Informativ", "{{NULL|" )
                           :gsub( "%{%{Lesenswert", "{{NULL|" )
                           :gsub( "%{%{Review", "{{NULL|" )
                           :gsub( "%{%{Auflagen-Diagramm", "{{NULL|" )
                           :gsub( "%{%{Auflagenvergleich-relativ", "{{NULL|" )
                           :gsub( "%{%{Gesprochene Version", "{{NULL|" )
                           :gsub( "%{%{Löschantragstext", "{{NULL|" )
                           :gsub( "%{%{Taxobox", "{{NULL|" )
                           :gsub( "%{%{SEITENTITEL:", "{{NULL|" )
                           :gsub( "%{%{DISPLAYTITLE:", "{{NULL|" )
                           :gsub( "%[%[Kategorie:", "[[:Kategorie:" )
                    r  =  Frame:preprocess( r ) .. r
                    if r:find( "UNIQ--ref-", 1, true ) then
                        Refs  =  true
                    end
                else
                    r  =  false
                end
            else
                fehler( "Waise", "Wie das?" )
            end
        else
            fehler( "Intern", "Lemma undefiniert" )
        end
    end
    return r
end -- fertig()



local function fertiger()
    -- Baumstruktur externer Links generieren
    local src = fertig()
    if src then
        local i = 1
        local j, s, s2
        ExtLinks  =  { }
        src  =  src .. " "
        while ( i ) do
            i  =  src:find( "//", i, true )
            if i then
                if i > 1 then
                    j  =  i - 1
                    s  =  src:sub( j, j )
                    if s:match( "[%s%[:]" ) then
                        s  =  src:sub( i,  src:find( "%s", i ) - 1 )
                        j  =  s:find( "''", 4, true )
                        if j then
                            s  =  s:sub( 1,  j - 1 )
                        end
                        j  =  s:find( "[%[%]<]", 4 )
                        if j then
                            s  =  s:sub( 1,  j - 1 )
                        end
                        if s:match( "^//%w[%w%.%%_-]*%.%w%w" ) then
                            s, s2  =  fair( s )
                            if not ExtLinks[ s ] then
                                ExtLinks[ s ]  =  { }
                            end
                            ExtLinks[ s ][ s2 ]  =  true
                        end
                        i  =  i + #s
                    end
                    i  =  i + 2
                else
                    i  =  i + 3
                end
            end
        end -- while i
    else
        ExtLinks  =  false
    end
end -- fertiger()



local function fertiges( attempt )
    -- Kann URL im Artikeltext vorkommen, oder wurde sie eliminiert?
    -- Parameter:
    --     attempt  -- URL
    -- Returns: true, wenn eliminiert, oder false
    local s = type( ExtLinks )
    local r
    if s == "nil" then
        fertiger()
        s = type( ExtLinks )
    end
    if s == "table" then
        local i = attempt:find( "//", 1, true )
        local pq, s2
        r  =  true
        s  =  attempt
        if i then
            s  =  s:sub( i )
        end
        s, s2  =  fair( s )
        pq     =  ExtLinks[ s ]
        if pq then
            i  =  s2:find( "%", 4, true )
            if i then
                i   =  i - 1
                if i == 0 then
                    s2  =  ""
                else
                    s2  =  s2:sub( 1, i )
                end
            else
                i  =  #s2
            end
            if i == 0 then
                r  =  false
            else
                for k, v in pairs( pq ) do
                    if k:sub( 1, i ) == s2 then
                        r  =  false
                        break -- for k, v
                    end
                end -- for k, v
            end
        end
    end
    return r
end -- fertiges()



local function fetch( args )
    -- Analysiere Parameterliste
    -- Parameter:
    --     args  -- table mit Parameterliste
    -- Returns:
    --     1  -- table mit Steuerungs-Infos
    --     2  -- table mit Weblink-Infos als string
    local k, v
    local rC  =  { }
    local rW  =  { }
    for k, v in pairs( args ) do
        if v then
            v  =  mw.text.trim( v )
            if v == "" then
                v  =  false
            end
        end
        if type( k ) == "number" then
            if v then
                rW[ k ]  =  v
            end
        else
            if Steuer:find(  string.format( "|%s|", k )  ) then
                rC[ k ]  =  v
            else
                fehler( "Unbekannt", k )
            end
        end
    end -- for k, v
    return  rC, rW
end -- fetch()



local function filter( assigned, at )
    -- Analysiere einen Weblink-Parameter
    -- Parameter:
    --     assigned  -- string mit Weblink-Parameter
    --     at        -- number des Parameters
    -- Returns:
    --     table mit Weblink-Infos, false wenn nicht mehr im Artikel
    local got      =  mw.text.split( assigned, "%s+" )
    local n        =  table.maxn( got )
    local r        =  { }
    local repos    =  { c  =  { min    = 8,
                                scheme = "^cit=(%w+)$",
                                sign   = "cit" },
                        w  =  { min    = 10,
                                scheme = "^wba=(%d+)$",
                                sign   = "wba" } }
    local strange  =  "^%a*:?//[^/]+/[!-~]*([^!-~])"
    local k, less, repo, s, scheme
    for i = 1, n do
        s  =  got[ i ]
        k  =  s:find( ":" )
        if k then
            scheme  =  s:sub( 1, k ):lower()
            k       =  Schemes:find( ":" .. scheme )
            if k then
                r.scheme  =  scheme
                if scheme == "mailto:" then
                    r.listedBlack  =  true
                    s              =  "mailto&#58;" .. s:sub( 8 )
                elseif not r.listedBlack  and
                       not URLutil.isResourceURL( s ) then
                    r.url     =  s
                    r.mode    =  false
                    r.lethal  =  true
                    break -- for i
                end
                r.url  =  s
                if s:find( "&#12" ) then
                    s  =  s:gsub( "&#123;&#123;", "{{" )
                           :gsub( "&#124;", "|" )
                           :gsub( "&#125;&#125;", "}}" )
                    r.linkBrackets  =  true
                end
                r.leave  =  falsified( s )
                if s:match( "[%(,:.;?!/\"\\]$" ) then
                    r.lastChSz      =  true
                    r.linkBrackets  =  true
                end
                if s:find( "%E2%80", 1, true ) then
                    r.lunatic  =  true
                    if s:find( "%E2%80%8E", 1, true ) then
                        r.lastChLRM  =  true
                    elseif s:find( "%E2%80%98", 1, true )  and
                           s:find( "%E2%80%99", 1, true ) then
                        r.lunatic  =  false
                    elseif s:find( "%E2%80%9C", 1, true )  and
                           s:find( "%E2%80%9D", 1, true ) then
                        r.lunatic  =  false
                    elseif s:find( "%E2%80%9E", 1, true )   and
                           ( s:find( "%E2%80%9C", 1, true )  or
                             s:find( "%E2%80%9D", 1, true ) ) then
                        r.lunatic  =  false
                    end
                end
                r.strangeCh  =  mw.ustring.match( s, strange )
                if s:find( "%%7C" ) then
                    s  =  s:gsub( "^.*%%7C(.*)$", "%1" )
                    if not s:find( "[&#]" )  and
                       not s:find( "/$" )  and
                       not s:find( "%w%.%a" )  and
                       not s:find( "%20", 1, true ) then
                        r.syntaxPipe  =  s
                    end
                end
                r.lazy  =  ( i < n )
                if r.lazy then
                    if r.listedBlack then
                        fehler( "Zuviel",
                                string.format( "Parameter %d=%s",
                                               at,  got[ i +1 ] ) )
                    end
                elseif not r.weitere  and  not r.listedBlack then
                    r.loose  =  fertiges( r.url )
                end
            else
                fehler( "Kennung",
                        string.format( "Parameter %d=%s", at, s ) )
            end
            break -- for i
        else
            less  =  false
            k     =  s:sub( 1, 1 )
            if k:match( "%d" ) then
                if s:match( "^%d%d%d$" ) then
                    r.mode  =  tonumber( s )
                    s       =  false
                elseif s:match( "^20%d%d$" ) then
                    r.schonmal  =  s
                    s           =  false
                end
            elseif k == "-" then
                if #s > 1 then
                    s     =  s:sub( 2 )
                    k     =  s:sub( 1, 1 )
                    if k == " " then
                        r.lastRemove  =  true
                    end
                    less  =  true
                else
                    r.lastRemove  =  true
                    s             =  false
                end
            elseif k == "+" then
                r.weitere  =  s:sub( 2 )
                s          =  false
            elseif k == "p" then
                if s == "proto" then
                    r.locked  =  true
                    s         =  false
                end
            elseif k == "s" then
                if s == "sbl" then
                    r.listedBlack  =  true
                    s              =  false
                end
            end
            repo  =  repos[ k ]
            if repo then
                k  =  s:match( repo.scheme )
                if k then
                    if #k > repo.min then
                        if less then
                            k  =  "-" .. k
                        end
                        r[ repo.sign ]  =  k
                        s               =  false
                    end
                end
            end
            if s then
                fehler( "Kennung",
                        string.format( "Parameter %d=%s", at, s ) )
            end
        end
    end -- for i
    if r  and  not r.url then
        fehler( "Ohne", tostring( at ) )
        r.lazy  =  true
    end
    return  r
end -- filter()



local function finde( alle )
    -- Anzahlen der Weblinks
    -- Parameter:
    --     alle  --  table mit Weblink-strings
    -- Returns:
    --     1  -- number mit Zahl der aktiven Weblinks
    --     2  -- number mit Zahl der deaktivierten Weblinks
    --     3  -- table mit Weblink-tables
    --     4  -- number mit Zahl der unauffindbaren Weblinks
    local r0  =  0
    local r1  =  0
    local rx  =  0
    local r   =  { }
    local k, v
    for k, v in pairs( alle ) do
        got  =  filter( alle[ k ], k )
        if got then
            if got.lazy then
                r0  =  r0 + 1
            else
                r1      =  r1 + 1
                r[ k ]  =  got
                if got.loose then
                    rx  =  rx + 1
                end
            end
        end
    end -- for k, v
    return  r1, r0, r, rx
end -- finde()



local function fire( art )
    -- Melde Kategorie an
    -- Parameter:
    --     art  -- string mit Schlagwort zum Typ
    local t = Kategorien[ art ]
    if t then
        t.e  =  true
    else
        fehler( "Intern",  "Kategorie:" .. art )
    end
end -- fire()



local function firelist()
    -- Alle Kategorien
    -- Returns: string mit allen Kategorien
    local r  =  ""
    for k, v in pairs( Kategorien ) do
        if v.e then
            r = string.format( "%s [[%s%s]]",
                               r, KategorieBeginn, v.s )
        end
    end -- for k, v
    return r
end -- firelist()



local function flink( ask, anzeige )
    -- Weblinksuche verlinken
    -- Parameter:
    --     ask      -- string mit Such-URL-Sequenz
    --     anzeige  -- string mit Linktitel
    -- Returns: string mit betiteltem Wikilink/Weblink
    local r
    if true then
        r  =  ask:gsub( "%]", "%%5D" )
                 :gsub( "|", "%%7C" )
                 :gsub( "}}", "%%7D%%7D" )
        r  =  string.format( "[%s/%s?%s&%s%s %s]",
                             mw.site.server, "w/index.php",
                             "title=Special:LinkSearch",
                             "target=", r, anzeige )
    else
        r  =  string.format( "[%s/%s?%s=%s%% %s]",
                             "https://tools.wmflabs.org",
                             "giftbot/weblinksuche.fcgi",
                             "namespace=0&target",
                             ask:gsub( "_", "\\_" )
                                :gsub( "%%20", "%%2520" )
                                :gsub( "%%", "\\%%" )
                                :gsub( "&", "%%26" ),
                             anzeige )
    end
    return r
end -- flink()



local function flip( access )
    -- Wikilink auf Artikel mit curid zeigen
    -- Parameter:
    --     access  -- string mit curid
    -- Returns: string mit Wikilink und vermutetem Lemma, oder false
    local r
    if access:match( "^%d+$" ) then
        local show  =  mw.title.new( tonumber( access ) )
        if show then
            show  =  show.text
        else
            show  =  string.format( "(Seite #%s nicht gefunden)",
                                    access )
        end
        r  =  string.format( " [[Special:Redirect/page/%s|%s]]",
                             access, show )
    else
        fehler( "Seite", access )
        r  =  false
    end
    return  r
end -- flip()



local function flipper( about )
    -- Links auf weitere Artikel mit gleicher URL auflisten
    -- Parameter:
    --     about  -- table mit aktivem Weblink
    --                .url
    --                .weitere
    --                    string mit Komma-getrennter Liste von curid
    -- Returns: string, oder false
    local got  =  mw.text.split( about.weitere, "," )
    local n    =  table.maxn( got )
    local r    =  ""
    local s
    if n == 1 then
        r  =  flip( got[ 1 ] )
    else
        for i = 1, n do
            s  =  got[ i ]
            if s:match( "^%d+$" ) then
                s  =  string.format( " [[Special:Redirect/page/%s|%s]]",
                                     s, s )
                r  =  r .. s
            else
                fehler( "Seite", s )
            end
        end -- for i
    end
    if r == "" then
        r  =  false
    else
        r  =  string.format( "%s (%s)",
                             r,  flink( about.url, "aktuell" ) )
        -- oder immer anzeigen, nicht nur zur Bot-Zeit?? Dann eins rauf.
    end
    return  r
end -- flipper()



local function flop( about )
    -- Spam-Weblink darstellen
    -- Parameter:
    --     about  -- table mit aktivem Weblink
    --                .scheme
    --                .url
    -- Returns: string
    local r  =  about.url
    if about.scheme ~= "mailto:" then
        local i  =  r:find( "%\\%\\" )
        if i then
            r  =  r:sub( i + 2 )
        else
            r  =  r:substr( 9 )
            fehler( "Spam", "backslash fehlt" )
        end
    end
    fire( "Spam" )
    r  =  string.format( "%s: '''%s'''", Spam, r )
    return  r
end -- flop()



local function flutsch( about )
    -- Korrigierbares Weblink darstellen
    -- Parameter:
    --     about  -- table mit aktivem Weblink
    --                .strangeCh
    --                .lastChSz
    --                .leave
    --                .lethal
    --                .linkBrackets
    --                .locked
    --                .loose
    --                .syntaxPipe
    --                .mode
    --               (.scheme)
    --                .schonmal
    --                .url
    --                .cit
    --                .wba
    --               (.weitere)
    -- Returns: string mit Item-Text und ggf. Unterpunkten
    local k      =  about.mode
    local repos  =  { "cit", "wba" }
    local saved  =  false
    local r, s
    if about.linkBrackets then
        r  =  string.format( "[%s %s]", about.url, about.url )
    else
        r  =  about.url
    end
    if about.leave then
        r  =  string.format( "%s\n** %s", r, Skip )
        fire( "FalschPositiv" )
    end
    for j, v in pairs( repos ) do
        s  =  fallback( about, v )
        if s then
            if saved then
                saved  =  string.format( "%s * %s",  s, saved )
            else
                saved  =  s
            end
        end
    end -- for j, v
    if saved then
        r  =  string.format( "%s\n** %s %s",
                             r, Stored, saved )
    end
    if k  and  k ~= 404 then
        local linksearch
        r  =  string.format( "%s\n** ", r )
        if k == 502  or  k == 505  then
            r           =  r .. Stat_502
            linksearch  =  true
        elseif k < 100  or  k > 599 then
            r           =  r .. string.format( Stat_9nn, k )
            linksearch  =  true
            k           =  false
        elseif k >= 500 then
            r           =  r .. Stat_5nn
            linksearch  =  true
        elseif k < 300 then
            r  =  r .. Stat_1_2
        elseif k < 400 then
            r  =  r .. Stat_3nn
        else
            r  =  r .. Stat_4nn
        end
        if k then
            r  =  string.format( "%s ([[HTTP-Statuscode]] %d)",
                                 r, k )
        end
        if linksearch then
            s  =  URLutil.getHost( about.url )
            if s then
                s  =  string.format( "%s%s/",
                                     URLutil.getScheme( about.url ),
                                     s )
                r  =  string.format( "%s %s",
                                     r,  flink( s, Suche500 ) )
            end
        end
    end
    if about.lethal then
        r  =  string.format( "%s\n** '''%s'''", r, SyntURL )
        fire( "ResourceURL" )
    end
    if about.loose then
        r  =  string.format( "%s\n** %s", r, Solved )
    end
    if about.locked then
        r  =  string.format( "%s\n** %s", r, Secure )
    end
    if about.schonmal then
        s  =  string.format( Schonmal, about.schonmal )
        r  =  string.format( "%s\n** %s", r, s )
    end
    if about.url:find( "%[" ) then
        r  =  string.format( "%s\n** %s", r, Sonderz )
        fire( "Klammer" )
    end
    if about.syntaxPipe then
        r  =  string.format( "%s\n** %s %s",
                             r, SyntPipe, favour( "Pipe" ) )
        s  =  about.url:match( "^(.+)%%7C" )
        if s then
            local suffix
            if about.syntaxPipe == "" then
                suffix = "&#124;"
            else
                suffix = string.format( "&#124;%s&#93;",
                                        about.syntaxPipe )
            end
            r  =  string.format( "%s\n** [%s %s]'''%s'''",
                                 r, s, s, suffix )
        end
        fire( "SyntaxPipe" )
    end
    if about.lunatic then
        if about.lastChLRM then
            r  =  string.format( "%s\n** %s", r, SchreibR )
            s  =  "SchreibR"
        else
            r  =  string.format( "%s\n** %s", r, Strange )
            s  =  "Unicode"
        end
        fire( s )
    end
--  if about.lastChSz then
--      s  =  about.url:match( "^(.+)%A$" )
--      if s then
--          r  =  string.format( "%s\n** %s\n**: %s",
--                               r, Satzzch, s )
--          fire( "Sonderzeichen" )
--      end
--  end
    if about.lastRemove  and
       not about.url:match( "^%l*:?//[^/]+/$" ) then
        r  =  string.format( "%s\n** %s", r, Suffix )
        fire( "Sonderzeichen" )
    end
    if about.strangeCh then
        r  =  string.format( "%s\n** %s %s: %s",
                             r,
                             Superz,
                             favour( "NonASCII" ),
                             about.strangeCh )
        fire( "NonASCII" )
    end
    family( about.url )
    --  Privatfunktion Benutzer:Boshomi
        r  =  string.format( HideURLs, r, addpraefixURL( about.url ) )
    return  r
end -- flutsch()



local function focus( args )
    -- Analysiere Steuerparameter und Einbindung
    -- Parameter:
    --     args  -- table mit Steuerungs-Infos
    -- Returns:
    --     false wenn Analyse sinnvoll
    --     string wenn etwas auszugeben
    local r  =  ""
    if args.Lauf then
        if args.Lauf == Serie then
            local current  =  mw.title.getCurrentTitle()
            if current.namespace == 1 then
                PageText = current.text
                if current.isSubpage then
                    local artikel  =  mw.title.new( PageText, 0 )
                    if artikel.exists  then
                        r  =  false
                    else
                        if not args.Archiv  or  args.Archiv == "0" then
                            args.Archiv  =  false
                        else
                            args.Archiv  =  true
                            fire( "Archiviert" )
                        end
                        if not args.Unterseite
                           or  args.Unterseite == "0" then
                            args.Unterseite  =  false
                        else
                            args.Unterseite  =  true
                            fire( "Solidus" )
                        end
                        if ( args.Archiv or args.Unterseite )  and
                           ( args.Archiv ~= args.Unterseite ) then
                            if args.Archiv then
                                r  =  ""
                            else
                                r  =  false
                            end
                        else
                            fehler( "Unterseite", "ohne Parameter" )
                        end
                    end
                else
                    r  =  false
                end
            else
                fehler( "Namensraum", "Nur Artikel-Diskussion" )
            end
        else
            r  =  "Ehemaliger Bot-Lauf: " .. args.Lauf
            if args.Bot then
                r  =  string.format( "%s (%s)",  r, args.Bot )
            end
        end
    else
        fehler( "Lauf", "Pflichtangabe!" )
    end
    return r
end -- focus()



local function format( about )
    -- Weblink menschenfreundlich darstellen
    -- Parameter:
    --     about  -- table mit aktivem Weblink
    --               (.lazy)
    --                .lastRemove
    --                .listedBlack
    --                .mode
    --                .scheme
    --                .schonmal
    --                .url
    --                .cit
    --                .wba
    --                .weitere
    -- Returns: string mit *-Auflistungspunkt
    local r  =  "\n* "
    if about.listedBlack then
        r  =  r .. flop( about )
    else
        r  =  r .. flutsch( about )
    end
    if about.weitere then
        local s  =  flipper( about )
        if s then
            r  =  string.format( "%s\n** %s %s",
                                 r, Same, s )
        end
    end
    return  r
end -- format()



local function furnish( alle, action )
    -- Weblink-tables menschenfreundlich darstellen
    -- Parameter:
    --     alle    -- table mit aktiven Weblink-tables
    --     action  -- string mit Lauf-Kennung
    -- Returns: string mit Auflistung der Weblinks
    families()
    r  =  string.format( "<div id='%s-%s' class='%s'>",
                         Class, action, Class )
    for k, v in pairs( alle ) do
        r  =  r .. format( v )
    end -- for k, v
    r  =  string.format( "%s\n</div>%s",  r,  families( 1 ) )
    return  r
end -- furnish()



local function f( args, frame )
    -- Hauptfunktion zur Steuerung des Gesamtablaufs
    -- Parameter:
    --     args   -- table mit Parameterliste
    --     frame  -- Umgebungsobjekt, oder nil
    -- Returns: string mit formatiertem Gesamtergebnis
    local m  =  0
    local r  =  ""
    local ctrl, el  =  fetch( args )
    local s  =  focus( ctrl )
    if not s then
        local lucky, j, n, span, wl
        lucky, URLutil = pcall( require, "Modul:URLutil" )
        if type( URLutil ) == "table" then
            local exc
            URLutil     =  URLutil.URLutil()
            lucky, exc  =  pcall( mw.loadData, Ausnahmen )
            if type( exc ) == "table" then
                Exceptions  =  { }
                for k, v in pairs( exc ) do
                    if type( v ) == "string"   then
                        table.insert( Exceptions, v )
                    else
                        fehler( "Modul",
                                "Tabelle der Ausnahmen defekt: "
                                .. tostring( v ) )
                        Exceptions  =  false
                        break -- for k, v
                    end
                end -- for k, v
                if Exceptions then
                    ExceptionCount  =  table.maxn( Exceptions )
                    if ExceptionCount == 0 then
                        Exceptions  =  false
                    end
                end
                Frame  =  frame
                n, m, wl, j  =  finde( el )
            else
                fehler( "Modul", exc )
                m  =  -1
                n  =  -1
            end
        else
            fehler( "Modul", URLutil )
            m  =  -1
            n  =  -1
        end
        if n > 0 then
            if Frame then
                local params  =  { tostring( n ),
                                   args.Bot }
                r  =  Frame:expandTemplate{ title = Sub, args = params }
            else
                r  =  string.format( "((template/sub %d %s))",
                                     n, args.Bot )
            end
            r  = r .. furnish( wl, ctrl.Lauf )
            fire( "Bot" )
        end
        r  =  r .. Frame:preprocess(
                       "<div style='display:none'><references /></div>" )
        if m > 0 then
            s  =  string.format( Silent, "\n", m )
            if n == 0  and  not args.Archiv then
                fire( "Deaktiviert" )
            end
            span  =  "0"
        elseif n == 0 then
            s  =  Shrinked
            fire( "Leer" )
            span  =  "00"
        elseif j == n  and  not Refs then
            s  =  Shrinked
            fire( "Unauffindbar" )
            span    =  "000"
            Source  =  ""
        end
        if span then
            span  =  string.format( "class='%s %s-%s'",
                                    Class, Class, span )
            s     =  string.format( "<span id='%s-%s' %s>%s</span>",
                                    Class, ctrl.Lauf, span, s )
        end
    end
    if s then
        if r == "" then
            r  =  string.format( "'''[[%s|Defekter Weblink]]''' * ",
                                 Vorlage )
        end
        r  =  r .. s
        if not Fehler then
            if s ~= "" then
                r  =  r .. "<br />"
            end
            if m > 0 then
                r  =  r .. Source
            end
        end
    end
    if Fehler then
        r  =  r .. fehlerliste()
    end
    return  string.format( "%s%s\n", r, firelist() )
end -- f()



-- Export
local p = {}

function p.test( a )
    local lucky, r = pcall( f, a )
    return r
end

function p.f( frame )
    local lucky, r = pcall( f, frame:getParent().args, frame )
    if not lucky then
        r = string.format( "%s[[%s%s]]",
                           tostring( r ),
                           KategorieBeginn,
                           Kategorien.Intern.s )
    end
    return r
end

function p.failsafe()
    return Serial
end

function p.families()
    return families( 2 )
end

function p.focus()
    return Serie
end

return p