Merge pull request #302 from chris5560/master
authorJo-Philipp Wich <jow@openwrt.org>
Sun, 8 Feb 2015 18:57:20 +0000 (19:57 +0100)
committerJo-Philipp Wich <jow@openwrt.org>
Sun, 8 Feb 2015 18:57:20 +0000 (19:57 +0100)
luci-themes: luci.main.mediaurlbase not set correctly

120 files changed:
applications/luci-app-coovachilli/luasrc/model/cbi/coovachilli_network.lua
applications/luci-app-firewall/luasrc/view/firewall/cbi_addforward.htm
applications/luci-app-ocserv/luasrc/model/cbi/ocserv/main.lua
applications/luci-app-olsr/luasrc/controller/olsr.lua
applications/luci-app-privoxy/Makefile [new file with mode: 0644]
applications/luci-app-privoxy/luasrc/controller/privoxy.lua [new file with mode: 0755]
applications/luci-app-privoxy/luasrc/model/cbi/privoxy/apperror.lua [new file with mode: 0755]
applications/luci-app-privoxy/luasrc/model/cbi/privoxy/detail.lua [new file with mode: 0755]
applications/luci-app-privoxy/luasrc/view/privoxy/detail_logview.htm [new file with mode: 0755]
applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm [new file with mode: 0644]
applications/luci-app-privoxy/po/de/privoxy.po [new file with mode: 0644]
applications/luci-app-privoxy/po/templates/privoxy.pot [new file with mode: 0644]
applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy [new file with mode: 0755]
applications/luci-app-siitwizard/luasrc/model/cbi/siitwizard.lua
applications/luci-app-splash/luasrc/controller/splash/splash.lua
applications/luci-app-splash/luasrc/view/admin_status/splash.htm
applications/luci-app-splash/root/usr/sbin/luci-splash
build/luadoc/doc.lua [new file with mode: 0755]
build/luadoc/luadoc/config.lua [new file with mode: 0644]
build/luadoc/luadoc/doclet/debug.lua [new file with mode: 0644]
build/luadoc/luadoc/doclet/formatter.lua [new file with mode: 0644]
build/luadoc/luadoc/doclet/html.lua [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/constant.lp [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/file.lp [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/function.lp [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/index.lp [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/luadoc.css [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/menu.lp [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/module.lp [new file with mode: 0644]
build/luadoc/luadoc/doclet/html/table.lp [new file with mode: 0644]
build/luadoc/luadoc/doclet/raw.lua [new file with mode: 0644]
build/luadoc/luadoc/init.lua [new file with mode: 0644]
build/luadoc/luadoc/lp.lua [new file with mode: 0644]
build/luadoc/luadoc/taglet/standard.lua [new file with mode: 0644]
build/luadoc/luadoc/taglet/standard/tags.lua [new file with mode: 0644]
build/luadoc/luadoc/util.lua [new file with mode: 0644]
build/makedocs.sh
build/zoneinfo2lua.pl
contrib/luadoc/Makefile [deleted file]
contrib/luadoc/hostfiles/bin/luadoc [deleted file]
contrib/luadoc/lua/luadoc/config.lua [deleted file]
contrib/luadoc/lua/luadoc/doclet/debug.lua [deleted file]
contrib/luadoc/lua/luadoc/doclet/formatter.lua [deleted file]
contrib/luadoc/lua/luadoc/doclet/html.lua [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/constant.lp [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/file.lp [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/function.lp [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/index.lp [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/luadoc.css [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/menu.lp [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/module.lp [deleted file]
contrib/luadoc/lua/luadoc/doclet/html/table.lp [deleted file]
contrib/luadoc/lua/luadoc/doclet/raw.lua [deleted file]
contrib/luadoc/lua/luadoc/init.lua [deleted file]
contrib/luadoc/lua/luadoc/lp.lua [deleted file]
contrib/luadoc/lua/luadoc/taglet/standard.lua [deleted file]
contrib/luadoc/lua/luadoc/taglet/standard/tags.lua [deleted file]
contrib/luadoc/lua/luadoc/util.lua [deleted file]
libs/luci-lib-httpclient/luasrc/httpclient.lua
libs/luci-lib-ip/Makefile [new file with mode: 0644]
libs/luci-lib-ip/src/Makefile [new file with mode: 0644]
libs/luci-lib-ip/src/ip.c [new file with mode: 0644]
libs/luci-lib-ip/src/ip.luadoc [new file with mode: 0644]
libs/luci-lib-json/luasrc/json.lua
libs/luci-lib-json/luasrc/json.luadoc [new file with mode: 0644]
libs/luci-lib-jsonc/Makefile [new file with mode: 0644]
libs/luci-lib-jsonc/src/Makefile [new file with mode: 0644]
libs/luci-lib-jsonc/src/jsonc.c [new file with mode: 0644]
libs/luci-lib-jsonc/src/jsonc.luadoc [new file with mode: 0644]
libs/luci-lib-nixio/docsrc/CHANGELOG.lua
libs/luci-lib-nixio/docsrc/README.lua
libs/luci-lib-nixio/src/Makefile
libs/luci-lib-rpcc/luasrc/rpcc.lua
libs/luci-lib-rpcc/luasrc/rpcc.luadoc [new file with mode: 0644]
libs/luci-lib-rpcc/luasrc/rpcc/ruci.lua
libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc [new file with mode: 0644]
luci.mk
modules/luci-base/Makefile
modules/luci-base/luasrc/dispatcher.lua
modules/luci-base/luasrc/dispatcher.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/http.lua
modules/luci-base/luasrc/http.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/http/protocol.lua
modules/luci-base/luasrc/http/protocol.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/http/protocol/conditionals.lua
modules/luci-base/luasrc/http/protocol/conditionals.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/http/protocol/date.lua
modules/luci-base/luasrc/http/protocol/date.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/http/protocol/mime.lua
modules/luci-base/luasrc/http/protocol/mime.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/i18n.lua
modules/luci-base/luasrc/i18n.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/ip.lua [deleted file]
modules/luci-base/luasrc/ltn12.lua
modules/luci-base/luasrc/model/ipkg.lua
modules/luci-base/luasrc/model/ipkg.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/model/uci.lua
modules/luci-base/luasrc/model/uci.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/sys.lua
modules/luci-base/luasrc/sys.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/sys/iptparser.lua
modules/luci-base/luasrc/sys/iptparser.luadoc [new file with mode: 0644]
modules/luci-base/luasrc/sys/zoneinfo/tzdata.lua
modules/luci-base/luasrc/sys/zoneinfo/tzoffset.lua
modules/luci-base/luasrc/tools/webadmin.lua
modules/luci-base/luasrc/util.lua
modules/luci-base/luasrc/util.luadoc [new file with mode: 0644]
modules/luci-mod-admin-full/luasrc/controller/admin/system.lua
modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/dhcp.lua
modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/hosts.lua
modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/ifaces.lua
modules/luci-mod-admin-full/luasrc/model/cbi/admin_network/routes.lua
modules/luci-mod-admin-full/luasrc/view/admin_network/wifi_overview.htm
modules/luci-mod-admin-full/luasrc/view/admin_status/routes.htm
modules/luci-mod-admin-mini/luasrc/model/cbi/mini/dhcp.lua
modules/luci-mod-freifunk/luasrc/view/freifunk/public_status.htm
protocols/luci-proto-ipv6/Makefile
protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua [new file with mode: 0644]
protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua [new file with mode: 0644]
protocols/luci-proto-openconnect/luasrc/model/cbi/admin_network/proto_openconnect.lua

index e1a084a..025bc17 100644 (file)
@@ -2,8 +2,8 @@
 -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
-require("luci.sys")
-require("luci.ip")
+local sys = require"luci.sys"
+local ip  = require "luci.ip"
 
 m = Map("coovachilli")
 
@@ -16,8 +16,8 @@ s1:option( Value, "tundev" ).optional = true
 s1:option( Value, "txqlen" ).optional = true
 
 net = s1:option( Value, "net" )
-for _, route in ipairs(luci.sys.net.routes()) do
-       if route.device ~= "lo" and route.dest:prefix() < 32 then
+for _, route in ipairs(ip.routes({ family = 4, type = 1 })) do
+       if route.dest:prefix() > 0 and route.dest:prefix() < 32 then
                net:value( route.dest:string() )
        end
 end
@@ -41,7 +41,7 @@ s2 = m:section(TypedSection, "dhcp")
 s2.anonymous = true
 
 dif = s2:option( Value, "dhcpif" )
-for _, nif in ipairs(luci.sys.net.devices()) do
+for _, nif in ipairs(sys.net.devices()) do
        if nif ~= "lo" then dif:value(nif) end
 end
 
index 3726f64..3c46e22 100644 (file)
@@ -6,7 +6,8 @@
        for _, z in ipairs(fw:get_zones()) do
                if z:name() ~= "wan" then
                        izl[#izl+1] = z
-               elseif z:name() ~= "lan" then
+               end
+               if z:name() ~= "lan" then
                        ezl[#ezl+1] = z
                end
        end
index 23f6f5c..d1cc155 100644 (file)
@@ -90,6 +90,10 @@ local pip = s:taboption("general", Flag, "predictable_ips", translate("Predictab
        translate("The assigned IPs will be selected deterministically"))
 pip.default = "1"
 
+local compr = s:taboption("general", Flag, "compression", translate("Enable compression"),
+       translate("Enable compression"))
+compr.default = "1"
+
 local udp = s:taboption("general", Flag, "udp", translate("Enable UDP"),
        translate("Enable UDP channel support; this must be enabled unless you know what you are doing"))
 udp.default = "1"
index 3615372..74deb71 100644 (file)
@@ -1,5 +1,8 @@
 module("luci.controller.olsr", package.seeall)
 
+local neigh_table = nil
+local ifaddr_table = nil
+
 function index()
        local ipv4,ipv6
        if nixio.fs.access("/etc/config/olsrd") then
@@ -93,6 +96,46 @@ function action_json()
        http.write('{"v4":' .. jsonreq4 .. ', "v6":' .. jsonreq6 .. '}')
 end
 
+
+local function local_mac_lookup(ipaddr)
+       local _, ifa, dev
+
+       ipaddr = tostring(ipaddr)
+
+       if not ifaddr_table then
+               ifaddr_table = nixio.getifaddrs()
+       end
+
+       -- ipaddr -> ifname
+       for _, ifa in ipairs(ifaddr_table) do
+               if ifa.addr == ipaddr then
+                       dev = ifa.name
+                       break
+               end
+       end
+
+       -- ifname -> macaddr
+       for _, ifa in ipairs(ifaddr_table) do
+               if ifa.name == dev and ifa.family == "packet" then
+                       return ifa.addr
+               end
+       end
+end
+
+local function remote_mac_lookup(ipaddr)
+       local _, n
+
+       if not neigh_table then
+               neigh_table = luci.ip.neighbors()
+       end
+
+       for _, n in ipairs(neigh_table) do
+               if n.mac and n.dest and n.dest:equal(ipaddr) then
+                       return n.mac
+               end
+       end
+end
+
 function action_neigh(json)
        local data, has_v4, has_v6, error = fetch_jsoninfo('links')
 
@@ -107,17 +150,13 @@ function action_neigh(json)
        local sys = require "luci.sys"
        local assoclist = {}
        --local neightbl = require "neightbl"
+       local ntm = require "luci.model.network"
        local ipc = require "luci.ip"
+       local nxo = require "nixio"
+       local defaultgw
 
-       luci.sys.net.routes(function(r) 
-               if r.dest:prefix() == 0 then 
-                       defaultgw = r.gateway:string() 
-               end
-       end)
-
-       if not defaultgw then
-               defaultgw = luci.util.exec("ip route list exact 0.0.0.0/0 table all"):match(" via (%S+)")
-       end
+       ipc.routes({ family = 4, type = 1, dest_exact = "0.0.0.0/0" },
+               function(rt) defaultgw = rt.gw end)
 
        local function compare(a,b)
                if a.proto == b.proto then
@@ -138,14 +177,10 @@ function action_neigh(json)
        end
 
        for k, v in ipairs(data) do
-               local interface
                local snr = 0
                local signal = 0
                local noise = 0
-               local arptable = sys.net.arptable()
                local mac = ""
-               local rmac = ""
-               local lmac = ""
                local ip
                local neihgt = {}
                
@@ -155,45 +190,11 @@ function action_neigh(json)
                                v.hostname = hostname
                        end
                end
-               if v.proto == '4' then
-                       uci:foreach("network", "interface",function(vif)
-                               if vif.ipaddr and vif.ipaddr == v.localIP then
-                                       interface = vif['.name'] or vif.interface
-                                       lmac = string.lower(vif.macaddr or "") 
-                                       return
-                               end
-                       end)
-                       for _, arpt in ipairs(arptable) do
-                               ip = arpt['IP address']
-                               if ip == v.remoteIP then
-                                       rmac = string.lower(arpt['HW address'] or "")
-                               end
-                       end
-               elseif v.proto == '6' then
-                       uci:foreach("network", "interface",function(vif)
-                               local name = vif['.name']
-                               local net = ntm:get_network(name)
-                               local device = net and net:get_interface()
-                               local locip = ipc.IPv6(v.localIP)
-                               if device and device:ip6addrs() and locip then
-                                       for _, a in ipairs(device:ip6addrs()) do
-                                               if not a:is6linklocal() then
-                                                       if a:host() == locip:host() then
-                                                               interface = name
-                                                               --neihgt = neightbl.get(device.ifname) or {}
-                                                       end
-                                               end
-                                       end
-                               end
-                       end)
-                       --[[
-                       for ip,mac in pairs(neihgt) do
-                               if ip == v.remoteIP then
-                                       rmac = mac
-                               end
-                       end
-                       ]]--
-               end
+
+               local interface = ntm:get_status_by_address(v.localIP)
+               local lmac = local_mac_lookup(v.localIP)
+               local rmac = remote_mac_lookup(v.remoteIP)
+
                for _, val in ipairs(assoclist) do
                        if val.network == interface and val.list then
                                for assocmac, assot in pairs(val.list) do
diff --git a/applications/luci-app-privoxy/Makefile b/applications/luci-app-privoxy/Makefile
new file mode 100644 (file)
index 0000000..4c1fc57
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2008-2015 The LuCI Team <luci@lists.subsignal.org>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+# LuCI specific settings
+LUCI_TITLE:=LuCI Support for Privoxy WEB proxy
+LUCI_DEPENDS:=+luci-mod-admin-full +privoxy
+LUCI_PKGARCH:=all
+
+PKG_NAME:=luci-app-privoxy
+
+# Version == major.minor.patch
+# increase "minor" on new functionality and "patch" on patches/optimization
+PKG_VERSION:=1.0.2
+
+# Release == build
+# increase on changes of translation files
+PKG_RELEASE:=0
+
+PKG_LICENSE:=Apache-2.0
+PKG_MAINTAINER:=Christian Schoenebeck <christian.schoenebeck@gmail.com>
+
+define Package/$(PKG_NAME)/config
+# shown in make menuconfig <Help>
+help
+       $(LUCI_TITLE)
+
+       Version   : $(PKG_VERSION)-$(PKG_RELEASE)
+       Maintainer: $(PKG_MAINTAINER)
+endef
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/applications/luci-app-privoxy/luasrc/controller/privoxy.lua b/applications/luci-app-privoxy/luasrc/controller/privoxy.lua
new file mode 100755 (executable)
index 0000000..de73d0c
--- /dev/null
@@ -0,0 +1,117 @@
+-- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
+-- Licensed under the Apache License, Version 2.0
+
+module("luci.controller.privoxy", package.seeall)
+
+local NX   = require "nixio"
+local NXFS = require "nixio.fs"
+local HTTP = require "luci.http"
+local UCI  = require "luci.model.uci"
+local SYS  = require "luci.sys"
+
+PRIVOXY_MIN = "3.0.22-0"       -- minimum version of service required
+
+function index()
+       local _sys      = require "luci.sys"
+       local _verinst  = _sys.exec([[opkg list-installed ]] .. "privoxy" .. [[ | cut -d " " -f 3 ]])
+       local _cmd      = [[opkg compare-versions "]] .. _verinst .. [[" ">=" "]] .. "3.0.22-0" .. [["]]
+       local _verok    = tonumber(_sys.call(_cmd))
+
+       -- check config file and version
+       if not nixio.fs.access("/etc/config/privoxy") or (_verok == 0) then
+               entry( {"admin", "services", "privoxy"}, cbi("privoxy/apperror",
+                       {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true }), _("Privoxy WEB proxy"), 59)
+       else
+               entry( {"admin", "services", "privoxy"}, cbi("privoxy/detail"), _("Privoxy WEB proxy"), 59)
+               entry( {"admin", "services", "privoxy", "logview"}, call("logread") ).leaf = true
+               entry( {"admin", "services", "privoxy", "startstop"}, call("startstop") ).leaf = true
+               entry( {"admin", "services", "privoxy", "status"}, call("get_pid") ).leaf = true
+       end
+end
+
+-- called by XHR.get from detail_logview.htm
+function logread()
+       -- read application settings
+       local uci     = UCI.cursor()
+       local logdir  = uci:get("privoxy", "privoxy", "logdir") or "/var/log"
+       local logfile = uci:get("privoxy", "privoxy", "logfile") or "privoxy.log"
+       uci:unload("privoxy")
+
+       local lfile=logdir .. "/" .. logfile
+       local ldata=NXFS.readfile(lfile)
+       if not ldata or #ldata == 0 then
+               ldata="_nodata_"
+       end
+       HTTP.write(ldata)
+end
+
+-- called by XHR.get from detail_startstop.htm
+function startstop()
+       local pid = get_pid(true)
+       if pid > 0 then
+               SYS.call("/etc/init.d/privoxy stop")
+               NX.nanosleep(1)         -- sleep a second
+               if NX.kill(pid, 0) then -- still running
+                       NX.kill(pid, 9) -- send SIGKILL
+               end
+               pid = 0
+       else
+               SYS.call("/etc/init.d/privoxy start")
+               NX.nanosleep(1)         -- sleep a second
+               pid = tonumber(NXFS.readfile("/var/run/privoxy.pid") or 0 )
+               if pid > 0 and not NX.kill(pid, 0) then
+                       pid = 0         -- process did not start
+               end
+       end
+       HTTP.write(tostring(pid))       -- HTTP needs string not number
+end
+
+-- called by XHR.poll from detail_startstop.htm
+-- and from lua (with parameter "true")
+function get_pid(from_lua)
+       local pid = tonumber(NXFS.readfile("/var/run/privoxy.pid") or 0 )
+       if pid > 0 and not NX.kill(pid, 0) then
+               pid = 0
+       end
+       if from_lua then
+               return pid
+       else
+               HTTP.write(tostring(pid))       -- HTTP needs string not number
+       end
+end
+
+-- get the "name" of the current active theme
+function get_theme()
+       local _uci  = UCI.cursor()
+       local _base = _uci:get("luci", "main", "mediaurlbase")  -- only pathname
+       _uci:unload("luci")
+
+       for k, v in pairs(luci.config.themes) do
+               if k:sub(1, 1) ~= "." and v == _base then
+                       return k
+               end
+       end
+       return nil
+end
+
+-- replacement of build-in Flag.parse of cbi.lua
+-- modified to mark section as changed if value changes
+-- current parse did not do this, but it is done AbstaractValue.parse()
+function flag_parse(self, section)
+       local fexists = self.map:formvalue(
+               luci.cbi.FEXIST_PREFIX .. self.config .. "." .. section .. "." .. self.option)
+
+       if fexists then
+               local fvalue = self:formvalue(section) and self.enabled or self.disabled
+               local cvalue = self:cfgvalue(section)
+               if fvalue ~= self.default or (not self.optional and not self.rmempty) then
+                       self:write(section, fvalue)
+               else
+                       self:remove(section)
+               end
+               if (fvalue ~= cvalue) then self.section.changed = true end
+       else
+               self:remove(section)
+               self.section.changed = true
+       end
+end
diff --git a/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/apperror.lua b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/apperror.lua
new file mode 100755 (executable)
index 0000000..fcbb880
--- /dev/null
@@ -0,0 +1,47 @@
+-- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
+-- Licensed under the Apache License, Version 2.0
+
+local CTRL = require "luci.controller.privoxy" -- this application's controller
+local DISP = require "luci.dispatcher"
+local SYS  = require "luci.sys"
+
+local HELP = [[<a href="http://www.privoxy.org/user-manual/config.html#%s" target="_blank">%s</a>]]
+
+-- cbi-map -- ##################################################################
+local m        = Map("privoxy")
+m.title        = [[</a><a href="javascript:alert(']]
+               .. translate("Version Information")
+               .. [[\n\nluci-app-privoxy]]
+               .. [[\n\t]] .. translate("Version") .. [[:\t]]
+               .. SYS.exec([[opkg list-installed ]] .. [[luci-app-privoxy]] .. [[ | cut -d " " -f 3 ]])
+               .. [[\n\nprivoxy ]] .. translate("required") .. [[:]]
+               .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.PRIVOXY_MIN .. [[ ]] .. translate("or higher")
+               .. [[\n\nprivoxy ]] .. translate("installed") .. [[:]]
+               .. [[\n\t]] .. translate("Version") .. [[:\t]]
+               .. SYS.exec([[opkg list-installed ]] .. [[privoxy]] .. [[ | cut -d " " -f 3 ]])
+               .. [[\n\n]]
+       .. [[')">]]
+       .. translate("Privoxy WEB proxy")
+m.description = translate("Privoxy is a non-caching web proxy with advanced filtering "
+               .. "capabilities for enhancing privacy, modifying web page data and HTTP headers, "
+               .. "controlling access, and removing ads and other obnoxious Internet junk.")
+
+-- cbi-section -- ##############################################################
+local s = m:section(SimpleSection)
+s.title = [[<font color="red">]] .. [[<strong>]]
+       .. translate("Software update required")
+       .. [[</strong>]] .. [[</font>]]
+
+-- old privoxy sofware version --------------------------------------------------------------
+local v    = s:option(DummyValue, "_update_needed")
+v.titleref = DISP.build_url("admin", "system", "packages")
+v.rawhtml  = true
+--v.title    = [[<h3>]] .. [[<font color="red">]] .. [[<strong>]]
+--        .. translate("Software update required")
+--        .. [[</strong>]] .. [[</font>]] .. [[</h3>]] .. [[<br />]]
+v.value    = [[<h3>]] .. [[<strong>]]
+          .. translate("The currently installed 'privoxy' package is not supported by LuCI application.")
+          .. [[<br />]]
+          .. translate("Please update to the current version!")
+          .. [[</strong>]] .. [[</h3>]]
+return m
diff --git a/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/detail.lua b/applications/luci-app-privoxy/luasrc/model/cbi/privoxy/detail.lua
new file mode 100755 (executable)
index 0000000..2c846a1
--- /dev/null
@@ -0,0 +1,928 @@
+-- Copyright 2014 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
+-- Licensed under the Apache License, Version 2.0
+
+local NXFS = require "nixio.fs"
+local SYS  = require "luci.sys"
+local UTIL = require "luci.util"
+local DTYP = require "luci.cbi.datatypes"
+local CTRL = require "luci.controller.privoxy" -- this application's controller
+
+-- Bootstrap theme needs 2 or 3 additional linefeeds for tab description for better optic
+local LFLF = (CTRL.get_theme() == "Bootstrap") and [[<br /><br /><br />]] or [[]]
+local HELP = [[<a href="http://www.privoxy.org/user-manual/config.html#%s" target="_blank">%s</a>]]
+
+-- cbi-map -- ##################################################################
+local m        = Map("privoxy")
+m.title        = [[</a><a href="javascript:alert(']]
+               .. translate("Version Information")
+               .. [[\n\nluci-app-privoxy]]
+               .. [[\n\t]] .. translate("Version") .. [[:\t]]
+               .. SYS.exec([[opkg list-installed ]] .. [[luci-app-privoxy]] .. [[ | cut -d " " -f 3 ]])
+               .. [[\n\nprivoxy ]] .. translate("required") .. [[:]]
+               .. [[\n\t]] .. translate("Version") .. [[:\t]] .. CTRL.PRIVOXY_MIN .. [[ ]] .. translate("or higher")
+               .. [[\n\nprivoxy ]] .. translate("installed") .. [[:]]
+               .. [[\n\t]] .. translate("Version") .. [[:\t]]
+               .. SYS.exec([[opkg list-installed ]] .. [[privoxy]] .. [[ | cut -d " " -f 3 ]])
+               .. [[\n\n]]
+       .. [[')">]]
+       .. translate("Privoxy WEB proxy")
+m.description = translate("Privoxy is a non-caching web proxy with advanced filtering "
+               .. "capabilities for enhancing privacy, modifying web page data and HTTP headers, "
+               .. "controlling access, and removing ads and other obnoxious Internet junk.")
+               .. [[<br /><strong>]]
+               .. translate("For help use link at the relevant option")
+               .. [[</strong>]]
+function m.commit_handler(self)
+       if self.changed then    -- changes ?
+               os.execute("/etc/init.d/privoxy reload &")      -- reload configuration
+       end
+end
+
+-- cbi-section -- ##############################################################
+local ns = m:section( NamedSection, "privoxy", "privoxy")
+
+ns:tab("local",
+       translate("Local Set-up"),
+       translate("If you intend to operate Privoxy for more users than just yourself, "
+               .. "it might be a good idea to let them know how to reach you, what you block "
+               .. "and why you do that, your policies, etc.")
+               .. LFLF )
+local function err_tab_local(title, msg)
+       return string.format(translate("Local Set-up") .. " - %s: %s", title, msg )
+end
+
+ns:tab("filter",
+       translate("Files and Directories"),
+       translate("Privoxy can (and normally does) use a number of other files "
+               .. "for additional configuration, help and logging. This section of "
+               .. "the configuration file tells Privoxy where to find those other files.")
+               .. LFLF )
+local function err_tab_filter(title, msg)
+       return string.format(translate("Files and Directories") .. " - %s: %s", title, msg )
+end
+
+ns:tab("access",
+       translate("Access Control"),
+       translate("This tab controls the security-relevant aspects of Privoxy's configuration.")
+               .. LFLF )
+local function err_tab_access(title, msg)
+       return string.format(translate("Access Control") .. " - %s: %s", title, msg )
+end
+
+ns:tab("forward",
+       translate("Forwarding"),
+       translate("Configure here the routing of HTTP requests through a chain of multiple proxies. "
+               .. "Note that parent proxies can severely decrease your privacy level. "
+               .. "Also specified here are SOCKS proxies.")
+               .. LFLF )
+
+ns:tab("misc",
+       translate("Miscellaneous"),
+       nil)
+local function err_tab_misc(self, msg)
+       return string.format(translate("Miscellaneous") .. " - %s: %s", self.title_base, msg )
+end
+
+ns:tab("debug",
+       translate("Logging"),
+       nil )
+
+ns:tab("logview",
+       translate("Log File Viewer"),
+       nil )
+
+-- tab: local -- ###############################################################
+
+-- start/stop button -----------------------------------------------------------
+local btn      = ns:taboption("local", Button, "_startstop")
+btn.title      = translate("Start / Stop")
+btn.description        = translate("Start/Stop Privoxy WEB Proxy")
+btn.template   = "privoxy/detail_startstop"
+function btn.cfgvalue(self, section)
+       local pid = CTRL.get_pid(true)
+       if pid > 0 then
+               btn.inputtitle  = "PID: " .. pid
+               btn.inputstyle  = "reset"
+               btn.disabled    = false
+       else
+               btn.inputtitle  = translate("Start")
+               btn.inputstyle  = "apply"
+               btn.disabled    = false
+       end
+       return true
+end
+
+-- enabled ---------------------------------------------------------------------
+local ena      = ns:taboption("local", Flag, "_enabled")
+ena.title       = translate("Enabled")
+ena.description = translate("Enable/Disable autostart of Privoxy on system startup and interface events")
+ena.orientation = "horizontal" -- put description under the checkbox
+ena.rmempty    = false
+function ena.cfgvalue(self, section)
+       return (SYS.init.enabled("privoxy")) and "1" or "0"
+end
+function ena.validate(self, value)
+       error("Validate " .. value)
+end
+function ena.write(self, section, value)
+       --error("Write " .. value)
+       if value == "1" then
+               return SYS.init.enable("privoxy")
+       else
+               return SYS.init.disable("privoxy")
+       end
+end
+
+-- hostname --------------------------------------------------------------------
+local hn       = ns:taboption("local", Value, "hostname")
+hn.title       = string.format(HELP, "HOSTNAME", "Hostname" )
+hn.description = translate("The hostname shown on the CGI pages.")
+hn.placeholder = SYS.hostname()
+hn.optional    = true
+hn.rmempty     = true
+
+-- user-manual -----------------------------------------------------------------
+local um       = ns:taboption("local", Value, "user_manual")
+um.title       = string.format(HELP, "USER-MANUAL", "User Manual" )
+um.description = translate("Location of the Privoxy User Manual.")
+um.placeholder = "http://www.privoxy.org/user-manual/"
+um.optional    = true
+um.rmempty     = true
+
+-- admin-address ---------------------------------------------------------------
+local aa       = ns:taboption("local", Value, "admin_address")
+aa.title_base  = "Admin Email"
+aa.title       = string.format(HELP, "ADMIN-ADDRESS", aa.title_base )
+aa.description = translate("An email address to reach the Privoxy administrator.")
+aa.placeholder = "privoxy.admin@example.com"
+aa.optional    = true
+aa.rmempty     = true
+function aa.validate(self, value)
+       if not value or #value == 0 then
+               return ""
+       end
+       if not (value:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?")) then
+               return nil, err_tab_local(self.title_base, translate("Invalid email address") )
+       end
+       return value
+end
+
+-- proxy-info-url --------------------------------------------------------------
+local piu      = ns:taboption("local", Value, "proxy_info_url")
+piu.title      = string.format(HELP, "PROXY-INFO-URL", "Proxy Info URL" )
+piu.description        = translate("A URL to documentation about the local Privoxy setup, configuration or policies.")
+piu.optional   = true
+piu.rmempty    = true
+
+-- trust-info-url --------------------------------------------------------------
+local tiu      = ns:taboption("local", DynamicList, "trust_info_url")
+tiu.title      = string.format(HELP, "TRUST-INFO-URL", "Trust Info URLs" )
+tiu.description        = translate("A URL to be displayed in the error page that users will see if access to an untrusted page is denied.")
+               .. [[<br /><strong>]]
+               .. translate("The value of this option only matters if the experimental trust mechanism has been activated.")
+               .. [[</strong>]]
+tiu.optional   = true
+tiu.rmepty     = true
+
+-- tab: filter -- ##############################################################
+
+-- logdir ----------------------------------------------------------------------
+local ld       = ns:taboption("filter", Value, "logdir")
+ld.title_base  = "Log Directory"
+ld.title       = string.format(HELP, "LOGDIR", ld.title_base )
+ld.description = translate("The directory where all logging takes place (i.e. where the logfile is located).")
+               .. [[<br />]]
+               .. translate("No trailing '/', please.")
+ld.default     = "/var/log"
+ld.rmempty     = false
+function ld.validate(self, value)
+       if not value or #value == 0 then
+               return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No Directory given!") )
+       elseif not NXFS.access(value) then
+               return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") )
+       else
+               return value
+       end
+end
+
+-- logfile ---------------------------------------------------------------------
+local lf       = ns:taboption("filter", Value, "logfile")
+lf.title_base  = "Log File"
+lf.title       = string.format(HELP, "LOGFILE", lf.title_base )
+lf.description = translate("The log file to use. File name, relative to log directory.")
+lf.default     = "privoxy.log"
+lf.rmempty     = false
+function lf.validate(self, value)
+       if not value or #value == 0 then
+               return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No File given!") )
+       else
+               return value
+       end
+end
+
+-- confdir ---------------------------------------------------------------------
+local cd       = ns:taboption("filter", Value, "confdir")
+cd.title_base  = "Configuration Directory"
+cd.title       = string.format(HELP, "CONFDIR", cd.title_base )
+cd.description = translate("The directory where the other configuration files are located.")
+               .. [[<br />]]
+               .. translate("No trailing '/', please.")
+cd.default     = "/etc/privoxy"
+cd.rmempty     = false
+function cd.validate(self, value)
+       if not value or #value == 0 then
+               return nil, err_tab_filter(self.title_base, translate("Mandatory Input: No Directory given!") )
+       elseif not NXFS.access(value) then
+               return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") )
+       else
+               return value
+       end
+end
+
+-- templdir --------------------------------------------------------------------
+local td       = ns:taboption("filter", Value, "templdir")
+td.title_base  = "Template Directory"
+td.title       = string.format(HELP, "TEMPLDIR", td.title_base )
+td.description = translate("An alternative directory where the templates are loaded from.")
+               .. [[<br />]]
+               .. translate("No trailing '/', please.")
+td.placeholder = "/etc/privoxy/templates"
+td.rmempty     = true
+function td.validate(self, value)
+       if not NXFS.access(value) then
+               return nil, err_tab_filter(self.title_base, translate("Directory does not exist!") )
+       else
+               return value
+       end
+end
+
+-- actionsfile -----------------------------------------------------------------
+local af       = ns:taboption("filter", DynamicList, "actionsfile")
+af.title_base  = "Action Files"
+af.title       = string.format(HELP, "ACTIONSFILE", af.title_base)
+af.description = translate("The actions file(s) to use. Multiple actionsfile lines are permitted, and are in fact recommended!")
+               .. [[<br /><strong>match-all.action := </strong>]]
+               .. translate("Actions that are applied to all sites and maybe overruled later on.")
+               .. [[<br /><strong>default.action := </strong>]]
+               .. translate("Main actions file")
+               .. [[<br /><strong>user.action := </strong>]]
+               .. translate("User customizations")
+af.rmempty     = false
+function af.validate(self, value)
+       if not value or #value == 0 then
+               return nil, err_tab_access(self.title_base, translate("Mandatory Input: No files given!") )
+       end
+       local confdir = cd:formvalue(ns.section)
+       local err     = false
+       local file    = ""
+       if type(value) == "table" then
+               local x
+               for _, x in ipairs(value) do
+                       if x and #x > 0 then
+                               if not NXFS.access(confdir .."/".. x) then
+                                       err  = true
+                                       file = x
+                                       break   -- break/leave for on error
+                               end
+                       end
+               end
+       else
+               if not NXFS.access(confdir .."/".. value) then
+                       err  = true
+                       file = value
+               end
+       end
+       if err then
+               return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file)
+       end
+       return value
+end
+
+-- filterfile ------------------------------------------------------------------
+local ff       = ns:taboption("filter", DynamicList, "filterfile")
+ff.title_base  = "Filter files"
+ff.title       = string.format(HELP, "FILTERFILE", ff.title_base )
+ff.description = translate("The filter files contain content modification rules that use regular expressions.")
+ff.rmempty     = false
+function ff.validate(self, value)
+       if not value or #value == 0 then
+               return nil, err_tab_access(self.title_base, translate("Mandatory Input: No files given!") )
+       end
+       local confdir = cd:formvalue(ns.section)
+       local err     = false
+       local file    = ""
+       if type(value) == "table" then
+               local x
+               for _, x in ipairs(value) do
+                       if x and #x > 0 then
+                               if not NXFS.access(confdir .."/".. x) then
+                                       err  = true
+                                       file = x
+                                       break   -- break/leave for on error
+                               end
+                       end
+               end
+       else
+               if not NXFS.access(confdir .."/".. value) then
+                       err  = true
+                       file = value
+               end
+       end
+       if err then
+               return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file )
+       end
+       return value
+end
+
+-- trustfile -------------------------------------------------------------------
+local tf       = ns:taboption("filter", Value, "trustfile")
+tf.title_base  = "Trust file"
+tf.title       = string.format(HELP, "TRUSTFILE", tf.title_base )
+tf.description = translate("The trust mechanism is an experimental feature for building white-lists "
+               .."and should be used with care.")
+               .. [[<br /><strong>]]
+               .. translate("It is NOT recommended for the casual user.")
+               .. [[</strong>]]
+tf.placeholder = "sites.trust"
+tf.rmempty     = true
+function tf.validate(self, value)
+       local confdir = cd:formvalue(ns.section)
+       local err     = false
+       local file    = ""
+       if type(value) == "table" then
+               local x
+               for _, x in ipairs(value) do
+                       if x and #x > 0 then
+                               if not NCFS.access(confdir .."/".. x) then
+                                       err  = true
+                                       file = x
+                                       break   -- break/leave for on error
+                               end
+                       end
+               end
+       else
+               if not NXFS.access(confdir .."/".. value) then
+                       err  = true
+                       file = value
+               end
+       end
+       if err then
+               return nil, string.format(err_tab_filter(self.title_base, translate("File '%s' not found inside Configuration Directory") ), file )
+       end
+       return value
+end
+
+-- tab: access -- ##############################################################
+
+-- listen-address --------------------------------------------------------------
+local la       = ns:taboption("access", DynamicList, "listen_address")
+la.title_base  = "Listen addresses"
+la.title       = string.format(HELP, "LISTEN-ADDRESS", la.title_base )
+la.description = translate("The address and TCP port on which Privoxy will listen for client requests.")
+               .. [[<br />]]
+               .. translate("Syntax: ")
+               .. "IPv4:Port / [IPv6]:Port / Host:Port"
+la.default     = "127.0.0.1:8118"
+la.rmempty     = false
+function la.validate(self, value)
+       if not value or #value == 0 then
+               return nil, err_tab_access(self.title_base, translate("Mandatory Input: No Data given!") )
+       end
+
+       local function check_value(v)
+               local _ret = UTIL.split(v, "]:")
+               local _ip
+               if _ret[2] then -- ip6 with port
+                       _ip   = string.gsub(_ret[1], "%[", "")  -- remove "[" at beginning
+                       if not DTYP.ip6addr(_ip) then
+                               return translate("Mandatory Input: No valid IPv6 address given!")
+                       elseif not DTYP.port(_ret[2]) then
+                               return translate("Mandatory Input: No valid Port given!")
+                       else
+                               return nil
+                       end
+               end
+               _ret = UTIL.split(v, ":")
+               if not _ret[2] then
+                       return translate("Mandatory Input: No Port given!")
+               end
+               if #_ret[1] > 0 and not DTYP.host(_ret[1]) then -- :8118 is valid address
+                       return translate("Mandatory Input: No valid IPv4 address or host given!")
+               elseif not DTYP.port(_ret[2]) then
+                       return translate("Mandatory Input: No valid Port given!")
+               else
+                       return nil
+               end
+       end
+
+       local err   = ""
+       local entry = ""
+       if type(value) == "table" then
+               local x
+               for _, x in ipairs(value) do
+                       if x and #x > 0 then
+                               err = check_value(x)
+                               if err then
+                                       entry = x
+                                       break
+                               end
+                       end
+               end
+       else
+               err = check_value(value)
+               entry = value
+       end
+       if err then
+               return nil, string.format(err_tab_access(self.title_base, err .. " - %s"), entry )
+       end
+       return value
+end
+
+-- permit-access ---------------------------------------------------------------
+local pa       = ns:taboption("access", DynamicList, "permit_access")
+pa.title       = string.format(HELP, "ACLS", "Permit access" )
+pa.description = translate("Who can access what.")
+               .. [[<br /><strong>]]
+               .. translate("Please read Privoxy manual for details!")
+               .. [[</strong>]]
+pa.rmempty     = true
+
+-- deny-access -----------------------------------------------------------------
+local da       = ns:taboption("access", DynamicList, "deny_access")
+da.title       = string.format(HELP, "ACLS", "Deny Access" )
+da.description = translate("Who can access what.")
+               .. [[<br /><strong>]]
+               .. translate("Please read Privoxy manual for details!")
+               .. [[</strong>]]
+da.rmempty     = true
+
+-- buffer-limit ----------------------------------------------------------------
+local bl       = ns:taboption("access", Value, "buffer_limit")
+bl.title_base  = "Buffer Limit"
+bl.title       = string.format(HELP, "BUFFER-LIMIT", bl.title_base )
+bl.description = translate("Maximum size (in KB) of the buffer for content filtering.")
+               .. [[<br />]]
+               .. translate("Value range 1 to 4096, no entry defaults to 4096")
+bl.default     = 4096
+bl.rmempty     = true
+function bl.validate(self, value)
+       local v = tonumber(value)
+       if not v then
+               return nil, err_tab_access(self.title_base, translate("Value is not a number") )
+       elseif v < 1 or v > 4096 then
+               return nil, err_tab_access(self.title_base, translate("Value not between 1 and 4096") )
+       elseif v == self.default then
+               return ""       -- dont need to save default
+       end
+       return value
+end
+
+-- toggle ----------------------------------------------------------------------
+local tgl      = ns:taboption("access", Flag, "toggle")
+tgl.title      = string.format(HELP, "TOGGLE", "Toggle Status" )
+tgl.description        = translate("Enable/Disable filtering when Privoxy starts.")
+               .. [[<br />]]
+               .. translate("Disabled == Transparent Proxy Mode")
+tgl.orientation        = "horizontal"
+tgl.default    = "1"
+tgl.rmempty    = false
+function tgl.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- enable-remote-toggle --------------------------------------------------------
+local ert      = ns:taboption("access", Flag, "enable_remote_toggle")
+ert.title      = string.format(HELP, "ENABLE-REMOTE-TOGGLE", "Enable remote toggle" )
+ert.description        = translate("Whether or not the web-based toggle feature may be used.")
+ert.orientation        = "horizontal"
+ert.rmempty    = true
+function ert.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- enable-remote-http-toggle ---------------------------------------------------
+local eht      = ns:taboption("access", Flag, "enable_remote_http_toggle")
+eht.title      = string.format(HELP, "ENABLE-REMOTE-HTTP-TOGGLE", "Enable remote toggle via HTTP" )
+eht.description        = translate("Whether or not Privoxy recognizes special HTTP headers to change toggle state.")
+               .. [[<br /><strong>]]
+               .. translate("This option will be removed in future releases as it has been obsoleted by the more general header taggers.")
+               .. [[</strong>]]
+eht.orientation        = "horizontal"
+eht.rmempty    = true
+function eht.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- enable-edit-actions ---------------------------------------------------------
+local eea      = ns:taboption("access", Flag, "enable_edit_actions")
+eea.title      = string.format(HELP, "ENABLE-EDIT-ACTIONS", "Enable action file editor" )
+eea.description        = translate("Whether or not the web-based actions file editor may be used.")
+eea.orientation        = "horizontal"
+eea.rmempty    = true
+function eea.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- enforce-blocks --------------------------------------------------------------
+local eb       = ns:taboption("access", Flag, "enforce_blocks")
+eb.title       = string.format(HELP, "ENFORCE-BLOCKS", "Enforce page blocking" )
+eb.description = translate("If enabled, Privoxy hides the 'go there anyway' link. "
+               .. "The user obviously should not be able to bypass any blocks.")
+eb.orientation = "horizontal"
+eb.rmempty     = true
+function eb.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- tab: forward -- #############################################################
+
+-- enable-proxy-authentication-forwarding --------------------------------------
+local paf      = ns:taboption("forward", Flag, "enable_proxy_authentication_forwarding")
+paf.title      = string.format(HELP, "ENABLE-PROXY-AUTHENTICATION-FORWARDING",
+               translate("Enable proxy authentication forwarding") )
+paf.description        = translate("Whether or not proxy authentication through Privoxy should work.")
+               .. [[<br /><strong>]]
+               .. translate("Enabling this option is NOT recommended if there is no parent proxy that requires authentication!")
+               .. [[</strong>]]
+--paf.orientation      = "horizontal"
+paf.rmempty    = true
+function paf.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- forward ---------------------------------------------------------------------
+local fwd      = ns:taboption("forward", DynamicList, "forward")
+fwd.title      = string.format(HELP, "FORWARD", "Forward HTTP" )
+fwd.description        = translate("To which parent HTTP proxy specific requests should be routed.")
+               .. [[<br />]]
+               .. translate("Syntax: target_pattern http_parent[:port]")
+fwd.rmempty    = true
+
+-- forward-socks4 --------------------------------------------------------------
+local fs4      = ns:taboption("forward", DynamicList, "forward_socks4")
+fs4.title      = string.format(HELP, "SOCKS", "Forward SOCKS 4" )
+fs4.description        = translate("Through which SOCKS proxy (and optionally to which parent HTTP proxy) specific requests should be routed.")
+               .. [[<br />]]
+               .. translate("Syntax: target_pattern socks_proxy[:port] http_parent[:port]")
+fs4.rmempty    = true
+
+-- forward-socks4a -------------------------------------------------------------
+local f4a      = ns:taboption("forward", DynamicList, "forward_socks4a")
+f4a.title      = string.format(HELP, "SOCKS", "Forward SOCKS 4A" )
+f4a.description = fs4.description
+f4a.rmempty    = true
+
+-- forward-socks5 --------------------------------------------------------------
+local fs5      = ns:taboption("forward", DynamicList, "forward_socks5")
+fs5.title      = string.format(HELP, "SOCKS", "Forward SOCKS 5" )
+fs5.description = fs4.description
+fs5.rmempty    = true
+
+-- forward-socks5t -------------------------------------------------------------
+local f5t      = ns:taboption("forward", DynamicList, "forward_socks5t")
+f5t.title      = string.format(HELP, "SOCKS", "Forward SOCKS 5t" )
+f5t.description = fs4.description
+f5t.rmempty    = true
+
+-- tab: misc -- ################################################################
+
+-- accept-intercepted-requests -------------------------------------------------
+local air      = ns:taboption("misc", Flag, "accept_intercepted_requests")
+air.title      = string.format(HELP, "ACCEPT-INTERCEPTED-REQUESTS", "Accept intercepted requests" )
+air.description        = translate("Whether intercepted requests should be treated as valid.")
+air.orientation        = "horizontal"
+air.rmempty    = true
+function air.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- allow-cgi-request-crunching -------------------------------------------------
+local crc      = ns:taboption("misc", Flag, "allow_cgi_request_crunching")
+crc.title      = string.format(HELP, "ALLOW-CGI-REQUEST-CRUNCHING", "Allow CGI request crunching" )
+crc.description        = translate("Whether requests to Privoxy's CGI pages can be blocked or redirected.")
+crc.orientation        = "horizontal"
+crc.rmempty    = true
+function crc.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- split-large-forms -----------------------------------------------------------
+local slf      = ns:taboption("misc", Flag, "split_large_forms")
+slf.title      = string.format(HELP, "SPLIT-LARGE-FORMS", "Split large forms" )
+slf.description        = translate("Whether the CGI interface should stay compatible with broken HTTP clients.")
+slf.orientation        = "horizontal"
+slf.rmempty    = true
+function slf.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- keep-alive-timeout ----------------------------------------------------------
+local kat      = ns:taboption("misc", Value, "keep_alive_timeout")
+kat.title_base = "Keep-alive timeout"
+kat.title      = string.format(HELP, "KEEP-ALIVE-TIMEOUT", kat.title_base)
+kat.description        = translate("Number of seconds after which an open connection will no longer be reused.")
+kat.rmempty    = true
+function kat.validate(self, value)
+       local v = tonumber(value)
+       if not v then
+               return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
+       elseif v < 1 then
+               return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
+       end
+       return value
+end
+
+-- tolerate-pipelining ---------------------------------------------------------
+local tp       = ns:taboption("misc", Flag, "tolerate_pipelining")
+tp.title       = string.format(HELP, "TOLERATE-PIPELINING", "Tolerate pipelining" )
+tp.description = translate("Whether or not pipelined requests should be served.")
+tp.orientation = "horizontal"
+tp.rmempty     = true
+function tp.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- default-server-timeout ------------------------------------------------------
+local dst      = ns:taboption("misc", Value, "default_server_timeout")
+dst.title_base = "Default server timeout"
+dst.title      = string.format(HELP, "DEFAULT-SERVER-TIMEOUT", dst.title_base)
+dst.description        = translate("Assumed server-side keep-alive timeout (in seconds) if not specified by the server.")
+dst.rmempty    = true
+function dst.validate(self, value)
+       local v = tonumber(value)
+       if not v then
+               return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
+       elseif v < 1 then
+               return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
+       end
+       return value
+end
+
+-- connection-sharing ----------------------------------------------------------
+local cs       = ns:taboption("misc", Flag, "connection_sharing")
+cs.title       = string.format(HELP, "CONNECTION-SHARING", "Connection sharing" )
+cs.description = translate("Whether or not outgoing connections that have been kept alive should be shared between different incoming connections.")
+cs.orientation = "horizontal"
+cs.rmempty     = true
+function cs.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- socket-timeout --------------------------------------------------------------
+local st       = ns:taboption("misc", Value, "socket_timeout")
+st.title_base  = "Socket timeout"
+st.title       = string.format(HELP, "SOCKET-TIMEOUT", st.title_base )
+st.description = translate("Number of seconds after which a socket times out if no data is received.")
+st.default     = 300
+st.rmempty     = true
+function st.validate(self, value)
+       local v = tonumber(value)
+       if not v then
+               return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
+       elseif v < 1 then
+               return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
+       elseif v == self.default then
+               return ""       -- dont need to save default
+       end
+       return value
+end
+
+-- max-client-connections ------------------------------------------------------
+local mcc      = ns:taboption("misc", Value, "max_client_connections")
+mcc.title_base = "Max. client connections"
+mcc.title      = string.format(HELP, "MAX-CLIENT-CONNECTIONS", mcc.title_base )
+mcc.description        = translate("Maximum number of client connections that will be served.")
+mcc.default    = 128
+mcc.rmempty    = true
+function mcc.validate(self, value)
+       local v = tonumber(value)
+       if not v then
+               return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
+       elseif v < 1 then
+               return nil, err_tab_misc(self.title_base, translate("Value not greater 0 or empty") )
+       elseif v == self.default then
+               return ""       -- dont need to save default
+       end
+       return value
+end
+
+-- handle-as-empty-doc-returns-ok ----------------------------------------------
+local her      = ns:taboption("misc", Flag, "handle_as_empty_doc_returns_ok")
+her.title      = string.format(HELP, "HANDLE-AS-EMPTY-DOC-RETURNS-OK", "Handle as empty doc returns ok" )
+her.description        = translate("The status code Privoxy returns for pages blocked with +handle-as-empty-document.")
+her.orientation        = "horizontal"
+her.rmempty    = true
+function her.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- enable-compression ----------------------------------------------------------
+local ec       = ns:taboption("misc", Flag, "enable_compression")
+ec.title       = string.format(HELP, "ENABLE-COMPRESSION", "Enable compression" )
+ec.description = translate("Whether or not buffered content is compressed before delivery.")
+ec.orientation = "horizontal"
+ec.rmempty     = true
+function ec.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- compression-level -----------------------------------------------------------
+local cl       = ns:taboption("misc", Value, "compression_level")
+cl.title_base  = "Compression level"
+cl.title       = string.format(HELP, "COMPRESSION-LEVEL", cl.title_base )
+cl.description = translate("The compression level that is passed to the zlib library when compressing buffered content.")
+cl.default     = 1
+cl.rmempty     = true
+function cl.validate(self, value)
+       local v = tonumber(value)
+       if not v then
+               return nil, err_tab_misc(self.title_base, translate("Value is not a number") )
+       elseif v < 0 or v > 9 then
+               return nil, err_tab_misc(self.title_base, translate("Value not between 0 and 9") )
+       elseif v == self.default then
+               return ""       -- don't need to save default
+       end
+       return value
+end
+
+-- client-header-order ---------------------------------------------------------
+local cho      = ns:taboption("misc", Value, "client_header_order")
+cho.title      = string.format(HELP, "CLIENT-HEADER-ORDER", "Client header order" )
+cho.description        = translate("The order in which client headers are sorted before forwarding them.")
+               .. [[<br />]]
+               .. translate("Syntax: Client header names delimited by spaces.")
+cho.rmempty    = true
+
+-- "debug"-tab definition -- ###################################################
+
+-- single-threaded -------------------------------------------------------------
+local st       = ns:taboption("debug", Flag, "single_threaded")
+st.title       = string.format(HELP, "SINGLE-THREADED", "Single Threaded" )
+st.description = translate("Whether to run only one server thread.")
+               .. [[<br /><strong>]]
+               .. translate("This option is only there for debugging purposes. It will drastically reduce performance.")
+               .. [[</strong>]]
+st.rmempty     = true
+function st.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d1       = ns:taboption("debug", Flag, "debug_1")
+d1.title       = string.format(HELP, "DEBUG", "Debug 1" )
+d1.description = translate("Log the destination for each request Privoxy let through. See also 'Debug 1024'.")
+d1.rmempty     = true
+function d1.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d2       = ns:taboption("debug", Flag, "debug_2")
+d2.title       = string.format(HELP, "DEBUG", "Debug 2" )
+d2.description = translate("Show each connection status")
+d2.rmempty     = true
+function d2.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d3       = ns:taboption("debug", Flag, "debug_4")
+d3.title       = string.format(HELP, "DEBUG", "Debug 4" )
+d3.description = translate("Show I/O status")
+d3.rmempty     = true
+function d3.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d4       = ns:taboption("debug", Flag, "debug_8")
+d4.title       = string.format(HELP, "DEBUG", "Debug 8" )
+d4.description = translate("Show header parsing")
+d4.rmempty     = true
+function d4.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d5       = ns:taboption("debug", Flag, "debug_16")
+d5.title       = string.format(HELP, "DEBUG", "Debug 16" )
+d5.description = translate("Log all data written to the network")
+d5.rmempty     = true
+function d5.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d6       = ns:taboption("debug", Flag, "debug_32")
+d6.title       = string.format(HELP, "DEBUG", "Debug 32" )
+d6.description = translate("Debug force feature")
+d6.rmempty     = true
+function d6.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d7       = ns:taboption("debug", Flag, "debug_64")
+d7.title       = string.format(HELP, "DEBUG", "Debug 64" )
+d7.description = translate("Debug regular expression filters")
+d7.rmempty     = true
+function d7.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d8       = ns:taboption("debug", Flag, "debug_128")
+d8.title       = string.format(HELP, "DEBUG", "Debug 128" )
+d8.description = translate("Debug redirects")
+d8.rmempty     = true
+function d8.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d9       = ns:taboption("debug", Flag, "debug_256")
+d9.title       = string.format(HELP, "DEBUG", "Debug 256" )
+d9.description = translate("Debug GIF de-animation")
+d9.rmempty     = true
+function d9.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d10      = ns:taboption("debug", Flag, "debug_512")
+d10.title      = string.format(HELP, "DEBUG", "Debug 512" )
+d10.description        = translate("Common Log Format")
+d10.rmempty    = true
+function d10.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d11      = ns:taboption("debug", Flag, "debug_1024")
+d11.title      = string.format(HELP, "DEBUG", "Debug 1024" )
+d11.description        = translate("Log the destination for requests Privoxy didn't let through, and the reason why.")
+d11.rmempty    = true
+function d11.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d12      = ns:taboption("debug", Flag, "debug_2048")
+d12.title      = string.format(HELP, "DEBUG", "Debug 2048" )
+d12.description        = translate("CGI user interface")
+d12.rmempty    = true
+function d12.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d13      = ns:taboption("debug", Flag, "debug_4096")
+d13.title      = string.format(HELP, "DEBUG", "Debug 4096" )
+d13.description        = translate("Startup banner and warnings.")
+d13.rmempty    = true
+function d13.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d14      = ns:taboption("debug", Flag, "debug_8192")
+d14.title      = string.format(HELP, "DEBUG", "Debug 8192" )
+d14.description        = translate("Non-fatal errors - *we highly recommended enabling this*")
+d14.rmempty    = true
+function d14.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d15      = ns:taboption("debug", Flag, "debug_32768")
+d15.title      = string.format(HELP, "DEBUG", "Debug 32768" )
+d15.description        = translate("Log all data read from the network")
+d15.rmempty    = true
+function d15.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- debug -----------------------------------------------------------------------
+local d16      = ns:taboption("debug", Flag, "debug_65536")
+d16.title      = string.format(HELP, "DEBUG", "Debug 65536" )
+d16.description        = translate("Log the applying actions")
+d16.rmempty    = true
+function d16.parse(self, section)
+       CTRL.flag_parse(self, section)
+end
+
+-- tab: logview -- #############################################################
+
+local lv       = ns:taboption("logview", DummyValue, "_logview")
+lv.template    = "privoxy/detail_logview"
+lv.inputtitle  = translate("Read / Reread log file")
+lv.rows                = 50
+function lv.cfgvalue(self, section)
+       local lfile=self.map:get(ns.section, "logdir") .. "/" .. self.map:get(ns.section, "logfile")
+       if NXFS.access(lfile) then
+               return lfile .. "\n" .. translate("Please press [Read] button")
+       end
+       return lfile .. "\n" .. translate("File not found or empty")
+end
+
+return m
diff --git a/applications/luci-app-privoxy/luasrc/view/privoxy/detail_logview.htm b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_logview.htm
new file mode 100755 (executable)
index 0000000..3e19070
--- /dev/null
@@ -0,0 +1,56 @@
+
+<!-- ++ BEGIN ++ Privoxy ++ detail_logview.htm ++ -->
+<script type="text/javascript">//<![CDATA[
+       function onclick_logview(section, bottom)  {
+               // get elements
+               var txt = document.getElementById("cbid.privoxy.privoxy._logview.txt"); // TextArea
+               if ( !txt ) { return; } // security check
+               var lvXHR = new XHR();
+               lvXHR.get('<%=luci.dispatcher.build_url("admin", "services", "privoxy", "logview")%>', null,
+                       function(x) {
+                               if (x.responseText == "_nodata_")
+                                       txt.value = "<%:File not found or empty%>";
+                               else
+                                       txt.value = x.responseText;
+                               if (bottom)
+                                       txt.scrollTop = txt.scrollHeight;
+                               else
+                                       txt.scrollTop = 0;                      }
+               );
+       }
+//]]></script>
+
+<%+cbi/valueheader%>
+
+<br />
+
+<%
+-- one button on top, one at the buttom
+%>
+<input class="cbi-button cbi-input-button" style="align: center; width: 100%" type="button" onclick="onclick_logview(this.name, false)"
+<%=
+attr("name", section) .. attr("id", cbid .. ".btn1") .. attr("value", self.inputtitle)
+%> />
+
+<br /><br />
+
+<%
+-- set a readable style taken from openwrt theme for textarea#syslog
+-- in openwrt theme there are problems with a width of 100 so we check for theme and set to lower value
+%>
+<textarea style="width: <%if media == "/luci-static/openwrt.org" then%>98.7%<%else%>100%<%end%> ; min-height: 500px; border: 3px solid #cccccc; padding: 5px; font-family: monospace; resize: none;" wrap="off" readonly="readonly"
+<%=
+attr("name", cbid .. ".txt") .. attr("id", cbid .. ".txt") .. ifattr(self.rows, "rows")
+%> >
+<%-=pcdata(self:cfgvalue(section))-%>
+</textarea>
+<br /><br />
+
+<%
+-- one button on top, one at the buttom
+%>
+<input class="cbi-button cbi-input-button" style="align: center; width: 100%" type="button" onclick="onclick_logview(this.name, true)"
+<%= attr("name", section) .. attr("id", cbid .. ".btn2") .. attr("value", self.inputtitle) %> />
+
+<%+cbi/valuefooter%>
+<!-- ++ END ++ Privoxy ++ detail_logview.htm ++ -->
diff --git a/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm b/applications/luci-app-privoxy/luasrc/view/privoxy/detail_startstop.htm
new file mode 100644 (file)
index 0000000..b9de886
--- /dev/null
@@ -0,0 +1,49 @@
+
+<!-- ++ BEGIN ++ Privoxy ++ detail_startstop.htm ++ -->
+<script type="text/javascript">//<![CDATA[
+
+       // show XHR.poll/XHR.get response on button
+       function _data2elements(x) {
+               var btn = document.getElementById("cbid.privoxy.privoxy._startstop");
+               if ( ! btn ) { return; }        // security check
+               if (x.responseText == "0") {
+                       btn.value = "<%:Start%>";
+                       btn.className = "cbi-button cbi-button-apply";
+                       btn.disabled = false;
+               } else {
+                       btn.value = "PID: " + x.responseText;
+                       btn.className = "cbi-button cbi-button-reset";
+                       btn.disabled = false;
+               }
+       }
+
+       // event handler for start/stop button
+       function onclick_startstop(id) {
+               // do start/stop
+               var btnXHR = new XHR();
+               btnXHR.get('<%=luci.dispatcher.build_url("admin", "services", "privoxy", "startstop")%>', null,
+                       function(x) { _data2elements(x); }
+               );
+       }
+
+       XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "services", "privoxy", "status")%>', null,
+               function(x, data) { _data2elements(x); }
+       );
+
+//]]></script>
+
+<%+cbi/valueheader%>
+
+<% if self:cfgvalue(section) ~= false then
+-- We need to garantie that function cfgvalue run first to set missing parameters
+%>
+       <!-- style="font-size: 100%;" needed for openwrt theme to fix font size -->
+       <!-- type="button" onclick="..." enable standard onclick functionalty   -->
+       <input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" style="font-size: 100%;" type="button" onclick="onclick_startstop(this.id)"
+       <%=
+       attr("name", section) .. attr("id", cbid) .. attr("value", self.inputtitle) .. ifattr(self.disabled, "disabled")
+       %> />
+<% end %>
+
+<%+cbi/valuefooter%>
+<!-- ++ END ++ Privoxy ++ detail_startstop.htm ++ -->
diff --git a/applications/luci-app-privoxy/po/de/privoxy.po b/applications/luci-app-privoxy/po/de/privoxy.po
new file mode 100644 (file)
index 0000000..6ee3af4
--- /dev/null
@@ -0,0 +1,496 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: luci-app-privoxy\n"
+"POT-Creation-Date: 2015-01-18 21:48+0100\n"
+"PO-Revision-Date: 2015-01-18 21:51+0100\n"
+"Last-Translator: Christian Schoenebeck <christian.schoenebeck@gmail.com>\n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.4\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+
+msgid ""
+"A URL to be displayed in the error page that users will see if access to an "
+"untrusted page is denied."
+msgstr ""
+"Ein Link auf der Fehlerseite, der Benutzern angezeigt wird, wenn der Zugang "
+"zu einer nicht vertrauenswürdigen Seite verweigert wird."
+
+msgid ""
+"A URL to documentation about the local Privoxy setup, configuration or "
+"policies."
+msgstr ""
+"Ein Link zur Dokumentation Ã¼ber die lokale Privoxy Konfiguration und die "
+"Sicherheitseinstellungen."
+
+msgid "Access Control"
+msgstr "Zugriffskontrolle"
+
+msgid "Actions that are applied to all sites and maybe overruled later on."
+msgstr ""
+"Aktionen, die für alle Websites angewendet werden, und vielleicht später "
+"überschrieben werden."
+
+msgid "An alternative directory where the templates are loaded from."
+msgstr "Eine alternatives Verzeichnis, aus dem die Vorlagen geladen werden."
+
+msgid "An email address to reach the Privoxy administrator."
+msgstr "Eine E-Mail-Adresse, um die Privoxy-Administrator zu erreichen."
+
+msgid ""
+"Assumed server-side keep-alive timeout (in seconds) if not specified by the "
+"server."
+msgstr ""
+"Angenommenes serverseitiges Keep-Alive-Timeout (in Sekunden), falls nicht "
+"vom Server festgelegt."
+
+msgid "CGI user interface"
+msgstr "Protokolliert die CGI Benutzer Schnittstelle"
+
+msgid "Common Log Format"
+msgstr "Gemeinsames Protokollformat"
+
+msgid ""
+"Configure here the routing of HTTP requests through a chain of multiple "
+"proxies. Note that parent proxies can severely decrease your privacy level. "
+"Also specified here are SOCKS proxies."
+msgstr ""
+"Konfigurieren Sie hier das Weiterleiten von HTTP-Anforderungen durch eine "
+"Kette von mehreren Proxies. Beachten Sie, dass  Ã¼bergeordnete Proxies Ihre "
+"Privatsphäre stark verringern können. Auch hier angegeben werden SOCKS-"
+"Proxies."
+
+msgid "Debug GIF de-animation"
+msgstr "Protokolliert die GIF de-animation"
+
+msgid "Debug force feature"
+msgstr "Protokolliert die 'Force' Eigenschaft"
+
+msgid "Debug redirects"
+msgstr "Protokolliert Weiterleitungen"
+
+msgid "Debug regular expression filters"
+msgstr "Protokolliert Filter für reguläre Ausdrücke"
+
+msgid "Directory does not exist!"
+msgstr "Verzeichnis existiert nicht!"
+
+msgid "Disabled == Transparent Proxy Mode"
+msgstr "Deaktiviert == Transparent Proxy Betrieb"
+
+msgid "Enable proxy authentication forwarding"
+msgstr "Aktivieren die Weiterleitung von Proxy-Authentifizierungen"
+
+msgid ""
+"Enable/Disable autostart of Privoxy on system startup and interface events"
+msgstr ""
+"Aktivieren / Deaktivieren des Autostart von Privoxy beim Systemstart und "
+"Schnittstellenereignissen."
+
+msgid "Enable/Disable filtering when Privoxy starts."
+msgstr "Aktivieren / Deaktivieren der Filterung, wenn Privoxy startet."
+
+msgid "Enabled"
+msgstr "Aktiviert"
+
+msgid ""
+"Enabling this option is NOT recommended if there is no parent proxy that "
+"requires authentication!"
+msgstr ""
+"Die Aktivierung dieser Option wird NICHT empfohlen, wenn es keinen "
+"übergeordneten Proxy gibt, der eine Authentifizierung erfordert!"
+
+msgid "File '%s' not found inside Configuration Directory"
+msgstr "Datei '%s' nicht im Konfigurationsverzeichnis gefunden!"
+
+msgid "File not found or empty"
+msgstr "Datei nicht gefunden oder leer"
+
+msgid "Files and Directories"
+msgstr "Dateien und Verzeichnisse"
+
+msgid "For help use link at the relevant option"
+msgstr ""
+"Für Hilfe zur Verwendung, benutzen Sie die Verknüpfung der betreffenden "
+"Option."
+
+msgid "Forwarding"
+msgstr "Weiterleitung"
+
+msgid ""
+"If enabled, Privoxy hides the 'go there anyway' link. The user obviously "
+"should not be able to bypass any blocks."
+msgstr ""
+"Wenn aktiviert, verbirgt Privoxy den Link 'go there anyway'. Normalerweise "
+"sollten Benutzer nicht in der Lage sein, Blockierungen zu umgehen."
+
+msgid ""
+"If you intend to operate Privoxy for more users than just yourself, it might "
+"be a good idea to let them know how to reach you, what you block and why you "
+"do that, your policies, etc."
+msgstr ""
+"Wenn Sie beabsichtigen, Privoxy für mehr Nutzer als nur sich selbst zu "
+"betreiben, ist es eine gute Idee, sie wissen zu lassen, wie sie Sie "
+"erreichen können, was Sie blockieren und warum Sie das tun, etc."
+
+msgid "Invalid email address"
+msgstr "Ungültige Email Adresse"
+
+msgid "It is NOT recommended for the casual user."
+msgstr "Es wird NICHT für den gelegentlichen Anwender empfohlen."
+
+msgid "Local Set-up"
+msgstr "Lokale Einstellungen"
+
+msgid "Location of the Privoxy User Manual."
+msgstr "Ort des Privoxy Benutzer Handbuches"
+
+msgid "Log File Viewer"
+msgstr "Protokolldatei"
+
+msgid "Log all data read from the network"
+msgstr "Protokolliert alle Daten, die vom Netzwerk gelesen werden."
+
+msgid "Log all data written to the network"
+msgstr "Protokolliert alle Daten, die auf das Netzwerk geschrieben werden."
+
+msgid "Log the applying actions"
+msgstr "Protokiolliert angewendete Aktionen"
+
+msgid ""
+"Log the destination for each request Privoxy let through. See also 'Debug "
+"1024'."
+msgstr ""
+"Protokolliert das Ziel für jede Anforderung die Privoxy durchlässt. Siehe "
+"auch 'Debug 1024'."
+
+msgid ""
+"Log the destination for requests Privoxy didn't let through, and the reason "
+"why."
+msgstr ""
+"Protokolliert das Ziel für Anfragen die Privoxy nicht durchgelassen hat, und "
+"den Grund dafür."
+
+msgid "Logging"
+msgstr "Protokollierung"
+
+msgid "Main actions file"
+msgstr "Wichtige Aktionen-Datei"
+
+msgid "Mandatory Input: No Data given!"
+msgstr "Pflichtfeld: Keine Daten angegeben!"
+
+msgid "Mandatory Input: No Directory given!"
+msgstr "Pflichtfeld: Kein Verzeichnis angegeben!"
+
+msgid "Mandatory Input: No File given!"
+msgstr "Pflichtfeld: Keine Datei angegeben!"
+
+msgid "Mandatory Input: No Port given!"
+msgstr "Pflichtfeld: Kein Port angegeben!"
+
+msgid "Mandatory Input: No files given!"
+msgstr "Pflichtfeld: Keine Dateien angegeben!"
+
+msgid "Mandatory Input: No valid IPv4 address or host given!"
+msgstr ""
+"Pflichtfeld: Keine gültige IPv4 Adresse oder gültiger Hostname angegeben!"
+
+msgid "Mandatory Input: No valid IPv6 address given!"
+msgstr "Pflichtfeld: Keine gültige IPv6 Adresse angegeben!"
+
+msgid "Mandatory Input: No valid Port given!"
+msgstr "Pflichtfeld: Keine gültige Port Nummer angegeben!"
+
+msgid "Maximum number of client connections that will be served."
+msgstr "Maximale Anzahl von Client-Verbindungen."
+
+msgid "Maximum size (in KB) of the buffer for content filtering."
+msgstr "Maximale Größe (in KB) des Puffers für die Inhaltsfilterung."
+
+msgid "Miscellaneous"
+msgstr "Verschiedenes"
+
+msgid "No trailing '/', please."
+msgstr "Bitte kein '/' am Ende."
+
+msgid "Non-fatal errors - *we highly recommended enabling this*"
+msgstr ""
+"Protokolliert nicht schwerwiegende Fehler - * Es wird dringend empfohlen, "
+"dieses zu aktivieren *"
+
+msgid ""
+"Number of seconds after which a socket times out if no data is received."
+msgstr ""
+"Anzahl der Sekunden, nach der eine Socket Timeout erfolgt, wenn keine Daten "
+"empfangen werden."
+
+msgid ""
+"Number of seconds after which an open connection will no longer be reused."
+msgstr ""
+"Anzahl von Sekunden, nach der eine offene Verbindung nicht mehr "
+"wiederverwendet wird."
+
+msgid "Please press [Read] button"
+msgstr "Bitte Protokolldatei einlesen"
+
+msgid "Please read Privoxy manual for details!"
+msgstr "Bitte lesen Sie das Privoxy Handbuch für Details!"
+
+msgid "Please update to the current version!"
+msgstr "Aktualisieren Sie bitte auf die aktuelle Version!"
+
+msgid "Privoxy WEB proxy"
+msgstr "Privoxy WEB proxy"
+
+msgid ""
+"Privoxy can (and normally does) use a number of other files for additional "
+"configuration, help and logging. This section of the configuration file "
+"tells Privoxy where to find those other files."
+msgstr ""
+"Privoxy verwendet  (was in der Regel der Fall ist), eine Reihe von anderen "
+"Dateien für eine zusätzliche Konfiguration, Hilfe und Protokollierung. "
+"Dieser Abschnitt der Konfigurationsdatei definiert, wo diese Dateien zu "
+"finden sind."
+
+msgid ""
+"Privoxy is a non-caching web proxy with advanced filtering capabilities for "
+"enhancing privacy, modifying web page data and HTTP headers, controlling "
+"access, and removing ads and other obnoxious Internet junk."
+msgstr ""
+"Privoxy ist ein non-caching Web-Proxy mit erweiterten Filterfunktion zur "
+"Verbesserung der Privatsphäre. Er modifiziert Webseitendaten und HTTP-"
+"Header, kontrolliert den Zugang und das Entfernen von Anzeigen und anderem "
+"abscheulichen Internet Schrott."
+
+msgid "Read / Reread log file"
+msgstr "Protokolldatei (neu) lesen"
+
+msgid "Show I/O status"
+msgstr "Protokolliert den I/O Status"
+
+msgid "Show each connection status"
+msgstr "Protokolliert jeden Verbindungsstatus"
+
+msgid "Show header parsing"
+msgstr "Protokolliert das 'Header parsing'"
+
+msgid "Software update required"
+msgstr "Softwareaktualisierung nötig"
+
+msgid "Start"
+msgstr "Start"
+
+msgid "Start / Stop"
+msgstr "Start / Stopp"
+
+msgid "Start/Stop Privoxy WEB Proxy"
+msgstr "Start/Stopp Privoxy WEB Proxy"
+
+msgid "Startup banner and warnings."
+msgstr "Protokolliert Start-Meldungen und Warnungen"
+
+msgid "Syntax:"
+msgstr "Syntax:"
+
+msgid "Syntax: Client header names delimited by spaces."
+msgstr "Syntax: Client header Namen getrennt durch Leerzeichen."
+
+msgid "Syntax: target_pattern http_parent[:port]"
+msgstr "Syntax: target_pattern http_parent[:port]"
+
+msgid "Syntax: target_pattern socks_proxy[:port] http_parent[:port]"
+msgstr "Syntax: target_pattern socks_proxy[:port] http_parent[:port]"
+
+msgid ""
+"The actions file(s) to use. Multiple actionsfile lines are permitted, and "
+"are in fact recommended!"
+msgstr ""
+"Die zu verwendenden Aktion-Datei(en). Mehrere Dateien sind gestattet und "
+"empfohlen!"
+
+msgid ""
+"The address and TCP port on which Privoxy will listen for client requests."
+msgstr ""
+"Die Adresse und das TCP-Port, auf dem Privoxy auf Client-Anforderungen "
+"wartet."
+
+msgid ""
+"The compression level that is passed to the zlib library when compressing "
+"buffered content."
+msgstr ""
+"Die Komprimierungsstufe (0-9), die der zlib-Bibliothek beim Komprimieren "
+"gepufferten Inhaltes Ã¼bergeben wird."
+
+msgid ""
+"The currently installed 'privoxy' package is not supported by LuCI "
+"application."
+msgstr ""
+"Das aktuell installierte 'privoxy' Paket wird von dieser LuCI Anwendung "
+"NICHT unterstützt."
+
+msgid ""
+"The directory where all logging takes place (i.e. where the logfile is "
+"located)."
+msgstr "Das Verzeichnis in dem die Protokolldatei gespeichert wird."
+
+msgid "The directory where the other configuration files are located."
+msgstr "Das Verzeichnis in dem weitere Konfigurationsdateien gespeichert sind."
+
+msgid ""
+"The filter files contain content modification rules that use regular "
+"expressions."
+msgstr ""
+"Die Filterdateien enthalten Ã„nderung des Inhalts, die reguläre Ausdrücke "
+"als  Regeln verwenden."
+
+msgid "The hostname shown on the CGI pages."
+msgstr "Der Hostname der auf CGI-Seiten angezeigt wird."
+
+msgid "The log file to use. File name, relative to log directory."
+msgstr ""
+"Zu verwendende Protokolldatei. Dateiname relativ zum Protokoll-Verzeichnis."
+
+msgid "The order in which client headers are sorted before forwarding them."
+msgstr ""
+"Die Reihenfolge, in der Client-Header sortiert werden, bevor sie "
+"weitergeleitet werden."
+
+msgid ""
+"The status code Privoxy returns for pages blocked with +handle-as-empty-"
+"document."
+msgstr ""
+"Ob Statuscode 200(OK) oder 403(forbidden) für Seiten gemeldet wird, die "
+"durch den Filter 'handle-as-empty-document' blockiert werden."
+
+msgid ""
+"The trust mechanism is an experimental feature for building white-lists and "
+"should be used with care."
+msgstr ""
+"Der Trust-Mechanismus ist eine experimentelle Funktion für den Aufbau von "
+"White-Listen und sollte mit Vorsicht verwendet werden."
+
+msgid ""
+"The value of this option only matters if the experimental trust mechanism "
+"has been activated."
+msgstr ""
+"Der Wert dieser Option ist nur wirksam, wenn der experimentelle Trust-"
+"Mechanismus aktiviert wurde."
+
+msgid ""
+"This option is only there for debugging purposes. It will drastically reduce "
+"performance."
+msgstr ""
+"Diese Option ist ausschließlich zur Fehlersuche. Es wird drastisch die "
+"Leistung beeinträchtigt."
+
+msgid ""
+"This option will be removed in future releases as it has been obsoleted by "
+"the more general header taggers."
+msgstr "Diese Option wird in zukünftigen Versionen entfernt werden."
+
+msgid ""
+"This tab controls the security-relevant aspects of Privoxy's configuration."
+msgstr ""
+"Diese Registerkarte steuert die sicherheitsrelevanten Aspekte der Privoxy "
+"Konfiguration."
+
+msgid ""
+"Through which SOCKS proxy (and optionally to which parent HTTP proxy) "
+"specific requests should be routed."
+msgstr ""
+"An welchen SOCKS-Proxy (und gegebenenfalls an welchen Ã¼bergeordneten HTTP-"
+"Proxy) spezifischen Anforderungen weitergeleitet werden."
+
+msgid "To which parent HTTP proxy specific requests should be routed."
+msgstr ""
+"An welchen Ã¼bergeordneten HTTP-Proxy spezifischen Anforderungen "
+"weitergeleitet werden."
+
+msgid "User customizations"
+msgstr "Benutzerdefinierte Anpassungen"
+
+msgid "Value is not a number"
+msgstr "Eingabe ist keine Zahl"
+
+msgid "Value not between 0 and 9"
+msgstr "Wert nicht zwischen 0 und 9"
+
+msgid "Value not between 1 and 4096"
+msgstr "Wert nicht zwischen 1 und 4096"
+
+msgid "Value not greater 0 or empty"
+msgstr "Wert nicht größer 0 oder leer"
+
+msgid "Value range 1 to 4096, no entry defaults to 4096"
+msgstr "Wertebereich: 1 bis 4096; Keine Angabe setzt 4096."
+
+msgid "Version"
+msgstr "Version"
+
+msgid "Version Information"
+msgstr "Versionsinformation"
+
+msgid "Whether intercepted requests should be treated as valid."
+msgstr "Ob abgefangen Anfragen als gültig behandelt werden."
+
+msgid ""
+"Whether or not Privoxy recognizes special HTTP headers to change toggle "
+"state."
+msgstr ""
+"Ob Privoxy erkannte spezielle HTTP-Header zur Ã„nderung des Toggle-Status "
+"verwendet.."
+
+msgid "Whether or not buffered content is compressed before delivery."
+msgstr ""
+"Ob gepufferte Inhalte vor der Weiterleitung komprimiert werden oder nicht."
+
+msgid ""
+"Whether or not outgoing connections that have been kept alive should be "
+"shared between different incoming connections."
+msgstr ""
+"Ob ausgehende Verbindungen, die am Leben gehalten werden, für verschiedenen "
+"eingehenden Verbindungen gemeinsam genutzt werden oder nicht."
+
+msgid "Whether or not pipelined requests should be served."
+msgstr "Ob Pipeline-Anfragen bedient werden oder nicht."
+
+msgid "Whether or not proxy authentication through Privoxy should work."
+msgstr ""
+"Ob Proxy-Authentifizierungen durch Privoxy weitergeleitet werden oder nicht."
+
+msgid "Whether or not the web-based actions file editor may be used."
+msgstr "De-/Aktiviert den webbasierte Action-Datei Editor."
+
+msgid "Whether or not the web-based toggle feature may be used."
+msgstr "De-Aktiviert die webbasierte Umschaltfunktion."
+
+msgid "Whether requests to Privoxy's CGI pages can be blocked or redirected."
+msgstr ""
+"Ob Anfragen an Privoxy CGI-Seiten gesperrt oder umgeleitet werden können "
+"oder nicht."
+
+msgid ""
+"Whether the CGI interface should stay compatible with broken HTTP clients."
+msgstr ""
+"Ob die CGI-Schnittstelle mit broken HTTP-Clients kompatibel bleibt oder "
+"nicht."
+
+msgid "Whether to run only one server thread."
+msgstr "Ob nur ein Server-Thread ausgeführt wird."
+
+msgid "Who can access what."
+msgstr "Wer kann auf Was zugreifen."
+
+msgid "installed"
+msgstr "installiert"
+
+msgid "or higher"
+msgstr "oder höher"
+
+msgid "required"
+msgstr "benötigt"
diff --git a/applications/luci-app-privoxy/po/templates/privoxy.pot b/applications/luci-app-privoxy/po/templates/privoxy.pot
new file mode 100644 (file)
index 0000000..8f836be
--- /dev/null
@@ -0,0 +1,405 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid ""
+"A URL to be displayed in the error page that users will see if access to an "
+"untrusted page is denied."
+msgstr ""
+
+msgid ""
+"A URL to documentation about the local Privoxy setup, configuration or "
+"policies."
+msgstr ""
+
+msgid "Access Control"
+msgstr ""
+
+msgid "Actions that are applied to all sites and maybe overruled later on."
+msgstr ""
+
+msgid "An alternative directory where the templates are loaded from."
+msgstr ""
+
+msgid "An email address to reach the Privoxy administrator."
+msgstr ""
+
+msgid ""
+"Assumed server-side keep-alive timeout (in seconds) if not specified by the "
+"server."
+msgstr ""
+
+msgid "CGI user interface"
+msgstr ""
+
+msgid "Common Log Format"
+msgstr ""
+
+msgid ""
+"Configure here the routing of HTTP requests through a chain of multiple "
+"proxies. Note that parent proxies can severely decrease your privacy level. "
+"Also specified here are SOCKS proxies."
+msgstr ""
+
+msgid "Debug GIF de-animation"
+msgstr ""
+
+msgid "Debug force feature"
+msgstr ""
+
+msgid "Debug redirects"
+msgstr ""
+
+msgid "Debug regular expression filters"
+msgstr ""
+
+msgid "Directory does not exist!"
+msgstr ""
+
+msgid "Disabled == Transparent Proxy Mode"
+msgstr ""
+
+msgid "Enable proxy authentication forwarding"
+msgstr ""
+
+msgid ""
+"Enable/Disable autostart of Privoxy on system startup and interface events"
+msgstr ""
+
+msgid "Enable/Disable filtering when Privoxy starts."
+msgstr ""
+
+msgid "Enabled"
+msgstr ""
+
+msgid ""
+"Enabling this option is NOT recommended if there is no parent proxy that "
+"requires authentication!"
+msgstr ""
+
+msgid "File '%s' not found inside Configuration Directory"
+msgstr ""
+
+msgid "File not found or empty"
+msgstr ""
+
+msgid "Files and Directories"
+msgstr ""
+
+msgid "For help use link at the relevant option"
+msgstr ""
+
+msgid "Forwarding"
+msgstr ""
+
+msgid ""
+"If enabled, Privoxy hides the 'go there anyway' link. The user obviously "
+"should not be able to bypass any blocks."
+msgstr ""
+
+msgid ""
+"If you intend to operate Privoxy for more users than just yourself, it might "
+"be a good idea to let them know how to reach you, what you block and why you "
+"do that, your policies, etc."
+msgstr ""
+
+msgid "Invalid email address"
+msgstr ""
+
+msgid "It is NOT recommended for the casual user."
+msgstr ""
+
+msgid "Local Set-up"
+msgstr ""
+
+msgid "Location of the Privoxy User Manual."
+msgstr ""
+
+msgid "Log File Viewer"
+msgstr ""
+
+msgid "Log all data read from the network"
+msgstr ""
+
+msgid "Log all data written to the network"
+msgstr ""
+
+msgid "Log the applying actions"
+msgstr ""
+
+msgid ""
+"Log the destination for each request Privoxy let through. See also 'Debug "
+"1024'."
+msgstr ""
+
+msgid ""
+"Log the destination for requests Privoxy didn't let through, and the reason "
+"why."
+msgstr ""
+
+msgid "Logging"
+msgstr ""
+
+msgid "Main actions file"
+msgstr ""
+
+msgid "Mandatory Input: No Data given!"
+msgstr ""
+
+msgid "Mandatory Input: No Directory given!"
+msgstr ""
+
+msgid "Mandatory Input: No File given!"
+msgstr ""
+
+msgid "Mandatory Input: No Port given!"
+msgstr ""
+
+msgid "Mandatory Input: No files given!"
+msgstr ""
+
+msgid "Mandatory Input: No valid IPv4 address or host given!"
+msgstr ""
+
+msgid "Mandatory Input: No valid IPv6 address given!"
+msgstr ""
+
+msgid "Mandatory Input: No valid Port given!"
+msgstr ""
+
+msgid "Maximum number of client connections that will be served."
+msgstr ""
+
+msgid "Maximum size (in KB) of the buffer for content filtering."
+msgstr ""
+
+msgid "Miscellaneous"
+msgstr ""
+
+msgid "No trailing '/', please."
+msgstr ""
+
+msgid "Non-fatal errors - *we highly recommended enabling this*"
+msgstr ""
+
+msgid ""
+"Number of seconds after which a socket times out if no data is received."
+msgstr ""
+
+msgid ""
+"Number of seconds after which an open connection will no longer be reused."
+msgstr ""
+
+msgid "Please press [Read] button"
+msgstr ""
+
+msgid "Please read Privoxy manual for details!"
+msgstr ""
+
+msgid "Please update to the current version!"
+msgstr ""
+
+msgid "Privoxy WEB proxy"
+msgstr ""
+
+msgid ""
+"Privoxy can (and normally does) use a number of other files for additional "
+"configuration, help and logging. This section of the configuration file "
+"tells Privoxy where to find those other files."
+msgstr ""
+
+msgid ""
+"Privoxy is a non-caching web proxy with advanced filtering capabilities for "
+"enhancing privacy, modifying web page data and HTTP headers, controlling "
+"access, and removing ads and other obnoxious Internet junk."
+msgstr ""
+
+msgid "Read / Reread log file"
+msgstr ""
+
+msgid "Show I/O status"
+msgstr ""
+
+msgid "Show each connection status"
+msgstr ""
+
+msgid "Show header parsing"
+msgstr ""
+
+msgid "Software update required"
+msgstr ""
+
+msgid "Start"
+msgstr ""
+
+msgid "Start / Stop"
+msgstr ""
+
+msgid "Start/Stop Privoxy WEB Proxy"
+msgstr ""
+
+msgid "Startup banner and warnings."
+msgstr ""
+
+msgid "Syntax:"
+msgstr ""
+
+msgid "Syntax: Client header names delimited by spaces."
+msgstr ""
+
+msgid "Syntax: target_pattern http_parent[:port]"
+msgstr ""
+
+msgid "Syntax: target_pattern socks_proxy[:port] http_parent[:port]"
+msgstr ""
+
+msgid ""
+"The actions file(s) to use. Multiple actionsfile lines are permitted, and "
+"are in fact recommended!"
+msgstr ""
+
+msgid ""
+"The address and TCP port on which Privoxy will listen for client requests."
+msgstr ""
+
+msgid ""
+"The compression level that is passed to the zlib library when compressing "
+"buffered content."
+msgstr ""
+
+msgid ""
+"The currently installed 'privoxy' package is not supported by LuCI "
+"application."
+msgstr ""
+
+msgid ""
+"The directory where all logging takes place (i.e. where the logfile is "
+"located)."
+msgstr ""
+
+msgid "The directory where the other configuration files are located."
+msgstr ""
+
+msgid ""
+"The filter files contain content modification rules that use regular "
+"expressions."
+msgstr ""
+
+msgid "The hostname shown on the CGI pages."
+msgstr ""
+
+msgid "The log file to use. File name, relative to log directory."
+msgstr ""
+
+msgid "The order in which client headers are sorted before forwarding them."
+msgstr ""
+
+msgid ""
+"The status code Privoxy returns for pages blocked with +handle-as-empty-"
+"document."
+msgstr ""
+
+msgid ""
+"The trust mechanism is an experimental feature for building white-lists and "
+"should be used with care."
+msgstr ""
+
+msgid ""
+"The value of this option only matters if the experimental trust mechanism "
+"has been activated."
+msgstr ""
+
+msgid ""
+"This option is only there for debugging purposes. It will drastically reduce "
+"performance."
+msgstr ""
+
+msgid ""
+"This option will be removed in future releases as it has been obsoleted by "
+"the more general header taggers."
+msgstr ""
+
+msgid ""
+"This tab controls the security-relevant aspects of Privoxy's configuration."
+msgstr ""
+
+msgid ""
+"Through which SOCKS proxy (and optionally to which parent HTTP proxy) "
+"specific requests should be routed."
+msgstr ""
+
+msgid "To which parent HTTP proxy specific requests should be routed."
+msgstr ""
+
+msgid "User customizations"
+msgstr ""
+
+msgid "Value is not a number"
+msgstr ""
+
+msgid "Value not between 0 and 9"
+msgstr ""
+
+msgid "Value not between 1 and 4096"
+msgstr ""
+
+msgid "Value not greater 0 or empty"
+msgstr ""
+
+msgid "Value range 1 to 4096, no entry defaults to 4096"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "Version Information"
+msgstr ""
+
+msgid "Whether intercepted requests should be treated as valid."
+msgstr ""
+
+msgid ""
+"Whether or not Privoxy recognizes special HTTP headers to change toggle "
+"state."
+msgstr ""
+
+msgid "Whether or not buffered content is compressed before delivery."
+msgstr ""
+
+msgid ""
+"Whether or not outgoing connections that have been kept alive should be "
+"shared between different incoming connections."
+msgstr ""
+
+msgid "Whether or not pipelined requests should be served."
+msgstr ""
+
+msgid "Whether or not proxy authentication through Privoxy should work."
+msgstr ""
+
+msgid "Whether or not the web-based actions file editor may be used."
+msgstr ""
+
+msgid "Whether or not the web-based toggle feature may be used."
+msgstr ""
+
+msgid "Whether requests to Privoxy's CGI pages can be blocked or redirected."
+msgstr ""
+
+msgid ""
+"Whether the CGI interface should stay compatible with broken HTTP clients."
+msgstr ""
+
+msgid "Whether to run only one server thread."
+msgstr ""
+
+msgid "Who can access what."
+msgstr ""
+
+msgid "installed"
+msgstr ""
+
+msgid "or higher"
+msgstr ""
+
+msgid "required"
+msgstr ""
diff --git a/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy b/applications/luci-app-privoxy/root/etc/uci-defaults/luci-privoxy
new file mode 100755 (executable)
index 0000000..3405479
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# no longer needed for "Save and Apply" to restart privoxy
+# luci-app-privoxy calls /etc/init.d/privoxy reload
+uci -q batch <<-EOF >/dev/null
+       delete ucitrack.@privoxy[-1]
+       commit ucitrack
+EOF
+
+rm -f /tmp/luci-indexcache
+
+exit 0
index 4068cdb..0d73832 100644 (file)
@@ -1,23 +1,24 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
+-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
 local uci = require "luci.model.uci".cursor()
 local bit = require "nixio".bit
+local ip  = require "luci.ip"
 
 -------------------- Init --------------------
 
 --
 -- Find link-local address
 --
-LL_PREFIX = luci.ip.IPv6("fe80::/64")
 function find_ll()
-       for _, r in ipairs(luci.sys.net.routes6()) do
-               if LL_PREFIX:contains(r.dest) and r.dest:higher(LL_PREFIX) then
-                       return r.dest:sub(LL_PREFIX)
+       local _, r
+       for _, r in ipairs(ip.routes({ family = 6, dest = "fe80::/64" })) do
+               if r.dest:higher("fe80:0:0:0:ff:fe00:0:0") then
+                       return (r.dest - "fe80::")
                end
        end
-       return luci.ip.IPv6("::")
+       return ip.IPv6("::")
 end
 
 --
@@ -33,15 +34,15 @@ local ipv4_netsz  = uci:get("siit", "ipv4", "netsize")     or "24"
 --
 -- Find IPv4 allocation pool
 --
-local gv4_net = luci.ip.IPv4(ipv4_pool)
+local gv4_net = ip.IPv4(ipv4_pool)
 
 --
 -- Generate ULA
 --
-local ula = luci.ip.IPv6("::/64")
+local ula = ip.IPv6("::/64")
 
 for _, prefix in ipairs({ ula_prefix, ula_global, ula_subnet }) do
-       ula = ula:add(luci.ip.IPv6(prefix))
+       ula = ula:add(ip.IPv6(prefix))
 end
 
 ula = ula:add(find_ll())
@@ -72,7 +73,7 @@ uci:foreach("wireless", "wifi-device",
 lanip = f:field(Value, "ipaddr", "LAN IPv4 subnet")
 function lanip.formvalue(self, section)
        local val = self.map:formvalue(self:cbid(section))
-       local net = luci.ip.IPv4("%s/%i" %{ val, ipv4_netsz })
+       local net = ip.IPv4("%s/%i" %{ val, ipv4_netsz })
 
        if net then
                if gv4_net:contains(net) then
@@ -110,7 +111,7 @@ function mode.write(self, section, value)
        --
        -- Find LAN IPv4 range
        --
-       local lan_net = luci.ip.IPv4(
+       local lan_net = ip.IPv4(
                ( lanip:formvalue(section) or "172.16.0.1" ) .. "/" .. ipv4_netsz
        )
 
@@ -182,7 +183,7 @@ function mode.write(self, section, value)
                })
 
                -- use full siit subnet
-               siit_route = luci.ip.IPv6(siit_prefix .. "/96")
+               siit_route = ip.IPv6(siit_prefix .. "/96")
 
                -- v4 <-> siit route
                uci:delete_all("network", "route",
@@ -212,7 +213,7 @@ function mode.write(self, section, value)
                })
 
                -- derive siit subnet from lan config
-               siit_route = luci.ip.IPv6(
+               siit_route = ip.IPv6(
                        siit_prefix .. "/" .. (96 + lan_net:prefix())
                ):add(lan_net[2])
 
@@ -301,7 +302,7 @@ function mode.write(self, section, value)
 
        -- siit0 route
        uci:delete_all("network", "route6",
-               function(s) return siit_route:contains(luci.ip.IPv6(s.target)) end)
+               function(s) return siit_route:contains(ip.IPv6(s.target)) end)
 
        uci:section("network", "route6", nil, {
                interface = "siit0",
index 97d0400..4add435 100644 (file)
@@ -23,14 +23,26 @@ function index()
        page.leaf   = true
 end
 
+function ip_to_mac(ip)
+       local ipc = require "luci.ip"
+       local i, n
+
+       for i, n in ipairs(ipc.neighbors()) do
+               if n.mac and n.dest and n.dest:equal(ip) then
+                       return n.mac
+               end
+       end
+end
+
 function action_dispatch()
        local uci = luci.model.uci.cursor_state()
-       local mac = luci.sys.net.ip4mac(luci.http.getenv("REMOTE_ADDR")) or ""
+       local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR")) or ""
        local access = false
 
        uci:foreach("luci_splash", "lease", function(s)
                if s.mac and s.mac:lower() == mac then access = true end
        end)
+
        uci:foreach("luci_splash", "whitelist", function(s)
                if s.mac and s.mac:lower() == mac then access = true end
        end)
@@ -51,13 +63,13 @@ function blacklist()
 end
 
 function action_activate()
-       local ip = luci.http.getenv("REMOTE_ADDR") or "127.0.0.1"
-       local mac = luci.sys.net.ip4mac(ip:match("^[\[::ffff:]*(%d+.%d+%.%d+%.%d+)\]*$"))
+       local ipc = require "luci.ip"
+       local mac = ip_to_mac(luci.http.getenv("REMOTE_ADDR") or "127.0.0.1") or ""
        local uci_state = require "luci.model.uci".cursor_state()
        local blacklisted = false
        if mac and luci.http.formvalue("accept") then
                uci:foreach("luci_splash", "blacklist",
-                       function(s) if s.mac:lower() == mac or s.mac == mac then blacklisted = true end
+                       function(s) if s.mac and s.mac:lower() == mac then blacklisted = true end
                end)
                if blacklisted then     
                        luci.http.redirect(luci.dispatcher.build_url("splash" ,"blocked"))
index 67bb2fc..831fa75 100644 (file)
@@ -9,6 +9,7 @@ local utl = require "luci.util"
 local ipt = require "luci.sys.iptparser".IptParser()
 local uci = require "luci.model.uci".cursor_state()
 local wat = require "luci.tools.webadmin"
+local ipc = require "luci.ip"
 local fs  = require "nixio.fs"
 
 local clients = { }
@@ -100,10 +101,12 @@ if fs.access(leasefile) then
        end
 end
 
-for i, a in ipairs(luci.sys.net.arptable()) do
-       local c = clients[a["HW address"]:lower()]
-       if c and not c.ip then
-               c.ip = a["IP address"]
+for i, n in ipairs(ipc.neighbors({ family = 4 })) do
+       if n.mac and n.dest then
+               local c = clients[n.mac]
+               if c and not c.ip then
+                       c.ip = n.dest:string()
+               end
        end
 end
 
index 0f8bdc2..e566e9b 100755 (executable)
@@ -2,14 +2,11 @@
 
 utl = require "luci.util"
 sys = require "luci.sys"
+ipc = require "luci.ip"
 
-require("luci.model.uci")
-require("luci.sys.iptparser")
 
 -- Init state session
-local uci = luci.model.uci.cursor_state()
-local ipt = luci.sys.iptparser.IptParser()
-local net = sys.net
+local uci = require "luci.model.uci".cursor_state()
 local fs = require "nixio.fs"
 local ip = require "luci.ip"
 
@@ -139,6 +136,30 @@ function ipvalid(ipaddr)
        return false
 end
 
+function mac_to_ip(mac)
+       ipc.neighbors({ family = 4 }, function(n)
+               if n.mac == mac and n.dest then
+                       return n.dest:string()
+               end
+       end)
+end
+
+function mac_to_dev(mac)
+       ipc.neighbors({ family = 4 }, function(n)
+               if n.mac == mac and n.dev then
+                       return n.dev
+               end
+       end)
+end
+
+function ip_to_mac(ip)
+       ipc.neighbors({ family = 4 }, function(n)
+               if n.mac and n.dest and n.dest:equal(ip) then
+                       return n.mac
+               end
+       end)
+end
+
 function main(argv)
        local cmd = table.remove(argv, 1)
        local arg = argv[1]
@@ -157,7 +178,6 @@ function main(argv)
 
                lock()
 
-               local arp_cache      = net.arptable()
                local leased_macs    = get_known_macs("lease")
                local blacklist_macs = get_known_macs("blacklist")
                local whitelist_macs = get_known_macs("whitelist")
@@ -167,17 +187,12 @@ function main(argv)
                        if adr:find(":") then
                                mac = adr:lower()
                        else
-                               for _, e in ipairs(arp_cache) do
-                                       if e["IP address"] == adr then
-                                               mac = e["HW address"]:lower()
-                                               break
-                                       end
-                               end
+                               mac = ip_to_mac(adr)
                        end
 
                        if mac and cmd == "add-rules" then
                                if leased_macs[mac] then
-                                       add_lease(mac, arp_cache, true)
+                                       add_lease(mac, true)
                                elseif blacklist_macs[mac] then
                                        add_blacklist_rule(mac)
                                elseif whitelist_macs[mac] then
@@ -277,15 +292,6 @@ function main(argv)
        end
 end
 
--- Get current arp cache
-function get_arpcache()
-       local arpcache = { }
-       for _, entry in ipairs(net.arptable()) do
-               arpcache[entry["HW address"]:lower()] = { entry["Device"]:lower(), entry["IP address"]:lower() }
-       end
-       return arpcache
-end
-
 -- Get a list of known mac addresses
 function get_known_macs(list)
        local leased_macs = { }
@@ -355,17 +361,11 @@ function convert_mac_to_secname(mac)
 end
 
 -- Add a lease to state and invoke add_rule
-function add_lease(mac, arp, no_uci)
+function add_lease(mac, no_uci)
        mac = mac:lower()
 
        -- Get current ip address
-       local ipaddr
-       for _, entry in ipairs(arp or net.arptable()) do
-               if entry["HW address"]:lower() == mac then
-                       ipaddr = entry["IP address"]
-                       break
-               end
-       end
+       local ipaddr = mac_to_ip(mac)
 
        -- Add lease if there is an ip addr
        if ipaddr then
@@ -598,8 +598,6 @@ function sync()
        uci:revert("luci_splash_leases")
 
         
-       local arpcache = get_arpcache()
-
        local blackwhitelist = uci:get_all("luci_splash")
        local whitelist_total = 0
        local whitelist_online = 0
@@ -618,7 +616,7 @@ function sync()
                                leasecount = leasecount + 1
 
                                 -- only count leases_online for connected clients
-                                if arpcache[v.mac] then
+                               if mac_to_ip(v.mac) then
                                        leases_online = leases_online + 1
                                 end
 
@@ -643,7 +641,7 @@ function sync()
                        whitelist_total = whitelist_total + 1
                        if s.mac then
                                local mac = s.mac:lower()
-                               if arpcache[mac] then
+                               if mac_to_ip(mac) then
                                        whitelist_online = whitelist_online + 1
                                end
                        end
@@ -652,7 +650,7 @@ function sync()
                        blacklist_total = blacklist_total + 1
                        if s.mac then
                                local mac = s.mac:lower()
-                               if arpcache[mac] then
+                               if mac_to_ip(mac) then
                                        blacklist_online = blacklist_online + 1
                                end
                        end
@@ -693,7 +691,6 @@ end
 
 -- Show client info
 function list()
-       local arpcache = get_arpcache()
        -- Find traffic usage
        local function traffic(lease)
                local traffic_in  = 0
@@ -722,12 +719,11 @@ function list()
                if s[".type"] == "lease" and s.mac then
                        local ti, to = traffic(s)
                        local mac = s.mac:lower()
-                       local arp = arpcache[mac]
                        print(string.format(
                                "%-17s  %-15s  %-9s  %3dm  %-7s  %7dKB  %7dKB",
                                mac, s.ipaddr, "leased",
                                math.floor(( os.time() - tonumber(s.start) ) / 60),
-                               arp and arp[1] or "?", ti, to
+                               mac_to_dev(mac) or "?", ti, to
                        ))
                end
        end
@@ -738,11 +734,10 @@ function list()
        ) do
                if (s[".type"] == "whitelist" or s[".type"] == "blacklist") and s.mac then
                        local mac = s.mac:lower()
-                       local arp = arpcache[mac]
                        print(string.format(
                                "%-17s  %-15s  %-9s  %4s  %-7s  %9s  %9s",
-                               mac, arp and arp[2] or "?", s[".type"],
-                               "- ", arp and arp[1] or "?", "-", "-"
+                               mac, mac_to_ip(mac) or "?", s[".type"],
+                               "- ", mac_to_dev(mac) or "?", "-", "-"
                        ))
                end
        end
diff --git a/build/luadoc/doc.lua b/build/luadoc/doc.lua
new file mode 100755 (executable)
index 0000000..383dde2
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/env lua
+-------------------------------------------------------------------------------
+-- LuaDoc launcher.
+-- @release $Id: luadoc.lua.in,v 1.1 2008/02/17 06:42:51 jasonsantos Exp $
+-------------------------------------------------------------------------------
+
+--local source = debug.getinfo(1).source or ""
+--local mypath = source:match("@(.+)/[^/]+")
+
+--package.path = package.path .. ";" .. mypath .. "/?.lua;" .. mypath .. "/?/init.lua"
+
+require "luadoc.init"
+
+-------------------------------------------------------------------------------
+-- Print version number.
+
+local function print_version ()
+       print (string.format("%s\n%s\n%s", 
+               luadoc._VERSION, 
+               luadoc._DESCRIPTION, 
+               luadoc._COPYRIGHT))
+end
+
+-------------------------------------------------------------------------------
+-- Print usage message.
+
+local function print_help ()
+       print ("Usage: "..arg[0]..[[ [options|files]
+Generate documentation from files. Available options are:
+  -d path                      output directory path
+  -t path                      template directory path
+  -h, --help                   print this help and exit
+      --noindexpage            do not generate global index page
+      --nofiles                do not generate documentation for files
+      --nomodules              do not generate documentation for modules
+      --doclet doclet_module   doclet module to generate output
+      --taglet taglet_module   taglet module to parse input code
+  -q, --quiet                  suppress all normal output
+  -v, --version                print version information]])
+end
+
+local function off_messages (arg, i, options)
+       options.verbose = nil
+end
+
+-------------------------------------------------------------------------------
+-- Process options. TODO: use getopts.
+-- @class table
+-- @name OPTIONS
+
+local OPTIONS = {
+       d = function (arg, i, options)
+               local dir = arg[i+1]
+               if string.sub (dir, -2) ~= "/" then
+                       dir = dir..'/'
+               end
+               options.output_dir = dir
+               return 1
+       end,
+       t = function (arg, i, options)
+               local dir = arg[i+1]
+               if string.sub (dir, -2) ~= "/" then
+                       dir = dir..'/'
+               end
+               options.template_dir = dir
+               return 1
+       end,
+       h = print_help,
+       help = print_help,
+       q = off_messages,
+       quiet = off_messages,
+       v = print_version,
+       version = print_version,
+       doclet = function (arg, i, options)
+               options.doclet = arg[i+1]
+               return 1
+       end,
+       taglet = function (arg, i, options)
+               options.taglet = arg[i+1]
+               return 1
+       end,
+}
+
+-------------------------------------------------------------------------------
+
+local function process_options (arg)
+       local files = {}
+       local options = require "luadoc.config"
+       local i = 1
+       while i <= #arg do
+               local argi = arg[i]
+               if string.sub (argi, 1, 1) ~= '-' then
+                       table.insert (files, argi)
+               else
+                       local opt = string.sub (argi, 2)
+                       if string.sub (opt, 1, 1) == '-' then
+                               opt = string.gsub (opt, "%-", "")
+                       end
+                       if OPTIONS[opt] then
+                               if OPTIONS[opt] (arg, i, options) then
+                                       i = i + 1
+                               end
+                       else
+                               options[opt] = 1
+                       end
+               end
+               i = i+1
+       end
+       return files, options
+end 
+
+-------------------------------------------------------------------------------
+-- Main function. Process command-line parameters and call luadoc processor.
+
+function main (arg)
+       -- Process options
+       local argc = #arg
+       if argc < 1 then
+               print_help ()
+               return
+       end
+       local files, options = process_options (arg)
+       return luadoc.main(files, options)
+end
+
+main(arg)
diff --git a/build/luadoc/luadoc/config.lua b/build/luadoc/luadoc/config.lua
new file mode 100644 (file)
index 0000000..9e4b9de
--- /dev/null
@@ -0,0 +1,34 @@
+-------------------------------------------------------------------------------
+-- LuaDoc configuration file. This file contains the default options for 
+-- luadoc operation. These options can be overriden by the command line tool
+-- @see luadoc.print_help
+-- @release $Id: config.lua,v 1.6 2007/04/18 14:28:39 tomas Exp $
+-------------------------------------------------------------------------------
+
+module "luadoc.config"
+
+-------------------------------------------------------------------------------
+-- Default options
+-- @class table
+-- @name default_options
+-- @field output_dir default output directory for generated documentation, used
+-- by several doclets
+-- @field taglet parser used to analyze source code input
+-- @field doclet documentation generator
+-- @field template_dir directory with documentation templates, used by the html
+-- doclet
+-- @field verbose command line tool configuration to output processing 
+-- information
+
+local default_options = {
+       output_dir = "",
+       taglet = "luadoc.taglet.standard",
+       doclet = "luadoc.doclet.html",
+       -- TODO: find a way to define doclet specific options
+       template_dir = "luadoc/doclet/html/",
+       nomodules = false,
+       nofiles = false,
+       verbose = true,
+}
+
+return default_options
diff --git a/build/luadoc/luadoc/doclet/debug.lua b/build/luadoc/luadoc/doclet/debug.lua
new file mode 100644 (file)
index 0000000..0b75f84
--- /dev/null
@@ -0,0 +1,46 @@
+-----------------------------------------------------------------
+-- LuaDoc debugging facilities.
+-- @release $Id: debug.lua,v 1.3 2007/04/18 14:28:39 tomas Exp $
+-----------------------------------------------------------------
+
+module "luadoc.doclet.debug"
+
+function printline()
+       print(string.rep('-', 79))
+end
+
+-----------------------------------------------------------------
+-- Print debug information about document
+-- @param doc Table with the structured documentation.
+
+function start (doc)
+       print("Files:")
+       for _, filepath in ipairs(doc.files) do
+               print('\t', filepath)
+       end
+       printline()
+
+       print("Modules:")
+       for _, modulename in ipairs(doc.modules) do
+               print('\t', modulename)
+       end
+       printline()
+       
+       for i, v in pairs(doc.files) do
+               print('\t', i, v)
+       end
+       printline()
+       for i, v in pairs(doc.files[doc.files[1]]) do
+               print(i, v)
+       end
+       
+       printline()
+       for i, v in pairs(doc.files[doc.files[1]].doc[1]) do
+               print(i, v)
+       end
+       printline()
+       print("Params")
+       for i, v in pairs(doc.files[doc.files[1]].doc[1].param) do
+               print(i, v)
+       end
+end
diff --git a/build/luadoc/luadoc/doclet/formatter.lua b/build/luadoc/luadoc/doclet/formatter.lua
new file mode 100644 (file)
index 0000000..2d72538
--- /dev/null
@@ -0,0 +1,84 @@
+-------------------------------------------------------------------------------
+-- Doclet to format source code according to LuaDoc standard tags. This doclet
+-- (re)write .lua files adding missing standard tags. Texts are formatted to
+-- 80 columns and function parameters are added based on code analysis.
+--
+-- @release $Id: formatter.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $
+-------------------------------------------------------------------------------
+
+local util = require "luadoc.util"
+local assert, ipairs, pairs, type = assert, ipairs, pairs, type
+local string = require"string"
+local table = require"table"
+
+module "luadoc.doclet.formatter"
+
+options = {
+       output_dir = "./",
+}
+
+-------------------------------------------------------------------------------
+-- Assembly the output filename for an input file.
+-- TODO: change the name of this function
+function out_file (filename)
+       local h = filename
+       h = options.output_dir..h
+       return h
+end
+
+-------------------------------------------------------------------------------
+-- Generate a new lua file for each input lua file. If the user does not 
+-- specify a different output directory input files will be rewritten.
+-- @param doc documentation table
+
+function start (doc)
+       local todo = "<TODO>"
+       
+       -- Process files
+       for i, file_doc in ipairs(doc.files) do
+               -- assembly the filename
+               local filename = out_file(file_doc.name)
+               luadoc.logger:info(string.format("generating file `%s'", filename))
+
+               -- TODO: confirm file overwrite
+               local f = posix.open(filename, "w")
+               assert(f, string.format("could not open `%s' for writing", filename))
+
+               for _, block in ipairs(file_doc.doc) do
+
+                       -- write reorganized comments
+                       f:write(string.rep("-", 80).."\n")
+                       
+                       -- description
+                       f:write(util.comment(util.wrap(block.description, 77)))
+                       f:write("\n")
+                       
+                       if block.class == "function" then
+                               -- parameters
+                               table.foreachi(block.param, function (_, param_name)
+                                       f:write(util.comment(util.wrap(string.format("@param %s %s", param_name, block.param[param_name] or todo), 77)))
+                                       f:write("\n")
+                               end)
+                               
+                               -- return
+                               if type(block.ret) == "table" then
+                                       table.foreachi(block.ret, function (_, ret)
+                                               f:write(util.comment(util.wrap(string.format("@return %s", ret), 77)).."\n")
+                                       end)
+                               else
+                                       f:write(util.comment(util.wrap(string.format("@return %s", block.ret or todo), 77)).."\n")
+                               end
+                       end
+                       
+                       -- TODO: usage
+                       -- TODO: see
+
+                       -- write code
+                       for _, line in ipairs(block.code) do
+                               f:write(line.."\n")
+                       end
+               end
+               
+               f:close()
+       end
+end
diff --git a/build/luadoc/luadoc/doclet/html.lua b/build/luadoc/luadoc/doclet/html.lua
new file mode 100644 (file)
index 0000000..e77fb74
--- /dev/null
@@ -0,0 +1,275 @@
+-------------------------------------------------------------------------------
+-- Doclet that generates HTML output. This doclet generates a set of html files
+-- based on a group of templates. The main templates are:
+-- <ul>
+-- <li>index.lp: index of modules and files;</li>
+-- <li>file.lp: documentation for a lua file;</li>
+-- <li>module.lp: documentation for a lua module;</li>
+-- <li>function.lp: documentation for a lua function. This is a
+-- sub-template used by the others.</li>
+-- </ul>
+--
+-- @release $Id: html.lua,v 1.29 2007/12/21 17:50:48 tomas Exp $
+-------------------------------------------------------------------------------
+
+local assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type = assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type
+local io = require"io"
+local posix = require "nixio.fs"
+local lp = require "luadoc.lp"
+local luadoc = require"luadoc"
+local package = package
+local string = require"string"
+local table = require"table"
+
+module "luadoc.doclet.html"
+
+-------------------------------------------------------------------------------
+-- Looks for a file `name' in given path. Removed from compat-5.1
+-- @param path String with the path.
+-- @param name String with the name to look for.
+-- @return String with the complete path of the file found
+--     or nil in case the file is not found.
+
+local function search (path, name)
+  for c in string.gfind(path, "[^;]+") do
+    c = string.gsub(c, "%?", name)
+    local f = io.open(c)
+    if f then   -- file exist?
+      f:close()
+      return c
+    end
+  end
+  return nil    -- file not found
+end
+
+-------------------------------------------------------------------------------
+-- Include the result of a lp template into the current stream.
+
+function include (template, env)
+       -- template_dir is relative to package.path
+       local templatepath = options.template_dir .. template
+
+       -- search using package.path (modified to search .lp instead of .lua
+       local search_path = string.gsub(package.path, "%.lua", "")
+       local templatepath = search(search_path, templatepath)
+       assert(templatepath, string.format("template `%s' not found", template))
+
+       env = env or {}
+       env.table = table
+       env.io = io
+       env.lp = lp
+       env.ipairs = ipairs
+       env.tonumber = tonumber
+       env.tostring = tostring
+       env.type = type
+       env.luadoc = luadoc
+       env.options = options
+
+       return lp.include(templatepath, env)
+end
+
+-------------------------------------------------------------------------------
+-- Returns a link to a html file, appending "../" to the link to make it right.
+-- @param html Name of the html file to link to
+-- @return link to the html file
+
+function link (html, from)
+       local h = html
+       from = from or ""
+       string.gsub(from, "/", function () h = "../" .. h end)
+       return h
+end
+
+-------------------------------------------------------------------------------
+-- Returns the name of the html file to be generated from a module.
+-- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
+-- @param modulename Name of the module to be processed, may be a .lua file or
+-- a .luadoc file.
+-- @return name of the generated html file for the module
+
+function module_link (modulename, doc, from)
+       -- TODO: replace "." by "/" to create directories?
+       -- TODO: how to deal with module names with "/"?
+       assert(modulename)
+       assert(doc)
+       from = from or ""
+
+       if doc.modules[modulename] == nil then
+--             logger:error(string.format("unresolved reference to module `%s'", modulename))
+               return
+       end
+
+       local href = "modules/" .. modulename .. ".html"
+       string.gsub(from, "/", function () href = "../" .. href end)
+       return href
+end
+
+-------------------------------------------------------------------------------
+-- Returns the name of the html file to be generated from a lua(doc) file.
+-- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
+-- @param to Name of the file to be processed, may be a .lua file or
+-- a .luadoc file.
+-- @param from path of where am I, based on this we append ..'s to the
+-- beginning of path
+-- @return name of the generated html file
+
+function file_link (to, from)
+       assert(to)
+       from = from or ""
+
+       local href = to
+       href = string.gsub(href, "lua$", "html")
+       href = string.gsub(href, "luadoc$", "html")
+       href = "files/" .. href
+       string.gsub(from, "/", function () href = "../" .. href end)
+       return href
+end
+
+-------------------------------------------------------------------------------
+-- Returns a link to a function or to a table
+-- @param fname name of the function or table to link to.
+-- @param doc documentation table
+-- @param kind String specying the kinf of element to link ("functions" or "tables").
+
+function link_to (fname, doc, module_doc, file_doc, from, kind)
+       assert(fname)
+       assert(doc)
+       from = from or ""
+       kind = kind or "functions"
+
+       if file_doc then
+               for _, func_name in pairs(file_doc[kind]) do
+                       if func_name == fname then
+                               return file_link(file_doc.name, from) .. "#" .. fname
+                       end
+               end
+       end
+
+       if module_doc and module_doc[kind] then
+               for func_name, tbl in pairs(module_doc[kind]) do
+                       if func_name == fname then
+                               return "#" .. fname
+                       end
+               end
+       end
+
+       local _, _, modulename, fname = string.find(fname, "^(.-)[%.%:]?([^%.%:]*)$")
+       assert(fname)
+
+       -- if fname does not specify a module, use the module_doc
+       if string.len(modulename) == 0 and module_doc then
+               modulename = module_doc.name
+       end
+
+       local module_doc = doc.modules[modulename]
+       if not module_doc then
+--             logger:error(string.format("unresolved reference to function `%s': module `%s' not found", fname, modulename))
+               return
+       end
+
+       for _, func_name in pairs(module_doc[kind]) do
+               if func_name == fname then
+                       return module_link(modulename, doc, from) .. "#" .. fname
+               end
+       end
+
+--     logger:error(string.format("unresolved reference to function `%s' of module `%s'", fname, modulename))
+end
+
+-------------------------------------------------------------------------------
+-- Make a link to a file, module or function
+
+function symbol_link (symbol, doc, module_doc, file_doc, from)
+       assert(symbol)
+       assert(doc)
+
+       local href =
+--             file_link(symbol, from) or
+               module_link(symbol, doc, from) or
+               link_to(symbol, doc, module_doc, file_doc, from, "functions") or
+               link_to(symbol, doc, module_doc, file_doc, from, "tables")
+
+       if not href then
+               logger:error(string.format("unresolved reference to symbol `%s'", symbol))
+       end
+
+       return href or ""
+end
+
+-------------------------------------------------------------------------------
+-- Assembly the output filename for an input file.
+-- TODO: change the name of this function
+function out_file (filename)
+       local h = filename
+       h = string.gsub(h, "lua$", "html")
+       h = string.gsub(h, "luadoc$", "html")
+       h = "files/" .. h
+--     h = options.output_dir .. string.gsub (h, "^.-([%w_]+%.html)$", "%1")
+       h = options.output_dir .. h
+       return h
+end
+
+-------------------------------------------------------------------------------
+-- Assembly the output filename for a module.
+-- TODO: change the name of this function
+function out_module (modulename)
+       local h = modulename .. ".html"
+       h = "modules/" .. h
+       h = options.output_dir .. h
+       return h
+end
+
+-----------------------------------------------------------------
+-- Generate the output.
+-- @param doc Table with the structured documentation.
+
+function start (doc)
+       -- Generate index file
+       if (#doc.files > 0 or #doc.modules > 0) and (not options.noindexpage) then
+               local filename = options.output_dir.."index.html"
+               logger:info(string.format("generating file `%s'", filename))
+               local f = posix.open(filename, "w")
+               assert(f, string.format("could not open `%s' for writing", filename))
+               io.output(f)
+               include("index.lp", { doc = doc })
+               f:close()
+       end
+
+       -- Process modules
+       if not options.nomodules then
+               for _, modulename in ipairs(doc.modules) do
+                       local module_doc = doc.modules[modulename]
+                       -- assembly the filename
+                       local filename = out_module(modulename)
+                       logger:info(string.format("generating file `%s'", filename))
+
+                       local f = posix.open(filename, "w")
+                       assert(f, string.format("could not open `%s' for writing", filename))
+                       io.output(f)
+                       include("module.lp", { doc = doc, module_doc = module_doc })
+                       f:close()
+               end
+       end
+
+       -- Process files
+       if not options.nofiles then
+               for _, filepath in ipairs(doc.files) do
+                       local file_doc = doc.files[filepath]
+                       -- assembly the filename
+                       local filename = out_file(file_doc.name)
+                       logger:info(string.format("generating file `%s'", filename))
+
+                       local f = posix.open(filename, "w")
+                       assert(f, string.format("could not open `%s' for writing", filename))
+                       io.output(f)
+                       include("file.lp", { doc = doc, file_doc = file_doc} )
+                       f:close()
+               end
+       end
+
+       -- copy extra files
+       local f = posix.open(options.output_dir.."luadoc.css", "w")
+       io.output(f)
+       include("luadoc.css")
+       f:close()
+end
diff --git a/build/luadoc/luadoc/doclet/html/constant.lp b/build/luadoc/luadoc/doclet/html/constant.lp
new file mode 100644 (file)
index 0000000..2e35392
--- /dev/null
@@ -0,0 +1,28 @@
+<%
+if module_doc then
+       from = "modules/"..module_doc.name
+elseif file_doc then
+       from = "files/.."..file_doc.name
+else
+       from = ""
+end
+%>
+
+<dt><%=const.private and "local " or ""%><a name="<%=const.name%>"></a><strong><%=const.name:gsub(".+%.","")%></strong></dt>
+<dd>
+<%=const.description or ""%>
+
+<%if type(const.see) == "string" then %>
+<h3>See also:</h3>
+       <a href="<%=const.see%>"><%=const.see%></a>
+<%elseif type(const.see) == "table" and #const.see > 0 then %>
+<h3>See also:</h3>
+<ul>
+       <%for i = 1, #const.see do%>
+       <li><a href="<%=luadoc.doclet.html.symbol_link(const.see[i], doc, module_doc, file_doc, from)%>">
+               <%=const.see[i]:gsub(".+%.","")%>
+       </a>
+       <%end%>
+</ul
+<%end%>
+</dd>
diff --git a/build/luadoc/luadoc/doclet/html/file.lp b/build/luadoc/luadoc/doclet/html/file.lp
new file mode 100644 (file)
index 0000000..68f4864
--- /dev/null
@@ -0,0 +1,112 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>Reference</title>
+    <link rel="stylesheet" href="<%=luadoc.doclet.html.link('luadoc.css', 'files/'..file_doc.name)%>" type="text/css" />
+       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
+</head>
+
+<body>
+<div id="container">
+
+<div id="product">
+       <div id="product_logo"></div>
+       <div id="product_name"><big><b></b></big></div>
+       <div id="product_description"></div>
+</div> <!-- id="product" -->
+
+<div id="main">
+
+<div id="navigation">
+<%=luadoc.doclet.html.include("menu.lp", { doc=doc, file_doc=file_doc })%>
+
+</div> <!-- id="navigation" -->
+
+<div id="content">
+
+<h1>File <code><%=file_doc.name%></code></h1>
+
+<%if file_doc.description then%>
+<p><%=file_doc.description%></p>
+<%end%>
+<%if file_doc.author then%>
+<p><b><%= #file_doc.author>1 and "Authors" or "Author" %>:</b>
+<table class="authors_list">
+<%for _, author in ipairs(file_doc.author) do%>
+       <tr><td class="name"><%= author %></td></tr>
+<%end%>
+</table>
+</p>
+<%end%>
+<%if file_doc.copyright then%>
+<p>Copyright &copy;<%=file_doc.copyright%></p>
+<%end%>
+<%if file_doc.release then%>
+<p><small><b>Release:</b> <%=file_doc.release%></small></p>
+<%end%>
+
+<%if #file_doc.functions > 0 then%>
+<h2>Functions</h2>
+<table class="function_list">
+<%for _, func_name in ipairs(file_doc.functions) do
+  local func_data = file_doc.functions[func_name]%>
+       <tr>
+       <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_name%></a>&nbsp;(<%=table.concat(func_data.param or {}, ", ")%>)</td>
+       <td class="summary"><%=func_data.summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<%if #file_doc.tables > 0 then%>
+<h2>Tables</h2>
+<table class="table_list">
+<%for _, tab_name in ipairs(file_doc.tables) do%>
+       <tr>
+       <td class="name" nowrap><a href="#<%=tab_name%>"><%=tab_name%></a></td>
+       <td class="summary"><%=file_doc.tables[tab_name].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<br/>
+<br/>
+
+
+
+<%if #file_doc.functions > 0 then%>
+<h2><a name="functions"></a>Functions</h2>
+<dl class="function">
+<%for _, func_name in ipairs(file_doc.functions) do%>
+<%=luadoc.doclet.html.include("function.lp", { doc=doc, file_doc=file_doc, func=file_doc.functions[func_name] })%>
+<%end%>
+</dl>
+<%end%>
+
+
+<%if #file_doc.tables > 0 then%>
+<h2><a name="tables"></a>Tables</h2>
+<dl class="table">
+<%for _, tab_name in ipairs(file_doc.tables) do%>
+<%=luadoc.doclet.html.include("table.lp", { doc=doc, file_doc=file_doc, tab=file_doc.tables[tab_name] })%>
+<%end%>
+</dl>
+<%end%>
+
+
+
+</div> <!-- id="content" -->
+
+</div> <!-- id="main" -->
+
+<div id="about">
+       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" --> 
+</body>
+</html>
diff --git a/build/luadoc/luadoc/doclet/html/function.lp b/build/luadoc/luadoc/doclet/html/function.lp
new file mode 100644 (file)
index 0000000..29d403e
--- /dev/null
@@ -0,0 +1,64 @@
+<%
+if module_doc then
+       from = "modules/"..module_doc.name
+elseif file_doc then
+       from = "files/.."..file_doc.name
+else
+       from = ""
+end
+%>
+
+<dt><%=func.private and "local " or ""%><a name="<%=func.name%>"></a><strong><%=func.printname%></strong>&nbsp;(<%=table.concat(func.param or {}, ", ")%>)</dt>
+<dd>
+<%=func.description or ""%>
+
+<%if type(func.param) == "table" and #func.param > 0 then%>
+<h3>Parameters</h3>
+<ul>
+       <%for p = 1, #func.param do%>
+       <li>
+         <%=func.param[p]%>: <%=func.param[func.param[p]] or ""%>
+       </li>
+       <%end%>
+</ul>
+<%end%>
+
+
+<%if type(func.usage) == "string" then%>
+<h3>Usage:</h3>
+<%=func.usage%>
+<%elseif type(func.usage) == "table" then%>
+<h3>Usage</h3>
+<ul>
+       <%for _, usage in ipairs(func.usage) do%>
+       <li><%= usage %>
+       <%end%>
+</ul>
+<%end%>
+
+<%if type(func.ret) == "string" then%>
+<h3>Return value:</h3>
+<%=func.ret%>
+<%elseif type(func.ret) == "table" then%>
+<h3>Return values:</h3>
+<ol>
+       <%for _, ret in ipairs(func.ret) do%>
+       <li><%= ret %>
+       <%end%>
+</ol>
+<%end%>
+
+<%if type(func.see) == "string" then %>
+<h3>See also:</h3>
+       <a href="<%=func.see%>"><%=func.see%></a>
+<%elseif type(func.see) == "table" and #func.see > 0 then %>
+<h3>See also:</h3>
+<ul>
+       <%for i = 1, #func.see do%>
+       <li><a href="<%=luadoc.doclet.html.symbol_link(func.see[i], doc, module_doc, file_doc, from)%>">
+               <%=(oop and func.see[i]:gsub("%.",":") or func.see[i]:gsub(".+%.",""))%>
+       </a>
+       <%end%>
+</ul>
+<%end%>
+</dd>
diff --git a/build/luadoc/luadoc/doclet/html/index.lp b/build/luadoc/luadoc/doclet/html/index.lp
new file mode 100644 (file)
index 0000000..b4b9f5c
--- /dev/null
@@ -0,0 +1,67 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>Reference</title>
+    <link rel="stylesheet" href="<%=luadoc.doclet.html.link("luadoc.css")%>" type="text/css" />
+       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
+</head>
+
+<body>
+<div id="container">
+
+<div id="product">
+       <div id="product_logo"></div>
+       <div id="product_name"><big><b></b></big></div>
+       <div id="product_description"></div>
+</div> <!-- id="product" -->
+
+<div id="main">
+
+<div id="navigation">
+<%=luadoc.doclet.html.include("menu.lp", { doc=doc })%>
+
+</div> <!-- id="navigation" -->
+
+<div id="content">
+
+
+<%if not options.nomodules and #doc.modules > 0 then%>
+<h2>Modules</h2>
+<table class="module_list">
+<!--<tr><td colspan="2">Modules</td></tr>-->
+<%for _, modulename in ipairs(doc.modules) do%>
+       <tr>
+               <td class="name"><a href="<%=luadoc.doclet.html.module_link(modulename, doc)%>"><%=modulename%></a></td>
+               <td class="summary"><%=doc.modules[modulename].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+
+<%if not options.nofiles and #doc.files > 0 then%>
+<h2>Files</h2>
+<table class="file_list">
+<!--<tr><td colspan="2">Files</td></tr>-->
+<%for _, filepath in ipairs(doc.files) do%>
+       <tr>
+               <td class="name"><a href="<%=luadoc.doclet.html.file_link(filepath)%>"><%=filepath%></a></td>
+               <td class="summary"></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+</div> <!-- id="content" -->
+
+</div> <!-- id="main" -->
+
+<div id="about">
+       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" --> 
+</body>
+</html>
diff --git a/build/luadoc/luadoc/doclet/html/luadoc.css b/build/luadoc/luadoc/doclet/html/luadoc.css
new file mode 100644 (file)
index 0000000..f9f9749
--- /dev/null
@@ -0,0 +1,285 @@
+body { 
+    margin-left: 1em; 
+    margin-right: 1em; 
+    font-family: arial, helvetica, geneva, sans-serif;
+       background-color:#ffffff; margin:0px;
+}
+
+code {
+    font-family: "Andale Mono", monospace; 
+}
+
+tt {
+    font-family: "Andale Mono", monospace; 
+}
+
+body, td, th { font-size: 11pt; }
+
+h1, h2, h3, h4 { margin-left: 0em; }
+
+textarea, pre, tt { font-size:10pt; }
+body, td, th { color:#000000; }
+small { font-size:0.85em; }
+h1 { font-size:1.5em; }
+h2 { font-size:1.25em; }
+h3 { font-size:1.15em; }
+h4 { font-size:1.06em; }
+
+a:link { font-weight:bold; color: #004080; text-decoration: none; }
+a:visited { font-weight:bold; color: #006699; text-decoration: none; }
+a:link:hover { text-decoration:underline; }
+hr { color:#cccccc }
+img { border-width: 0px; }
+
+
+h3 { padding: 1em 0 0.5em; }
+
+p { margin-left: 1em; }
+
+p.name { 
+    font-family: "Andale Mono", monospace; 
+    padding-top: 1em;
+    margin-left: 0em; 
+}
+
+blockquote { margin-left: 3em; }
+
+pre.example {
+    background-color: rgb(245, 245, 245);
+    border-top-width: 1px;
+    border-right-width: 1px;
+    border-bottom-width: 1px;
+    border-left-width: 1px;
+    border-top-style: solid;
+    border-right-style: solid;
+    border-bottom-style: solid;
+    border-left-style: solid;
+    border-top-color: silver;
+    border-right-color: silver;
+    border-bottom-color: silver;
+    border-left-color: silver;
+    padding: 1em;
+    margin-left: 1em;
+    margin-right: 1em;
+    font-family: "Andale Mono", monospace; 
+    font-size: smaller;
+}
+
+
+hr { 
+    margin-left: 0em;
+       background: #00007f; 
+       border: 0px;
+       height: 1px;
+}
+
+ul { list-style-type: disc; }
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+table.index ul { padding-top: 0em; margin-top: 0em; }
+
+table {
+    border: 1px solid black;
+    border-collapse: collapse;
+    margin: 1em auto;
+}
+th {
+    border: 1px solid black;
+    padding: 0.5em;
+}
+td {
+    border: 1px solid black;
+    padding: 0.5em;
+}
+div.header, div.footer { margin-left: 0em; }
+
+#container
+{
+       margin-left: 1em;
+       margin-right: 1em;
+       background-color: #f0f0f0;
+}
+
+#product
+{
+       text-align: center;
+       border-bottom: 1px solid #cccccc;
+       background-color: #ffffff;
+}
+
+#product big {
+       font-size: 2em;
+}
+
+#product_logo
+{
+}
+
+#product_name
+{
+}
+
+#product_description
+{
+}
+
+#main
+{
+       background-color: #f0f0f0;
+       border-left: 2px solid #cccccc;
+}
+
+#navigation
+{
+       float: left;
+       width: 18em;
+       margin: 0;
+       vertical-align: top;
+       background-color: #f0f0f0;
+       overflow:visible;
+}
+
+#navigation h1 {
+       background-color:#e7e7e7;
+       font-size:1.1em;
+       color:#000000;
+       text-align:left;
+       margin:0px;
+       padding:0.2em;
+       border-top:1px solid #dddddd;
+       border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+       font-size:1em;
+       list-style-type: none;
+       padding: 0;
+       margin: 1px;
+}
+
+#navigation li
+{
+       text-indent: -1em;
+       margin: 0em 0em 0em 0.5em;
+       display: block;
+       padding: 3px 0px 0px 12px;
+}
+
+#navigation li li a
+{
+       padding: 0px 3px 0px -1em;
+}
+
+#content
+{
+       margin-left: 18em;
+       padding: 1em;
+       border-left: 2px solid #cccccc;
+       border-right: 2px solid #cccccc;
+       background-color: #ffffff;
+}
+
+#about
+{
+       clear: both;
+       margin: 0;
+       padding: 5px;
+       border-top: 2px solid #cccccc;
+       background-color: #ffffff;
+}
+
+@media print {
+       body { 
+               font: 12pt "Times New Roman", "TimeNR", Times, serif;
+       }
+       a { font-weight:bold; color: #004080; text-decoration: underline; }
+       
+       #main\r  {\r              background-color: #ffffff;\r             border-left: 0px;\r      }\r      
+       #container\r     {\r              margin-left: 2%;\r               margin-right: 2%;\r              background-color: #ffffff;\r     }
+       
+       #content\r       {\r              margin-left: 0px;\r              padding: 1em;\r          border-left: 0px;\r              border-right: 0px;\r             background-color: #ffffff;\r     }
+       
+       #navigation\r    {\r              display: none;
+       }
+       pre.example {
+               font-family: "Andale Mono", monospace; 
+               font-size: 10pt;
+               page-break-inside: avoid;
+       }
+}
+
+table.module_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.module_list td.name { background-color: #f0f0f0; }
+table.module_list td.summary { width: 100%; }
+
+table.file_list
+{
+       border-width: 1px;
+       border-style: solid;
+       border-color: #cccccc;
+       border-collapse: collapse;
+}
+table.file_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.file_list td.name { background-color: #f0f0f0; }
+table.file_list td.summary { width: 100%; }
+
+
+table.function_list
+{
+       border-width: 1px;
+       border-style: solid;
+       border-color: #cccccc;
+       border-collapse: collapse;
+}
+table.function_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.function_list td.name { background-color: #f0f0f0; }
+table.function_list td.summary { width: 100%; }
+
+
+table.table_list
+{
+       border-width: 1px;
+       border-style: solid;
+       border-color: #cccccc;
+       border-collapse: collapse;
+}
+table.table_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.table_list td.name { background-color: #f0f0f0; }
+table.table_list td.summary { width: 100%; }
+
+dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.function dd {padding: 0.5em 0;}
+dl.function h3 {margin: 0; font-size: medium;}
+
+dl.table dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.table dd {padding-bottom: 1em;}
+dl.table h3 {padding: 0; margin: 0; font-size: medium;}
+
+#TODO: make module_list, file_list, function_list, table_list inherit from a list
+
diff --git a/build/luadoc/luadoc/doclet/html/menu.lp b/build/luadoc/luadoc/doclet/html/menu.lp
new file mode 100644 (file)
index 0000000..0fe3652
--- /dev/null
@@ -0,0 +1,55 @@
+<%
+if module_doc then
+       from = "modules/"..module_doc.name
+elseif file_doc then
+       from = "files/.."..file_doc.name
+else
+       from = ""
+end
+%>
+
+<h1>LuaDoc</h1>
+<ul>
+       <%if not module_doc and not file_doc then%>
+       <li><strong>Index</strong></li>
+       <%else%>
+       <li><a href="<%=luadoc.doclet.html.link("index.html", from)%>">Index</a></li>
+       <%end%>
+</ul>
+
+
+<!-- Module list -->
+<%if not options.nomodules and #doc.modules > 0 then%>
+<h1>Modules</h1>
+<ul>
+<%for _, modulename in ipairs(doc.modules) do
+       if module_doc and module_doc.name == modulename then%>
+       <li><strong><%=modulename%></strong></li>
+       <%else%>
+       <li>
+               <a href="<%=luadoc.doclet.html.module_link(modulename, doc, from)%>"><%=modulename%></a>
+       </li>
+<%     end
+end%>
+</ul>
+<%end%>
+
+
+<!-- File list -->
+<%if not options.nofiles and #doc.files > 0 then%>
+<h1>Files</h1>
+<ul>
+<%for _, filepath in ipairs(doc.files) do
+       if file_doc and file_doc.name == filepath then%>
+       <li><strong><%=filepath%></strong></li>
+       <%else%>
+       <li>
+               <a href="<%=luadoc.doclet.html.file_link(filepath, from)%>"><%=filepath%></a>
+       </li>
+<%     end
+end%>
+</ul>
+<%end%>
+
+
+
diff --git a/build/luadoc/luadoc/doclet/html/module.lp b/build/luadoc/luadoc/doclet/html/module.lp
new file mode 100644 (file)
index 0000000..0798c1b
--- /dev/null
@@ -0,0 +1,155 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>Reference</title>
+    <link rel="stylesheet" href="<%=luadoc.doclet.html.link('luadoc.css', 'modules/'..module_doc.name)%>" type="text/css" />
+       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
+</head>
+
+<body>
+<div id="container">
+
+<div id="product">
+       <div id="product_logo"></div>
+       <div id="product_name"><big><b></b></big></div>
+       <div id="product_description"></div>
+</div> <!-- id="product" -->
+
+<div id="main">
+
+<div id="navigation">
+<%=luadoc.doclet.html.include("menu.lp", { doc=doc, module_doc=module_doc })%>
+<% oop = not not ( module_doc.doc[1] and module_doc.doc[1].cstyle == "instance" ) %>
+
+</div><!-- id="navigation" -->
+
+<div id="content">
+
+<h1><%=( oop and "Object Instance" or "Class" )%> <code><%=module_doc.name%></code></h1>
+
+<p><%=module_doc.description%></p>
+<%if module_doc.author then%>
+<p><b><%= #module_doc.author>1 and "Authors" or "Author" %>:</b>
+<table class="authors_list">
+<%for _, author in ipairs(module_doc.author) do%>
+       <tr><td class="name"><%= author %></td></tr>
+<%end%>
+</table>
+</p>
+<%end%>
+<%if module_doc.copyright then%>
+<p>Copyright&copy; <%=module_doc.copyright%></p>
+<%end%>
+<%if module_doc.release then%>
+<p><small><b>Release:</b> <%=module_doc.release%></small></p>
+<%end%>
+
+<%if #module_doc.constants > 0 then %>
+<h2>Constants</h2>
+<table class="function_list">
+<%for _, const_name in ipairs(module_doc.constants) do
+  local const_data = module_doc.constants[const_name]%>
+       <tr>
+       <td class="name" nowrap><code><%=const_data.private and "local " or ""%><%=(const_name:gsub(".+%.",""))%></code></td>
+       <td class="summary"><%=const_data.summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+<% local funcs = { }; if #module_doc.functions > 0 then %>
+<h2>Functions</h2>
+<table class="function_list">
+<%
+for _, func_name in ipairs(module_doc.functions) do
+  funcs[#funcs+1] = func_name
+end
+
+table.sort(funcs, function(a, b)
+  local func_data_a = module_doc.functions[a]
+  local func_data_b = module_doc.functions[b]
+  local x = func_data_a.sort or a
+  local y = func_data_b.sort or b
+  return x < y
+end)
+
+for _, func_name in ipairs(funcs) do
+  local func_data = module_doc.functions[func_name]
+
+  func_data.printname = func_name:gsub("^%d+#", "")
+  if oop then
+       func_data.printname = func_data.printname:gsub("%.", ":")
+  else
+    func_data.printname = func_data.printname:gsub("^.+%.", "")
+  end
+%>
+       <tr>
+       <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_data.printname%></a>&nbsp;(<%=table.concat(module_doc.functions[func_name].param or {}, ", ")%>)</td>
+       <td class="summary"><%=module_doc.functions[func_name].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<% local tabs = { }; if #module_doc.tables > 0 then%>
+<h2>Tables</h2>
+<table class="table_list">
+<%
+for _, tab_name in ipairs(module_doc.tables) do
+  tabs[#tabs+1] = tab_name
+end
+
+table.sort(tabs, function(a, b)
+  local tab_data_a = module_doc.tables[a]
+  local tab_data_b = module_doc.tables[b]
+  local x = tab_data_a.sort or a
+  local y = tab_data_b.sort or b
+  return x < y
+end)
+
+for _, tab_name in ipairs(tabs) do
+%>
+       <tr>
+       <td class="name" nowrap><a href="#<%=tab_name%>"><%=tab_name%></a></td>
+       <td class="summary"><%=module_doc.tables[tab_name].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<br/>
+<br/>
+
+<%if #module_doc.functions > 0 then%>
+<h2><a name="functions"></a>Functions</h2>
+<dl class="function">
+<%for _, func_name in ipairs(funcs) do%>
+<%=luadoc.doclet.html.include("function.lp", { doc=doc, module_doc=module_doc, func=module_doc.functions[func_name], oop=oop })%>
+<%end%>
+</dl>
+<%end%>
+
+<%if #module_doc.tables > 0 then%>
+<h2><a name="tables"></a>Tables</h2>
+<dl class="table">
+<%for _, tab_name in ipairs(tabs) do%>
+<%=luadoc.doclet.html.include("table.lp", { doc=doc, module_doc=module_doc, tab=module_doc.tables[tab_name] })%>
+<%end%>
+</dl>
+<%end%>
+
+
+</div> <!-- id="content" -->
+
+</div> <!-- id="main" -->
+
+<div id="about">
+       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" -->
+</body>
+</html>
diff --git a/build/luadoc/luadoc/doclet/html/table.lp b/build/luadoc/luadoc/doclet/html/table.lp
new file mode 100644 (file)
index 0000000..5cd0239
--- /dev/null
@@ -0,0 +1,15 @@
+<dt><a name="<%=tab.name%>"></a><strong><%=tab.name%></strong></dt>
+<dd><%=tab.description%>
+
+<%if type(tab.field) == "table" and #tab.field > 0 then%>
+<em>Fields</em>
+<ul>
+       <%for p = 1, #tab.field do%>
+       <li>
+         <%=tab.field[p]%>: <%=tab.field[tab.field[p]] or ""%>
+       </li>
+       <%end%>
+</ul>
+<%end%>
+
+</dd>
diff --git a/build/luadoc/luadoc/doclet/raw.lua b/build/luadoc/luadoc/doclet/raw.lua
new file mode 100644 (file)
index 0000000..1e880b8
--- /dev/null
@@ -0,0 +1,12 @@
+-----------------------------------------------------------------
+-- @release $Id: raw.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $
+-----------------------------------------------------------------
+
+module "luadoc.doclet.raw"
+
+-----------------------------------------------------------------
+-- Generate the output.
+-- @param doc Table with the structured documentation.
+
+function start (doc)
+end
diff --git a/build/luadoc/luadoc/init.lua b/build/luadoc/luadoc/init.lua
new file mode 100644 (file)
index 0000000..649515d
--- /dev/null
@@ -0,0 +1,50 @@
+-------------------------------------------------------------------------------
+-- LuaDoc main function.
+-- @release $Id: init.lua,v 1.4 2008/02/17 06:42:51 jasonsantos Exp $
+-------------------------------------------------------------------------------
+
+local require = require
+
+local util = require "luadoc.util"
+
+logger = {}
+
+module ("luadoc")
+
+-------------------------------------------------------------------------------
+-- LuaDoc version number.
+
+_COPYRIGHT = "Copyright (c) 2003-2007 The Kepler Project"
+_DESCRIPTION = "Documentation Generator Tool for the Lua language"
+_VERSION = "LuaDoc 3.0.1"
+
+-------------------------------------------------------------------------------
+-- Main function
+-- @see luadoc.doclet.html, luadoc.doclet.formatter, luadoc.doclet.raw
+-- @see luadoc.taglet.standard
+
+function main (files, options)
+       logger = util.loadlogengine(options)
+
+       -- load config file
+       if options.config ~= nil then
+               -- load specified config file
+               dofile(options.config)
+       else
+               -- load default config file
+               require("luadoc.config")
+       end
+       
+       local taglet = require(options.taglet)
+       local doclet = require(options.doclet)
+
+       -- analyze input
+       taglet.options = options
+       taglet.logger = logger
+       local doc = taglet.start(files)
+
+       -- generate output
+       doclet.options = options
+       doclet.logger = logger
+       doclet.start(doc)
+end
diff --git a/build/luadoc/luadoc/lp.lua b/build/luadoc/luadoc/lp.lua
new file mode 100644 (file)
index 0000000..adf84f9
--- /dev/null
@@ -0,0 +1,130 @@
+----------------------------------------------------------------------------
+-- Lua Pages Template Preprocessor.
+--
+-- @release $Id: lp.lua,v 1.7 2007/04/18 14:28:39 tomas Exp $
+----------------------------------------------------------------------------
+
+local assert, error, getfenv, loadstring, setfenv = assert, error, getfenv, loadstring, setfenv
+local find, format, gsub, strsub = string.find, string.format, string.gsub, string.sub
+local concat, tinsert = table.concat, table.insert
+local open = io.open
+
+module (...)
+
+----------------------------------------------------------------------------
+-- function to do output
+local outfunc = "io.write"
+-- accepts the old expression field: `$| <Lua expression> |$'
+local compatmode = true
+
+--
+-- Builds a piece of Lua code which outputs the (part of the) given string.
+-- @param s String.
+-- @param i Number with the initial position in the string.
+-- @param f Number with the final position in the string (default == -1).
+-- @return String with the correspondent Lua code which outputs the part of the string.
+--
+local function out (s, i, f)
+       s = strsub(s, i, f or -1)
+       if s == "" then return s end
+       -- we could use `%q' here, but this way we have better control
+       s = gsub(s, "([\\\n\'])", "\\%1")
+       -- substitute '\r' by '\'+'r' and let `loadstring' reconstruct it
+       s = gsub(s, "\r", "\\r")
+       return format(" %s('%s'); ", outfunc, s)
+end
+
+
+----------------------------------------------------------------------------
+-- Translate the template to Lua code.
+-- @param s String to translate.
+-- @return String with translated code.
+----------------------------------------------------------------------------
+function translate (s)
+       if compatmode then
+               s = gsub(s, "$|(.-)|%$", "<?lua = %1 ?>")
+               s = gsub(s, "<!%-%-$$(.-)$$%-%->", "<?lua %1 ?>")
+       end
+       s = gsub(s, "<%%(.-)%%>", "<?lua %1 ?>")
+       local res = {}
+       local start = 1   -- start of untranslated part in `s'
+       while true do
+               local ip, fp, target, exp, code = find(s, "<%?(%w*)[ \t]*(=?)(.-)%?>", start)
+               if not ip then break end
+               tinsert(res, out(s, start, ip-1))
+               if target ~= "" and target ~= "lua" then
+                       -- not for Lua; pass whole instruction to the output
+                       tinsert(res, out(s, ip, fp))
+               else
+                       if exp == "=" then   -- expression?
+                               tinsert(res, format(" %s(%s);", outfunc, code))
+                       else  -- command
+                               tinsert(res, format(" %s ", code))
+                       end
+               end
+               start = fp + 1
+       end
+       tinsert(res, out(s, start))
+       return concat(res)
+end
+
+
+----------------------------------------------------------------------------
+-- Defines the name of the output function.
+-- @param f String with the name of the function which produces output.
+
+function setoutfunc (f)
+       outfunc = f
+end
+
+----------------------------------------------------------------------------
+-- Turns on or off the compatibility with old CGILua 3.X behavior.
+-- @param c Boolean indicating if the compatibility mode should be used.
+
+function setcompatmode (c)
+       compatmode = c
+end
+
+----------------------------------------------------------------------------
+-- Internal compilation cache.
+
+local cache = {}
+
+----------------------------------------------------------------------------
+-- Translates a template into a Lua function.
+-- Does NOT execute the resulting function.
+-- Uses a cache of templates.
+-- @param string String with the template to be translated.
+-- @param chunkname String with the name of the chunk, for debugging purposes.
+-- @return Function with the resulting translation.
+
+function compile (string, chunkname)
+       local f, err = cache[string]
+       if f then return f end
+       f, err = loadstring (translate (string), chunkname)
+       if not f then error (err, 3) end
+       cache[string] = f
+       return f
+end
+
+----------------------------------------------------------------------------
+-- Translates and executes a template in a given file.
+-- The translation creates a Lua function which will be executed in an
+-- optionally given environment.
+-- @param filename String with the name of the file containing the template.
+-- @param env Table with the environment to run the resulting function.
+
+function include (filename, env)
+       -- read the whole contents of the file
+       local fh = assert (open (filename))
+       local src = fh:read("*a")
+       fh:close()
+       -- translates the file into a function
+       local prog = compile (src, '@'..filename)
+       local _env
+       if env then
+               _env = getfenv (prog)
+               setfenv (prog, env)
+       end
+       prog ()
+end
diff --git a/build/luadoc/luadoc/taglet/standard.lua b/build/luadoc/luadoc/taglet/standard.lua
new file mode 100644 (file)
index 0000000..ef925f8
--- /dev/null
@@ -0,0 +1,567 @@
+-------------------------------------------------------------------------------
+-- @release $Id: standard.lua,v 1.39 2007/12/21 17:50:48 tomas Exp $
+-------------------------------------------------------------------------------
+
+local assert, pairs, tostring, type = assert, pairs, tostring, type
+local io = require "io"
+local posix = require "nixio.fs"
+local luadoc = require "luadoc"
+local util = require "luadoc.util"
+local tags = require "luadoc.taglet.standard.tags"
+local string = require "string"
+local table = require "table"
+
+module 'luadoc.taglet.standard'
+
+-------------------------------------------------------------------------------
+-- Creates an iterator for an array base on a class type.
+-- @param t array to iterate over
+-- @param class name of the class to iterate over
+
+function class_iterator (t, class)
+       return function ()
+               local i = 1
+               return function ()
+                       while t[i] and t[i].class ~= class do
+                               i = i + 1
+                       end
+                       local v = t[i]
+                       i = i + 1
+                       return v
+               end
+       end
+end
+
+-- Patterns for function recognition
+local identifiers_list_pattern = "%s*(.-)%s*"
+local identifier_pattern = "[^%(%s]+"
+local function_patterns = {
+       "^()%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)",
+       "^%s*(local%s)%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)",
+       "^()%s*("..identifier_pattern..")%s*%=%s*function%s*%("..identifiers_list_pattern.."%)",
+}
+
+-------------------------------------------------------------------------------
+-- Checks if the line contains a function definition
+-- @param line string with line text
+-- @return function information or nil if no function definition found
+
+local function check_function (line)
+       line = util.trim(line)
+
+       local info = table.foreachi(function_patterns, function (_, pattern)
+               local r, _, l, id, param = string.find(line, pattern)
+               if r ~= nil then
+                       return {
+                               name = id,
+                               private = (l == "local"),
+                               param = { } --util.split("%s*,%s*", param),
+                       }
+               end
+       end)
+
+       -- TODO: remove these assert's?
+       if info ~= nil then
+               assert(info.name, "function name undefined")
+               assert(info.param, string.format("undefined parameter list for function `%s'", info.name))
+       end
+
+       return info
+end
+
+-------------------------------------------------------------------------------
+-- Checks if the line contains a module definition.
+-- @param line string with line text
+-- @param currentmodule module already found, if any
+-- @return the name of the defined module, or nil if there is no module
+-- definition
+
+local function check_module (line, currentmodule)
+       line = util.trim(line)
+
+       -- module"x.y"
+       -- module'x.y'
+       -- module[[x.y]]
+       -- module("x.y")
+       -- module('x.y')
+       -- module([[x.y]])
+       -- module(...)
+
+       local r, _, modulename = string.find(line, "^module%s*[%s\"'(%[]+([^,\"')%]]+)")
+       if r then
+               -- found module definition
+               logger:debug(string.format("found module `%s'", modulename))
+               return modulename
+       end
+       return currentmodule
+end
+
+-- Patterns for constant recognition
+local constant_patterns = {
+       "^()%s*([A-Z][A-Z0-9_]*)%s*=",
+       "^%s*(local%s)%s*([A-Z][A-Z0-9_]*)%s*=",
+}
+
+-------------------------------------------------------------------------------
+-- Checks if the line contains a constant definition
+-- @param line string with line text
+-- @return constant information or nil if no constant definition found
+
+local function check_constant (line)
+       line = util.trim(line)
+
+       local info = table.foreachi(constant_patterns, function (_, pattern)
+               local r, _, l, id = string.find(line, pattern)
+               if r ~= nil then
+                       return {
+                               name = id,
+                               private = (l == "local"),
+                       }
+               end
+       end)
+
+       -- TODO: remove these assert's?
+       if info ~= nil then
+               assert(info.name, "constant name undefined")
+       end
+
+       return info
+end
+
+-------------------------------------------------------------------------------
+-- Extracts summary information from a description. The first sentence of each
+-- doc comment should be a summary sentence, containing a concise but complete
+-- description of the item. It is important to write crisp and informative
+-- initial sentences that can stand on their own
+-- @param description text with item description
+-- @return summary string or nil if description is nil
+
+local function parse_summary (description)
+       -- summary is never nil...
+       description = description or ""
+
+       -- append an " " at the end to make the pattern work in all cases
+       description = description.." "
+
+       -- read until the first period followed by a space or tab
+       local summary = string.match(description, "(.-%.)[%s\t]")
+
+       -- if pattern did not find the first sentence, summary is the whole description
+       summary = summary or description
+
+       return summary
+end
+
+-------------------------------------------------------------------------------
+-- @param f file handle
+-- @param line current line being parsed
+-- @param modulename module already found, if any
+-- @return current line
+-- @return code block
+-- @return modulename if found
+
+local function parse_code (f, line, modulename)
+       local code = {}
+       while line ~= nil do
+               if string.find(line, "^[\t ]*%-%-%-") then
+                       -- reached another luadoc block, end this parsing
+                       return line, code, modulename
+               else
+                       -- look for a module definition
+                       modulename = check_module(line, modulename)
+
+                       table.insert(code, line)
+                       line = f:read()
+               end
+       end
+       -- reached end of file
+       return line, code, modulename
+end
+
+-------------------------------------------------------------------------------
+-- Parses the information inside a block comment
+-- @param block block with comment field
+-- @return block parameter
+
+local function parse_comment (block, first_line, modulename)
+
+       -- get the first non-empty line of code
+       local code = table.foreachi(block.code, function(_, line)
+               if not util.line_empty(line) then
+                       -- `local' declarations are ignored in two cases:
+                       -- when the `nolocals' option is turned on; and
+                       -- when the first block of a file is parsed (this is
+                       --      necessary to avoid confusion between the top
+                       --      local declarations and the `module' definition.
+                       if (options.nolocals or first_line) and line:find"^%s*local" then
+                               return
+                       end
+                       return line
+               end
+       end)
+
+       -- parse first line of code
+       if code ~= nil then
+               local func_info = check_function(code)
+               local module_name = check_module(code)
+               local const_info = check_constant(code)
+               if func_info then
+                       block.class = "function"
+                       block.name = func_info.name
+                       block.param = func_info.param
+                       block.private = func_info.private
+               elseif const_info then
+                       block.class = "constant"
+                       block.name = const_info.name
+                       block.private = const_info.private
+               elseif module_name then
+                       block.class = "module"
+                       block.name = module_name
+                       block.param = {}
+               else
+                       block.param = {}
+               end
+       else
+               -- TODO: comment without any code. Does this means we are dealing
+               -- with a file comment?
+       end
+
+       -- parse @ tags
+       local currenttag = "description"
+       local currenttext
+
+       table.foreachi(block.comment, function (_, line)
+               line = util.trim_comment(line)
+
+               local r, _, tag, text = string.find(line, "@([_%w%.]+)%s+(.*)")
+               if r ~= nil then
+                       -- found new tag, add previous one, and start a new one
+                       -- TODO: what to do with invalid tags? issue an error? or log a warning?
+                       tags.handle(currenttag, block, currenttext)
+
+                       currenttag = tag
+                       currenttext = text
+               else
+                       currenttext = util.concat(currenttext, "\n" .. line)
+                       assert(string.sub(currenttext, 1, 1) ~= " ", string.format("`%s', `%s'", currenttext, line))
+               end
+       end)
+       tags.handle(currenttag, block, currenttext)
+
+       -- extracts summary information from the description
+       block.summary = parse_summary(block.description)
+       assert(string.sub(block.description, 1, 1) ~= " ", string.format("`%s'", block.description))
+
+       if block.name and block.class == "module" then
+               modulename = block.name
+       end
+
+       return block, modulename
+end
+
+-------------------------------------------------------------------------------
+-- Parses a block of comment, started with ---. Read until the next block of
+-- comment.
+-- @param f file handle
+-- @param line being parsed
+-- @param modulename module already found, if any
+-- @return line
+-- @return block parsed
+-- @return modulename if found
+
+local function parse_block (f, line, modulename, first)
+       local multiline = not not (line and line:match("%[%["))
+       local block = {
+               comment = {},
+               code = {},
+       }
+
+       while line ~= nil do
+               if (multiline == true and string.find(line, "%]%]") ~= nil) or
+                  (multiline == false and string.find(line, "^[\t ]*%-%-") == nil) then
+                       -- reached end of comment, read the code below it
+                       -- TODO: allow empty lines
+                       line, block.code, modulename = parse_code(f, line, modulename)
+
+                       -- parse information in block comment
+                       block, modulename = parse_comment(block, first, modulename)
+
+                       return line, block, modulename
+               else
+                       table.insert(block.comment, line)
+                       line = f:read()
+               end
+       end
+       -- reached end of file
+
+       -- parse information in block comment
+       block, modulename = parse_comment(block, first, modulename)
+
+       return line, block, modulename
+end
+
+-------------------------------------------------------------------------------
+-- Parses a file documented following luadoc format.
+-- @param filepath full path of file to parse
+-- @param doc table with documentation
+-- @return table with documentation
+
+function parse_file (filepath, doc, handle, prev_line, prev_block, prev_modname)
+       local blocks = { prev_block }
+       local modulename = prev_modname
+
+       -- read each line
+       local f = handle or io.open(filepath, "r")
+       local i = 1
+       local line = prev_line or f:read()
+       local first = true
+       while line ~= nil do
+
+               if string.find(line, "^[\t ]*%-%-%-") then
+                       -- reached a luadoc block
+                       local block, newmodname
+                       line, block, newmodname = parse_block(f, line, modulename, first)
+
+                       if modulename and newmodname and newmodname ~= modulename then
+                               doc = parse_file( nil, doc, f, line, block, newmodname )
+                       else
+                               table.insert(blocks, block)
+                               modulename = newmodname
+                       end
+               else
+                       -- look for a module definition
+                       local newmodname = check_module(line, modulename)
+
+                       if modulename and newmodname and newmodname ~= modulename then
+                               parse_file( nil, doc, f )
+                       else
+                               modulename = newmodname
+                       end
+
+                       -- TODO: keep beginning of file somewhere
+
+                       line = f:read()
+               end
+               first = false
+               i = i + 1
+       end
+
+       if not handle then
+               f:close()
+       end
+
+       if filepath then
+               -- store blocks in file hierarchy
+               assert(doc.files[filepath] == nil, string.format("doc for file `%s' already defined", filepath))
+               table.insert(doc.files, filepath)
+               doc.files[filepath] = {
+                       type = "file",
+                       name = filepath,
+                       doc = blocks,
+       --              functions = class_iterator(blocks, "function"),
+       --              tables = class_iterator(blocks, "table"),
+               }
+       --
+               local first = doc.files[filepath].doc[1]
+               if first and modulename then
+                       doc.files[filepath].author = first.author
+                       doc.files[filepath].copyright = first.copyright
+                       doc.files[filepath].description = first.description
+                       doc.files[filepath].release = first.release
+                       doc.files[filepath].summary = first.summary
+               end
+       end
+
+       -- if module definition is found, store in module hierarchy
+       if modulename ~= nil then
+               if modulename == "..." then
+                       assert( filepath, "Can't determine name for virtual module from filepatch" )
+                       modulename = string.gsub (filepath, "%.lua$", "")
+                       modulename = string.gsub (modulename, "/", ".")
+               end
+               if doc.modules[modulename] ~= nil then
+                       -- module is already defined, just add the blocks
+                       table.foreachi(blocks, function (_, v)
+                               table.insert(doc.modules[modulename].doc, v)
+                       end)
+               else
+                       -- TODO: put this in a different module
+                       table.insert(doc.modules, modulename)
+                       doc.modules[modulename] = {
+                               type = "module",
+                               name = modulename,
+                               doc = blocks,
+--                             functions = class_iterator(blocks, "function"),
+--                             tables = class_iterator(blocks, "table"),
+                               author = first and first.author,
+                               copyright = first and first.copyright,
+                               description = "",
+                               release = first and first.release,
+                               summary = "",
+                       }
+
+                       -- find module description
+                       for m in class_iterator(blocks, "module")() do
+                               doc.modules[modulename].description = util.concat(
+                                       doc.modules[modulename].description,
+                                       m.description)
+                               doc.modules[modulename].summary = util.concat(
+                                       doc.modules[modulename].summary,
+                                       m.summary)
+                               if m.author then
+                                       doc.modules[modulename].author = m.author
+                               end
+                               if m.copyright then
+                                       doc.modules[modulename].copyright = m.copyright
+                               end
+                               if m.release then
+                                       doc.modules[modulename].release = m.release
+                               end
+                               if m.name then
+                                       doc.modules[modulename].name = m.name
+                               end
+                       end
+                       doc.modules[modulename].description = doc.modules[modulename].description or (first and first.description) or ""
+                       doc.modules[modulename].summary = doc.modules[modulename].summary or (first and first.summary) or ""
+               end
+
+               -- make functions table
+               doc.modules[modulename].functions = {}
+               for f in class_iterator(blocks, "function")() do
+                       if f and f.name then
+                               table.insert(doc.modules[modulename].functions, f.name)
+                               doc.modules[modulename].functions[f.name] = f
+                       end
+               end
+
+               -- make tables table
+               doc.modules[modulename].tables = {}
+               for t in class_iterator(blocks, "table")() do
+                       if t and t.name then
+                               table.insert(doc.modules[modulename].tables, t.name)
+                               doc.modules[modulename].tables[t.name] = t
+                       end
+               end
+
+               -- make constants table
+               doc.modules[modulename].constants = {}
+               for c in class_iterator(blocks, "constant")() do
+                       if c and c.name then
+                               table.insert(doc.modules[modulename].constants, c.name)
+                               doc.modules[modulename].constants[c.name] = c
+                       end
+               end
+       end
+
+       if filepath then
+               -- make functions table
+               doc.files[filepath].functions = {}
+               for f in class_iterator(blocks, "function")() do
+                       if f and f.name then
+                               table.insert(doc.files[filepath].functions, f.name)
+                               doc.files[filepath].functions[f.name] = f
+                       end
+               end
+
+               -- make tables table
+               doc.files[filepath].tables = {}
+               for t in class_iterator(blocks, "table")() do
+                       if t and t.name then
+                               table.insert(doc.files[filepath].tables, t.name)
+                               doc.files[filepath].tables[t.name] = t
+                       end
+               end
+       end
+
+       return doc
+end
+
+-------------------------------------------------------------------------------
+-- Checks if the file is terminated by ".lua" or ".luadoc" and calls the
+-- function that does the actual parsing
+-- @param filepath full path of the file to parse
+-- @param doc table with documentation
+-- @return table with documentation
+-- @see parse_file
+
+function file (filepath, doc)
+       local patterns = { "%.lua$", "%.luadoc$" }
+       local valid = table.foreachi(patterns, function (_, pattern)
+               if string.find(filepath, pattern) ~= nil then
+                       return true
+               end
+       end)
+
+       if valid then
+               logger:info(string.format("processing file `%s'", filepath))
+               doc = parse_file(filepath, doc)
+       end
+
+       return doc
+end
+
+-------------------------------------------------------------------------------
+-- Recursively iterates through a directory, parsing each file
+-- @param path directory to search
+-- @param doc table with documentation
+-- @return table with documentation
+
+function directory (path, doc)
+       for f in posix.dir(path) do
+               local fullpath = path .. "/" .. f
+               local attr = posix.stat(fullpath)
+               assert(attr, string.format("error stating file `%s'", fullpath))
+
+               if attr.type == "reg" then
+                       doc = file(fullpath, doc)
+               elseif attr.type == "dir" and f ~= "." and f ~= ".." then
+                       doc = directory(fullpath, doc)
+               end
+       end
+       return doc
+end
+
+-- Recursively sorts the documentation table
+local function recsort (tab)
+       table.sort (tab)
+       -- sort list of functions by name alphabetically
+       for f, doc in pairs(tab) do
+               if doc.functions then
+                       table.sort(doc.functions)
+               end
+               if doc.tables then
+                       table.sort(doc.tables)
+               end
+       end
+end
+
+-------------------------------------------------------------------------------
+
+function start (files, doc)
+       assert(files, "file list not specified")
+
+       -- Create an empty document, or use the given one
+       doc = doc or {
+               files = {},
+               modules = {},
+       }
+       assert(doc.files, "undefined `files' field")
+       assert(doc.modules, "undefined `modules' field")
+
+       table.foreachi(files, function (_, path)
+               local attr = posix.stat(path)
+               assert(attr, string.format("error stating path `%s'", path))
+
+               if attr.type == "reg" then
+                       doc = file(path, doc)
+               elseif attr.type == "dir" then
+                       doc = directory(path, doc)
+               end
+       end)
+
+       -- order arrays alphabetically
+       recsort(doc.files)
+       recsort(doc.modules)
+
+       return doc
+end
diff --git a/build/luadoc/luadoc/taglet/standard/tags.lua b/build/luadoc/luadoc/taglet/standard/tags.lua
new file mode 100644 (file)
index 0000000..e9d0354
--- /dev/null
@@ -0,0 +1,191 @@
+-------------------------------------------------------------------------------
+-- Handlers for several tags
+-- @release $Id: tags.lua,v 1.8 2007/09/05 12:39:09 tomas Exp $
+-------------------------------------------------------------------------------
+
+local luadoc = require "luadoc"
+local util = require "luadoc.util"
+local string = require "string"
+local table = require "table"
+local assert, type, tostring, tonumber = assert, type, tostring, tonumber
+
+module "luadoc.taglet.standard.tags"
+
+-------------------------------------------------------------------------------
+
+local function author (tag, block, text)
+       block[tag] = block[tag] or {}
+       if not text then
+               luadoc.logger:warn("author `name' not defined [["..text.."]]: skipping")
+               return
+       end
+       table.insert (block[tag], text)
+end
+
+-------------------------------------------------------------------------------
+-- Set the class of a comment block. Classes can be "module", "function",
+-- "table". The first two classes are automatic, extracted from the source code
+
+local function class (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function cstyle (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function sort (tag, block, text)
+       block[tag] = tonumber(text) or 0
+end
+
+-------------------------------------------------------------------------------
+
+local function copyright (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function description (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function field (tag, block, text)
+       if block["class"] ~= "table" then
+               luadoc.logger:warn("documenting `field' for block that is not a `table'")
+       end
+       block[tag] = block[tag] or {}
+
+       local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)")
+       assert(name, "field name not defined")
+
+       table.insert(block[tag], name)
+       block[tag][name] = desc
+end
+
+-------------------------------------------------------------------------------
+-- Set the name of the comment block. If the block already has a name, issue
+-- an error and do not change the previous value
+
+local function name (tag, block, text)
+       if block[tag] and block[tag] ~= text then
+               luadoc.logger:error(string.format("block name conflict: `%s' -> `%s'", block[tag], text))
+       end
+
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+-- Processes a parameter documentation.
+-- @param tag String with the name of the tag (it must be "param" always).
+-- @param block Table with previous information about the block.
+-- @param text String with the current line beeing processed.
+
+local function param (tag, block, text)
+       block[tag] = block[tag] or {}
+       -- TODO: make this pattern more flexible, accepting empty descriptions
+       local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)")
+       if not name then
+               luadoc.logger:warn("parameter `name' not defined [["..text.."]]: skipping")
+               return
+       end
+       local i = table.foreachi(block[tag], function (i, v)
+               if v == name then
+                       return i
+               end
+       end)
+       if i == nil then
+               luadoc.logger:warn(string.format("documenting undefined parameter `%s'", name))
+               table.insert(block[tag], name)
+       end
+       block[tag][name] = desc
+end
+
+-------------------------------------------------------------------------------
+
+local function release (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function ret (tag, block, text)
+       tag = "ret"
+       if type(block[tag]) == "string" then
+               block[tag] = { block[tag], text }
+       elseif type(block[tag]) == "table" then
+               table.insert(block[tag], text)
+       else
+               block[tag] = text
+       end
+end
+
+-------------------------------------------------------------------------------
+-- @see ret
+
+local function see (tag, block, text)
+       -- see is always an array
+       block[tag] = block[tag] or {}
+
+       -- remove trailing "."
+       text = string.gsub(text, "(.*)%.$", "%1")
+
+       local s = util.split("%s*,%s*", text)
+
+       table.foreachi(s, function (_, v)
+               table.insert(block[tag], v)
+       end)
+end
+
+-------------------------------------------------------------------------------
+-- @see ret
+
+local function usage (tag, block, text)
+       if type(block[tag]) == "string" then
+               block[tag] = { block[tag], text }
+       elseif type(block[tag]) == "table" then
+               table.insert(block[tag], text)
+       else
+               block[tag] = text
+       end
+end
+
+-------------------------------------------------------------------------------
+
+local handlers = {}
+handlers["author"] = author
+handlers["class"] = class
+handlers["cstyle"] = cstyle
+handlers["copyright"] = copyright
+handlers["description"] = description
+handlers["field"] = field
+handlers["name"] = name
+handlers["param"] = param
+handlers["release"] = release
+handlers["return"] = ret
+handlers["see"] = see
+handlers["sort"] = sort
+handlers["usage"] = usage
+
+-------------------------------------------------------------------------------
+
+function handle (tag, block, text)
+       if not handlers[tag] then
+               luadoc.logger:error(string.format("undefined handler for tag `%s'", tag))
+               return
+       end
+
+       if text then
+               text = text:gsub("`([^\n]-)`", "<code>%1</code>")
+               text = text:gsub("`(.-)`", "<pre>%1</pre>")
+       end
+
+--     assert(handlers[tag], string.format("undefined handler for tag `%s'", tag))
+       return handlers[tag](tag, block, text)
+end
diff --git a/build/luadoc/luadoc/util.lua b/build/luadoc/luadoc/util.lua
new file mode 100644 (file)
index 0000000..e1058d5
--- /dev/null
@@ -0,0 +1,201 @@
+-------------------------------------------------------------------------------
+-- General utilities.
+-- @release $Id: util.lua,v 1.16 2008/02/17 06:42:51 jasonsantos Exp $
+-------------------------------------------------------------------------------
+
+local posix = require "nixio.fs"
+local type, table, string, io, assert, tostring, setmetatable, pcall = type, table, string, io, assert, tostring, setmetatable, pcall
+
+-------------------------------------------------------------------------------
+-- Module with several utilities that could not fit in a specific module
+
+module "luadoc.util"
+
+-------------------------------------------------------------------------------
+-- Removes spaces from the begining and end of a given string
+-- @param s string to be trimmed
+-- @return trimmed string
+
+function trim (s)
+       return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
+end
+
+-------------------------------------------------------------------------------
+-- Removes spaces from the begining and end of a given string, considering the
+-- string is inside a lua comment.
+-- @param s string to be trimmed
+-- @return trimmed string
+-- @see trim
+-- @see string.gsub
+
+function trim_comment (s)
+       s = string.gsub(s, "^%s*%-%-+%[%[(.*)$", "%1")
+       s = string.gsub(s, "^%s*%-%-+(.*)$", "%1")
+       return s
+end
+
+-------------------------------------------------------------------------------
+-- Checks if a given line is empty
+-- @param line string with a line
+-- @return true if line is empty, false otherwise
+
+function line_empty (line)
+       return (string.len(trim(line)) == 0)
+end
+
+-------------------------------------------------------------------------------
+-- Appends two string, but if the first one is nil, use to second one
+-- @param str1 first string, can be nil
+-- @param str2 second string
+-- @return str1 .. " " .. str2, or str2 if str1 is nil
+
+function concat (str1, str2)
+       if str1 == nil or string.len(str1) == 0 then
+               return str2
+       else
+               return str1 .. " " .. str2
+       end
+end
+
+-------------------------------------------------------------------------------
+-- Split text into a list consisting of the strings in text,
+-- separated by strings matching delim (which may be a pattern).
+-- @param delim if delim is "" then action is the same as %s+ except that
+-- field 1 may be preceeded by leading whitespace
+-- @usage split(",%s*", "Anna, Bob, Charlie,Dolores")
+-- @usage split(""," x y") gives {"x","y"}
+-- @usage split("%s+"," x y") gives {"", "x","y"}
+-- @return array with strings
+-- @see table.concat
+
+function split(delim, text)
+       local list = {}
+       if string.len(text) > 0 then
+               delim = delim or ""
+               local pos = 1
+               -- if delim matches empty string then it would give an endless loop
+               if string.find("", delim, 1) and delim ~= "" then
+                       error("delim matches empty string!")
+               end
+               local first, last
+               while 1 do
+                       if delim ~= "" then
+                               first, last = string.find(text, delim, pos)
+                       else
+                               first, last = string.find(text, "%s+", pos)
+                               if first == 1 then
+                                       pos = last+1
+                                       first, last = string.find(text, "%s+", pos)
+                               end
+                       end
+                       if first then -- found?
+                               table.insert(list, string.sub(text, pos, first-1))
+                               pos = last+1
+                       else
+                               table.insert(list, string.sub(text, pos))
+                               break
+                       end
+               end
+       end
+       return list
+end
+
+-------------------------------------------------------------------------------
+-- Comments a paragraph.
+-- @param text text to comment with "--", may contain several lines
+-- @return commented text
+
+function comment (text)
+       text = string.gsub(text, "\n", "\n-- ")
+       return "-- " .. text
+end
+
+-------------------------------------------------------------------------------
+-- Wrap a string into a paragraph.
+-- @param s string to wrap
+-- @param w width to wrap to [80]
+-- @param i1 indent of first line [0]
+-- @param i2 indent of subsequent lines [0]
+-- @return wrapped paragraph
+
+function wrap(s, w, i1, i2)
+       w = w or 80
+       i1 = i1 or 0
+       i2 = i2 or 0
+       assert(i1 < w and i2 < w, "the indents must be less than the line width")
+       s = string.rep(" ", i1) .. s
+       local lstart, len = 1, string.len(s)
+       while len - lstart > w do
+               local i = lstart + w
+               while i > lstart and string.sub(s, i, i) ~= " " do i = i - 1 end
+               local j = i
+               while j > lstart and string.sub(s, j, j) == " " do j = j - 1 end
+               s = string.sub(s, 1, j) .. "\n" .. string.rep(" ", i2) ..
+                       string.sub(s, i + 1, -1)
+               local change = i2 + 1 - (i - j)
+               lstart = j + change
+               len = len + change
+       end
+       return s
+end
+
+-------------------------------------------------------------------------------
+-- Opens a file, creating the directories if necessary
+-- @param filename full path of the file to open (or create)
+-- @param mode mode of opening
+-- @return file handle
+
+function posix.open (filename, mode)
+       local f = io.open(filename, mode)
+       if f == nil then
+               filename = string.gsub(filename, "\\", "/")
+               local dir = ""
+               for d in string.gfind(filename, ".-/") do
+                       dir = dir .. d
+                       posix.mkdir(dir)
+               end
+               f = io.open(filename, mode)
+       end
+       return f
+end
+
+
+----------------------------------------------------------------------------------
+-- Creates a Logger with LuaLogging, if present. Otherwise, creates a mock logger.
+-- @param options a table with options for the logging mechanism
+-- @return logger object that will implement log methods
+
+function loadlogengine(options)
+       local logenabled = pcall(function()
+               require "logging"
+               require "logging.console"
+       end)
+
+       local logging = logenabled and logging
+
+       if logenabled then
+               if options.filelog then
+                       logger = logging.file("luadoc.log") -- use this to get a file log
+               else
+                       logger = logging.console("[%level] %message\n")
+               end
+
+               if options.verbose then
+                       logger:setLevel(logging.INFO)
+               else
+                       logger:setLevel(logging.WARN)
+               end
+
+       else
+               noop = {__index=function(...)
+                       return function(...)
+                               -- noop
+                       end
+               end}
+
+               logger = {}
+               setmetatable(logger, noop)
+       end
+
+       return logger
+end
index 2f7f573..a9fc760 100755 (executable)
@@ -1,2 +1,14 @@
-luadoc -d $2 --no-files $(for f in $(find $1 -name '*.lua' -type f); do if grep -q -- "@return" $f; then echo $f; fi; done)
-echo API-Documentation was created in $2.
+#!/bin/bash
+
+topdir=$(pwd)
+
+[ -f "$topdir/build/makedocs.sh" -a -n "$1" ] || {
+       echo "Please execute as ./build/makedocs.sh [output directory]" >&2
+       exit 1
+}
+
+(
+       cd "$topdir/build/luadoc/"
+       find "$topdir/libs/" "$topdir/modules/" -type f -name '*.lua' -or -name '*.luadoc' | \
+               xargs grep -l '@return' | xargs ./doc.lua --no-files -d "$1"
+)
index 4a90dae..d3f0403 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 # zoneinfo2lua.pl - Make Lua module from /usr/share/zoneinfo
-# Execute from within /usr/share/zoneinfo
+# Execute from within root of Luci feed, usually feeds/luci
 # $Id$
 
 use strict;
@@ -51,16 +51,7 @@ open(O, "> $tzdout/tzdata.lua") || die "open($tzdout/tzdata.lua): $!\n";
 
 print STDERR "Writing time zones to $tzdout/tzdata.lua ... ";
 print O <<HEAD;
---[[
-LuCI - Autogenerated Zoneinfo Module
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
+-- Licensed to the public under the Apache License 2.0.
 
 module "luci.sys.zoneinfo.tzdata"
 
@@ -81,16 +72,7 @@ open (O, "> $tzdout/tzoffset.lua") || die "open($tzdout/tzoffset.lua): $!\n";
 
 print STDERR "Writing time offsets to $tzdout/tzoffset.lua ... ";
 print O <<HEAD;
---[[
-LuCI - Autogenerated Zoneinfo Module
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-]]--
+-- Licensed to the public under the Apache License 2.0.
 
 module "luci.sys.zoneinfo.tzoffset"
 
diff --git a/contrib/luadoc/Makefile b/contrib/luadoc/Makefile
deleted file mode 100644 (file)
index 81a96f6..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-include ../../build/config.mk
-include ../../build/module.mk
\ No newline at end of file
diff --git a/contrib/luadoc/hostfiles/bin/luadoc b/contrib/luadoc/hostfiles/bin/luadoc
deleted file mode 100755 (executable)
index ba99a37..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/usr/bin/env lua
--------------------------------------------------------------------------------
--- LuaDoc launcher.
--- @release $Id: luadoc.lua.in,v 1.1 2008/02/17 06:42:51 jasonsantos Exp $
--------------------------------------------------------------------------------
-
-require "luadoc"
-
--------------------------------------------------------------------------------
--- Print version number.
-
-local function print_version ()
-       print (string.format("%s\n%s\n%s", 
-               luadoc._VERSION, 
-               luadoc._DESCRIPTION, 
-               luadoc._COPYRIGHT))
-end
-
--------------------------------------------------------------------------------
--- Print usage message.
-
-local function print_help ()
-       print ("Usage: "..arg[0]..[[ [options|files]
-Generate documentation from files. Available options are:
-  -d path                      output directory path
-  -t path                      template directory path
-  -h, --help                   print this help and exit
-      --noindexpage            do not generate global index page
-      --nofiles                do not generate documentation for files
-      --nomodules              do not generate documentation for modules
-      --doclet doclet_module   doclet module to generate output
-      --taglet taglet_module   taglet module to parse input code
-  -q, --quiet                  suppress all normal output
-  -v, --version                print version information]])
-end
-
-local function off_messages (arg, i, options)
-       options.verbose = nil
-end
-
--------------------------------------------------------------------------------
--- Process options. TODO: use getopts.
--- @class table
--- @name OPTIONS
-
-local OPTIONS = {
-       d = function (arg, i, options)
-               local dir = arg[i+1]
-               if string.sub (dir, -2) ~= "/" then
-                       dir = dir..'/'
-               end
-               options.output_dir = dir
-               return 1
-       end,
-       t = function (arg, i, options)
-               local dir = arg[i+1]
-               if string.sub (dir, -2) ~= "/" then
-                       dir = dir..'/'
-               end
-               options.template_dir = dir
-               return 1
-       end,
-       h = print_help,
-       help = print_help,
-       q = off_messages,
-       quiet = off_messages,
-       v = print_version,
-       version = print_version,
-       doclet = function (arg, i, options)
-               options.doclet = arg[i+1]
-               return 1
-       end,
-       taglet = function (arg, i, options)
-               options.taglet = arg[i+1]
-               return 1
-       end,
-}
-
--------------------------------------------------------------------------------
-
-local function process_options (arg)
-       local files = {}
-       local options = require "luadoc.config"
-       local i = 1
-       while i <= #arg do
-               local argi = arg[i]
-               if string.sub (argi, 1, 1) ~= '-' then
-                       table.insert (files, argi)
-               else
-                       local opt = string.sub (argi, 2)
-                       if string.sub (opt, 1, 1) == '-' then
-                               opt = string.gsub (opt, "%-", "")
-                       end
-                       if OPTIONS[opt] then
-                               if OPTIONS[opt] (arg, i, options) then
-                                       i = i + 1
-                               end
-                       else
-                               options[opt] = 1
-                       end
-               end
-               i = i+1
-       end
-       return files, options
-end 
-
--------------------------------------------------------------------------------
--- Main function. Process command-line parameters and call luadoc processor.
-
-function main (arg)
-       -- Process options
-       local argc = #arg
-       if argc < 1 then
-               print_help ()
-               return
-       end
-       local files, options = process_options (arg)
-       return luadoc.main(files, options)
-end
-
-main(arg)
diff --git a/contrib/luadoc/lua/luadoc/config.lua b/contrib/luadoc/lua/luadoc/config.lua
deleted file mode 100644 (file)
index 9e4b9de..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
--------------------------------------------------------------------------------
--- LuaDoc configuration file. This file contains the default options for 
--- luadoc operation. These options can be overriden by the command line tool
--- @see luadoc.print_help
--- @release $Id: config.lua,v 1.6 2007/04/18 14:28:39 tomas Exp $
--------------------------------------------------------------------------------
-
-module "luadoc.config"
-
--------------------------------------------------------------------------------
--- Default options
--- @class table
--- @name default_options
--- @field output_dir default output directory for generated documentation, used
--- by several doclets
--- @field taglet parser used to analyze source code input
--- @field doclet documentation generator
--- @field template_dir directory with documentation templates, used by the html
--- doclet
--- @field verbose command line tool configuration to output processing 
--- information
-
-local default_options = {
-       output_dir = "",
-       taglet = "luadoc.taglet.standard",
-       doclet = "luadoc.doclet.html",
-       -- TODO: find a way to define doclet specific options
-       template_dir = "luadoc/doclet/html/",
-       nomodules = false,
-       nofiles = false,
-       verbose = true,
-}
-
-return default_options
diff --git a/contrib/luadoc/lua/luadoc/doclet/debug.lua b/contrib/luadoc/lua/luadoc/doclet/debug.lua
deleted file mode 100644 (file)
index 0b75f84..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
------------------------------------------------------------------
--- LuaDoc debugging facilities.
--- @release $Id: debug.lua,v 1.3 2007/04/18 14:28:39 tomas Exp $
------------------------------------------------------------------
-
-module "luadoc.doclet.debug"
-
-function printline()
-       print(string.rep('-', 79))
-end
-
------------------------------------------------------------------
--- Print debug information about document
--- @param doc Table with the structured documentation.
-
-function start (doc)
-       print("Files:")
-       for _, filepath in ipairs(doc.files) do
-               print('\t', filepath)
-       end
-       printline()
-
-       print("Modules:")
-       for _, modulename in ipairs(doc.modules) do
-               print('\t', modulename)
-       end
-       printline()
-       
-       for i, v in pairs(doc.files) do
-               print('\t', i, v)
-       end
-       printline()
-       for i, v in pairs(doc.files[doc.files[1]]) do
-               print(i, v)
-       end
-       
-       printline()
-       for i, v in pairs(doc.files[doc.files[1]].doc[1]) do
-               print(i, v)
-       end
-       printline()
-       print("Params")
-       for i, v in pairs(doc.files[doc.files[1]].doc[1].param) do
-               print(i, v)
-       end
-end
diff --git a/contrib/luadoc/lua/luadoc/doclet/formatter.lua b/contrib/luadoc/lua/luadoc/doclet/formatter.lua
deleted file mode 100644 (file)
index 2d72538..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
--------------------------------------------------------------------------------
--- Doclet to format source code according to LuaDoc standard tags. This doclet
--- (re)write .lua files adding missing standard tags. Texts are formatted to
--- 80 columns and function parameters are added based on code analysis.
---
--- @release $Id: formatter.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $
--------------------------------------------------------------------------------
-
-local util = require "luadoc.util"
-local assert, ipairs, pairs, type = assert, ipairs, pairs, type
-local string = require"string"
-local table = require"table"
-
-module "luadoc.doclet.formatter"
-
-options = {
-       output_dir = "./",
-}
-
--------------------------------------------------------------------------------
--- Assembly the output filename for an input file.
--- TODO: change the name of this function
-function out_file (filename)
-       local h = filename
-       h = options.output_dir..h
-       return h
-end
-
--------------------------------------------------------------------------------
--- Generate a new lua file for each input lua file. If the user does not 
--- specify a different output directory input files will be rewritten.
--- @param doc documentation table
-
-function start (doc)
-       local todo = "<TODO>"
-       
-       -- Process files
-       for i, file_doc in ipairs(doc.files) do
-               -- assembly the filename
-               local filename = out_file(file_doc.name)
-               luadoc.logger:info(string.format("generating file `%s'", filename))
-
-               -- TODO: confirm file overwrite
-               local f = posix.open(filename, "w")
-               assert(f, string.format("could not open `%s' for writing", filename))
-
-               for _, block in ipairs(file_doc.doc) do
-
-                       -- write reorganized comments
-                       f:write(string.rep("-", 80).."\n")
-                       
-                       -- description
-                       f:write(util.comment(util.wrap(block.description, 77)))
-                       f:write("\n")
-                       
-                       if block.class == "function" then
-                               -- parameters
-                               table.foreachi(block.param, function (_, param_name)
-                                       f:write(util.comment(util.wrap(string.format("@param %s %s", param_name, block.param[param_name] or todo), 77)))
-                                       f:write("\n")
-                               end)
-                               
-                               -- return
-                               if type(block.ret) == "table" then
-                                       table.foreachi(block.ret, function (_, ret)
-                                               f:write(util.comment(util.wrap(string.format("@return %s", ret), 77)).."\n")
-                                       end)
-                               else
-                                       f:write(util.comment(util.wrap(string.format("@return %s", block.ret or todo), 77)).."\n")
-                               end
-                       end
-                       
-                       -- TODO: usage
-                       -- TODO: see
-
-                       -- write code
-                       for _, line in ipairs(block.code) do
-                               f:write(line.."\n")
-                       end
-               end
-               
-               f:close()
-       end
-end
diff --git a/contrib/luadoc/lua/luadoc/doclet/html.lua b/contrib/luadoc/lua/luadoc/doclet/html.lua
deleted file mode 100644 (file)
index e77fb74..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
--------------------------------------------------------------------------------
--- Doclet that generates HTML output. This doclet generates a set of html files
--- based on a group of templates. The main templates are:
--- <ul>
--- <li>index.lp: index of modules and files;</li>
--- <li>file.lp: documentation for a lua file;</li>
--- <li>module.lp: documentation for a lua module;</li>
--- <li>function.lp: documentation for a lua function. This is a
--- sub-template used by the others.</li>
--- </ul>
---
--- @release $Id: html.lua,v 1.29 2007/12/21 17:50:48 tomas Exp $
--------------------------------------------------------------------------------
-
-local assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type = assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type
-local io = require"io"
-local posix = require "nixio.fs"
-local lp = require "luadoc.lp"
-local luadoc = require"luadoc"
-local package = package
-local string = require"string"
-local table = require"table"
-
-module "luadoc.doclet.html"
-
--------------------------------------------------------------------------------
--- Looks for a file `name' in given path. Removed from compat-5.1
--- @param path String with the path.
--- @param name String with the name to look for.
--- @return String with the complete path of the file found
---     or nil in case the file is not found.
-
-local function search (path, name)
-  for c in string.gfind(path, "[^;]+") do
-    c = string.gsub(c, "%?", name)
-    local f = io.open(c)
-    if f then   -- file exist?
-      f:close()
-      return c
-    end
-  end
-  return nil    -- file not found
-end
-
--------------------------------------------------------------------------------
--- Include the result of a lp template into the current stream.
-
-function include (template, env)
-       -- template_dir is relative to package.path
-       local templatepath = options.template_dir .. template
-
-       -- search using package.path (modified to search .lp instead of .lua
-       local search_path = string.gsub(package.path, "%.lua", "")
-       local templatepath = search(search_path, templatepath)
-       assert(templatepath, string.format("template `%s' not found", template))
-
-       env = env or {}
-       env.table = table
-       env.io = io
-       env.lp = lp
-       env.ipairs = ipairs
-       env.tonumber = tonumber
-       env.tostring = tostring
-       env.type = type
-       env.luadoc = luadoc
-       env.options = options
-
-       return lp.include(templatepath, env)
-end
-
--------------------------------------------------------------------------------
--- Returns a link to a html file, appending "../" to the link to make it right.
--- @param html Name of the html file to link to
--- @return link to the html file
-
-function link (html, from)
-       local h = html
-       from = from or ""
-       string.gsub(from, "/", function () h = "../" .. h end)
-       return h
-end
-
--------------------------------------------------------------------------------
--- Returns the name of the html file to be generated from a module.
--- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
--- @param modulename Name of the module to be processed, may be a .lua file or
--- a .luadoc file.
--- @return name of the generated html file for the module
-
-function module_link (modulename, doc, from)
-       -- TODO: replace "." by "/" to create directories?
-       -- TODO: how to deal with module names with "/"?
-       assert(modulename)
-       assert(doc)
-       from = from or ""
-
-       if doc.modules[modulename] == nil then
---             logger:error(string.format("unresolved reference to module `%s'", modulename))
-               return
-       end
-
-       local href = "modules/" .. modulename .. ".html"
-       string.gsub(from, "/", function () href = "../" .. href end)
-       return href
-end
-
--------------------------------------------------------------------------------
--- Returns the name of the html file to be generated from a lua(doc) file.
--- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
--- @param to Name of the file to be processed, may be a .lua file or
--- a .luadoc file.
--- @param from path of where am I, based on this we append ..'s to the
--- beginning of path
--- @return name of the generated html file
-
-function file_link (to, from)
-       assert(to)
-       from = from or ""
-
-       local href = to
-       href = string.gsub(href, "lua$", "html")
-       href = string.gsub(href, "luadoc$", "html")
-       href = "files/" .. href
-       string.gsub(from, "/", function () href = "../" .. href end)
-       return href
-end
-
--------------------------------------------------------------------------------
--- Returns a link to a function or to a table
--- @param fname name of the function or table to link to.
--- @param doc documentation table
--- @param kind String specying the kinf of element to link ("functions" or "tables").
-
-function link_to (fname, doc, module_doc, file_doc, from, kind)
-       assert(fname)
-       assert(doc)
-       from = from or ""
-       kind = kind or "functions"
-
-       if file_doc then
-               for _, func_name in pairs(file_doc[kind]) do
-                       if func_name == fname then
-                               return file_link(file_doc.name, from) .. "#" .. fname
-                       end
-               end
-       end
-
-       if module_doc and module_doc[kind] then
-               for func_name, tbl in pairs(module_doc[kind]) do
-                       if func_name == fname then
-                               return "#" .. fname
-                       end
-               end
-       end
-
-       local _, _, modulename, fname = string.find(fname, "^(.-)[%.%:]?([^%.%:]*)$")
-       assert(fname)
-
-       -- if fname does not specify a module, use the module_doc
-       if string.len(modulename) == 0 and module_doc then
-               modulename = module_doc.name
-       end
-
-       local module_doc = doc.modules[modulename]
-       if not module_doc then
---             logger:error(string.format("unresolved reference to function `%s': module `%s' not found", fname, modulename))
-               return
-       end
-
-       for _, func_name in pairs(module_doc[kind]) do
-               if func_name == fname then
-                       return module_link(modulename, doc, from) .. "#" .. fname
-               end
-       end
-
---     logger:error(string.format("unresolved reference to function `%s' of module `%s'", fname, modulename))
-end
-
--------------------------------------------------------------------------------
--- Make a link to a file, module or function
-
-function symbol_link (symbol, doc, module_doc, file_doc, from)
-       assert(symbol)
-       assert(doc)
-
-       local href =
---             file_link(symbol, from) or
-               module_link(symbol, doc, from) or
-               link_to(symbol, doc, module_doc, file_doc, from, "functions") or
-               link_to(symbol, doc, module_doc, file_doc, from, "tables")
-
-       if not href then
-               logger:error(string.format("unresolved reference to symbol `%s'", symbol))
-       end
-
-       return href or ""
-end
-
--------------------------------------------------------------------------------
--- Assembly the output filename for an input file.
--- TODO: change the name of this function
-function out_file (filename)
-       local h = filename
-       h = string.gsub(h, "lua$", "html")
-       h = string.gsub(h, "luadoc$", "html")
-       h = "files/" .. h
---     h = options.output_dir .. string.gsub (h, "^.-([%w_]+%.html)$", "%1")
-       h = options.output_dir .. h
-       return h
-end
-
--------------------------------------------------------------------------------
--- Assembly the output filename for a module.
--- TODO: change the name of this function
-function out_module (modulename)
-       local h = modulename .. ".html"
-       h = "modules/" .. h
-       h = options.output_dir .. h
-       return h
-end
-
------------------------------------------------------------------
--- Generate the output.
--- @param doc Table with the structured documentation.
-
-function start (doc)
-       -- Generate index file
-       if (#doc.files > 0 or #doc.modules > 0) and (not options.noindexpage) then
-               local filename = options.output_dir.."index.html"
-               logger:info(string.format("generating file `%s'", filename))
-               local f = posix.open(filename, "w")
-               assert(f, string.format("could not open `%s' for writing", filename))
-               io.output(f)
-               include("index.lp", { doc = doc })
-               f:close()
-       end
-
-       -- Process modules
-       if not options.nomodules then
-               for _, modulename in ipairs(doc.modules) do
-                       local module_doc = doc.modules[modulename]
-                       -- assembly the filename
-                       local filename = out_module(modulename)
-                       logger:info(string.format("generating file `%s'", filename))
-
-                       local f = posix.open(filename, "w")
-                       assert(f, string.format("could not open `%s' for writing", filename))
-                       io.output(f)
-                       include("module.lp", { doc = doc, module_doc = module_doc })
-                       f:close()
-               end
-       end
-
-       -- Process files
-       if not options.nofiles then
-               for _, filepath in ipairs(doc.files) do
-                       local file_doc = doc.files[filepath]
-                       -- assembly the filename
-                       local filename = out_file(file_doc.name)
-                       logger:info(string.format("generating file `%s'", filename))
-
-                       local f = posix.open(filename, "w")
-                       assert(f, string.format("could not open `%s' for writing", filename))
-                       io.output(f)
-                       include("file.lp", { doc = doc, file_doc = file_doc} )
-                       f:close()
-               end
-       end
-
-       -- copy extra files
-       local f = posix.open(options.output_dir.."luadoc.css", "w")
-       io.output(f)
-       include("luadoc.css")
-       f:close()
-end
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/constant.lp b/contrib/luadoc/lua/luadoc/doclet/html/constant.lp
deleted file mode 100644 (file)
index 2e35392..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<%
-if module_doc then
-       from = "modules/"..module_doc.name
-elseif file_doc then
-       from = "files/.."..file_doc.name
-else
-       from = ""
-end
-%>
-
-<dt><%=const.private and "local " or ""%><a name="<%=const.name%>"></a><strong><%=const.name:gsub(".+%.","")%></strong></dt>
-<dd>
-<%=const.description or ""%>
-
-<%if type(const.see) == "string" then %>
-<h3>See also:</h3>
-       <a href="<%=const.see%>"><%=const.see%></a>
-<%elseif type(const.see) == "table" and #const.see > 0 then %>
-<h3>See also:</h3>
-<ul>
-       <%for i = 1, #const.see do%>
-       <li><a href="<%=luadoc.doclet.html.symbol_link(const.see[i], doc, module_doc, file_doc, from)%>">
-               <%=const.see[i]:gsub(".+%.","")%>
-       </a>
-       <%end%>
-</ul
-<%end%>
-</dd>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/file.lp b/contrib/luadoc/lua/luadoc/doclet/html/file.lp
deleted file mode 100644 (file)
index 67926b4..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-<head>
-    <title>Reference</title>
-    <link rel="stylesheet" href="<%=luadoc.doclet.html.link('luadoc.css', 'files/'..file_doc.name)%>" type="text/css" />
-       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
-</head>
-
-<body>
-<div id="container">
-
-<div id="product">
-       <div id="product_logo"></div>
-       <div id="product_name"><big><b></b></big></div>
-       <div id="product_description"></div>
-</div> <!-- id="product" -->
-
-<div id="main">
-
-<div id="navigation">
-<%=luadoc.doclet.html.include("menu.lp", { doc=doc, file_doc=file_doc })%>
-
-</div> <!-- id="navigation" -->
-
-<div id="content">
-
-<h1>File <code><%=file_doc.name%></code></h1>
-
-<%if file_doc.description then%>
-<p><%=file_doc.description%></p>
-<%end%>
-<%if file_doc.author then%>
-<p><b><%= #file_doc.author>1 and "Authors" or "Author" %>:</b>
-<table class="authors_list">
-<%for _, author in ipairs(file_doc.author) do%>
-       <tr><td class="name"><%= author %></td></tr>
-<%end%>
-</table>
-</p>
-<%end%>
-<%if file_doc.copyright then%>
-<p>Copyright &copy;<%=file_doc.copyright%></p>
-<%end%>
-<%if file_doc.release then%>
-<p><small><b>Release:</b> <%=file_doc.release%></small></p>
-<%end%>
-
-<%if #file_doc.functions > 0 then%>
-<h2>Functions</h2>
-<table class="function_list">
-<%for _, func_name in ipairs(file_doc.functions) do
-  local func_data = file_doc.functions[func_name]%>
-       <tr>
-       <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_name%></a>&nbsp;(<%=table.concat(func_data.param, ", ")%>)</td>
-       <td class="summary"><%=func_data.summary%></td>
-       </tr>
-<%end%>
-</table>
-<%end%>
-
-
-<%if #file_doc.tables > 0 then%>
-<h2>Tables</h2>
-<table class="table_list">
-<%for _, tab_name in ipairs(file_doc.tables) do%>
-       <tr>
-       <td class="name" nowrap><a href="#<%=tab_name%>"><%=tab_name%></a></td>
-       <td class="summary"><%=file_doc.tables[tab_name].summary%></td>
-       </tr>
-<%end%>
-</table>
-<%end%>
-
-
-<br/>
-<br/>
-
-
-
-<%if #file_doc.functions > 0 then%>
-<h2><a name="functions"></a>Functions</h2>
-<dl class="function">
-<%for _, func_name in ipairs(file_doc.functions) do%>
-<%=luadoc.doclet.html.include("function.lp", { doc=doc, file_doc=file_doc, func=file_doc.functions[func_name] })%>
-<%end%>
-</dl>
-<%end%>
-
-
-<%if #file_doc.tables > 0 then%>
-<h2><a name="tables"></a>Tables</h2>
-<dl class="table">
-<%for _, tab_name in ipairs(file_doc.tables) do%>
-<%=luadoc.doclet.html.include("table.lp", { doc=doc, file_doc=file_doc, tab=file_doc.tables[tab_name] })%>
-<%end%>
-</dl>
-<%end%>
-
-
-
-</div> <!-- id="content" -->
-
-</div> <!-- id="main" -->
-
-<div id="about">
-       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
-</div> <!-- id="about" -->
-
-</div> <!-- id="container" --> 
-</body>
-</html>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/function.lp b/contrib/luadoc/lua/luadoc/doclet/html/function.lp
deleted file mode 100644 (file)
index a870ff8..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<%
-if module_doc then
-       from = "modules/"..module_doc.name
-elseif file_doc then
-       from = "files/.."..file_doc.name
-else
-       from = ""
-end
-%>
-
-<dt><%=func.private and "local " or ""%><a name="<%=func.name%>"></a><strong><%=(oop and func.name:gsub("%.",":") or func.name:gsub(".+%.",""))%></strong>&nbsp;(<%=table.concat(func.param or {}, ", ")%>)</dt>
-<dd>
-<%=func.description or ""%>
-
-<%if type(func.param) == "table" and #func.param > 0 then%>
-<h3>Parameters</h3>
-<ul>
-       <%for p = 1, #func.param do%>
-       <li>
-         <%=func.param[p]%>: <%=func.param[func.param[p]] or ""%>
-       </li>
-       <%end%>
-</ul>
-<%end%>
-
-
-<%if type(func.usage) == "string" then%>
-<h3>Usage:</h3>
-<%=func.usage%>
-<%elseif type(func.usage) == "table" then%>
-<h3>Usage</h3>
-<ul>
-       <%for _, usage in ipairs(func.usage) do%>
-       <li><%= usage %>
-       <%end%>
-</ul>
-<%end%>
-
-<%if type(func.ret) == "string" then%>
-<h3>Return value:</h3>
-<%=func.ret%>
-<%elseif type(func.ret) == "table" then%>
-<h3>Return values:</h3>
-<ol>
-       <%for _, ret in ipairs(func.ret) do%>
-       <li><%= ret %>
-       <%end%>
-</ol>
-<%end%>
-
-<%if type(func.see) == "string" then %>
-<h3>See also:</h3>
-       <a href="<%=func.see%>"><%=func.see%></a>
-<%elseif type(func.see) == "table" and #func.see > 0 then %>
-<h3>See also:</h3>
-<ul>
-       <%for i = 1, #func.see do%>
-       <li><a href="<%=luadoc.doclet.html.symbol_link(func.see[i], doc, module_doc, file_doc, from)%>">
-               <%=(oop and func.see[i]:gsub("%.",":") or func.see[i]:gsub(".+%.",""))%>
-       </a>
-       <%end%>
-</ul
-<%end%>
-</dd>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/index.lp b/contrib/luadoc/lua/luadoc/doclet/html/index.lp
deleted file mode 100644 (file)
index b4b9f5c..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-<head>
-    <title>Reference</title>
-    <link rel="stylesheet" href="<%=luadoc.doclet.html.link("luadoc.css")%>" type="text/css" />
-       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
-</head>
-
-<body>
-<div id="container">
-
-<div id="product">
-       <div id="product_logo"></div>
-       <div id="product_name"><big><b></b></big></div>
-       <div id="product_description"></div>
-</div> <!-- id="product" -->
-
-<div id="main">
-
-<div id="navigation">
-<%=luadoc.doclet.html.include("menu.lp", { doc=doc })%>
-
-</div> <!-- id="navigation" -->
-
-<div id="content">
-
-
-<%if not options.nomodules and #doc.modules > 0 then%>
-<h2>Modules</h2>
-<table class="module_list">
-<!--<tr><td colspan="2">Modules</td></tr>-->
-<%for _, modulename in ipairs(doc.modules) do%>
-       <tr>
-               <td class="name"><a href="<%=luadoc.doclet.html.module_link(modulename, doc)%>"><%=modulename%></a></td>
-               <td class="summary"><%=doc.modules[modulename].summary%></td>
-       </tr>
-<%end%>
-</table>
-<%end%>
-
-
-
-<%if not options.nofiles and #doc.files > 0 then%>
-<h2>Files</h2>
-<table class="file_list">
-<!--<tr><td colspan="2">Files</td></tr>-->
-<%for _, filepath in ipairs(doc.files) do%>
-       <tr>
-               <td class="name"><a href="<%=luadoc.doclet.html.file_link(filepath)%>"><%=filepath%></a></td>
-               <td class="summary"></td>
-       </tr>
-<%end%>
-</table>
-<%end%>
-
-</div> <!-- id="content" -->
-
-</div> <!-- id="main" -->
-
-<div id="about">
-       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
-</div> <!-- id="about" -->
-
-</div> <!-- id="container" --> 
-</body>
-</html>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css b/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css
deleted file mode 100644 (file)
index bc0f98a..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-body { 
-    margin-left: 1em; 
-    margin-right: 1em; 
-    font-family: arial, helvetica, geneva, sans-serif;
-       background-color:#ffffff; margin:0px;
-}
-
-code {
-    font-family: "Andale Mono", monospace; 
-}
-
-tt {
-    font-family: "Andale Mono", monospace; 
-}
-
-body, td, th { font-size: 11pt; }
-
-h1, h2, h3, h4 { margin-left: 0em; }
-
-textarea, pre, tt { font-size:10pt; }
-body, td, th { color:#000000; }
-small { font-size:0.85em; }
-h1 { font-size:1.5em; }
-h2 { font-size:1.25em; }
-h3 { font-size:1.15em; }
-h4 { font-size:1.06em; }
-
-a:link { font-weight:bold; color: #004080; text-decoration: none; }
-a:visited { font-weight:bold; color: #006699; text-decoration: none; }
-a:link:hover { text-decoration:underline; }
-hr { color:#cccccc }
-img { border-width: 0px; }
-
-
-h3 { padding-top: 1em; }
-
-p { margin-left: 1em; }
-
-p.name { 
-    font-family: "Andale Mono", monospace; 
-    padding-top: 1em;
-    margin-left: 0em; 
-}
-
-blockquote { margin-left: 3em; }
-
-pre.example {
-    background-color: rgb(245, 245, 245);
-    border-top-width: 1px;
-    border-right-width: 1px;
-    border-bottom-width: 1px;
-    border-left-width: 1px;
-    border-top-style: solid;
-    border-right-style: solid;
-    border-bottom-style: solid;
-    border-left-style: solid;
-    border-top-color: silver;
-    border-right-color: silver;
-    border-bottom-color: silver;
-    border-left-color: silver;
-    padding: 1em;
-    margin-left: 1em;
-    margin-right: 1em;
-    font-family: "Andale Mono", monospace; 
-    font-size: smaller;
-}
-
-
-hr { 
-    margin-left: 0em;
-       background: #00007f; 
-       border: 0px;
-       height: 1px;
-}
-
-ul { list-style-type: disc; }
-
-table.index { border: 1px #00007f; }
-table.index td { text-align: left; vertical-align: top; }
-table.index ul { padding-top: 0em; margin-top: 0em; }
-
-table {
-    border: 1px solid black;
-       border-collapse: collapse;
-    margin-left: auto;
-    margin-right: auto;
-}
-th {
-    border: 1px solid black;
-    padding: 0.5em;
-}
-td {
-    border: 1px solid black;
-    padding: 0.5em;
-}
-div.header, div.footer { margin-left: 0em; }
-
-#container
-{
-       margin-left: 1em;
-       margin-right: 1em;
-       background-color: #f0f0f0;
-}
-
-#product
-{
-       text-align: center;
-       border-bottom: 1px solid #cccccc;
-       background-color: #ffffff;
-}
-
-#product big {
-       font-size: 2em;
-}
-
-#product_logo
-{
-}
-
-#product_name
-{
-}
-
-#product_description
-{
-}
-
-#main
-{
-       background-color: #f0f0f0;
-       border-left: 2px solid #cccccc;
-}
-
-#navigation
-{
-       float: left;
-       width: 18em;
-       margin: 0;
-       vertical-align: top;
-       background-color: #f0f0f0;
-       overflow:visible;
-}
-
-#navigation h1 {
-       background-color:#e7e7e7;
-       font-size:1.1em;
-       color:#000000;
-       text-align:left;
-       margin:0px;
-       padding:0.2em;
-       border-top:1px solid #dddddd;
-       border-bottom:1px solid #dddddd;
-}
-
-#navigation ul
-{
-       font-size:1em;
-       list-style-type: none;
-       padding: 0;
-       margin: 1px;
-}
-
-#navigation li
-{
-       text-indent: -1em;
-       margin: 0em 0em 0em 0.5em;
-       display: block;
-       padding: 3px 0px 0px 12px;
-}
-
-#navigation li li a
-{
-       padding: 0px 3px 0px -1em;
-}
-
-#content
-{
-       margin-left: 18em;
-       padding: 1em;
-       border-left: 2px solid #cccccc;
-       border-right: 2px solid #cccccc;
-       background-color: #ffffff;
-}
-
-#about
-{
-       clear: both;
-       margin: 0;
-       padding: 5px;
-       border-top: 2px solid #cccccc;
-       background-color: #ffffff;
-}
-
-@media print {
-       body { 
-               font: 12pt "Times New Roman", "TimeNR", Times, serif;
-       }
-       a { font-weight:bold; color: #004080; text-decoration: underline; }
-       
-       #main\r  {\r              background-color: #ffffff;\r             border-left: 0px;\r      }\r      
-       #container\r     {\r              margin-left: 2%;\r               margin-right: 2%;\r              background-color: #ffffff;\r     }
-       
-       #content\r       {\r              margin-left: 0px;\r              padding: 1em;\r          border-left: 0px;\r              border-right: 0px;\r             background-color: #ffffff;\r     }
-       
-       #navigation\r    {\r              display: none;
-       }
-       pre.example {
-               font-family: "Andale Mono", monospace; 
-               font-size: 10pt;
-               page-break-inside: avoid;
-       }
-}
-
-table.module_list td
-{
-       border-width: 1px;
-       padding: 3px;
-       border-style: solid;
-       border-color: #cccccc;
-}
-table.module_list td.name { background-color: #f0f0f0; }
-table.module_list td.summary { width: 100%; }
-
-table.file_list
-{
-       border-width: 1px;
-       border-style: solid;
-       border-color: #cccccc;
-       border-collapse: collapse;
-}
-table.file_list td
-{
-       border-width: 1px;
-       padding: 3px;
-       border-style: solid;
-       border-color: #cccccc;
-}
-table.file_list td.name { background-color: #f0f0f0; }
-table.file_list td.summary { width: 100%; }
-
-
-table.function_list
-{
-       border-width: 1px;
-       border-style: solid;
-       border-color: #cccccc;
-       border-collapse: collapse;
-}
-table.function_list td
-{
-       border-width: 1px;
-       padding: 3px;
-       border-style: solid;
-       border-color: #cccccc;
-}
-table.function_list td.name { background-color: #f0f0f0; }
-table.function_list td.summary { width: 100%; }
-
-
-table.table_list
-{
-       border-width: 1px;
-       border-style: solid;
-       border-color: #cccccc;
-       border-collapse: collapse;
-}
-table.table_list td
-{
-       border-width: 1px;
-       padding: 3px;
-       border-style: solid;
-       border-color: #cccccc;
-}
-table.table_list td.name { background-color: #f0f0f0; }
-table.table_list td.summary { width: 100%; }
-
-dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
-dl.function dd {padding-bottom: 1em;}
-dl.function h3 {padding: 0; margin: 0; font-size: medium;}
-
-dl.table dt {border-top: 1px solid #ccc; padding-top: 1em;}
-dl.table dd {padding-bottom: 1em;}
-dl.table h3 {padding: 0; margin: 0; font-size: medium;}
-
-#TODO: make module_list, file_list, function_list, table_list inherit from a list
-
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/menu.lp b/contrib/luadoc/lua/luadoc/doclet/html/menu.lp
deleted file mode 100644 (file)
index 0fe3652..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-<%
-if module_doc then
-       from = "modules/"..module_doc.name
-elseif file_doc then
-       from = "files/.."..file_doc.name
-else
-       from = ""
-end
-%>
-
-<h1>LuaDoc</h1>
-<ul>
-       <%if not module_doc and not file_doc then%>
-       <li><strong>Index</strong></li>
-       <%else%>
-       <li><a href="<%=luadoc.doclet.html.link("index.html", from)%>">Index</a></li>
-       <%end%>
-</ul>
-
-
-<!-- Module list -->
-<%if not options.nomodules and #doc.modules > 0 then%>
-<h1>Modules</h1>
-<ul>
-<%for _, modulename in ipairs(doc.modules) do
-       if module_doc and module_doc.name == modulename then%>
-       <li><strong><%=modulename%></strong></li>
-       <%else%>
-       <li>
-               <a href="<%=luadoc.doclet.html.module_link(modulename, doc, from)%>"><%=modulename%></a>
-       </li>
-<%     end
-end%>
-</ul>
-<%end%>
-
-
-<!-- File list -->
-<%if not options.nofiles and #doc.files > 0 then%>
-<h1>Files</h1>
-<ul>
-<%for _, filepath in ipairs(doc.files) do
-       if file_doc and file_doc.name == filepath then%>
-       <li><strong><%=filepath%></strong></li>
-       <%else%>
-       <li>
-               <a href="<%=luadoc.doclet.html.file_link(filepath, from)%>"><%=filepath%></a>
-       </li>
-<%     end
-end%>
-</ul>
-<%end%>
-
-
-
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/module.lp b/contrib/luadoc/lua/luadoc/doclet/html/module.lp
deleted file mode 100644 (file)
index daa7086..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html>
-<head>
-    <title>Reference</title>
-    <link rel="stylesheet" href="<%=luadoc.doclet.html.link('luadoc.css', 'modules/'..module_doc.name)%>" type="text/css" />
-       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
-</head>
-
-<body>
-<div id="container">
-
-<div id="product">
-       <div id="product_logo"></div>
-       <div id="product_name"><big><b></b></big></div>
-       <div id="product_description"></div>
-</div> <!-- id="product" -->
-
-<div id="main">
-
-<div id="navigation">
-<%=luadoc.doclet.html.include("menu.lp", { doc=doc, module_doc=module_doc })%>
-<% oop = ( module_doc.doc[1].cstyle == "instance" ) and true or false %>
-
-</div><!-- id="navigation" -->
-
-<div id="content">
-
-<h1><%=( oop and "Object Instance" or "Class" )%> <code><%=module_doc.name%></code></h1>
-
-<p><%=module_doc.description%></p>
-<%if module_doc.author then%>
-<p><b><%= #module_doc.author>1 and "Authors" or "Author" %>:</b>
-<table class="authors_list">
-<%for _, author in ipairs(module_doc.author) do%>
-       <tr><td class="name"><%= author %></td></tr>
-<%end%>
-</table>
-</p>
-<%end%>
-<%if module_doc.copyright then%>
-<p>Copyright&copy; <%=module_doc.copyright%></p>
-<%end%>
-<%if module_doc.release then%>
-<p><small><b>Release:</b> <%=module_doc.release%></small></p>
-<%end%>
-
-<%if #module_doc.constants > 0 then %>
-<h2>Constants</h2>
-<table class="function_list">
-<%for _, const_name in ipairs(module_doc.constants) do
-  local const_data = module_doc.constants[const_name]%>
-       <tr>
-       <td class="name" nowrap><code><%=const_data.private and "local " or ""%><%=(const_name:gsub(".+%.",""))%></code></td>
-       <td class="summary"><%=const_data.summary%></td>
-       </tr>
-<%end%>
-</table>
-<%end%>
-
-<%if #module_doc.functions > 0 then %>
-<h2>Functions</h2>
-<table class="function_list">
-<%for _, func_name in ipairs(module_doc.functions) do
-  local func_data = module_doc.functions[func_name]%>
-       <tr>
-       <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=(oop and func_name:gsub("%.",":") or func_name:gsub(".+%.",""))%></a>&nbsp;(<%=table.concat(module_doc.functions[func_name].param or {}, ", ")%>)</td>
-       <td class="summary"><%=module_doc.functions[func_name].summary%></td>
-       </tr>
-<%end%>
-</table>
-<%end%>
-
-
-<%if #module_doc.tables > 0 then%>
-<h2>Tables</h2>
-<table class="table_list">
-<%for _, tab_name in ipairs(module_doc.tables) do%>
-       <tr>
-       <td class="name" nowrap><a href="#<%=tab_name%>"><%=tab_name%></a></td>
-       <td class="summary"><%=module_doc.tables[tab_name].summary%></td>
-       </tr>
-<%end%>
-</table>
-<%end%>
-
-
-<br/>
-<br/>
-
-<%if #module_doc.functions > 0 then%>
-<h2><a name="functions"></a>Functions</h2>
-<dl class="function">
-<%for _, func_name in ipairs(module_doc.functions) do%>
-<%=luadoc.doclet.html.include("function.lp", { doc=doc, module_doc=module_doc, func=module_doc.functions[func_name], oop=oop })%>
-<%end%>
-</dl>
-<%end%>
-
-<%if #module_doc.tables > 0 then%>
-<h2><a name="tables"></a>Tables</h2>
-<dl class="table">
-<%for _, tab_name in ipairs(module_doc.tables) do%>
-<%=luadoc.doclet.html.include("table.lp", { doc=doc, module_doc=module_doc, tab=module_doc.tables[tab_name] })%>
-<%end%>
-</dl>
-<%end%>
-
-
-</div> <!-- id="content" -->
-
-</div> <!-- id="main" -->
-
-<div id="about">
-       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
-</div> <!-- id="about" -->
-
-</div> <!-- id="container" -->
-</body>
-</html>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/table.lp b/contrib/luadoc/lua/luadoc/doclet/html/table.lp
deleted file mode 100644 (file)
index 5cd0239..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<dt><a name="<%=tab.name%>"></a><strong><%=tab.name%></strong></dt>
-<dd><%=tab.description%>
-
-<%if type(tab.field) == "table" and #tab.field > 0 then%>
-<em>Fields</em>
-<ul>
-       <%for p = 1, #tab.field do%>
-       <li>
-         <%=tab.field[p]%>: <%=tab.field[tab.field[p]] or ""%>
-       </li>
-       <%end%>
-</ul>
-<%end%>
-
-</dd>
diff --git a/contrib/luadoc/lua/luadoc/doclet/raw.lua b/contrib/luadoc/lua/luadoc/doclet/raw.lua
deleted file mode 100644 (file)
index 1e880b8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
------------------------------------------------------------------
--- @release $Id: raw.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $
------------------------------------------------------------------
-
-module "luadoc.doclet.raw"
-
------------------------------------------------------------------
--- Generate the output.
--- @param doc Table with the structured documentation.
-
-function start (doc)
-end
diff --git a/contrib/luadoc/lua/luadoc/init.lua b/contrib/luadoc/lua/luadoc/init.lua
deleted file mode 100644 (file)
index 649515d..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
--------------------------------------------------------------------------------
--- LuaDoc main function.
--- @release $Id: init.lua,v 1.4 2008/02/17 06:42:51 jasonsantos Exp $
--------------------------------------------------------------------------------
-
-local require = require
-
-local util = require "luadoc.util"
-
-logger = {}
-
-module ("luadoc")
-
--------------------------------------------------------------------------------
--- LuaDoc version number.
-
-_COPYRIGHT = "Copyright (c) 2003-2007 The Kepler Project"
-_DESCRIPTION = "Documentation Generator Tool for the Lua language"
-_VERSION = "LuaDoc 3.0.1"
-
--------------------------------------------------------------------------------
--- Main function
--- @see luadoc.doclet.html, luadoc.doclet.formatter, luadoc.doclet.raw
--- @see luadoc.taglet.standard
-
-function main (files, options)
-       logger = util.loadlogengine(options)
-
-       -- load config file
-       if options.config ~= nil then
-               -- load specified config file
-               dofile(options.config)
-       else
-               -- load default config file
-               require("luadoc.config")
-       end
-       
-       local taglet = require(options.taglet)
-       local doclet = require(options.doclet)
-
-       -- analyze input
-       taglet.options = options
-       taglet.logger = logger
-       local doc = taglet.start(files)
-
-       -- generate output
-       doclet.options = options
-       doclet.logger = logger
-       doclet.start(doc)
-end
diff --git a/contrib/luadoc/lua/luadoc/lp.lua b/contrib/luadoc/lua/luadoc/lp.lua
deleted file mode 100644 (file)
index adf84f9..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-----------------------------------------------------------------------------
--- Lua Pages Template Preprocessor.
---
--- @release $Id: lp.lua,v 1.7 2007/04/18 14:28:39 tomas Exp $
-----------------------------------------------------------------------------
-
-local assert, error, getfenv, loadstring, setfenv = assert, error, getfenv, loadstring, setfenv
-local find, format, gsub, strsub = string.find, string.format, string.gsub, string.sub
-local concat, tinsert = table.concat, table.insert
-local open = io.open
-
-module (...)
-
-----------------------------------------------------------------------------
--- function to do output
-local outfunc = "io.write"
--- accepts the old expression field: `$| <Lua expression> |$'
-local compatmode = true
-
---
--- Builds a piece of Lua code which outputs the (part of the) given string.
--- @param s String.
--- @param i Number with the initial position in the string.
--- @param f Number with the final position in the string (default == -1).
--- @return String with the correspondent Lua code which outputs the part of the string.
---
-local function out (s, i, f)
-       s = strsub(s, i, f or -1)
-       if s == "" then return s end
-       -- we could use `%q' here, but this way we have better control
-       s = gsub(s, "([\\\n\'])", "\\%1")
-       -- substitute '\r' by '\'+'r' and let `loadstring' reconstruct it
-       s = gsub(s, "\r", "\\r")
-       return format(" %s('%s'); ", outfunc, s)
-end
-
-
-----------------------------------------------------------------------------
--- Translate the template to Lua code.
--- @param s String to translate.
--- @return String with translated code.
-----------------------------------------------------------------------------
-function translate (s)
-       if compatmode then
-               s = gsub(s, "$|(.-)|%$", "<?lua = %1 ?>")
-               s = gsub(s, "<!%-%-$$(.-)$$%-%->", "<?lua %1 ?>")
-       end
-       s = gsub(s, "<%%(.-)%%>", "<?lua %1 ?>")
-       local res = {}
-       local start = 1   -- start of untranslated part in `s'
-       while true do
-               local ip, fp, target, exp, code = find(s, "<%?(%w*)[ \t]*(=?)(.-)%?>", start)
-               if not ip then break end
-               tinsert(res, out(s, start, ip-1))
-               if target ~= "" and target ~= "lua" then
-                       -- not for Lua; pass whole instruction to the output
-                       tinsert(res, out(s, ip, fp))
-               else
-                       if exp == "=" then   -- expression?
-                               tinsert(res, format(" %s(%s);", outfunc, code))
-                       else  -- command
-                               tinsert(res, format(" %s ", code))
-                       end
-               end
-               start = fp + 1
-       end
-       tinsert(res, out(s, start))
-       return concat(res)
-end
-
-
-----------------------------------------------------------------------------
--- Defines the name of the output function.
--- @param f String with the name of the function which produces output.
-
-function setoutfunc (f)
-       outfunc = f
-end
-
-----------------------------------------------------------------------------
--- Turns on or off the compatibility with old CGILua 3.X behavior.
--- @param c Boolean indicating if the compatibility mode should be used.
-
-function setcompatmode (c)
-       compatmode = c
-end
-
-----------------------------------------------------------------------------
--- Internal compilation cache.
-
-local cache = {}
-
-----------------------------------------------------------------------------
--- Translates a template into a Lua function.
--- Does NOT execute the resulting function.
--- Uses a cache of templates.
--- @param string String with the template to be translated.
--- @param chunkname String with the name of the chunk, for debugging purposes.
--- @return Function with the resulting translation.
-
-function compile (string, chunkname)
-       local f, err = cache[string]
-       if f then return f end
-       f, err = loadstring (translate (string), chunkname)
-       if not f then error (err, 3) end
-       cache[string] = f
-       return f
-end
-
-----------------------------------------------------------------------------
--- Translates and executes a template in a given file.
--- The translation creates a Lua function which will be executed in an
--- optionally given environment.
--- @param filename String with the name of the file containing the template.
--- @param env Table with the environment to run the resulting function.
-
-function include (filename, env)
-       -- read the whole contents of the file
-       local fh = assert (open (filename))
-       local src = fh:read("*a")
-       fh:close()
-       -- translates the file into a function
-       local prog = compile (src, '@'..filename)
-       local _env
-       if env then
-               _env = getfenv (prog)
-               setfenv (prog, env)
-       end
-       prog ()
-end
diff --git a/contrib/luadoc/lua/luadoc/taglet/standard.lua b/contrib/luadoc/lua/luadoc/taglet/standard.lua
deleted file mode 100644 (file)
index 17a3058..0000000
+++ /dev/null
@@ -1,565 +0,0 @@
--------------------------------------------------------------------------------
--- @release $Id: standard.lua,v 1.39 2007/12/21 17:50:48 tomas Exp $
--------------------------------------------------------------------------------
-
-local assert, pairs, tostring, type = assert, pairs, tostring, type
-local io = require "io"
-local posix = require "nixio.fs"
-local luadoc = require "luadoc"
-local util = require "luadoc.util"
-local tags = require "luadoc.taglet.standard.tags"
-local string = require "string"
-local table = require "table"
-
-module 'luadoc.taglet.standard'
-
--------------------------------------------------------------------------------
--- Creates an iterator for an array base on a class type.
--- @param t array to iterate over
--- @param class name of the class to iterate over
-
-function class_iterator (t, class)
-       return function ()
-               local i = 1
-               return function ()
-                       while t[i] and t[i].class ~= class do
-                               i = i + 1
-                       end
-                       local v = t[i]
-                       i = i + 1
-                       return v
-               end
-       end
-end
-
--- Patterns for function recognition
-local identifiers_list_pattern = "%s*(.-)%s*"
-local identifier_pattern = "[^%(%s]+"
-local function_patterns = {
-       "^()%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)",
-       "^%s*(local%s)%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)",
-       "^()%s*("..identifier_pattern..")%s*%=%s*function%s*%("..identifiers_list_pattern.."%)",
-}
-
--------------------------------------------------------------------------------
--- Checks if the line contains a function definition
--- @param line string with line text
--- @return function information or nil if no function definition found
-
-local function check_function (line)
-       line = util.trim(line)
-
-       local info = table.foreachi(function_patterns, function (_, pattern)
-               local r, _, l, id, param = string.find(line, pattern)
-               if r ~= nil then
-                       return {
-                               name = id,
-                               private = (l == "local"),
-                               param = { } --util.split("%s*,%s*", param),
-                       }
-               end
-       end)
-
-       -- TODO: remove these assert's?
-       if info ~= nil then
-               assert(info.name, "function name undefined")
-               assert(info.param, string.format("undefined parameter list for function `%s'", info.name))
-       end
-
-       return info
-end
-
--------------------------------------------------------------------------------
--- Checks if the line contains a module definition.
--- @param line string with line text
--- @param currentmodule module already found, if any
--- @return the name of the defined module, or nil if there is no module
--- definition
-
-local function check_module (line, currentmodule)
-       line = util.trim(line)
-
-       -- module"x.y"
-       -- module'x.y'
-       -- module[[x.y]]
-       -- module("x.y")
-       -- module('x.y')
-       -- module([[x.y]])
-       -- module(...)
-
-       local r, _, modulename = string.find(line, "^module%s*[%s\"'(%[]+([^,\"')%]]+)")
-       if r then
-               -- found module definition
-               logger:debug(string.format("found module `%s'", modulename))
-               return modulename
-       end
-       return currentmodule
-end
-
--- Patterns for constant recognition
-local constant_patterns = {
-       "^()%s*([A-Z][A-Z0-9_]*)%s*=",
-       "^%s*(local%s)%s*([A-Z][A-Z0-9_]*)%s*=",
-}
-
--------------------------------------------------------------------------------
--- Checks if the line contains a constant definition
--- @param line string with line text
--- @return constant information or nil if no constant definition found
-
-local function check_constant (line)
-       line = util.trim(line)
-
-       local info = table.foreachi(constant_patterns, function (_, pattern)
-               local r, _, l, id = string.find(line, pattern)
-               if r ~= nil then
-                       return {
-                               name = id,
-                               private = (l == "local"),
-                       }
-               end
-       end)
-
-       -- TODO: remove these assert's?
-       if info ~= nil then
-               assert(info.name, "constant name undefined")
-       end
-
-       return info
-end
-
--------------------------------------------------------------------------------
--- Extracts summary information from a description. The first sentence of each
--- doc comment should be a summary sentence, containing a concise but complete
--- description of the item. It is important to write crisp and informative
--- initial sentences that can stand on their own
--- @param description text with item description
--- @return summary string or nil if description is nil
-
-local function parse_summary (description)
-       -- summary is never nil...
-       description = description or ""
-
-       -- append an " " at the end to make the pattern work in all cases
-       description = description.." "
-
-       -- read until the first period followed by a space or tab
-       local summary = string.match(description, "(.-%.)[%s\t]")
-
-       -- if pattern did not find the first sentence, summary is the whole description
-       summary = summary or description
-
-       return summary
-end
-
--------------------------------------------------------------------------------
--- @param f file handle
--- @param line current line being parsed
--- @param modulename module already found, if any
--- @return current line
--- @return code block
--- @return modulename if found
-
-local function parse_code (f, line, modulename)
-       local code = {}
-       while line ~= nil do
-               if string.find(line, "^[\t ]*%-%-%-") then
-                       -- reached another luadoc block, end this parsing
-                       return line, code, modulename
-               else
-                       -- look for a module definition
-                       modulename = check_module(line, modulename)
-
-                       table.insert(code, line)
-                       line = f:read()
-               end
-       end
-       -- reached end of file
-       return line, code, modulename
-end
-
--------------------------------------------------------------------------------
--- Parses the information inside a block comment
--- @param block block with comment field
--- @return block parameter
-
-local function parse_comment (block, first_line, modulename)
-
-       -- get the first non-empty line of code
-       local code = table.foreachi(block.code, function(_, line)
-               if not util.line_empty(line) then
-                       -- `local' declarations are ignored in two cases:
-                       -- when the `nolocals' option is turned on; and
-                       -- when the first block of a file is parsed (this is
-                       --      necessary to avoid confusion between the top
-                       --      local declarations and the `module' definition.
-                       if (options.nolocals or first_line) and line:find"^%s*local" then
-                               return
-                       end
-                       return line
-               end
-       end)
-
-       -- parse first line of code
-       if code ~= nil then
-               local func_info = check_function(code)
-               local module_name = check_module(code)
-               local const_info = check_constant(code)
-               if func_info then
-                       block.class = "function"
-                       block.name = func_info.name
-                       block.param = func_info.param
-                       block.private = func_info.private
-               elseif const_info then
-                       block.class = "constant"
-                       block.name = const_info.name
-                       block.private = const_info.private
-               elseif module_name then
-                       block.class = "module"
-                       block.name = module_name
-                       block.param = {}
-               else
-                       block.param = {}
-               end
-       else
-               -- TODO: comment without any code. Does this means we are dealing
-               -- with a file comment?
-       end
-
-       -- parse @ tags
-       local currenttag = "description"
-       local currenttext
-
-       table.foreachi(block.comment, function (_, line)
-               line = util.trim_comment(line)
-
-               local r, _, tag, text = string.find(line, "@([_%w%.]+)%s+(.*)")
-               if r ~= nil then
-                       -- found new tag, add previous one, and start a new one
-                       -- TODO: what to do with invalid tags? issue an error? or log a warning?
-                       tags.handle(currenttag, block, currenttext)
-
-                       currenttag = tag
-                       currenttext = text
-               else
-                       currenttext = util.concat(currenttext, line)
-                       assert(string.sub(currenttext, 1, 1) ~= " ", string.format("`%s', `%s'", currenttext, line))
-               end
-       end)
-       tags.handle(currenttag, block, currenttext)
-
-       -- extracts summary information from the description
-       block.summary = parse_summary(block.description)
-       assert(string.sub(block.description, 1, 1) ~= " ", string.format("`%s'", block.description))
-
-       if block.name and block.class == "module" then
-               modulename = block.name
-       end
-
-       return block, modulename
-end
-
--------------------------------------------------------------------------------
--- Parses a block of comment, started with ---. Read until the next block of
--- comment.
--- @param f file handle
--- @param line being parsed
--- @param modulename module already found, if any
--- @return line
--- @return block parsed
--- @return modulename if found
-
-local function parse_block (f, line, modulename, first)
-       local block = {
-               comment = {},
-               code = {},
-       }
-
-       while line ~= nil do
-               if string.find(line, "^[\t ]*%-%-") == nil then
-                       -- reached end of comment, read the code below it
-                       -- TODO: allow empty lines
-                       line, block.code, modulename = parse_code(f, line, modulename)
-
-                       -- parse information in block comment
-                       block, modulename = parse_comment(block, first, modulename)
-
-                       return line, block, modulename
-               else
-                       table.insert(block.comment, line)
-                       line = f:read()
-               end
-       end
-       -- reached end of file
-
-       -- parse information in block comment
-       block, modulename = parse_comment(block, first, modulename)
-
-       return line, block, modulename
-end
-
--------------------------------------------------------------------------------
--- Parses a file documented following luadoc format.
--- @param filepath full path of file to parse
--- @param doc table with documentation
--- @return table with documentation
-
-function parse_file (filepath, doc, handle, prev_line, prev_block, prev_modname)
-       local blocks = { prev_block }
-       local modulename = prev_modname
-
-       -- read each line
-       local f = handle or io.open(filepath, "r")
-       local i = 1
-       local line = prev_line or f:read()
-       local first = true
-       while line ~= nil do
-
-               if string.find(line, "^[\t ]*%-%-%-") then
-                       -- reached a luadoc block
-                       local block, newmodname
-                       line, block, newmodname = parse_block(f, line, modulename, first)
-
-                       if modulename and newmodname and newmodname ~= modulename then
-                               doc = parse_file( nil, doc, f, line, block, newmodname )
-                       else
-                               table.insert(blocks, block)
-                               modulename = newmodname
-                       end
-               else
-                       -- look for a module definition
-                       local newmodname = check_module(line, modulename)
-
-                       if modulename and newmodname and newmodname ~= modulename then
-                               parse_file( nil, doc, f )
-                       else
-                               modulename = newmodname
-                       end
-
-                       -- TODO: keep beginning of file somewhere
-
-                       line = f:read()
-               end
-               first = false
-               i = i + 1
-       end
-
-       if not handle then
-               f:close()
-       end
-
-       if filepath then
-               -- store blocks in file hierarchy
-               assert(doc.files[filepath] == nil, string.format("doc for file `%s' already defined", filepath))
-               table.insert(doc.files, filepath)
-               doc.files[filepath] = {
-                       type = "file",
-                       name = filepath,
-                       doc = blocks,
-       --              functions = class_iterator(blocks, "function"),
-       --              tables = class_iterator(blocks, "table"),
-               }
-       --
-               local first = doc.files[filepath].doc[1]
-               if first and modulename then
-                       doc.files[filepath].author = first.author
-                       doc.files[filepath].copyright = first.copyright
-                       doc.files[filepath].description = first.description
-                       doc.files[filepath].release = first.release
-                       doc.files[filepath].summary = first.summary
-               end
-       end
-
-       -- if module definition is found, store in module hierarchy
-       if modulename ~= nil then
-               if modulename == "..." then
-                       assert( filepath, "Can't determine name for virtual module from filepatch" )
-                       modulename = string.gsub (filepath, "%.lua$", "")
-                       modulename = string.gsub (modulename, "/", ".")
-               end
-               if doc.modules[modulename] ~= nil then
-                       -- module is already defined, just add the blocks
-                       table.foreachi(blocks, function (_, v)
-                               table.insert(doc.modules[modulename].doc, v)
-                       end)
-               else
-                       -- TODO: put this in a different module
-                       table.insert(doc.modules, modulename)
-                       doc.modules[modulename] = {
-                               type = "module",
-                               name = modulename,
-                               doc = blocks,
---                             functions = class_iterator(blocks, "function"),
---                             tables = class_iterator(blocks, "table"),
-                               author = first and first.author,
-                               copyright = first and first.copyright,
-                               description = "",
-                               release = first and first.release,
-                               summary = "",
-                       }
-
-                       -- find module description
-                       for m in class_iterator(blocks, "module")() do
-                               doc.modules[modulename].description = util.concat(
-                                       doc.modules[modulename].description,
-                                       m.description)
-                               doc.modules[modulename].summary = util.concat(
-                                       doc.modules[modulename].summary,
-                                       m.summary)
-                               if m.author then
-                                       doc.modules[modulename].author = m.author
-                               end
-                               if m.copyright then
-                                       doc.modules[modulename].copyright = m.copyright
-                               end
-                               if m.release then
-                                       doc.modules[modulename].release = m.release
-                               end
-                               if m.name then
-                                       doc.modules[modulename].name = m.name
-                               end
-                       end
-                       doc.modules[modulename].description = doc.modules[modulename].description or (first and first.description) or ""
-                       doc.modules[modulename].summary = doc.modules[modulename].summary or (first and first.summary) or ""
-               end
-
-               -- make functions table
-               doc.modules[modulename].functions = {}
-               for f in class_iterator(blocks, "function")() do
-                       if f and f.name then
-                               table.insert(doc.modules[modulename].functions, f.name)
-                               doc.modules[modulename].functions[f.name] = f
-                       end
-               end
-
-               -- make tables table
-               doc.modules[modulename].tables = {}
-               for t in class_iterator(blocks, "table")() do
-                       if t and t.name then
-                               table.insert(doc.modules[modulename].tables, t.name)
-                               doc.modules[modulename].tables[t.name] = t
-                       end
-               end
-
-               -- make constants table
-               doc.modules[modulename].constants = {}
-               for c in class_iterator(blocks, "constant")() do
-                       if c and c.name then
-                               table.insert(doc.modules[modulename].constants, c.name)
-                               doc.modules[modulename].constants[c.name] = c
-                       end
-               end
-       end
-
-       if filepath then
-               -- make functions table
-               doc.files[filepath].functions = {}
-               for f in class_iterator(blocks, "function")() do
-                       if f and f.name then
-                               table.insert(doc.files[filepath].functions, f.name)
-                               doc.files[filepath].functions[f.name] = f
-                       end
-               end
-
-               -- make tables table
-               doc.files[filepath].tables = {}
-               for t in class_iterator(blocks, "table")() do
-                       if t and t.name then
-                               table.insert(doc.files[filepath].tables, t.name)
-                               doc.files[filepath].tables[t.name] = t
-                       end
-               end
-       end
-
-       return doc
-end
-
--------------------------------------------------------------------------------
--- Checks if the file is terminated by ".lua" or ".luadoc" and calls the
--- function that does the actual parsing
--- @param filepath full path of the file to parse
--- @param doc table with documentation
--- @return table with documentation
--- @see parse_file
-
-function file (filepath, doc)
-       local patterns = { "%.lua$", "%.luadoc$" }
-       local valid = table.foreachi(patterns, function (_, pattern)
-               if string.find(filepath, pattern) ~= nil then
-                       return true
-               end
-       end)
-
-       if valid then
-               logger:info(string.format("processing file `%s'", filepath))
-               doc = parse_file(filepath, doc)
-       end
-
-       return doc
-end
-
--------------------------------------------------------------------------------
--- Recursively iterates through a directory, parsing each file
--- @param path directory to search
--- @param doc table with documentation
--- @return table with documentation
-
-function directory (path, doc)
-       for f in posix.dir(path) do
-               local fullpath = path .. "/" .. f
-               local attr = posix.stat(fullpath)
-               assert(attr, string.format("error stating file `%s'", fullpath))
-
-               if attr.type == "reg" then
-                       doc = file(fullpath, doc)
-               elseif attr.type == "dir" and f ~= "." and f ~= ".." then
-                       doc = directory(fullpath, doc)
-               end
-       end
-       return doc
-end
-
--- Recursively sorts the documentation table
-local function recsort (tab)
-       table.sort (tab)
-       -- sort list of functions by name alphabetically
-       for f, doc in pairs(tab) do
-               if doc.functions then
-                       table.sort(doc.functions)
-               end
-               if doc.tables then
-                       table.sort(doc.tables)
-               end
-       end
-end
-
--------------------------------------------------------------------------------
-
-function start (files, doc)
-       assert(files, "file list not specified")
-
-       -- Create an empty document, or use the given one
-       doc = doc or {
-               files = {},
-               modules = {},
-       }
-       assert(doc.files, "undefined `files' field")
-       assert(doc.modules, "undefined `modules' field")
-
-       table.foreachi(files, function (_, path)
-               local attr = posix.stat(path)
-               assert(attr, string.format("error stating path `%s'", path))
-
-               if attr.type == "reg" then
-                       doc = file(path, doc)
-               elseif attr.type == "dir" then
-                       doc = directory(path, doc)
-               end
-       end)
-
-       -- order arrays alphabetically
-       recsort(doc.files)
-       recsort(doc.modules)
-
-       return doc
-end
diff --git a/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua b/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua
deleted file mode 100644 (file)
index d03df82..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
--------------------------------------------------------------------------------
--- Handlers for several tags
--- @release $Id: tags.lua,v 1.8 2007/09/05 12:39:09 tomas Exp $
--------------------------------------------------------------------------------
-
-local luadoc = require "luadoc"
-local util = require "luadoc.util"
-local string = require "string"
-local table = require "table"
-local assert, type, tostring = assert, type, tostring
-
-module "luadoc.taglet.standard.tags"
-
--------------------------------------------------------------------------------
-
-local function author (tag, block, text)
-       block[tag] = block[tag] or {}
-       if not text then
-               luadoc.logger:warn("author `name' not defined [["..text.."]]: skipping")
-               return
-       end
-       table.insert (block[tag], text)
-end
-
--------------------------------------------------------------------------------
--- Set the class of a comment block. Classes can be "module", "function",
--- "table". The first two classes are automatic, extracted from the source code
-
-local function class (tag, block, text)
-       block[tag] = text
-end
-
--------------------------------------------------------------------------------
-
-local function cstyle (tag, block, text)
-       block[tag] = text
-end
-
--------------------------------------------------------------------------------
-
-local function copyright (tag, block, text)
-       block[tag] = text
-end
-
--------------------------------------------------------------------------------
-
-local function description (tag, block, text)
-       block[tag] = text
-end
-
--------------------------------------------------------------------------------
-
-local function field (tag, block, text)
-       if block["class"] ~= "table" then
-               luadoc.logger:warn("documenting `field' for block that is not a `table'")
-       end
-       block[tag] = block[tag] or {}
-
-       local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)")
-       assert(name, "field name not defined")
-
-       table.insert(block[tag], name)
-       block[tag][name] = desc
-end
-
--------------------------------------------------------------------------------
--- Set the name of the comment block. If the block already has a name, issue
--- an error and do not change the previous value
-
-local function name (tag, block, text)
-       if block[tag] and block[tag] ~= text then
-               luadoc.logger:error(string.format("block name conflict: `%s' -> `%s'", block[tag], text))
-       end
-
-       block[tag] = text
-end
-
--------------------------------------------------------------------------------
--- Processes a parameter documentation.
--- @param tag String with the name of the tag (it must be "param" always).
--- @param block Table with previous information about the block.
--- @param text String with the current line beeing processed.
-
-local function param (tag, block, text)
-       block[tag] = block[tag] or {}
-       -- TODO: make this pattern more flexible, accepting empty descriptions
-       local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)")
-       if not name then
-               luadoc.logger:warn("parameter `name' not defined [["..text.."]]: skipping")
-               return
-       end
-       local i = table.foreachi(block[tag], function (i, v)
-               if v == name then
-                       return i
-               end
-       end)
-       if i == nil then
-               luadoc.logger:warn(string.format("documenting undefined parameter `%s'", name))
-               table.insert(block[tag], name)
-       end
-       block[tag][name] = desc
-end
-
--------------------------------------------------------------------------------
-
-local function release (tag, block, text)
-       block[tag] = text
-end
-
--------------------------------------------------------------------------------
-
-local function ret (tag, block, text)
-       tag = "ret"
-       if type(block[tag]) == "string" then
-               block[tag] = { block[tag], text }
-       elseif type(block[tag]) == "table" then
-               table.insert(block[tag], text)
-       else
-               block[tag] = text
-       end
-end
-
--------------------------------------------------------------------------------
--- @see ret
-
-local function see (tag, block, text)
-       -- see is always an array
-       block[tag] = block[tag] or {}
-
-       -- remove trailing "."
-       text = string.gsub(text, "(.*)%.$", "%1")
-
-       local s = util.split("%s*,%s*", text)
-
-       table.foreachi(s, function (_, v)
-               table.insert(block[tag], v)
-       end)
-end
-
--------------------------------------------------------------------------------
--- @see ret
-
-local function usage (tag, block, text)
-       if type(block[tag]) == "string" then
-               block[tag] = { block[tag], text }
-       elseif type(block[tag]) == "table" then
-               table.insert(block[tag], text)
-       else
-               block[tag] = text
-       end
-end
-
--------------------------------------------------------------------------------
-
-local handlers = {}
-handlers["author"] = author
-handlers["class"] = class
-handlers["cstyle"] = cstyle
-handlers["copyright"] = copyright
-handlers["description"] = description
-handlers["field"] = field
-handlers["name"] = name
-handlers["param"] = param
-handlers["release"] = release
-handlers["return"] = ret
-handlers["see"] = see
-handlers["usage"] = usage
-
--------------------------------------------------------------------------------
-
-function handle (tag, block, text)
-       if not handlers[tag] then
-               luadoc.logger:error(string.format("undefined handler for tag `%s'", tag))
-               return
-       end
---     assert(handlers[tag], string.format("undefined handler for tag `%s'", tag))
-       return handlers[tag](tag, block, text)
-end
diff --git a/contrib/luadoc/lua/luadoc/util.lua b/contrib/luadoc/lua/luadoc/util.lua
deleted file mode 100644 (file)
index acaaac8..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
--------------------------------------------------------------------------------
--- General utilities.
--- @release $Id: util.lua,v 1.16 2008/02/17 06:42:51 jasonsantos Exp $
--------------------------------------------------------------------------------
-
-local posix = require "nixio.fs"
-local type, table, string, io, assert, tostring, setmetatable, pcall = type, table, string, io, assert, tostring, setmetatable, pcall
-
--------------------------------------------------------------------------------
--- Module with several utilities that could not fit in a specific module
-
-module "luadoc.util"
-
--------------------------------------------------------------------------------
--- Removes spaces from the begining and end of a given string
--- @param s string to be trimmed
--- @return trimmed string
-
-function trim (s)
-       return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
-end
-
--------------------------------------------------------------------------------
--- Removes spaces from the begining and end of a given string, considering the
--- string is inside a lua comment.
--- @param s string to be trimmed
--- @return trimmed string
--- @see trim
--- @see string.gsub
-
-function trim_comment (s)
-       s = string.gsub(s, "%-%-+(.*)$", "%1")
-       return trim(s)
-end
-
--------------------------------------------------------------------------------
--- Checks if a given line is empty
--- @param line string with a line
--- @return true if line is empty, false otherwise
-
-function line_empty (line)
-       return (string.len(trim(line)) == 0)
-end
-
--------------------------------------------------------------------------------
--- Appends two string, but if the first one is nil, use to second one
--- @param str1 first string, can be nil
--- @param str2 second string
--- @return str1 .. " " .. str2, or str2 if str1 is nil
-
-function concat (str1, str2)
-       if str1 == nil or string.len(str1) == 0 then
-               return str2
-       else
-               return str1 .. " " .. str2
-       end
-end
-
--------------------------------------------------------------------------------
--- Split text into a list consisting of the strings in text,
--- separated by strings matching delim (which may be a pattern). 
--- @param delim if delim is "" then action is the same as %s+ except that 
--- field 1 may be preceeded by leading whitespace
--- @usage split(",%s*", "Anna, Bob, Charlie,Dolores")
--- @usage split(""," x y") gives {"x","y"}
--- @usage split("%s+"," x y") gives {"", "x","y"}
--- @return array with strings
--- @see table.concat
-
-function split(delim, text)
-       local list = {}
-       if string.len(text) > 0 then
-               delim = delim or ""
-               local pos = 1
-               -- if delim matches empty string then it would give an endless loop
-               if string.find("", delim, 1) and delim ~= "" then 
-                       error("delim matches empty string!")
-               end
-               local first, last
-               while 1 do
-                       if delim ~= "" then 
-                               first, last = string.find(text, delim, pos)
-                       else
-                               first, last = string.find(text, "%s+", pos)
-                               if first == 1 then
-                                       pos = last+1
-                                       first, last = string.find(text, "%s+", pos)
-                               end
-                       end
-                       if first then -- found?
-                               table.insert(list, string.sub(text, pos, first-1))
-                               pos = last+1
-                       else
-                               table.insert(list, string.sub(text, pos))
-                               break
-                       end
-               end
-       end
-       return list
-end
-
--------------------------------------------------------------------------------
--- Comments a paragraph.
--- @param text text to comment with "--", may contain several lines
--- @return commented text
-
-function comment (text)
-       text = string.gsub(text, "\n", "\n-- ")
-       return "-- " .. text
-end
-
--------------------------------------------------------------------------------
--- Wrap a string into a paragraph.
--- @param s string to wrap
--- @param w width to wrap to [80]
--- @param i1 indent of first line [0]
--- @param i2 indent of subsequent lines [0]
--- @return wrapped paragraph
-
-function wrap(s, w, i1, i2)
-       w = w or 80
-       i1 = i1 or 0
-       i2 = i2 or 0
-       assert(i1 < w and i2 < w, "the indents must be less than the line width")
-       s = string.rep(" ", i1) .. s
-       local lstart, len = 1, string.len(s)
-       while len - lstart > w do
-               local i = lstart + w
-               while i > lstart and string.sub(s, i, i) ~= " " do i = i - 1 end
-               local j = i
-               while j > lstart and string.sub(s, j, j) == " " do j = j - 1 end
-               s = string.sub(s, 1, j) .. "\n" .. string.rep(" ", i2) ..
-                       string.sub(s, i + 1, -1)
-               local change = i2 + 1 - (i - j)
-               lstart = j + change
-               len = len + change
-       end
-       return s
-end
-
--------------------------------------------------------------------------------
--- Opens a file, creating the directories if necessary
--- @param filename full path of the file to open (or create)
--- @param mode mode of opening
--- @return file handle
-
-function posix.open (filename, mode)
-       local f = io.open(filename, mode)
-       if f == nil then
-               filename = string.gsub(filename, "\\", "/")
-               local dir = ""
-               for d in string.gfind(filename, ".-/") do
-                       dir = dir .. d
-                       posix.mkdir(dir)
-               end
-               f = io.open(filename, mode)
-       end
-       return f
-end
-
-
-----------------------------------------------------------------------------------
--- Creates a Logger with LuaLogging, if present. Otherwise, creates a mock logger.
--- @param options a table with options for the logging mechanism
--- @return logger object that will implement log methods
-
-function loadlogengine(options)
-       local logenabled = pcall(function()
-               require "logging"
-               require "logging.console"
-       end)
-       
-       local logging = logenabled and logging
-       
-       if logenabled then
-               if options.filelog then
-                       logger = logging.file("luadoc.log") -- use this to get a file log
-               else
-                       logger = logging.console("[%level] %message\n")
-               end
-       
-               if options.verbose then
-                       logger:setLevel(logging.INFO)
-               else
-                       logger:setLevel(logging.WARN)
-               end
-               
-       else
-               noop = {__index=function(...)
-                       return function(...)
-                               -- noop
-                       end
-               end}
-               
-               logger = {} 
-               setmetatable(logger, noop)
-       end
-       
-       return logger
-end
-
index 94c2e9e..c76cc54 100644 (file)
@@ -97,7 +97,11 @@ end
 function request_raw(uri, options)
        options = options or {}
        local pr, auth, host, port, path
-       
+
+       if options.params then
+               uri = uri .. '?' .. http.urlencode_params(options.params)
+       end
+
        if uri:find("%[") then
                if uri:find("@") then
                        pr, auth, host, port, path = uri:match("(%w+)://(.+)@(%b[]):?([0-9]*)(.*)")
@@ -176,20 +180,8 @@ function request_raw(uri, options)
                options.method = options.method or "POST"
        end
 
-       -- Assemble message
-       local message = {(options.method or "GET") .. " " .. path .. " " .. protocol}
-       
-       for k, v in pairs(headers) do
-               if type(v) == "string" or type(v) == "number" then
-                       message[#message+1] = k .. ": " .. v
-               elseif type(v) == "table" then
-                       for i, j in ipairs(v) do
-                               message[#message+1] = k .. ": " .. j
-                       end
-               end
-       end
-       
        if options.cookies then
+               local cookiedata = {}
                for _, c in ipairs(options.cookies) do
                        local cdo = c.flags.domain
                        local cpa = c.flags.path
@@ -197,11 +189,29 @@ function request_raw(uri, options)
                         and (cpa == path or cpa == "/" or cpa .. "/" == path:sub(#cpa+1))
                         and (not c.flags.secure or pr == "https")
                        then
-                               message[#message+1] = "Cookie: " .. c.key .. "=" .. c.value
+                               cookiedata[#cookiedata+1] = c.key .. "=" .. c.value
                        end 
                end
+               if headers["Cookie"] then
+                       headers["Cookie"] = headers["Cookie"] .. "; " .. table.concat(cookiedata, "; ")
+               else
+                       headers["Cookie"] = table.concat(cookiedata, "; ")
+               end
        end
+
+       -- Assemble message
+       local message = {(options.method or "GET") .. " " .. path .. " " .. protocol}
        
+       for k, v in pairs(headers) do
+               if type(v) == "string" or type(v) == "number" then
+                       message[#message+1] = k .. ": " .. v
+               elseif type(v) == "table" then
+                       for i, j in ipairs(v) do
+                               message[#message+1] = k .. ": " .. j
+                       end
+               end
+       end
+
        message[#message+1] = ""
        message[#message+1] = ""
        
@@ -323,7 +333,7 @@ function request_raw(uri, options)
                end
        end
        
-       return response.code, response, linesrc(true), sock
+       return response.code, response, linesrc(true)..sock:readall(), sock
 end
 
 function cookie_parse(cookiestr)
diff --git a/libs/luci-lib-ip/Makefile b/libs/luci-lib-ip/Makefile
new file mode 100644 (file)
index 0000000..eb80dcb
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2015 LuCI Team <luci@lists.subsignal.org>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Lua library for IP calculation and routing information
+LUCI_DEPENDS:=+liblua +libnl-tiny
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/libs/luci-lib-ip/src/Makefile b/libs/luci-lib-ip/src/Makefile
new file mode 100644 (file)
index 0000000..76abd27
--- /dev/null
@@ -0,0 +1,17 @@
+IP_CFLAGS = -std=gnu99 -I$(STAGING_DIR)/usr/include/libnl-tiny/
+IP_LDFLAGS = -llua -lm -lnl-tiny
+IP_OBJ = ip.o
+IP_LIB = ip.so
+
+%.o: %.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(LUA_CFLAGS) $(IP_CFLAGS) $(FPIC) -c -o $@ $<
+
+compile: $(IP_OBJ)
+       $(CC) $(LDFLAGS) -shared -o $(IP_LIB) $(IP_OBJ) $(IP_LDFLAGS)
+
+install: compile
+       mkdir -p $(DESTDIR)/usr/lib/lua/luci
+       cp $(IP_LIB) $(DESTDIR)/usr/lib/lua/luci/$(IP_LIB)
+
+clean:
+       rm -f *.o *.so
diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c
new file mode 100644 (file)
index 0000000..b91966c
--- /dev/null
@@ -0,0 +1,1392 @@
+/*
+Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <arpa/inet.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#include <netlink/socket.h>
+#include <linux/rtnetlink.h>
+
+#define LUCI_IP "luci.ip"
+#define LUCI_IP_CIDR "luci.ip.cidr"
+
+#define RTA_INT(x)     (*(int *)RTA_DATA(x))
+#define RTA_U32(x)     (*(uint32_t *)RTA_DATA(x))
+
+static int hz = 0;
+static struct nl_sock *sock = NULL;
+
+typedef struct {
+       union {
+               struct in_addr v4;
+               struct in6_addr v6;
+       } addr;
+       int len;
+       int bits;
+       int family;
+       bool exact;
+} cidr_t;
+
+struct dump_filter {
+       bool get;
+       int family;
+       int iif;
+       int oif;
+       int type;
+       int scope;
+       int proto;
+       int table;
+       cidr_t gw;
+       cidr_t from;
+       cidr_t src;
+       cidr_t dst;
+       struct ether_addr mac;
+};
+
+struct dump_state {
+       int index;
+       int pending;
+       int callback;
+       struct lua_State *L;
+       struct dump_filter *filter;
+};
+
+
+static int _cidr_new(lua_State *L, int index, int family, bool mask);
+
+static cidr_t *L_checkcidr (lua_State *L, int index, cidr_t *p)
+{
+       if (lua_type(L, index) == LUA_TUSERDATA)
+               return luaL_checkudata(L, index, LUCI_IP_CIDR);
+
+       if (_cidr_new(L, index, p ? p->family : 0, false))
+               return lua_touserdata(L, -1);
+
+       luaL_error(L, "Invalid operand");
+       return NULL;
+}
+
+static bool parse_mask(int family, const char *mask, int *bits)
+{
+       char *e;
+       struct in_addr m;
+       struct in6_addr m6;
+
+       if (family == AF_INET && inet_pton(AF_INET, mask, &m))
+       {
+               for (*bits = 0, m.s_addr = ntohl(m.s_addr);
+                        *bits < 32 && (m.s_addr << *bits) & 0x80000000;
+                        ++*bits);
+       }
+       else if (family == AF_INET6 && inet_pton(AF_INET6, mask, &m6))
+       {
+               for (*bits = 0;
+                        *bits < 128 && (m6.s6_addr[*bits / 8] << (*bits % 8)) & 128;
+                        ++*bits);
+       }
+       else
+       {
+               *bits = strtoul(mask, &e, 10);
+
+               if (e == mask || *e != 0 || *bits > ((family == AF_INET) ? 32 : 128))
+                       return false;
+       }
+
+       return true;
+}
+
+static bool parse_cidr(const char *dest, cidr_t *pp)
+{
+       char *p, buf[INET6_ADDRSTRLEN * 2 + 2];
+       uint8_t bitlen = 0;
+
+       strncpy(buf, dest, sizeof(buf) - 1);
+
+       p = strchr(buf, '/');
+
+       if (p)
+               *p++ = 0;
+
+       if (inet_pton(AF_INET, buf, &pp->addr.v4))
+       {
+               bitlen = 32;
+               pp->family = AF_INET;
+               pp->len = sizeof(struct in_addr);
+       }
+       else if (inet_pton(AF_INET6, buf, &pp->addr.v6))
+       {
+               bitlen = 128;
+               pp->family = AF_INET6;
+               pp->len = sizeof(struct in6_addr);
+       }
+       else
+               return false;
+
+       if (p)
+       {
+               if (!parse_mask(pp->family, p, &pp->bits))
+                       return false;
+       }
+       else
+       {
+               pp->bits = bitlen;
+       }
+
+       return true;
+}
+
+static int L_getint(lua_State *L, int index, const char *name)
+{
+       int rv = 0;
+
+       lua_getfield(L, index, name);
+
+       if (lua_type(L, -1) == LUA_TNUMBER)
+               rv = lua_tonumber(L, -1);
+
+       lua_pop(L, 1);
+
+       return rv;
+}
+
+static const char * L_getstr(lua_State *L, int index, const char *name)
+{
+       const char *rv = NULL;
+
+       lua_getfield(L, index, name);
+
+       if (lua_type(L, -1) == LUA_TSTRING)
+               rv = lua_tostring(L, -1);
+
+       lua_pop(L, 1);
+
+       return rv;
+}
+
+static void L_setint(struct lua_State *L, const char *name, uint32_t n)
+{
+       lua_pushinteger(L, n);
+       lua_setfield(L, -2, name);
+}
+
+static void L_setbool(struct lua_State *L, const char *name, bool val)
+{
+       lua_pushboolean(L, val);
+       lua_setfield(L, -2, name);
+}
+
+static void L_setaddr(struct lua_State *L, const char *name,
+                      int family, void *addr, int bits)
+{
+       cidr_t *p;
+
+       if (!addr)
+               return;
+
+       p = lua_newuserdata(L, sizeof(*p));
+
+       if (!p)
+               return;
+
+       if (family == AF_INET)
+       {
+               p->family = AF_INET;
+               p->bits = (bits < 0) ? 32 : bits;
+               p->len = sizeof(p->addr.v4);
+               p->addr.v4 = *(struct in_addr *)addr;
+       }
+       else
+       {
+               p->family = AF_INET6;
+               p->bits = (bits < 0) ? 128 : bits;
+               p->len = sizeof(p->addr.v6);
+               p->addr.v6 = *(struct in6_addr *)addr;
+       }
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       lua_setfield(L, -2, name);
+}
+
+static void L_setstr(struct lua_State *L, const char *name, const char *val)
+{
+       lua_pushstring(L, val);
+       lua_setfield(L, -2, name);
+}
+
+static void L_setdev(struct lua_State *L, const char *name,
+                     struct nlattr *attr)
+{
+       char buf[32];
+
+       if (if_indextoname(RTA_INT(attr), buf))
+               L_setstr(L, name, buf);
+}
+
+static int L_checkbits(lua_State *L, int index, cidr_t *p)
+{
+       int bits;
+
+       if (lua_gettop(L) < index || lua_isnil(L, index))
+       {
+               bits = p->bits;
+       }
+       else if (lua_type(L, index) == LUA_TNUMBER)
+       {
+               bits = lua_tointeger(L, index);
+
+               if (bits < 0 || bits > ((p->family == AF_INET) ? 32 : 128))
+                       return luaL_error(L, "Invalid prefix size");
+       }
+       else if (lua_type(L, index) == LUA_TSTRING)
+       {
+               if (!parse_mask(p->family, lua_tostring(L, index), &bits))
+                       return luaL_error(L, "Invalid netmask format");
+       }
+       else
+       {
+               return luaL_error(L, "Invalid data type");
+       }
+
+       return bits;
+}
+
+static int _cidr_new(lua_State *L, int index, int family, bool mask)
+{
+       uint32_t n;
+       const char *addr;
+       cidr_t cidr = { }, *cidrp;
+
+       if (lua_type(L, index) == LUA_TNUMBER)
+       {
+               n = htonl(lua_tointeger(L, index));
+
+               if (family == AF_INET6)
+               {
+                       cidr.family = AF_INET6;
+                       cidr.bits = 128;
+                       cidr.len = sizeof(cidr.addr.v6);
+                       cidr.addr.v6.s6_addr[12] = n;
+                       cidr.addr.v6.s6_addr[13] = (n >> 8);
+                       cidr.addr.v6.s6_addr[14] = (n >> 16);
+                       cidr.addr.v6.s6_addr[15] = (n >> 24);
+               }
+               else
+               {
+                       cidr.family = AF_INET;
+                       cidr.bits = 32;
+                       cidr.len = sizeof(cidr.addr.v4);
+                       cidr.addr.v4.s_addr = n;
+               }
+       }
+       else
+       {
+               addr = luaL_checkstring(L, index);
+
+               if (!parse_cidr(addr, &cidr))
+                       return 0;
+
+               if (family && cidr.family != family)
+                       return 0;
+
+               if (mask)
+                       cidr.bits = L_checkbits(L, index + 1, &cidr);
+       }
+
+       if (!(cidrp = lua_newuserdata(L, sizeof(*cidrp))))
+               return 0;
+
+       *cidrp = cidr;
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_new(lua_State *L)
+{
+       return _cidr_new(L, 1, 0, true);
+}
+
+static int cidr_ipv4(lua_State *L)
+{
+       return _cidr_new(L, 1, AF_INET, true);
+}
+
+static int cidr_ipv6(lua_State *L)
+{
+       return _cidr_new(L, 1, AF_INET6, true);
+}
+
+static int cidr_is4(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+
+       lua_pushboolean(L, p->family == AF_INET);
+       return 1;
+}
+
+static int cidr_is4rfc1918(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+       uint32_t a = htonl(p->addr.v4.s_addr);
+
+       lua_pushboolean(L, (p->family == AF_INET &&
+                           ((a >= 0x0A000000 && a <= 0x0AFFFFFF) ||
+                            (a >= 0xAC100000 && a <= 0xAC1FFFFF) ||
+                            (a >= 0xC0A80000 && a <= 0xC0A8FFFF))));
+
+       return 1;
+}
+
+static int cidr_is4linklocal(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+       uint32_t a = htonl(p->addr.v4.s_addr);
+
+       lua_pushboolean(L, (p->family == AF_INET &&
+                           a >= 0xA9FE0000 &&
+                           a <= 0xA9FEFFFF));
+
+       return 1;
+}
+
+static bool _is_mapped4(cidr_t *p)
+{
+       return (p->family == AF_INET6 &&
+               p->addr.v6.s6_addr[0] == 0 &&
+               p->addr.v6.s6_addr[1] == 0 &&
+               p->addr.v6.s6_addr[2] == 0 &&
+               p->addr.v6.s6_addr[3] == 0 &&
+               p->addr.v6.s6_addr[4] == 0 &&
+               p->addr.v6.s6_addr[5] == 0 &&
+               p->addr.v6.s6_addr[6] == 0 &&
+               p->addr.v6.s6_addr[7] == 0 &&
+               p->addr.v6.s6_addr[8] == 0 &&
+               p->addr.v6.s6_addr[9] == 0 &&
+               p->addr.v6.s6_addr[10] == 0xFF &&
+               p->addr.v6.s6_addr[11] == 0xFF);
+}
+
+static int cidr_is6mapped4(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+
+       lua_pushboolean(L, _is_mapped4(p));
+       return 1;
+}
+
+static int cidr_is6(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+
+       lua_pushboolean(L, p->family == AF_INET6);
+       return 1;
+}
+
+static int cidr_is6linklocal(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+
+       lua_pushboolean(L, (p->family == AF_INET6 &&
+                           p->addr.v6.s6_addr[0] == 0xFE &&
+                           p->addr.v6.s6_addr[1] >= 0x80 &&
+                           p->addr.v6.s6_addr[1] <= 0xBF));
+
+       return 1;
+}
+
+static int _cidr_cmp(lua_State *L)
+{
+       cidr_t *a = L_checkcidr(L, 1, NULL);
+       cidr_t *b = L_checkcidr(L, 2, NULL);
+
+       if (a->family != b->family)
+               return (a->family - b->family);
+
+       return memcmp(&a->addr.v6, &b->addr.v6, a->len);
+}
+
+static int cidr_lower(lua_State *L)
+{
+       lua_pushboolean(L, _cidr_cmp(L) < 0);
+       return 1;
+}
+
+static int cidr_higher(lua_State *L)
+{
+       lua_pushboolean(L, _cidr_cmp(L) > 0);
+       return 1;
+}
+
+static int cidr_equal(lua_State *L)
+{
+       lua_pushboolean(L, _cidr_cmp(L) == 0);
+       return 1;
+}
+
+static int cidr_lower_equal(lua_State *L)
+{
+       lua_pushboolean(L, _cidr_cmp(L) <= 0);
+       return 1;
+}
+
+static int cidr_prefix(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+       int bits = L_checkbits(L, 2, p);
+
+       p->bits = bits;
+       lua_pushinteger(L, p->bits);
+       return 1;
+}
+
+static void _apply_mask(cidr_t *p, int bits, bool inv)
+{
+       uint8_t b, i;
+
+       if (bits <= 0)
+       {
+               memset(&p->addr.v6, inv * 0xFF, p->len);
+       }
+       else if (p->family == AF_INET && bits <= 32)
+       {
+               if (inv)
+                       p->addr.v4.s_addr |= ntohl((1 << (32 - bits)) - 1);
+               else
+                       p->addr.v4.s_addr &= ntohl(~((1 << (32 - bits)) - 1));
+       }
+       else if (p->family == AF_INET6 && bits <= 128)
+       {
+               for (i = 0; i < sizeof(p->addr.v6.s6_addr); i++)
+               {
+                       b = (bits > 8) ? 8 : bits;
+                       if (inv)
+                               p->addr.v6.s6_addr[i] |= ~((uint8_t)(0xFF << (8 - b)));
+                       else
+                               p->addr.v6.s6_addr[i] &= (uint8_t)(0xFF << (8 - b));
+                       bits -= b;
+               }
+       }
+}
+
+static int cidr_network(lua_State *L)
+{
+       cidr_t *p1 = L_checkcidr(L, 1, NULL), *p2;
+       int bits = L_checkbits(L, 2, p1);
+
+       if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
+               return 0;
+
+       *p2 = *p1;
+       p2->bits = (p1->family == AF_INET) ? 32 : 128;
+       _apply_mask(p2, bits, false);
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_host(lua_State *L)
+{
+       cidr_t *p1 = L_checkcidr(L, 1, NULL);
+       cidr_t *p2 = lua_newuserdata(L, sizeof(*p2));
+
+       if (!p2)
+               return 0;
+
+       *p2 = *p1;
+       p2->bits = (p1->family == AF_INET) ? 32 : 128;
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_mask(lua_State *L)
+{
+       cidr_t *p1 = L_checkcidr(L, 1, NULL), *p2;
+       int bits = L_checkbits(L, 2, p1);
+
+       if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
+               return 0;
+
+       p2->bits = (p1->family == AF_INET) ? 32 : 128;
+       p2->family = p1->family;
+
+       memset(&p2->addr.v6.s6_addr, 0xFF, sizeof(p2->addr.v6.s6_addr));
+       _apply_mask(p2, bits, false);
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_broadcast(lua_State *L)
+{
+       cidr_t *p1 = L_checkcidr(L, 1, NULL);
+       cidr_t *p2;
+       int bits = L_checkbits(L, 2, p1);
+
+       if (p1->family == AF_INET6)
+               return 0;
+
+       if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
+               return 0;
+
+       *p2 = *p1;
+       p2->bits = (p1->family == AF_INET) ? 32 : 128;
+       _apply_mask(p2, bits, true);
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_mapped4(lua_State *L)
+{
+       cidr_t *p1 = L_checkcidr(L, 1, NULL);
+       cidr_t *p2;
+
+       if (!_is_mapped4(p1))
+               return 0;
+
+       if (!(p2 = lua_newuserdata(L, sizeof(*p2))))
+               return 0;
+
+       p2->family = AF_INET;
+       p2->bits = (p1->bits > 32) ? 32 : p1->bits;
+       memcpy(&p2->addr.v4, p1->addr.v6.s6_addr + 12, sizeof(p2->addr.v4));
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_contains(lua_State *L)
+{
+       cidr_t *p1 = L_checkcidr(L, 1, NULL);
+       cidr_t *p2 = L_checkcidr(L, 2, NULL);
+       cidr_t a = *p1, b = *p2;
+       bool rv = false;
+
+       if (p1->family == p2->family && p1->bits <= p2->bits)
+       {
+               _apply_mask(&a, p1->bits, false);
+               _apply_mask(&b, p1->bits, false);
+
+               rv = !memcmp(&a.addr.v6, &b.addr.v6, a.len);
+       }
+
+       lua_pushboolean(L, rv);
+       return 1;
+}
+
+#define S6_BYTE(a, i) \
+       (a)->addr.v6.s6_addr[sizeof((a)->addr.v6.s6_addr) - (i) - 1]
+
+static int _cidr_add_sub(lua_State *L, bool add)
+{
+       cidr_t *p1 = L_checkcidr(L, 1, NULL);
+       cidr_t *p2 = L_checkcidr(L, 2, p1);
+       cidr_t r = *p1;
+       bool inplace = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : false;
+       bool ok = true;
+       uint8_t i, carry;
+       uint32_t a, b;
+
+       if (p1->family == p2->family)
+       {
+               if (p1->family == AF_INET6)
+               {
+                       for (i = 0, carry = 0; i < sizeof(r.addr.v6.s6_addr); i++)
+                       {
+                               if (add)
+                               {
+                                       S6_BYTE(&r, i) = S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry;
+                                       carry = (S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry) / 256;
+                               }
+                               else
+                               {
+                                       S6_BYTE(&r, i) = (S6_BYTE(p1, i) - S6_BYTE(p2, i) - carry);
+                                       carry = (S6_BYTE(p1, i) < (S6_BYTE(p2, i) + carry));
+                               }
+                       }
+
+                       /* would over/underflow */
+                       if (carry)
+                       {
+                               memset(&r.addr.v6, add * 0xFF, sizeof(r.addr.v6));
+                               ok = false;
+                       }
+               }
+               else
+               {
+                       a = ntohl(p1->addr.v4.s_addr);
+                       b = ntohl(p2->addr.v4.s_addr);
+
+                       /* would over/underflow */
+                       if ((add && (UINT_MAX - a) < b) || (!add && a < b))
+                       {
+                               r.addr.v4.s_addr = add * 0xFFFFFFFF;
+                               ok = false;
+                       }
+                       else
+                       {
+                               r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b);
+                       }
+               }
+       }
+       else
+       {
+               ok = false;
+       }
+
+       if (inplace)
+       {
+               *p1 = r;
+               lua_pushboolean(L, ok);
+               return 1;
+       }
+
+       if (!(p1 = lua_newuserdata(L, sizeof(*p1))))
+               return 0;
+
+       *p1 = r;
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_add(lua_State *L)
+{
+       return _cidr_add_sub(L, true);
+}
+
+static int cidr_sub(lua_State *L)
+{
+       return _cidr_add_sub(L, false);
+}
+
+static int cidr_minhost(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+       cidr_t r = *p;
+       uint8_t i, rest, carry;
+
+       _apply_mask(&r, r.bits, false);
+
+       if (r.family == AF_INET6 && r.bits < 128)
+       {
+               r.bits = 128;
+
+               for (i = 0, carry = 1; i < sizeof(r.addr.v6.s6_addr); i++)
+               {
+                       rest = (S6_BYTE(&r, i) + carry) > 255;
+                       S6_BYTE(&r, i) += carry;
+                       carry = rest;
+               }
+       }
+       else if (r.family == AF_INET && r.bits < 32)
+       {
+               r.bits = 32;
+               r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1);
+       }
+
+       if (!(p = lua_newuserdata(L, sizeof(*p))))
+               return 0;
+
+       *p = r;
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_maxhost(lua_State *L)
+{
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+       cidr_t r = *p;
+
+       _apply_mask(&r, r.bits, true);
+
+       if (r.family == AF_INET && r.bits < 32)
+       {
+               r.bits = 32;
+               r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) - 1);
+       }
+       else if (r.family == AF_INET6)
+       {
+               r.bits = 128;
+       }
+
+       if (!(p = lua_newuserdata(L, sizeof(*p))))
+               return 0;
+
+       *p = r;
+
+       luaL_getmetatable(L, LUCI_IP_CIDR);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+static int cidr_gc (lua_State *L)
+{
+       return 0;
+}
+
+static int cidr_tostring (lua_State *L)
+{
+       char buf[INET6_ADDRSTRLEN];
+       cidr_t *p = L_checkcidr(L, 1, NULL);
+
+       if ((p->family == AF_INET && p->bits < 32) ||
+           (p->family == AF_INET6 && p->bits < 128))
+       {
+               lua_pushfstring(L, "%s/%d",
+                               inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)),
+                                               p->bits);
+       }
+       else
+       {
+               lua_pushstring(L, inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)));
+       }
+
+       return 1;
+}
+
+/*
+ * route functions
+ */
+
+static bool diff_prefix(int family, void *addr, int bits, cidr_t *p)
+{
+       uint8_t i, b, r;
+       uint32_t m;
+
+       if (!p->family)
+               return false;
+
+       if (!addr || p->family != family || p->bits > bits)
+               return true;
+
+       if (family == AF_INET6)
+       {
+               for (i = 0, r = p->bits; i < sizeof(struct in6_addr); i++)
+               {
+                       b = r ? (0xFF << (8 - ((r > 8) ? 8 : r))) : 0;
+
+                       if ((((struct in6_addr *)addr)->s6_addr[i] & b) !=
+                           (p->addr.v6.s6_addr[i] & b))
+                               return true;
+
+                       r -= ((r > 8) ? 8 : r);
+               }
+       }
+       else
+       {
+               m = p->bits ? htonl(~((1 << (32 - p->bits)) - 1)) : 0;
+
+               if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m))
+                       return true;
+       }
+
+       return (p->exact && p->bits != bits);
+}
+
+static int cb_dump_route(struct nl_msg *msg, void *arg)
+{
+       struct dump_state *s = arg;
+       struct dump_filter *f = s->filter;
+       struct nlmsghdr *hdr = nlmsg_hdr(msg);
+       struct rtmsg *rt = NLMSG_DATA(hdr);
+       struct nlattr *tb[RTA_MAX+1];
+       struct in6_addr *src, *dst, *gw, *from, def = { };
+       int iif, oif, bitlen;
+       uint32_t table;
+
+       if (hdr->nlmsg_type != RTM_NEWROUTE ||
+           (rt->rtm_family != AF_INET && rt->rtm_family != AF_INET6))
+               return NL_SKIP;
+
+       nlmsg_parse(hdr, sizeof(*rt), tb, RTA_MAX, NULL);
+
+       iif   = tb[RTA_IIF]     ? RTA_INT(tb[RTA_IIF])      : 0;
+       oif   = tb[RTA_OIF]     ? RTA_INT(tb[RTA_OIF])      : 0;
+       table = tb[RTA_TABLE]   ? RTA_U32(tb[RTA_TABLE])    : rt->rtm_table;
+       from  = tb[RTA_SRC]     ? RTA_DATA(tb[RTA_SRC])     : NULL;
+       src   = tb[RTA_PREFSRC] ? RTA_DATA(tb[RTA_PREFSRC]) : NULL;
+       dst   = tb[RTA_DST]     ? RTA_DATA(tb[RTA_DST])     : &def;
+       gw    = tb[RTA_GATEWAY] ? RTA_DATA(tb[RTA_GATEWAY]) : NULL;
+
+       bitlen = (rt->rtm_family == AF_INET6) ? 128 : 32;
+
+       if ((f->type   && rt->rtm_type     != f->type)   ||
+           (f->family && rt->rtm_family   != f->family) ||
+           (f->proto  && rt->rtm_protocol != f->proto)  ||
+           (f->scope  && rt->rtm_scope    != f->scope)  ||
+               (f->iif    && iif              != f->iif)    ||
+               (f->oif    && oif              != f->oif)    ||
+               (f->table  && table            != f->table)  ||
+           diff_prefix(rt->rtm_family, from, rt->rtm_src_len, &f->from) ||
+           diff_prefix(rt->rtm_family, dst,  rt->rtm_dst_len, &f->dst)  ||
+           diff_prefix(rt->rtm_family, gw,   bitlen, &f->gw)            ||
+           diff_prefix(rt->rtm_family, src,  bitlen, &f->src))
+               goto out;
+
+       if (s->callback)
+               lua_pushvalue(s->L, 2);
+
+       lua_newtable(s->L);
+
+       L_setint(s->L, "type", rt->rtm_type);
+       L_setint(s->L, "family", (rt->rtm_family == AF_INET) ? 4 : 6);
+
+       L_setaddr(s->L, "dest", rt->rtm_family, dst, rt->rtm_dst_len);
+
+       if (gw)
+               L_setaddr(s->L, "gw", rt->rtm_family, gw, -1);
+
+       if (from)
+               L_setaddr(s->L, "from", rt->rtm_family, from, rt->rtm_src_len);
+
+       if (iif)
+               L_setdev(s->L, "iif", tb[RTA_IIF]);
+
+       if (oif)
+               L_setdev(s->L, "dev", tb[RTA_OIF]);
+
+       L_setint(s->L, "table", table);
+       L_setint(s->L, "proto", rt->rtm_protocol);
+       L_setint(s->L, "scope", rt->rtm_scope);
+
+       if (src)
+               L_setaddr(s->L, "src", rt->rtm_family, src, -1);
+
+       if (tb[RTA_PRIORITY])
+               L_setint(s->L, "metric", RTA_U32(tb[RTA_PRIORITY]));
+
+       if (rt->rtm_family == AF_INET6 && tb[RTA_CACHEINFO])
+       {
+               struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]);
+
+               if (ci->rta_expires)
+               {
+                       if (ci->rta_expires)
+                               L_setint(s->L, "expires", ci->rta_expires / hz);
+
+                       if (ci->rta_error != 0)
+                               L_setint(s->L, "error", ci->rta_error);
+               }
+       }
+
+       s->index++;
+
+       if (s->callback)
+               lua_call(s->L, 1, 0);
+       else if (hdr->nlmsg_flags & NLM_F_MULTI)
+               lua_rawseti(s->L, -2, s->index);
+
+out:
+       s->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
+       return NL_SKIP;
+}
+
+static int
+cb_done(struct nl_msg *msg, void *arg)
+{
+       struct dump_state *s = arg;
+       s->pending = 0;
+       return NL_STOP;
+}
+
+static int
+cb_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
+{
+       struct dump_state *s = arg;
+       s->pending = 0;
+       return NL_STOP;
+}
+
+static int _error(lua_State *L, int code, const char *msg)
+{
+       lua_pushnil(L);
+       lua_pushnumber(L, code ? code : errno);
+       lua_pushstring(L, msg ? msg : strerror(errno));
+
+       return 3;
+}
+
+static int _route_dump(lua_State *L, struct dump_filter *filter)
+{
+       int flags = NLM_F_REQUEST;
+       struct dump_state s = {
+               .L = L,
+               .pending = 1,
+               .index = 0,
+               .callback = lua_isfunction(L, 2),
+               .filter = filter
+       };
+
+       if (!hz)
+               hz = sysconf(_SC_CLK_TCK);
+
+       if (!sock)
+       {
+               sock = nl_socket_alloc();
+               if (!sock)
+                       return _error(L, -1, "Out of memory");
+
+               if (nl_connect(sock, NETLINK_ROUTE))
+                       return _error(L, 0, NULL);
+       }
+
+       struct nl_msg *msg;
+       struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+       struct rtmsg rtm = {
+               .rtm_family = filter->family,
+               .rtm_dst_len = filter->dst.bits,
+               .rtm_src_len = filter->src.bits
+       };
+
+       if (!filter->get)
+               flags |= NLM_F_DUMP;
+
+       msg = nlmsg_alloc_simple(RTM_GETROUTE, flags);
+       if (!msg)
+               goto out;
+
+       nlmsg_append(msg, &rtm, sizeof(rtm), 0);
+
+       if (filter->get)
+               nla_put(msg, RTA_DST, filter->dst.len, &filter->dst.addr.v6);
+
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_route, &s);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &s);
+       nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &s);
+
+       nl_send_auto_complete(sock, msg);
+
+       if (!filter->get && !s.callback)
+               lua_newtable(L);
+
+       while (s.pending > 0)
+               nl_recvmsgs(sock, cb);
+
+       nlmsg_free(msg);
+
+out:
+       nl_cb_put(cb);
+       return (s.callback == 0);
+}
+
+static int route_get(lua_State *L)
+{
+       struct dump_filter filter = { .get = true };
+       const char *dest = luaL_checkstring(L, 1);
+
+       if (!parse_cidr(dest, &filter.dst))
+               return _error(L, -1, "Invalid destination");
+
+       filter.family = filter.dst.family;
+
+       return _route_dump(L, &filter);
+}
+
+static int route_dump(lua_State *L)
+{
+       const char *s;
+       cidr_t p = { };
+       struct dump_filter filter = { };
+
+       if (lua_type(L, 1) == LUA_TTABLE)
+       {
+               filter.family = L_getint(L, 1, "family");
+
+               if (filter.family == 4)
+                       filter.family = AF_INET;
+               else if (filter.family == 6)
+                       filter.family = AF_INET6;
+               else
+                       filter.family = 0;
+
+               if ((s = L_getstr(L, 1, "iif")) != NULL)
+                       filter.iif = if_nametoindex(s);
+
+               if ((s = L_getstr(L, 1, "oif")) != NULL)
+                       filter.oif = if_nametoindex(s);
+
+               filter.type = L_getint(L, 1, "type");
+               filter.scope = L_getint(L, 1, "scope");
+               filter.proto = L_getint(L, 1, "proto");
+               filter.table = L_getint(L, 1, "table");
+
+               if ((s = L_getstr(L, 1, "gw")) != NULL && parse_cidr(s, &p))
+                       filter.gw = p;
+
+               if ((s = L_getstr(L, 1, "from")) != NULL && parse_cidr(s, &p))
+                       filter.from = p;
+
+               if ((s = L_getstr(L, 1, "src")) != NULL && parse_cidr(s, &p))
+                       filter.src = p;
+
+               if ((s = L_getstr(L, 1, "dest")) != NULL && parse_cidr(s, &p))
+                       filter.dst = p;
+
+               if ((s = L_getstr(L, 1, "from_exact")) != NULL && parse_cidr(s, &p))
+                       filter.from = p, filter.from.exact = true;
+
+               if ((s = L_getstr(L, 1, "dest_exact")) != NULL && parse_cidr(s, &p))
+                       filter.dst = p, filter.dst.exact = true;
+       }
+
+       return _route_dump(L, &filter);
+}
+
+
+static bool diff_macaddr(struct ether_addr *mac1, struct ether_addr *mac2)
+{
+       struct ether_addr empty = { };
+
+       if (!memcmp(mac2, &empty, sizeof(empty)))
+               return false;
+
+       if (!mac1 || memcmp(mac1, mac2, sizeof(empty)))
+               return true;
+
+       return false;
+}
+
+static int cb_dump_neigh(struct nl_msg *msg, void *arg)
+{
+       char buf[32];
+       struct ether_addr *mac;
+       struct in6_addr *dst;
+       struct dump_state *s = arg;
+       struct dump_filter *f = s->filter;
+       struct nlmsghdr *hdr = nlmsg_hdr(msg);
+       struct ndmsg *nd = NLMSG_DATA(hdr);
+       struct nlattr *tb[NDA_MAX+1];
+       int bitlen;
+
+       if (hdr->nlmsg_type != RTM_NEWNEIGH ||
+           (nd->ndm_family != AF_INET && nd->ndm_family != AF_INET6))
+               return NL_SKIP;
+
+       nlmsg_parse(hdr, sizeof(*nd), tb, NDA_MAX, NULL);
+
+       mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL;
+       dst = tb[NDA_DST]    ? RTA_DATA(tb[NDA_DST])    : NULL;
+
+       bitlen = (nd->ndm_family == AF_INET) ? 32 : 128;
+
+       if ((f->family && nd->ndm_family  != f->family) ||
+           (f->iif    && nd->ndm_ifindex != f->iif) ||
+               (f->type   && !(f->type & nd->ndm_state)) ||
+           diff_prefix(nd->ndm_family, dst, bitlen, &f->dst) ||
+           diff_macaddr(mac, &f->mac))
+               goto out;
+
+       if (s->callback)
+               lua_pushvalue(s->L, 2);
+
+       lua_newtable(s->L);
+
+       L_setint(s->L, "family", (nd->ndm_family == AF_INET) ? 4 : 6);
+       L_setstr(s->L, "dev", if_indextoname(nd->ndm_ifindex, buf));
+
+       L_setbool(s->L, "router", (nd->ndm_flags & NTF_ROUTER));
+       L_setbool(s->L, "proxy", (nd->ndm_flags & NTF_PROXY));
+
+       L_setbool(s->L, "incomplete", (nd->ndm_state & NUD_INCOMPLETE));
+       L_setbool(s->L, "reachable", (nd->ndm_state & NUD_REACHABLE));
+       L_setbool(s->L, "stale", (nd->ndm_state & NUD_STALE));
+       L_setbool(s->L, "delay", (nd->ndm_state & NUD_DELAY));
+       L_setbool(s->L, "probe", (nd->ndm_state & NUD_PROBE));
+       L_setbool(s->L, "failed", (nd->ndm_state & NUD_FAILED));
+       L_setbool(s->L, "noarp", (nd->ndm_state & NUD_NOARP));
+       L_setbool(s->L, "permanent", (nd->ndm_state & NUD_PERMANENT));
+
+       if (dst)
+               L_setaddr(s->L, "dest", nd->ndm_family, dst, -1);
+
+       if (mac)
+       {
+               snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
+                        mac->ether_addr_octet[0], mac->ether_addr_octet[1],
+                        mac->ether_addr_octet[2], mac->ether_addr_octet[3],
+                        mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
+
+               lua_pushstring(s->L, buf);
+               lua_setfield(s->L, -2, "mac");
+       }
+
+       s->index++;
+
+       if (s->callback)
+               lua_call(s->L, 1, 0);
+       else if (hdr->nlmsg_flags & NLM_F_MULTI)
+               lua_rawseti(s->L, -2, s->index);
+
+out:
+       s->pending = !!(hdr->nlmsg_flags & NLM_F_MULTI);
+       return NL_SKIP;
+}
+
+static int neighbor_dump(lua_State *L)
+{
+       cidr_t p = { };
+       const char *s;
+       struct ether_addr *mac;
+       struct dump_filter filter = { .type = 0xFF & ~NUD_NOARP };
+       struct dump_state st = {
+               .callback = lua_isfunction(L, 2),
+               .pending = 1,
+               .filter = &filter,
+               .L = L
+       };
+
+       if (lua_type(L, 1) == LUA_TTABLE)
+       {
+               filter.family = L_getint(L, 1, "family");
+
+               if (filter.family == 4)
+                       filter.family = AF_INET;
+               else if (filter.family == 6)
+                       filter.family = AF_INET6;
+               else
+                       filter.family = 0;
+
+               if ((s = L_getstr(L, 1, "dev")) != NULL)
+                       filter.iif = if_nametoindex(s);
+
+               if ((s = L_getstr(L, 1, "dest")) != NULL && parse_cidr(s, &p))
+                       filter.dst = p;
+
+               if ((s = L_getstr(L, 1, "mac")) != NULL &&
+                   (mac = ether_aton(s)) != NULL)
+                       filter.mac = *mac;
+       }
+
+       if (!sock)
+       {
+               sock = nl_socket_alloc();
+               if (!sock)
+                       return _error(L, -1, "Out of memory");
+
+               if (nl_connect(sock, NETLINK_ROUTE))
+                       return _error(L, 0, NULL);
+       }
+
+       struct nl_msg *msg;
+       struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+       struct ndmsg ndm = {
+               .ndm_family = filter.family
+       };
+
+       msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
+       if (!msg)
+               goto out;
+
+       nlmsg_append(msg, &ndm, sizeof(ndm), 0);
+
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_neigh, &st);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
+       nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st);
+
+       nl_send_auto_complete(sock, msg);
+
+       if (!st.callback)
+               lua_newtable(L);
+
+       while (st.pending > 0)
+               nl_recvmsgs(sock, cb);
+
+       nlmsg_free(msg);
+
+out:
+       nl_cb_put(cb);
+       return (st.callback == 0);
+}
+
+
+static int cb_dump_link(struct nl_msg *msg, void *arg)
+{
+       char *p, *addr, buf[48];
+       struct dump_state *s = arg;
+       struct nlmsghdr *hdr = nlmsg_hdr(msg);
+       struct ifinfomsg *ifm = NLMSG_DATA(hdr);
+       struct nlattr *tb[IFLA_MAX+1];
+       int i, len;
+
+       if (hdr->nlmsg_type != RTM_NEWLINK)
+               return NL_SKIP;
+
+       nlmsg_parse(hdr, sizeof(*ifm), tb, IFLA_MAX, NULL);
+
+       L_setbool(s->L, "up", (ifm->ifi_flags & IFF_RUNNING));
+       L_setint(s->L, "type", ifm->ifi_type);
+       L_setstr(s->L, "name", if_indextoname(ifm->ifi_index, buf));
+
+       if (tb[IFLA_MTU])
+               L_setint(s->L, "mtu", RTA_U32(tb[IFLA_MTU]));
+
+       if (tb[IFLA_TXQLEN])
+               L_setint(s->L, "qlen", RTA_U32(tb[IFLA_TXQLEN]));
+
+       if (tb[IFLA_MASTER])
+               L_setdev(s->L, "master", tb[IFLA_MASTER]);
+
+       if (tb[IFLA_ADDRESS])
+       {
+               len  = nla_len(tb[IFLA_ADDRESS]);
+               addr = nla_get_string(tb[IFLA_ADDRESS]);
+
+               if ((len * 3) <= sizeof(buf))
+               {
+                       for (p = buf, i = 0; i < len; i++)
+                               p += sprintf(p, "%s%02x", (i ? ":" : ""), (uint8_t)*addr++);
+
+                       L_setstr(s->L, "mac", buf);
+               }
+       }
+
+       s->pending = 0;
+       return NL_SKIP;
+}
+
+static int link_get(lua_State *L)
+{
+       const char *dev = luaL_checkstring(L, 1);
+       struct dump_state st = {
+               .pending = 1,
+               .L = L
+       };
+
+       if (!sock)
+       {
+               sock = nl_socket_alloc();
+               if (!sock)
+                       return _error(L, -1, "Out of memory");
+
+               if (nl_connect(sock, NETLINK_ROUTE))
+                       return _error(L, 0, NULL);
+       }
+
+       struct nl_msg *msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST);
+       struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
+       struct ifinfomsg ifm = { .ifi_index = if_nametoindex(dev) };
+
+       if (!msg || !cb)
+               return 0;
+
+       nlmsg_append(msg, &ifm, sizeof(ifm), 0);
+
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_link, &st);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
+       nl_cb_err(cb, NL_CB_CUSTOM, cb_error, &st);
+
+       lua_newtable(L);
+
+       nl_send_auto_complete(sock, msg);
+
+       while (st.pending > 0)
+               nl_recvmsgs(sock, cb);
+
+       nlmsg_free(msg);
+       nl_cb_put(cb);
+
+       return 1;
+}
+
+
+static const luaL_reg ip_methods[] = {
+       { "new",                        cidr_new          },
+       { "IPv4",                       cidr_ipv4         },
+       { "IPv6",                       cidr_ipv6         },
+
+       { "route",                      route_get         },
+       { "routes",                     route_dump        },
+
+       { "neighbors",          neighbor_dump     },
+
+       { "link",           link_get          },
+
+       { }
+};
+
+static const luaL_reg ip_cidr_methods[] = {
+       { "is4",                        cidr_is4          },
+       { "is4rfc1918",         cidr_is4rfc1918   },
+       { "is4linklocal",       cidr_is4linklocal },
+       { "is6",                        cidr_is6          },
+       { "is6linklocal",       cidr_is6linklocal },
+       { "is6mapped4",         cidr_is6mapped4   },
+       { "lower",                      cidr_lower        },
+       { "higher",                     cidr_higher       },
+       { "equal",                      cidr_equal        },
+       { "prefix",                     cidr_prefix       },
+       { "network",            cidr_network      },
+       { "host",                       cidr_host         },
+       { "mask",                       cidr_mask         },
+       { "broadcast",          cidr_broadcast    },
+       { "mapped4",            cidr_mapped4      },
+       { "contains",       cidr_contains     },
+       { "add",                        cidr_add          },
+       { "sub",                        cidr_sub          },
+       { "minhost",            cidr_minhost      },
+       { "maxhost",            cidr_maxhost      },
+       { "string",                     cidr_tostring     },
+
+       { "__lt",                       cidr_lower        },
+       { "__le",                       cidr_lower_equal  },
+       { "__eq",                       cidr_equal        },
+       { "__add",                      cidr_add          },
+       { "__sub",                      cidr_sub          },
+       { "__gc",                       cidr_gc           },
+       { "__tostring",         cidr_tostring     },
+
+       { }
+};
+
+int luaopen_luci_ip(lua_State *L)
+{
+       luaL_register(L, LUCI_IP, ip_methods);
+
+       luaL_newmetatable(L, LUCI_IP_CIDR);
+       luaL_register(L, NULL, ip_cidr_methods);
+       lua_pushvalue(L, -1);
+       lua_setfield(L, -2, "__index");
+       lua_pop(L, 1);
+
+       return 1;
+}
diff --git a/libs/luci-lib-ip/src/ip.luadoc b/libs/luci-lib-ip/src/ip.luadoc
new file mode 100644 (file)
index 0000000..0073883
--- /dev/null
@@ -0,0 +1,831 @@
+--- LuCI IP calculation and netlink access library.
+module "luci.ip"
+
+---[[
+Construct a new luci.ip.cidr instance and autodetect the address family.
+Throws an error if the given strings do not represent a valid address or
+if the given optional netmask is of a different family.
+@class function
+@sort 1
+@name new
+@param address String containing a valid IPv4 or IPv6 address, optionally
+with prefix size (CIDR notation) or netmask separated by slash.
+@param netmask String containing a valid IPv4 or IPv6 netmask or number
+containing a prefix size in bits (`0..32` for IPv4,
+`0..128` for IPv6). Overrides mask embedded in the first argument
+if specified. (optional)
+@return A `luci.ip.cidr` object representing the given
+address/mask range.
+@usage `addr = luci.ip.new("10.24.0.1/24")
+addr = luci.ip.new("10.24.0.1/255.255.255.0")
+addr = luci.ip.new("10.24.0.1", "255.255.255.0")        -- separate netmask
+addr = luci.ip.new("10.24.0.1/24", 16)                  -- override netmask
+
+addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64")
+addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::")
+addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::")
+addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask`
+@see IPv4
+@see IPv6
+]]
+
+---[[
+Construct a new IPv4 luci.ip.cidr instance.
+Throws an error if the given string does not represent a valid IPv4 address or
+if the given optional netmask is of a different family.
+@class function
+@sort 2
+@name IPv4
+@param address String containing a valid IPv4, optionally with prefix size
+(CIDR notation) or netmask separated by slash.
+@param netmask String containing a valid IPv4 netmask or number
+containing a prefix size between `0` and `32` bit.
+Overrides mask embedded in the first argument if specified. (optional)
+@return A `luci.ip.cidr` object representing the given IPv4 range.
+@usage `addr = luci.ip.new("10.24.0.1/24")
+addr = luci.ip.new("10.24.0.1/255.255.255.0")
+addr = luci.ip.new("10.24.0.1", "255.255.255.0")        -- separate netmask
+addr = luci.ip.new("10.24.0.1/24", 16)                  -- override netmask`
+@see IPv6
+]]
+
+---[[
+Construct a new IPv6 luci.ip.cidr instance.
+Throws an error if the given string does not represent a valid IPv6 address or
+if the given optional netmask is of a different family.
+@class function
+@sort 3
+@name IPv6
+@param address String containing a valid IPv6, optionally with prefix size
+(CIDR notation) or netmask separated by slash.
+@param netmask String containing a valid IPv4 netmask or number
+containing a prefix size between `0` and `128` bit.
+Overrides mask embedded in the first argument if specified. (optional)
+@return A `luci.ip.cidr` object representing the given IPv6 range.
+@usage `addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64")
+addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::")
+addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::")
+addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask`
+@see IPv4
+]]
+
+---[[
+Determine the route leading to the given destination.
+@class function
+@sort 4
+@name route
+@param address A `luci.ip.cidr` instance or a string containing
+a valid IPv4 or IPv6 range as specified by `luci.ip.new()`.
+@return <p>Table containing the fields described below.</p>
+<table id="routetable">
+<tr><th>Field</th><th>Description</th></tr>
+<tr><td>`type`<td>
+  <p>Route type with one of the following numeric values:</p>
+  <table>
+  <tr>
+       <td>`1`</td>
+       <td>`RTN_UNICAST` - Gateway or direct route</td>
+  </tr>
+  <tr>
+       <td>`2`</td>
+       <td>`RTN_LOCAL` - Accept locally</td>
+  </tr>
+  <tr>
+       <td>`3`</td>
+       <td>`RTN_BROADCAST` -
+           Accept locally as broadcast send as broadcast</td>
+  </tr>
+  <tr>
+       <td>`4`</td>
+       <td>`RTN_ANYCAST` -
+        Accept locally as broadcast but send as unicast</td>
+  </tr>
+  <tr>
+       <td>`5`</td>
+       <td>`RTN_MULTICAST` - Multicast route</td>
+  </tr>
+  </table>
+</td></tr>
+<tr>
+  <td>`family`</td>
+  <td>Number containing the route family, `4` for IPv4 or
+      `6` for IPv6</td>
+</tr>
+<tr>
+  <td>`dest`</td>
+  <td>Destination `luci.ip.cidr` instance</td>
+</tr>
+<tr>
+  <td>`gw`</td>
+  <td>Gateway `luci.ip.cidr` instance (optional)</td>
+</tr>
+<tr>
+  <td>`from`</td>
+  <td>Source address `luci.ip.cidr` instance (optional)</td>
+</tr>
+<tr>
+  <td>`src`</td>
+  <td>Preferred source `luci.ip.cidr` instance (optional)</td>
+</tr>
+<tr>
+  <td>`dev`</td>
+  <td>String containing the name of the outgoing interface</td>
+</tr>
+<tr>
+  <td>`iif`</td>
+  <td>String containing the name of the incoming interface (optional)</td>
+</tr>
+<tr>
+  <td>`table`</td>
+  <td>Number of the associated routing table (`0..65535`)</td>
+</tr>
+<tr>
+  <td>`proto`</td>
+  <td>Number of the associated routing protocol</td>
+</tr>
+<tr>
+  <td>`scope`</td>
+  <td>Number describing the scope of the route, most commonly
+      `0` for global or `253` for on-link</td>
+</tr>
+<tr>
+  <td>`metric`</td>
+  <td>Number describing the route metric (optional)</td>
+</tr>
+<tr>
+  <td>`expires`</td>
+  <td>Number of seconds the prefix is valid (IPv6 only, optional)</td>
+</tr>
+<tr>
+  <td>`error`</td>
+  <td>Route destination error code (optional)</td>
+</tr>
+</table>
+@usage <ul>
+<li>Find default gateway by getting route to Google's public NS server
+`rt = luci.ip.route("8.8.8.8")
+if rt ~= nil then
+       print("gateway is", rt.gw)
+end`</li>
+<li>Determine IPv6 upstream interface `rt = luci.ip.route("2001::/7")
+if rt ~= nil then
+       print("ipv6 upstream device is", rt.dev)
+end`</li>
+</ul>
+@see routes
+]]
+
+---[[
+Fetch all routes, optionally matching the given criteria.
+@class function
+@sort 5
+@name routes
+@param filter  <p>Table containing one or more of the possible filter
+critera described below (optional)</p><table>
+<tr><th>Field</th><th>Description</th></tr>
+<tr><td>`family`</td><td>
+ Number describing the address family to return - `4` selects
+ IPv4 routes, `6` IPv6 ones. Any other value selects both.
+</td></tr>
+<tr><td>`iif`</td><td>
+ String containing the incoming route interface to match.
+</td></tr>
+<tr><td>`oif`</td><td>
+ String containing the outgoing route interface to match.
+</td></tr>
+<tr><td>`type`</td><td>
+ Numeric type to match, e.g. `1` for unicast.
+</td></tr>
+<tr><td>`scope`</td><td>
+ Numeric scope to match, e.g. `253` for onlink.
+</td></tr>
+<tr><td>`proto`</td><td>
+ Numeric protocol to match, e.g. `2` for boot.
+</td></tr>
+<tr><td>`table`</td><td>
+ Numeric routing table to match (`0..65535`).
+</td></tr>
+<tr><td>`gw`</td><td>
+ String containing the gateway address to match. Can be in any notation
+ specified by `luci.ip.new()`. Prefix matching is performed when
+ comparing the routes, e.g. "192.168.1.0/24" would select routes with gateway
+ addresses `192.168.1.1 .. 192.168.1.255`.
+</td></tr>
+<tr><td>`dest`</td><td>
+ String containing the destination to match. Prefix matching is performed.
+</td></tr>
+<tr><td>`from`</td><td>
+ String containing the source address to match. Prefix matching is performed.
+</td></tr>
+<tr><td>`src`</td><td>
+ String containing the preferred source address to match.
+ Prefix matching is performed.
+</td></tr>
+<tr><td>`dest_exact`</td><td>
+ String containing the destination to match. Exact matching is performed,
+ e.g. `dest = "0.0.0.0/0"` would match <em>any</em> IPv4 route
+ while `dest_exact = "0.0.0.0/0"` will <em>only</em> match the
+ default route.
+</td></tr>
+<tr><td>`from_exact`</td><td>
+ String containing the source address to match. Exact matching is performed.
+</td></tr>
+</table>
+@param callback  <p>Callback function to invoke for each found route
+instead of returning one table of route objects (optional)</p>
+@return If no callback function is provided, a table of routes
+<a href="#routetable">as specified by `luci.ip.route()`</a>
+is returned. If a callback function is given, it is invoked for each route
+and nothing is returned.
+@see route
+@usage <ul>
+<li>Find all IPv4 default routes:
+`luci.ip.routes({ dest_exact = "0.0.0.0/0" }, function(rt)
+       print(rt.type, rt.gw, rt.dev)
+end)`</li>
+<li>Find all global IPv6 prefixes on the current system:
+`luci.ip.routes({ from = "2001::/7" }, function(rt)
+       print(rt.from)
+end)`</li>
+<li>Fetch all IPv4 routes:
+`routes = luci.ip.routes({ family = 4 })
+for _, rt in ipairs(routes) do
+       print(rt.dest, rt.gw, rt.dev)
+end`</li>
+</ul>
+]]
+
+---[[
+Fetches entries from the IPv4 ARP and IPv6 neighbour kernel table
+@class function
+@sort 6
+@name neighbors
+@param filter  <p>Table containing one or more of the possible filter
+critera described below (optional)</p><table>
+<tr><th>Field</th><th>Description</th></tr>
+<tr><td>`family`</td><td>
+ Number describing the address family to return - `4` selects
+ IPv4 ARP, `6` select IPv6 neighbour entries. Any other value
+ selects both.
+</td></tr>
+<tr><td>`dev`</td><td>
+ String containing the associated interface to match.
+</td></tr>
+<tr><td>`dest`</td><td>
+ String containing the associated address to match. Can be in any notation
+ specified by `luci.ip.new()`. Prefix matching is performed when
+ comparing the addresses, e.g. "192.168.1.0/24" would select ARP entries
+ for `192.168.1.1 .. 192.168.1.255`.
+</td></tr>
+<tr><td>`mac`</td><td>
+ String containing MAC address to match.
+</td></tr>
+</table>
+@param callback  <p>Callback function to invoke for each found neighbour
+entry instead of returning one table of neighbour entries (optional)</p>
+@return If no callback function is provided, a table of neighbour entries
+is returned. If a callback function is given, it is invoked for each entry
+and nothing is returned.
+
+A neighbour entry is a table containing the following fields:
+
+<table>
+<tr><th>Field</th><th>Description</th></tr>
+<tr>
+  <td>`family`</td>
+  <td>Number containing the neighbour entry family, `4` for IPv4
+      ARP or `6` for IPv6 NDP</td>
+</tr>
+<tr>
+  <td>`dev`</td>
+  <td>String containing the associated device of the neighbour entry</td>
+</tr>
+<tr>
+  <td>`dest`</td>
+  <td>IP address `luci.ip.cidr` instance</td>
+</tr>
+<tr>
+  <td>`mac`</td>
+  <td>String containing the associated MAC address</td>
+</tr>
+<tr>
+  <td>`router`</td>
+  <td>Boolean "true" if the neighbour entry is a router (IPv6, optional)</td>
+</tr>
+<tr>
+  <td>`proxy`</td>
+  <td>Boolean "true" if this is a proxy entry (optional)</td>
+</tr>
+<tr>
+  <td>`incomplete`</td>
+  <td>Boolean "true" if the entry is in incomplete state (optional)</td>
+</tr>
+<tr>
+  <td>`reachable`</td>
+  <td>Boolean "true" if the entry is in reachable state (optional)</td>
+</tr>
+<tr>
+  <td>`stale`</td>
+  <td>Boolean "true" if the entry is stale (optional)</td>
+</tr>
+<tr>
+  <td>`delay`</td>
+  <td>Boolean "true" if the entry is delayed (optional)</td>
+</tr>
+<tr>
+  <td>`probe`</td>
+  <td>Boolean "true" if the entry is in probe state (optional)</td>
+</tr>
+<tr>
+  <td>`failed`</td>
+  <td>Boolean "true" if the entry is in failed state (optional)</td>
+</tr>
+<tr>
+  <td>`noarp`</td>
+  <td>Boolean "true" if the entry is not caused by NDP or
+      ARP (optional)</td>
+</tr>
+<tr>
+  <td>`permanent`</td>
+  <td>Boolean "true" if the entry was statically configured from
+      userspace (optional)</td>
+</tr>
+</table>
+@usage <ul>
+<li>Find all ARP neighbours in the LAN:
+`luci.ip.neighbors({ dest = "192.168.0.0/16" }, function(n)
+       print(n.dest, n.mac)
+end)`</li>
+<li>Find all active IPv6 addresses of host with given MAC:
+`luci.ip.neighbors({ family = 6, mac = "00:21:63:75:aa:17" },
+       function(n)
+               print(n.dest)
+       end)`</li>
+</ul>
+]]
+
+---[[
+Fetch basic device information
+@class function
+@sort 7
+@name link
+@param device  String containing the network device to query
+@return  If the given interface is found, a table containing the fields
+described below is returned, else an empty table.
+
+<table>
+<tr><th>Field</th><th>Description</th></tr>
+<tr>
+  <td>`up`</td>
+  <td>Boolean indicating whether the device is in IFF_RUNNING state</td>
+</tr>
+<tr>
+  <td>`type`</td>
+  <td>Numeric value indicating the type of the device, e.g. `1`
+      for ethernet.</td>
+</tr>
+<tr>
+  <td>`name`</td>
+  <td>String containing the name of the device</td>
+</tr>
+<tr>
+  <td>`master`</td>
+  <td>If queried device is a bridge port, string containing the name of
+      parent bridge device (optional)</td>
+</tr>
+<tr>
+  <td>`mtu`</td>
+  <td>Number containing the current MTU of the device</td>
+</tr>
+<tr>
+  <td>`qlen`</td>
+  <td>Number containing the TX queue length of the device</td>
+</tr>
+<tr>
+  <td>`mac`</td>
+  <td>String containing the link local address of the device in
+      dotted hex notation</td>
+</tr>
+</table>
+@usage <ul>
+<li>Test whether device br-lan exists:
+`print(luci.ip.link("br-lan").name ~= nil)
+`</li>
+<li>Query MAC address of eth0:
+`print(luci.ip.link("eth0").mac)
+`</li>
+</ul>
+]]
+
+
+--- IP CIDR Object.
+-- Represents an IPv4 or IPv6 address range.
+-- @cstyle instance
+module "luci.ip.cidr"
+
+---[[
+Checks whether the CIDR instance is an IPv4 address range
+
+@class function
+@sort 1
+@name cidr.is4
+@see cidr.is6
+@return `true` if the CIDR is an IPv4 range, else `false`
+]]
+
+---[[
+Checks whether the CIDR instance is within the private RFC1918 address space
+
+@class function
+@sort 2
+@name cidr.is4rfc1918
+@return `true` if the entire range of this CIDR lies within one of
+       the ranges `10.0.0.0-10.255.255.255`,
+       `172.16.0.0-172.31.0.0` or
+       `192.168.0.0-192.168.255.255`, else `false`.
+@usage `local addr = luci.ip.new("192.168.45.2/24")
+if addr:is4rfc1918() then
+       print("Is a private address")
+end`
+]]
+
+---[[
+Checks whether the CIDR instance is an IPv4 link local (Zeroconf) address
+
+@class function
+@sort 3
+@name cidr.is4linklocal
+@return `true` if the entire range of this CIDR lies within the range
+       the range `169.254.0.0-169.254.255.255`, else `false`.
+@usage `local addr = luci.ip.new("169.254.34.125")
+if addr:is4linklocal() then
+       print("Is a zeroconf address")
+end`
+]]
+
+---[[
+Checks whether the CIDR instance is an IPv6 address range
+
+@class function
+@sort 4
+@name cidr.is6
+@see cidr.is4
+@return `true` if the CIDR is an IPv6 range, else `false`
+]]
+
+---[[
+Checks whether the CIDR instance is an IPv6 link local address
+
+@class function
+@sort 5
+@name cidr.is6linklocal
+@return `true` if the entire range of this CIDR lies within the range
+       the `fe80::/10` range, else `false`.
+@usage `local addr = luci.ip.new("fe92:53a:3216:af01:221:63ff:fe75:aa17/64")
+if addr:is6linklocal() then
+       print("Is a linklocal address")
+end`
+]]
+
+---[[
+Checks whether the CIDR instance is an IPv6 mapped IPv4 address
+
+@class function
+@sort 6
+@name cidr.is6mapped4
+@return `true` if the address is an IPv6 mapped IPv4 address in the
+       form `::ffff:1.2.3.4`.
+@usage `local addr = luci.ip.new("::ffff:192.168.1.1")
+if addr:is6mapped4() then
+       print("Is a mapped IPv4 address")
+end`
+]]
+
+---[[
+Checks whether this CIDR instance is lower than the given argument.
+The comparisation follows these rules:
+<ul><li>An IPv4 address is always lower than an IPv6 address</li>
+<li>Prefix sizes are ignored</li></ul>
+
+@class function
+@sort 7
+@name cidr.lower
+@param addr A `luci.ip.cidr` instance or a string convertable by
+       `luci.ip.new()` to compare against.
+@return `true` if this CIDR is lower than the given address,
+       else `false`.
+@usage `local addr = luci.ip.new("192.168.1.1")
+print(addr:lower(addr)) -- false
+print(addr:lower("10.10.10.10/24")) -- false
+print(addr:lower(luci.ip.new("::1"))) -- true
+print(addr:lower(luci.ip.new("192.168.200.1"))) -- true`
+@see cidr.higher
+@see cidr.equal
+]]
+
+---[[
+Checks whether this CIDR instance is higher than the given argument.
+The comparisation follows these rules:
+<ul><li>An IPv4 address is always lower than an IPv6 address</li>
+<li>Prefix sizes are ignored</li></ul>
+
+@class function
+@sort 8
+@name cidr.higher
+@param addr A `luci.ip.cidr` instance or a string convertable by
+       `luci.ip.new()` to compare against.
+@return `true` if this CIDR is higher than the given address,
+       else `false`.
+@usage `local addr = luci.ip.new("192.168.1.1")
+print(addr:higher(addr)) -- false
+print(addr:higher("10.10.10.10/24")) -- true
+print(addr:higher(luci.ip.new("::1"))) -- false
+print(addr:higher(luci.ip.new("192.168.200.1"))) -- false`
+@see cidr.lower
+@see cidr.equal
+]]
+
+---[[
+Checks whether this CIDR instance is equal to the given argument.
+
+@class function
+@sort 9
+@name cidr.equal
+@param addr A `luci.ip.cidr` instance or a string convertable by
+       `luci.ip.new()` to compare against.
+@return `true` if this CIDR is equal to the given address,
+       else `false`.
+@usage `local addr = luci.ip.new("192.168.1.1")
+print(addr:equal(addr)) -- true
+print(addr:equal("192.168.1.1")) -- true
+print(addr:equal(luci.ip.new("::1"))) -- false
+
+local addr6 = luci.ip.new("::1")
+print(addr6:equal("0:0:0:0:0:0:0:1/64")) -- true
+print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false`
+@see cidr.lower
+@see cidr.higher
+]]
+
+---[[
+Get or set prefix size of CIDR instance.
+If the optional mask parameter is given, the prefix size of this CIDR is altered
+else the current prefix size is returned.
+
+@class function
+@sort 10
+@name cidr.prefix
+@param mask Either a number containing the number of bits (`0..32`
+       for IPv4, `0..128` for IPv6) or a string containing a valid
+       netmask (optional)
+@return Bit count of the current prefix size
+@usage `local range = luci.ip.new("192.168.1.1/255.255.255.0")
+print(range:prefix()) -- 24
+
+range:prefix(16)
+print(range:prefix()) -- 16
+
+range:prefix("255.255.255.255")
+print(range:prefix()) -- 32`
+]]
+
+---[[
+Derive network address of CIDR instance.
+
+Returns a new CIDR instance representing the network address of this instance
+with all host parts masked out. The used prefix size can be overridden by the
+optional mask parameter.
+
+@class function
+@sort 11
+@name cidr.network
+@param mask Either a number containing the number of bits (`0..32`
+       for IPv4, `0..128` for IPv6) or a string containing a valid
+       netmask (optional)
+@return CIDR instance representing the network address
+@usage `local range = luci.ip.new("192.168.62.243/255.255.0.0")
+print(range:network())                -- "192.168.0.0"
+print(range:network(24))              -- "192.168.62.0"
+print(range:network("255.255.255.0")) -- "192.168.62.0"
+
+local range6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
+print(range6:network())               -- "fd9b:62b3:9cc5::"`
+]]
+
+---[[
+Derive host address of CIDR instance.
+
+This function essentially constructs a copy of this CIDR with the prefix size
+set to `32` for IPv4 and `128` for IPv6.
+
+@class function
+@sort 12
+@name cidr.host
+@return CIDR instance representing the host address
+@usage `local range = luci.ip.new("172.19.37.45/16")
+print(range)        -- "172.19.37.45/16"
+print(range:host()) -- "172.19.37.45"`
+]]
+
+---[[
+Derive netmask of CIDR instance.
+
+Constructs a CIDR instance representing the netmask of this instance. The used
+prefix size can be overridden by the optional mask parameter.
+
+@class function
+@sort 13
+@name cidr.mask
+@param mask Either a number containing the number of bits (`0..32`
+       for IPv4, `0..128` for IPv6) or a string containing a valid
+       netmask (optional)
+@return CIDR instance representing the netmask
+@usage `local range = luci.ip.new("172.19.37.45/16")
+print(range:mask())            -- "255.255.0.0"
+print(range:mask(24))          -- "255.255.255.0"
+print(range:mask("255.0.0.0")) -- "255.0.0.0"`
+]]
+
+---[[
+Derive broadcast address of CIDR instance.
+
+Constructs a CIDR instance representing the broadcast address of this instance.
+The used prefix size can be overridden by the optional mask parameter.
+
+This function has no effect on IPv6 instances, it will return nothing in this
+case.
+
+@class function
+@sort 14
+@name cidr.broadcast
+@param mask Either a number containing the number of bits (`0..32`
+       for IPv4, `0..128` for IPv6) or a string containing a valid
+       netmask (optional)
+@return Return a new CIDR instance representing the broadcast address if this
+       instance is an IPv4 range, else return nothing.
+@usage `local range = luci.ip.new("172.19.37.45/16")
+print(range:broadcast())            -- "172.19.255.255"
+print(range:broadcast(24))          -- "172.19.37.255"
+print(range:broadcast("255.0.0.0")) -- "172.255.255.255"`
+]]
+
+---[[
+Derive mapped IPv4 address of CIDR instance.
+
+Constructs a CIDR instance representing the IPv4 address of the IPv6 mapped
+IPv4 address in this instance.
+
+This function has no effect on IPv4 instances or IPv6 instances which are not a
+mapped address, it will return nothing in this case.
+
+@class function
+@sort 15
+@name cidr.mapped4
+@return Return a new CIDR instance representing the IPv4 address if this
+       instance is an IPv6 mapped IPv4 address, else return nothing.
+@usage `local addr = luci.ip.new("::ffff:172.16.19.1")
+print(addr:mapped4()) -- "172.16.19.1"`
+]]
+
+---[[
+Test whether CIDR contains given range.
+
+@class function
+@sort 16
+@name cidr.contains
+@param addr A `luci.ip.cidr` instance or a string convertable by
+       `luci.ip.new()` to test.
+@return `true` if this instance fully contains the given address else
+       `false`.
+@usage `local range = luci.ip.new("10.24.0.0/255.255.0.0")
+print(range:contains("10.24.5.1"))  -- true
+print(range:contains("::1"))        -- false
+print(range:contains("10.0.0.0/8")) -- false
+
+local range6 = luci.ip.new("fe80::/10")
+print(range6:contains("fe80::221:63f:fe75:aa17/64"))         -- true
+print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false`
+]]
+
+---[[
+Add given amount to CIDR instance. If the result would overflow the maximum
+address space, the result is set to the highest possible address.
+
+@class function
+@sort 17
+@name cidr.add
+@param amount A numeric value between 0 and 0xFFFFFFFF, a
+       `luci.ip.cidr` instance or a string convertable by
+       `luci.ip.new()`.
+@param inplace If `true`, modify this instance instead of returning
+       a new derived CIDR instance.
+@return <ul>
+       <li>When adding inplace: Return `true` if the addition succeded
+           or `false` when the addition overflowed.</li>
+       <li>When deriving new CIDR: Return new instance representing the value of
+        this instance plus the added amount or the highest possible address if
+           the addition overflowed the available address space.</li></ul>
+@usage `local addr = luci.ip.new("192.168.1.1/24")
+print(addr:add(250))         -- "192.168.1.251/24"
+print(addr:add("0.0.99.0"))  -- "192.168.100.1/24"
+
+addr:add(256, true)          -- true
+print(addr)                  -- "192.168.2.1/24
+
+addr:add("255.0.0.0", true)  -- false (overflow)
+print(addr)                  -- "255.255.255.255/24
+
+local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64")
+print(addr6:add(256))        -- "fe80::221:63f:fe75:ab17/64"
+print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64"
+
+addr:add(256, true)          -- true
+print(addr)                  -- "fe80::221:63f:fe75:ab17/64
+
+addr:add("ffff::", true)     -- false (overflow)
+print(addr)                  -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"`
+]]
+
+---[[
+Substract given amount from CIDR instance. If the result would under, the lowest
+possible address is returned.
+
+@class function
+@sort 18
+@name cidr.sub
+@param amount A numeric value between 0 and 0xFFFFFFFF, a
+       `luci.ip.cidr` instance or a string convertable by
+       `luci.ip.new()`.
+@param inplace If `true`, modify this instance instead of returning
+       a new derived CIDR instance.
+@return <ul>
+       <li>When substracting inplace: Return `true` if the substraction
+           succeded or `false` when the substraction underflowed.</li>
+       <li>When deriving new CIDR: Return new instance representing the value of
+        this instance minus the substracted amount or the lowest address if
+           the substraction underflowed.</li></ul>
+@usage `local addr = luci.ip.new("192.168.1.1/24")
+print(addr:sub(256))         -- "192.168.0.1/24"
+print(addr:sub("0.168.0.0")) -- "192.0.1.1/24"
+
+addr:sub(256, true)          -- true
+print(addr)                  -- "192.168.0.1/24
+
+addr:sub("255.0.0.0", true)  -- false (underflow)
+print(addr)                  -- "0.0.0.0/24
+
+local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64")
+print(addr6:sub(256))        -- "fe80::221:63f:fe75:a917/64"
+print(addr6:sub("::ffff:0")) -- "fe80::221:63e:fe76:aa17/64"
+
+addr:sub(256, true)          -- true
+print(addr)                  -- "fe80::221:63f:fe75:a917/64"
+
+addr:sub("ffff::", true)     -- false (underflow)
+print(addr)                  -- "::/64"`
+]]
+
+---[[
+Calculate the lowest possible host address within this CIDR instance.
+
+@class function
+@sort 19
+@name cidr.minhost
+@return Returns a new CIDR instance representing the lowest host address
+       within this range.
+@usage `local addr = luci.ip.new("192.168.123.56/24")
+print(addr:minhost())  -- "192.168.123.1"
+
+local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
+print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"`
+]]
+
+---[[
+Calculate the highest possible host address within this CIDR instance.
+
+@class function
+@sort 20
+@name cidr.maxhost
+@return Returns a new CIDR instance representing the highest host address
+       within this range.
+@usage `local addr = luci.ip.new("192.168.123.56/24")
+print(addr:maxhost())  -- "192.168.123.254" (.255 is broadcast)
+
+local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64")
+print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"`
+]]
+
+---[[
+Convert CIDR instance into string representation.
+
+If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the
+address is returned in the form "address/prefix" otherwise just "address".
+
+It is usually not required to call this function directly as CIDR objects
+define it as __tostring function in the associated metatable.
+
+@class function
+@sort 21
+@name cidr.string
+@return Returns a string representing the range or address of this CIDR instance
+]]
index 57b12c9..416b25f 100644 (file)
@@ -25,14 +25,9 @@ local char      = string.char
 
 local getmetatable = getmetatable
 
---- LuCI JSON-Library
--- @cstyle     instance
 module "luci.json"
 
 
---- Directly decode a JSON string
--- @param json JSON-String
--- @return Lua object
 function decode(json, ...)
        local a = ActiveDecoder(function() return nil end, ...)
        a.chunk = json
@@ -41,9 +36,6 @@ function decode(json, ...)
 end
 
 
---- Direcly encode a Lua object into a JSON string.
--- @param obj Lua Object
--- @return JSON string
 function encode(obj, ...)
        local out = {}
        local e = Encoder(obj, 1, ...):source()
@@ -56,19 +48,10 @@ function encode(obj, ...)
 end
 
 
---- Null replacement function
--- @return null
 function null()
        return null
 end
 
---- Create a new JSON-Encoder.
--- @class      function
--- @name       Encoder
--- @param data                 Lua-Object to be encoded.
--- @param buffersize   Blocksize of returned data source.
--- @param fastescape   Use non-standard escaping (don't escape control chars)
--- @return JSON-Encoder
 Encoder = util.class()
 
 function Encoder.__init__(self, data, buffersize, fastescape)
@@ -80,8 +63,6 @@ function Encoder.__init__(self, data, buffersize, fastescape)
        getmetatable(self).__call = Encoder.source
 end
 
---- Create an LTN12 source providing the encoded JSON-Data.
--- @return LTN12 source
 function Encoder.source(self)
        local source = coroutine.create(self.dispatch)
        return function()
@@ -208,11 +189,6 @@ Encoder.parsers = {
 }
 
 
---- Create a new JSON-Decoder.
--- @class      function
--- @name       Decoder
--- @param customnull Use luci.json.null instead of nil for decoding null
--- @return JSON-Decoder
 Decoder = util.class()
 
 function Decoder.__init__(self, customnull)
@@ -220,8 +196,6 @@ function Decoder.__init__(self, customnull)
        getmetatable(self).__call = Decoder.sink
 end
 
---- Create an LTN12 sink from the decoder object which accepts the JSON-Data.
--- @return LTN12 sink
 function Decoder.sink(self)
        local sink = coroutine.create(self.dispatch)
        return function(...)
@@ -230,8 +204,6 @@ function Decoder.sink(self)
 end
 
 
---- Get the decoded data packets after the rawdata has been sent to the sink.
--- @return Decoded data
 function Decoder.get(self)
        return self.data
 end
@@ -526,11 +498,6 @@ Decoder.parsers = {
 }
 
 
---- Create a new Active JSON-Decoder.
--- @class      function
--- @name       ActiveDecoder
--- @param   customnull Use luci.json.null instead of nil for decoding null
--- @return  Active JSON-Decoder
 ActiveDecoder = util.class(Decoder)
 
 function ActiveDecoder.__init__(self, source, customnull)
@@ -541,8 +508,6 @@ function ActiveDecoder.__init__(self, source, customnull)
 end
 
 
---- Fetches one JSON-object from given source
--- @return Decoded object
 function ActiveDecoder.get(self)
        local chunk, src_err, object
        if not self.chunk then
diff --git a/libs/luci-lib-json/luasrc/json.luadoc b/libs/luci-lib-json/luasrc/json.luadoc
new file mode 100644 (file)
index 0000000..d48dba1
--- /dev/null
@@ -0,0 +1,94 @@
+---[[
+LuCI JSON-Library
+
+@cstyle        instance
+module "luci.json"
+]]
+
+---[[
+Directly decode a JSON string
+
+@class function
+@name decode
+@param json JSON-String
+@return Lua object
+]]
+
+---[[
+Direcly encode a Lua object into a JSON string.
+
+@class function
+@name encode
+@param obj Lua Object
+@return JSON string
+]]
+
+---[[
+Null replacement function
+
+@class function
+@name null
+@return null
+]]
+
+---[[
+Create a new JSON-Encoder.
+
+@class function
+@name  Encoder
+@param data                    Lua-Object to be encoded.
+@param buffersize      Blocksize of returned data source.
+@param fastescape      Use non-standard escaping (don't escape control chars)
+@return JSON-Encoder
+]]
+
+---[[
+Create an LTN12 source providing the encoded JSON-Data.
+
+@class function
+@name Encoder.source
+@return LTN12 source
+]]
+
+---[[
+Create a new JSON-Decoder.
+
+@class function
+@name  Decoder
+@param customnull Use luci.json.null instead of nil for decoding null
+@return JSON-Decoder
+]]
+
+---[[
+Create an LTN12 sink from the decoder object which accepts the JSON-Data.
+
+@class function
+@name Decoder.sink
+@return LTN12 sink
+]]
+
+---[[
+Get the decoded data packets after the rawdata has been sent to the sink.
+
+@class function
+@name Decoder.get
+@return Decoded data
+]]
+
+---[[
+Create a new Active JSON-Decoder.
+
+@class function
+@name  ActiveDecoder
+@param   customnull    Use luci.json.null instead of nil for decoding null
+@return  Active JSON-Decoder
+]]
+
+---[[
+Fetches one JSON-object from given source
+
+@class function
+@name ActiveDecoder.get
+@return Decoded object
+]]
+
diff --git a/libs/luci-lib-jsonc/Makefile b/libs/luci-lib-jsonc/Makefile
new file mode 100644 (file)
index 0000000..6a63dab
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Copyright (C) 2015 LuCI Team <luci@lists.subsignal.org>
+#
+# This is free software, licensed under the Apache License, Version 2.0 .
+#
+
+include $(TOPDIR)/rules.mk
+
+LUCI_TITLE:=Lua binding for JSON-C
+LUCI_DEPENDS:=+liblua +libjson-c
+
+include ../../luci.mk
+
+# call BuildPackage - OpenWrt buildroot signature
diff --git a/libs/luci-lib-jsonc/src/Makefile b/libs/luci-lib-jsonc/src/Makefile
new file mode 100644 (file)
index 0000000..e15fbac
--- /dev/null
@@ -0,0 +1,17 @@
+JSONC_CFLAGS = -std=gnu99 -I$(STAGING_DIR)/usr/include/json-c/
+JSONC_LDFLAGS = -llua -lm -ljson-c
+JSONC_OBJ = jsonc.o
+JSONC_LIB = jsonc.so
+
+%.o: %.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(LUA_CFLAGS) $(JSONC_CFLAGS) $(FPIC) -c -o $@ $<
+
+compile: $(JSONC_OBJ)
+       $(CC) $(LDFLAGS) -shared -o $(JSONC_LIB) $(JSONC_OBJ) $(JSONC_LDFLAGS)
+
+install: compile
+       mkdir -p $(DESTDIR)/usr/lib/lua/luci
+       cp $(JSONC_LIB) $(DESTDIR)/usr/lib/lua/luci/$(JSONC_LIB)
+
+clean:
+       rm -f *.o *.so
diff --git a/libs/luci-lib-jsonc/src/jsonc.c b/libs/luci-lib-jsonc/src/jsonc.c
new file mode 100644 (file)
index 0000000..49cb21f
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#define _GNU_SOURCE
+
+#include <math.h>
+#include <stdbool.h>
+#include <json-c/json.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#define LUCI_JSONC "luci.jsonc"
+#define LUCI_JSONC_PARSER "luci.jsonc.parser"
+
+struct json_state {
+       struct json_object *obj;
+       struct json_tokener *tok;
+       enum json_tokener_error err;
+};
+
+static void _json_to_lua(lua_State *L, struct json_object *obj);
+static struct json_object * _lua_to_json(lua_State *L, int index);
+
+static int json_new(lua_State *L)
+{
+       struct json_state *s;
+       struct json_tokener *tok = json_tokener_new();
+
+       if (!tok)
+               return 0;
+
+       s = lua_newuserdata(L, sizeof(*s));
+
+       if (!s)
+       {
+               json_tokener_free(tok);
+               return 0;
+       }
+
+       s->tok = tok;
+       s->obj = NULL;
+       s->err = json_tokener_continue;
+
+       luaL_getmetatable(L, LUCI_JSONC_PARSER);
+       lua_setmetatable(L, -2);
+
+       return 1;
+}
+
+static int json_parse(lua_State *L)
+{
+       size_t len;
+       const char *json = luaL_checklstring(L, 1, &len);
+       struct json_state s = {
+               .tok = json_tokener_new()
+       };
+
+       if (!s.tok)
+               return 0;
+
+       s.obj = json_tokener_parse_ex(s.tok, json, len);
+       s.err = json_tokener_get_error(s.tok);
+
+       if (s.obj)
+       {
+               _json_to_lua(L, s.obj);
+               json_object_put(s.obj);
+       }
+       else
+       {
+               lua_pushnil(L);
+       }
+
+       if (s.err == json_tokener_continue)
+               s.err = json_tokener_error_parse_eof;
+
+       if (s.err)
+               lua_pushstring(L, json_tokener_error_desc(s.err));
+
+       json_tokener_free(s.tok);
+       return (1 + !!s.err);
+}
+
+static int json_stringify(lua_State *L)
+{
+       struct json_object *obj = _lua_to_json(L, 1);
+       bool pretty = lua_toboolean(L, 2);
+       int flags = 0;
+
+       if (pretty)
+               flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED;
+
+       lua_pushstring(L, json_object_to_json_string_ext(obj, flags));
+       return 1;
+}
+
+
+static int json_parse_chunk(lua_State *L)
+{
+       size_t len;
+       struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
+       const char *chunk = luaL_checklstring(L, 2, &len);
+
+       s->obj = json_tokener_parse_ex(s->tok, chunk, len);
+    s->err = json_tokener_get_error(s->tok);
+
+       if (!s->err)
+       {
+               lua_pushboolean(L, true);
+               return 1;
+       }
+       else if (s->err == json_tokener_continue)
+       {
+               lua_pushboolean(L, false);
+               return 1;
+       }
+
+       lua_pushnil(L);
+       lua_pushstring(L, json_tokener_error_desc(s->err));
+       return 2;
+}
+
+static void _json_to_lua(lua_State *L, struct json_object *obj)
+{
+       int n;
+
+       switch (json_object_get_type(obj))
+       {
+       case json_type_object:
+               lua_newtable(L);
+               json_object_object_foreach(obj, key, val)
+               {
+                       _json_to_lua(L, val);
+                       lua_setfield(L, -2, key);
+               }
+               break;
+
+       case json_type_array:
+               lua_newtable(L);
+               for (n = 0; n < json_object_array_length(obj); n++)
+               {
+                       _json_to_lua(L, json_object_array_get_idx(obj, n));
+                       lua_rawseti(L, -2, n + 1);
+               }
+               break;
+
+       case json_type_boolean:
+               lua_pushboolean(L, json_object_get_boolean(obj));
+               break;
+
+       case json_type_int:
+               lua_pushinteger(L, json_object_get_int(obj));
+               break;
+
+       case json_type_double:
+               lua_pushnumber(L, json_object_get_double(obj));
+               break;
+
+       case json_type_string:
+               lua_pushstring(L, json_object_get_string(obj));
+               break;
+
+       case json_type_null:
+               lua_pushnil(L);
+               break;
+       }
+}
+
+static int json_parse_get(lua_State *L)
+{
+       struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
+
+       if (!s->obj || s->err)
+               lua_pushnil(L);
+       else
+               _json_to_lua(L, s->obj);
+
+       return 1;
+}
+
+static int _lua_test_array(lua_State *L, int index)
+{
+       int max = 0;
+       lua_Number idx;
+
+       lua_pushnil(L);
+
+       /* check for non-integer keys */
+       while (lua_next(L, index))
+       {
+               if (lua_type(L, -2) != LUA_TNUMBER)
+                       goto out;
+
+               idx = lua_tonumber(L, -2);
+
+               if (idx != (lua_Number)(lua_Integer)idx)
+                       goto out;
+
+               if (idx <= 0)
+                       goto out;
+
+               if (idx > max)
+                       max = idx;
+
+               lua_pop(L, 1);
+               continue;
+
+out:
+               lua_pop(L, 2);
+               return 0;
+       }
+
+       /* check for holes */
+       //for (i = 1; i <= max; i++)
+       //{
+       //      lua_rawgeti(L, index, i);
+       //
+       //      if (lua_isnil(L, -1))
+       //      {
+       //              lua_pop(L, 1);
+       //              return 0;
+       //      }
+       //
+       //      lua_pop(L, 1);
+       //}
+
+       return max;
+}
+
+static struct json_object * _lua_to_json(lua_State *L, int index)
+{
+       lua_Number nd, ni;
+       struct json_object *obj;
+       const char *key;
+       int i, max;
+
+       switch (lua_type(L, index))
+       {
+       case LUA_TTABLE:
+               max = _lua_test_array(L, index);
+
+               if (max > 0)
+               {
+                       obj = json_object_new_array();
+
+                       if (!obj)
+                               return NULL;
+
+                       for (i = 1; i <= max; i++)
+                       {
+                               lua_rawgeti(L, index, i);
+
+                               json_object_array_put_idx(obj, i - 1,
+                                                         _lua_to_json(L, lua_gettop(L)));
+
+                               lua_pop(L, 1);
+                       }
+
+                       return obj;
+               }
+
+               obj = json_object_new_object();
+
+               if (!obj)
+                       return NULL;
+
+               lua_pushnil(L);
+
+               while (lua_next(L, index))
+               {
+                       lua_pushvalue(L, -2);
+                       key = lua_tostring(L, -1);
+
+                       json_object_object_add(obj, key,
+                                                                  _lua_to_json(L, lua_gettop(L) - 1));
+
+                       lua_pop(L, 2);
+               }
+
+               return obj;
+
+       case LUA_TNIL:
+               return NULL;
+
+       case LUA_TBOOLEAN:
+               return json_object_new_boolean(lua_toboolean(L, index));
+
+       case LUA_TNUMBER:
+               nd = lua_tonumber(L, index);
+               ni = lua_tointeger(L, index);
+
+               if (nd == ni)
+                       return json_object_new_int(nd);
+
+               return json_object_new_double(nd);
+
+       case LUA_TSTRING:
+               return json_object_new_string(lua_tostring(L, index));
+       }
+
+       return NULL;
+}
+
+static int json_parse_set(lua_State *L)
+{
+       struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
+
+       s->err = 0;
+       s->obj = _lua_to_json(L, 2);
+
+       return 0;
+}
+
+static int json_tostring(lua_State *L)
+{
+       struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
+       bool pretty = lua_toboolean(L, 2);
+       int flags = 0;
+
+       if (pretty)
+               flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED;
+
+       lua_pushstring(L, json_object_to_json_string_ext(s->obj, flags));
+       return 1;
+}
+
+static int json_gc(lua_State *L)
+{
+       struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
+
+       if (s->obj)
+               json_object_put(s->obj);
+
+       if (s->tok)
+               json_tokener_free(s->tok);
+
+       return 0;
+}
+
+
+static const luaL_reg jsonc_methods[] = {
+       { "new",                        json_new          },
+       { "parse",                      json_parse        },
+       { "stringify",          json_stringify    },
+
+       { }
+};
+
+static const luaL_reg jsonc_parser_methods[] = {
+       { "parse",                      json_parse_chunk  },
+       { "get",                        json_parse_get    },
+       { "set",                        json_parse_set    },
+       { "stringify",          json_tostring     },
+
+       { "__gc",                       json_gc           },
+       { "__tostring",         json_tostring     },
+
+       { }
+};
+
+
+int luaopen_luci_jsonc(lua_State *L)
+{
+       luaL_register(L, LUCI_JSONC, jsonc_methods);
+
+       luaL_newmetatable(L, LUCI_JSONC_PARSER);
+       luaL_register(L, NULL, jsonc_parser_methods);
+       lua_pushvalue(L, -1);
+       lua_setfield(L, -2, "__index");
+       lua_pop(L, 1);
+
+       return 1;
+}
diff --git a/libs/luci-lib-jsonc/src/jsonc.luadoc b/libs/luci-lib-jsonc/src/jsonc.luadoc
new file mode 100644 (file)
index 0000000..2ee9ceb
--- /dev/null
@@ -0,0 +1,134 @@
+--- LuCI JSON parsing and serialization library.
+-- The luci.jsonc class is a high level Lua binding to the JSON-C library to
+-- allow reading and writing JSON data with minimal overhead.
+module "luci.jsonc"
+
+---[[
+Construct a new luci.jsonc.parser instance.
+@class function
+@sort 1
+@name new
+@return A `luci.jsonc.parser` object representing a JSON-C tokener.
+@usage `parser = luci.jsonc.new()`
+]]
+
+---[[
+Parse a complete JSON string and convert it into a Lua data structure.
+@class function
+@sort 2
+@name parse
+@param json  A string containing the JSON data to parse, must be either a
+       JSON array or a JSON object.
+@return On success, a table containing the parsed JSON data is returned, on
+       failure the function returns `nil` and a string containing the reason of
+       the parse error.
+@usage `data = luci.jsonc.parse('{ "name": "John", "age": 34 }')
+print(data.name)  -- "John"`
+@see stringify
+]]
+
+---[[
+Convert given Lua data into a JSON string.
+
+This function recursively converts the given Lua data into a JSON string,
+ignoring any unsupported data. Lua tables are converted into JSON arrays if they
+only contain integer keys, mixed tables are turned into JSON objects with any
+existing numeric keys converted into strings.
+
+Lua functions, coroutines and userdata objects are ignored and Lua numbers are
+converted to integers if they do not contain fractional values.
+
+@class function
+@sort 3
+@name stringify
+@param data  The Lua data to convert, can be a table, string, boolean or number.
+@param pretty  A boolean value indicating whether the resulting JSON should be
+       pretty printed.
+@return Returns a string containing the JSON representation of the given Lua
+       data.
+@usage `json = luci.jsonc.stringify({ item = true, values = { 1, 2, 3 } })
+print(json)  -- '{"item":true,"values":[1,2,3]}'`
+@see parse
+]]
+
+
+--- LuCI JSON parser instance.
+-- A JSON parser instance is useful to parse JSON data chunk by chunk, without
+-- the need to assemble all data in advance.
+-- @cstyle instance
+module "luci.jsonc.parser"
+
+---[[
+Parses one chunk of JSON data.
+
+@class function
+@sort 1
+@name parser.parse
+@see parser.get
+@param json  String containing the JSON fragment to parse
+@return <ul>
+       <li>`true` if a complete JSON object has been parsed and no further input is
+           expected.</li>
+       <li>`false` if further input is required</li>
+       <li>`nil` if an error was encountered while parsing the current chunk.
+           In this case a string describing the parse error is returned as second
+           value.</li></ul>
+@usage `parser = luci.jsonc.new()
+
+while true do
+       chunk = ...  -- fetch a cunk of data, e.g. from a socket
+       finish, errmsg = <b>parser.parse(chunk)</b>
+
+       if finish == nil then
+               error("Cannot parse JSON: " .. errmsg)
+       end
+
+       if finish == true then
+               break
+       end
+end`
+]]
+
+---[[
+Convert parsed JSON data into Lua table.
+
+@class function
+@sort 2
+@name parser.get
+@see parser.parse
+@return Parsed JSON object converted into a Lua table or `nil` if the parser
+       didn't finish or encountered an error.
+@usage `parser = luci.jsonc.new()
+parser:parse('{ "example": "test" }')
+
+data = parser:get()
+print(data.example)  -- "test"`
+]]
+
+---[[
+Put Lua data into the parser.
+
+@class function
+@sort 3
+@name parser.set
+@see parser.stringify
+@param data  Lua data to put into the parser object. The data is converted to an
+       internal JSON representation that can be dumped with `stringify()`.
+       The conversion follows the rules described in `luci.jsonc.stringify`.
+@return Nothing is returned.
+@usage `parser = luci.jsonc.new()
+parser:set({ "some", "data" })`
+]]
+
+---[[
+Serialize current parser state as JSON.
+
+@class function
+@sort 4
+@name parser.stringify
+@param pretty A boolean value indicating whether the resulting JSON should be pretty printed.
+@return Returns the serialized JSON data of this parser instance.
+@usage `parser = luci.jsonc.new()
+parser:parse('{ "example": "test" }')
+print(parser:serialize())  -- '{"example":"test"}'`
+]]
index fb1cf16..aa31841 100644 (file)
@@ -1,5 +1,5 @@
 --- Changes and improvements.
-module "CHANGELOG"
+module "nixio.CHANGELOG"
 
 --- Service Release.
 -- <ul>
@@ -26,4 +26,4 @@ module "CHANGELOG"
 -- </ul>
 -- @class table
 -- @name 0.2
--- @return !
\ No newline at end of file
+-- @return !
index b957a69..ee3e3a2 100644 (file)
@@ -1,5 +1,5 @@
 --- General Information.
-module "README"
+module "nixio.README"
 
 --- General error handling information.
 -- <ul>
@@ -92,4 +92,4 @@ module "README"
 -- @usage Tes
 -- @class table
 -- @name TLS-Crypto
--- @return !
\ No newline at end of file
+-- @return !
index aaa4292..a7e9a77 100644 (file)
@@ -1,4 +1,4 @@
-OS            = $(shell uname)
+OS            = Linux
 AXTLS_VERSION = 1.2.1
 AXTLS_DIR     = axTLS
 AXTLS_FILE    = $(AXTLS_DIR)-$(AXTLS_VERSION).tar.gz
index fc52b7e..5558910 100644 (file)
@@ -9,17 +9,10 @@ local nixio = require "nixio", require "nixio.util"
 local tostring, assert, setmetatable = tostring, assert, setmetatable
 local error = error
 
---- LuCI RPC Client.
--- @cstyle instance
 module "luci.rpcc"
 
 RQLIMIT = 32 * nixio.const.buffersize
 
---- Create a new JSON-RPC stream client.
--- @class function
--- @param fd File descriptor
--- @param v1 Use protocol version 1.0
--- @return RPC Client
 Client = util.class()
 
 function Client.__init__(self, fd, v1)
@@ -29,11 +22,6 @@ function Client.__init__(self, fd, v1)
        self.v1 = v1
 end
 
---- Request an RP call and get the response.
--- @param method Remote method
--- @param params Parameters
--- @param notification Notification only?
--- @return response 
 function Client.request(self, method, params, notification)
        local oldchunk = self.decoder and self.decoder.chunk
        self.decoder = json.ActiveDecoder(self.fd:blocksource(nil, RQLIMIT))
@@ -58,9 +46,6 @@ function Client.request(self, method, params, notification)
        end
 end
 
---- Create a transparent RPC proxy.
--- @param prefix Method prefix
--- @return RPC Proxy object
 function Client.proxy(self, prefix)
        prefix = prefix or ""
        return setmetatable({}, {
diff --git a/libs/luci-lib-rpcc/luasrc/rpcc.luadoc b/libs/luci-lib-rpcc/luasrc/rpcc.luadoc
new file mode 100644 (file)
index 0000000..8c90ab5
--- /dev/null
@@ -0,0 +1,36 @@
+---[[
+LuCI RPC Client.
+
+@cstyle instance
+module "luci.rpcc"
+]]
+
+---[[
+Create a new JSON-RPC stream client.
+
+@class function
+@param fd File descriptor
+@param v1 Use protocol version 1.0
+@return RPC Client
+]]
+
+---[[
+Request an RP call and get the response.
+
+@class function
+@name Client.request
+@param method Remote method
+@param params Parameters
+@param notification Notification only?
+@return response 
+]]
+
+---[[
+Create a transparent RPC proxy.
+
+@class function
+@name Client.proxy
+@param prefix Method prefix
+@return RPC Proxy object
+]]
+
index 666d585..275c396 100644 (file)
@@ -5,16 +5,11 @@ local util = require "luci.util"
 local rawget, setmetatable = rawget, setmetatable
 local ipairs = ipairs
 
---- Transparent UCI over RPC client.
--- @cstyle instance
 module "luci.rpcc.ruci"
 
 
 local Proxy = util.class()
 
---- Create a new UCI over RPC proxy.
--- @param rpccl RPC client
--- @return Network transparent UCI module 
 function factory(rpccl)
        return {
                cursor = function(...)
diff --git a/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc b/libs/luci-lib-rpcc/luasrc/rpcc/ruci.luadoc
new file mode 100644 (file)
index 0000000..980ef46
--- /dev/null
@@ -0,0 +1,16 @@
+---[[
+Transparent UCI over RPC client.
+
+@cstyle instance
+module "luci.rpcc.ruci"
+]]
+
+---[[
+Create a new UCI over RPC proxy.
+
+@class function
+@name factory
+@param rpccl RPC client
+@return Network transparent UCI module 
+]]
+
diff --git a/luci.mk b/luci.mk
index 0aba5c5..ab09f0f 100644 (file)
--- a/luci.mk
+++ b/luci.mk
@@ -147,6 +147,7 @@ define Package/$(PKG_NAME)/install
        if [ -d $(PKG_BUILD_DIR)/luasrc ]; then \
          $(INSTALL_DIR) $(1)$(LUCI_LIBRARYDIR); \
          cp -pR $(PKG_BUILD_DIR)/luasrc/* $(1)$(LUCI_LIBRARYDIR)/; \
+         $(FIND) $(1)$(LUCI_LIBRARYDIR)/ -type f -name '*.luadoc' | $(XARGS) rm; \
          $(if $(CONFIG_LUCI_SRCDIET),$(call SrcDiet,$(1)$(LUCI_LIBRARYDIR)/),true); \
        else true; fi
        if [ -d $(PKG_BUILD_DIR)/htdocs ]; then \
index 8337fea..80bbda1 100644 (file)
@@ -12,7 +12,7 @@ LUCI_TYPE:=mod
 LUCI_BASENAME:=base
 
 LUCI_TITLE:=LuCI core libraries
-LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +rpcd
+LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +luci-lib-ip +rpcd
 
 PKG_SOURCE:=LuaSrcDiet-0.12.1.tar.bz2
 PKG_SOURCE_URL:=https://luasrcdiet.googlecode.com/files
index 155d31b..795b62b 100644 (file)
@@ -1,7 +1,6 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
 -- Licensed to the public under the Apache License 2.0.
 
---- LuCI web dispatcher.
 local fs = require "nixio.fs"
 local sys = require "luci.sys"
 local util = require "luci.util"
@@ -23,9 +22,6 @@ local index = nil
 local fi
 
 
---- Build the URL relative to the server webroot from given virtual path.
--- @param ...  Virtual path
--- @return             Relative URL
 function build_url(...)
        local path = {...}
        local url = { http.getenv("SCRIPT_NAME") or "" }
@@ -49,9 +45,6 @@ function build_url(...)
        return table.concat(url, "")
 end
 
---- Check whether a dispatch node shall be visible
--- @param node Dispatch node
--- @return             Boolean indicating whether the node should be visible
 function node_visible(node)
    if node then
          return not (
@@ -64,9 +57,6 @@ function node_visible(node)
    return false
 end
 
---- Return a sorted table of visible childs within a given node
--- @param node Dispatch node
--- @return             Ordered table of child node names
 function node_childs(node)
        local rv = { }
        if node then
@@ -86,9 +76,6 @@ function node_childs(node)
 end
 
 
---- Send a 404 error code and render the "error404" template if available.
--- @param message      Custom error message (optional)
--- @return                     false
 function error404(message)
        http.status(404, "Not Found")
        message = message or "Not Found"
@@ -101,9 +88,6 @@ function error404(message)
        return false
 end
 
---- Send a 500 error code and render the "error500" template if available.
--- @param message      Custom error message (optional)#
--- @return                     false
 function error500(message)
        util.perror(message)
        if not context.template_header_sent then
@@ -128,16 +112,22 @@ function authenticator.htmlauth(validator, accs, default)
                return user
        end
 
-       require("luci.i18n")
-       require("luci.template")
-       context.path = {}
-       luci.template.render("sysauth", {duser=default, fuser=user})
+       if context.urltoken.stok then
+               context.urltoken.stok = nil
+               http.header("Set-Cookie", "sysauth=; path="..build_url())
+               http.redirect(build_url())
+       else
+               require("luci.i18n")
+               require("luci.template")
+               context.path = {}
+               http.status(403, "Forbidden")
+               luci.template.render("sysauth", {duser=default, fuser=user})
+       end
+
        return false
 
 end
 
---- Dispatch an HTTP request.
--- @param request      LuCI HTTP Request object
 function httpdispatch(request, prefix)
        http.context.request = request
 
@@ -176,8 +166,6 @@ function httpdispatch(request, prefix)
        --context._disable_memtrace()
 end
 
---- Dispatches a LuCI virtual path.
--- @param request      Virtual path
 function dispatch(request)
        --context._disable_memtrace = require "luci.debug".trap_memtrace("l")
        local ctx = context
@@ -340,7 +328,6 @@ function dispatch(request)
 
                if not util.contains(accs, user) then
                        if authen then
-                               ctx.urltoken.stok = nil
                                local user, sess = authen(sys.user.checkpasswd, accs, def)
                                if not user or not util.contains(accs, user) then
                                        return
@@ -364,6 +351,7 @@ function dispatch(request)
 
                                        if sess then
                                                http.header("Set-Cookie", "sysauth=" .. sess.."; path="..build_url())
+                                               http.redirect(build_url(unpack(ctx.requestpath)))
                                                ctx.authsession = sess
                                                ctx.authuser = user
                                        end
@@ -445,7 +433,6 @@ function dispatch(request)
        end
 end
 
---- Generate the dispatching index using the native file-cache based strategy.
 function createindex()
        local controllers = { }
        local base = "%s/controller/" % util.libpath()
@@ -509,7 +496,6 @@ function createindex()
        end
 end
 
---- Create the dispatching tree from the index.
 -- Build the index before if it does not exist yet.
 function createtree()
        if not index then
@@ -548,9 +534,6 @@ function createtree()
        return tree
 end
 
---- Register a tree modifier.
--- @param      func    Modifier function
--- @param      order   Modifier order value (optional)
 function modifier(func, order)
        context.modifiers[#context.modifiers+1] = {
                func = func,
@@ -560,12 +543,6 @@ function modifier(func, order)
        }
 end
 
---- Clone a node of the dispatching tree to another position.
--- @param      path    Virtual path destination
--- @param      clone   Virtual path source
--- @param      title   Destination node title (optional)
--- @param      order   Destination node order value (optional)
--- @return                     Dispatching tree node
 function assign(path, clone, title, order)
        local obj  = node(unpack(path))
        obj.nodes  = nil
@@ -579,12 +556,6 @@ function assign(path, clone, title, order)
        return obj
 end
 
---- Create a new dispatching node and define common parameters.
--- @param      path    Virtual path
--- @param      target  Target function to call when dispatched.
--- @param      title   Destination node title
--- @param      order   Destination node order value (optional)
--- @return                     Dispatching tree node
 function entry(path, target, title, order)
        local c = node(unpack(path))
 
@@ -596,17 +567,11 @@ function entry(path, target, title, order)
        return c
 end
 
---- Fetch or create a dispatching node without setting the target module or
 -- enabling the node.
--- @param      ...             Virtual path
--- @return                     Dispatching tree node
 function get(...)
        return _create_node({...})
 end
 
---- Fetch or create a new dispatching node.
--- @param      ...             Virtual path
--- @return                     Dispatching tree node
 function node(...)
        local c = _create_node({...})
 
@@ -666,13 +631,10 @@ function _firstchild()
    dispatch(path)
 end
 
---- Alias the first (lowest order) page automatically
 function firstchild()
    return { type = "firstchild", target = _firstchild }
 end
 
---- Create a redirect to another dispatching node.
--- @param      ...             Virtual path destination
 function alias(...)
        local req = {...}
        return function(...)
@@ -684,9 +646,6 @@ function alias(...)
        end
 end
 
---- Rewrite the first x path values of the request.
--- @param      n               Number of path values to replace
--- @param      ...             Virtual path to replace removed path values with
 function rewrite(n, ...)
        local req = {...}
        return function(...)
@@ -725,9 +684,6 @@ local function _call(self, ...)
        end
 end
 
---- Create a function-call dispatching target.
--- @param      name    Target function of local controller
--- @param      ...             Additional parameters passed to the function
 function call(name, ...)
        return {type = "call", argv = {...}, name = name, target = _call}
 end
@@ -737,8 +693,6 @@ local _template = function(self, ...)
        require "luci.template".render(self.view)
 end
 
---- Create a template render dispatching target.
--- @param      name    Template to be rendered
 function template(name)
        return {type = "template", view = name, target = _template}
 end
@@ -844,8 +798,6 @@ local function _cbi(self, ...)
        end
 end
 
---- Create a CBI model dispatching target.
--- @param      model   CBI model to be rendered
 function cbi(model, config)
        return {type = "cbi", config = config, model = model, target = _cbi}
 end
@@ -858,9 +810,6 @@ local function _arcombine(self, ...)
        target:target(unpack(argv))
 end
 
---- Create a combined dispatching target for non argv and argv requests.
--- @param trg1 Overview Target
--- @param trg2 Detail Target
 function arcombine(trg1, trg2)
        return {type = "arcombine", env = getfenv(), target = _arcombine, targets = {trg1, trg2}}
 end
@@ -889,19 +838,12 @@ local function _form(self, ...)
        tpl.render("footer")
 end
 
---- Create a CBI form model dispatching target.
--- @param      model   CBI form model tpo be rendered
 function form(model)
        return {type = "cbi", model = model, target = _form}
 end
 
---- Access the luci.i18n translate() api.
--- @class  function
--- @name   translate
--- @param  text    Text to translate
 translate = i18n.translate
 
---- No-op function used to mark translation entries for menu labels.
 -- This function does not actually translate the given argument but
 -- is used by build/i18n-scan.pl to find translatable entries.
 function _(text)
diff --git a/modules/luci-base/luasrc/dispatcher.luadoc b/modules/luci-base/luasrc/dispatcher.luadoc
new file mode 100644 (file)
index 0000000..743463c
--- /dev/null
@@ -0,0 +1,220 @@
+---[[
+LuCI web dispatcher.
+]]
+module "luci.dispatcher"
+
+---[[
+Build the URL relative to the server webroot from given virtual path.
+
+@class function
+@name build_url
+@param ...     Virtual path
+@return                Relative URL
+]]
+
+---[[
+Check whether a dispatch node shall be visible
+
+@class function
+@name node_visible
+@param node    Dispatch node
+@return                Boolean indicating whether the node should be visible
+]]
+
+---[[
+Return a sorted table of visible childs within a given node
+
+@class function
+@name node_childs
+@param node    Dispatch node
+@return                Ordered table of child node names
+]]
+
+---[[
+Send a 404 error code and render the "error404" template if available.
+
+@class function
+@name error404
+@param message Custom error message (optional)
+@return                        false
+]]
+
+---[[
+Send a 500 error code and render the "error500" template if available.
+
+@class function
+@name error500
+@param message Custom error message (optional)#
+@return                        false
+]]
+
+---[[
+Dispatch an HTTP request.
+
+@class function
+@name httpdispatch
+@param request LuCI HTTP Request object
+]]
+
+---[[
+Dispatches a LuCI virtual path.
+
+@class function
+@name dispatch
+@param request Virtual path
+]]
+
+---[[
+Generate the dispatching index using the native file-cache based strategy.
+
+
+@class function
+@name createindex
+]]
+
+---[[
+Create the dispatching tree from the index.
+
+Build the index before if it does not exist yet.
+
+@class function
+@name createtree
+]]
+
+---[[
+Register a tree modifier.
+
+@class function
+@name modifier
+@param func    Modifier function
+@param order   Modifier order value (optional)
+]]
+
+---[[
+Clone a node of the dispatching tree to another position.
+
+@class function
+@name assign
+@param path    Virtual path destination
+@param clone   Virtual path source
+@param title   Destination node title (optional)
+@param order   Destination node order value (optional)
+@return                        Dispatching tree node
+]]
+
+---[[
+Create a new dispatching node and define common parameters.
+
+@class function
+@name entry
+@param path    Virtual path
+@param target  Target function to call when dispatched.
+@param title   Destination node title
+@param order   Destination node order value (optional)
+@return                        Dispatching tree node
+]]
+
+---[[
+Fetch or create a dispatching node without setting the target module or
+
+enabling the node.
+@class function
+@name get
+@param ...             Virtual path
+@return                        Dispatching tree node
+]]
+
+---[[
+Fetch or create a new dispatching node.
+
+@class function
+@name node
+@param ...             Virtual path
+@return                        Dispatching tree node
+]]
+
+---[[
+Alias the first (lowest order) page automatically
+
+
+@class function
+@name firstchild
+]]
+
+---[[
+Create a redirect to another dispatching node.
+
+@class function
+@name alias
+@param ...             Virtual path destination
+]]
+
+---[[
+Rewrite the first x path values of the request.
+
+@class function
+@name rewrite
+@param n               Number of path values to replace
+@param ...             Virtual path to replace removed path values with
+]]
+
+---[[
+Create a function-call dispatching target.
+
+@class function
+@name call
+@param name    Target function of local controller
+@param ...             Additional parameters passed to the function
+]]
+
+---[[
+Create a template render dispatching target.
+
+@class function
+@name template
+@param name    Template to be rendered
+]]
+
+---[[
+Create a CBI model dispatching target.
+
+@class function
+@name cbi
+@param model   CBI model to be rendered
+]]
+
+---[[
+Create a combined dispatching target for non argv and argv requests.
+
+@class function
+@name arcombine
+@param trg1    Overview Target
+@param trg2    Detail Target
+]]
+
+---[[
+Create a CBI form model dispatching target.
+
+@class function
+@name form
+@param model   CBI form model tpo be rendered
+]]
+
+---[[
+Access the luci.i18n translate() api.
+
+@class  function
+@name   translate
+@param  text    Text to translate
+]]
+
+---[[
+No-op function used to mark translation entries for menu labels.
+
+This function does not actually translate the given argument but
+is used by build/i18n-scan.pl to find translatable entries.
+
+@class function
+@name _
+]]
+
index a5329e5..a92d8af 100644 (file)
@@ -11,7 +11,6 @@ local table = require "table"
 local ipairs, pairs, next, type, tostring, error =
        ipairs, pairs, next, type, tostring, error
 
---- LuCI Web Framework high-level HTTP functions.
 module "luci.http"
 
 context = util.threadlocal()
@@ -101,7 +100,6 @@ function Request._parse_input(self)
        self.parsed_input = true
 end
 
---- Close the HTTP-Connection.
 function close()
        if not context.eoh then
                context.eoh = true
@@ -114,52 +112,31 @@ function close()
        end
 end
 
---- Return the request content if the request was of unknown type.
--- @return     HTTP request body
--- @return     HTTP request body length
 function content()
        return context.request:content()
 end
 
---- Get a certain HTTP input value or a table of all input values.
--- @param name         Name of the GET or POST variable to fetch
--- @param noparse      Don't parse POST data before getting the value
--- @return                     HTTP input value or table of all input value
 function formvalue(name, noparse)
        return context.request:formvalue(name, noparse)
 end
 
---- Get a table of all HTTP input values with a certain prefix.
--- @param prefix       Prefix
--- @return                     Table of all HTTP input values with given prefix
 function formvaluetable(prefix)
        return context.request:formvaluetable(prefix)
 end
 
---- Get the value of a certain HTTP-Cookie.
--- @param name         Cookie Name
--- @return                     String containing cookie data
 function getcookie(name)
        return context.request:getcookie(name)
 end
 
---- Get the value of a certain HTTP environment variable
 -- or the environment table itself.
--- @param name         Environment variable
--- @return                     HTTP environment value or environment table
 function getenv(name)
        return context.request:getenv(name)
 end
 
---- Set a handler function for incoming user file uploads.
--- @param callback     Handler function
 function setfilehandler(callback)
        return context.request:setfilehandler(callback)
 end
 
---- Send a HTTP-Header.
--- @param key  Header key
--- @param value Header value
 function header(key, value)
        if not context.headers then
                context.headers = {}
@@ -168,8 +145,6 @@ function header(key, value)
        coroutine.yield(2, key, value)
 end
 
---- Set the mime type of following content data.
--- @param mime Mimetype of following content
 function prepare_content(mime)
        if not context.headers or not context.headers["content-type"] then
                if mime == "application/xhtml+xml" then
@@ -183,15 +158,10 @@ function prepare_content(mime)
        end
 end
 
---- Get the RAW HTTP input source
--- @return     HTTP LTN12 source
 function source()
        return context.request.input
 end
 
---- Set the HTTP status code and status message.
--- @param code         Status code
--- @param message      Status message
 function status(code, message)
        code = code or 200
        message = message or "OK"
@@ -199,12 +169,8 @@ function status(code, message)
        coroutine.yield(1, code, message)
 end
 
---- Send a chunk of content data to the client.
 -- This function is as a valid LTN12 sink.
 -- If the content chunk is nil this function will automatically invoke close.
--- @param content      Content chunk
--- @param src_err      Error object from source (optional)
--- @see close
 function write(content, src_err)
        if not content then
                if src_err then
@@ -237,24 +203,16 @@ function write(content, src_err)
        end
 end
 
---- Splice data from a filedescriptor to the client.
--- @param fp   File descriptor
--- @param size Bytes to splice (optional)
 function splice(fd, size)
        coroutine.yield(6, fd, size)
 end
 
---- Redirects the client to a new URL and closes the connection.
--- @param url  Target URL
 function redirect(url)
        status(302, "Found")
        header("Location", url)
        close()
 end
 
---- Create a querystring out of a table of key - value pairs.
--- @param table                Query string source table
--- @return                     Encoded HTTP query string
 function build_querystring(q)
        local s = { "?" }
 
@@ -269,56 +227,10 @@ function build_querystring(q)
        return table.concat(s, "")
 end
 
---- Return the URL-decoded equivalent of a string.
--- @param str          URL-encoded string
--- @param no_plus      Don't decode + to " "
--- @return                     URL-decoded string
--- @see urlencode
 urldecode = protocol.urldecode
 
---- Return the URL-encoded equivalent of a string.
--- @param str          Source string
--- @return                     URL-encoded string
--- @see urldecode
 urlencode = protocol.urlencode
 
---- Send the given data as JSON encoded string.
--- @param data         Data to send
 function write_json(x)
-       if x == nil then
-               write("null")
-       elseif type(x) == "table" then
-               local k, v
-               if type(next(x)) == "number" then
-                       write("[ ")
-                       for k, v in ipairs(x) do
-                               write_json(v)
-                               if next(x, k) then
-                                       write(", ")
-                               end
-                       end
-                       write(" ]")
-               else
-                       write("{ ")
-                       for k, v in pairs(x) do
-                       write("%q: " % k)
-                               write_json(v)
-                               if next(x, k) then
-                                       write(", ")
-                               end
-                       end
-                       write(" }")
-               end
-       elseif type(x) == "number" or type(x) == "boolean" then
-               if (x ~= x) then
-                       -- NaN is the only value that doesn't equal to itself.
-                       write("Number.NaN")
-               else
-                       write(tostring(x))
-               end
-       else
-               write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
-                       return '\\u%04x' % c:byte(1)
-               end))
-       end
+       util.serialize_json(x, write)
 end
diff --git a/modules/luci-base/luasrc/http.luadoc b/modules/luci-base/luasrc/http.luadoc
new file mode 100644 (file)
index 0000000..4e31216
--- /dev/null
@@ -0,0 +1,166 @@
+---[[
+LuCI Web Framework high-level HTTP functions.
+
+module "luci.http"
+]]
+
+---[[
+Close the HTTP-Connection.
+
+
+@class function
+@name close
+]]
+
+---[[
+Return the request content if the request was of unknown type.
+
+@class function
+@name content
+@return        HTTP request body
+@return        HTTP request body length
+]]
+
+---[[
+Get a certain HTTP input value or a table of all input values.
+
+@class function
+@name formvalue
+@param name            Name of the GET or POST variable to fetch
+@param noparse Don't parse POST data before getting the value
+@return                        HTTP input value or table of all input value
+]]
+
+---[[
+Get a table of all HTTP input values with a certain prefix.
+
+@class function
+@name formvaluetable
+@param prefix  Prefix
+@return                        Table of all HTTP input values with given prefix
+]]
+
+---[[
+Get the value of a certain HTTP-Cookie.
+
+@class function
+@name getcookie
+@param name            Cookie Name
+@return                        String containing cookie data
+]]
+
+---[[
+Get the value of a certain HTTP environment variable
+
+or the environment table itself.
+@class function
+@name getenv
+@param name            Environment variable
+@return                        HTTP environment value or environment table
+]]
+
+---[[
+Set a handler function for incoming user file uploads.
+
+@class function
+@name setfilehandler
+@param callback        Handler function
+]]
+
+---[[
+Send a HTTP-Header.
+
+@class function
+@name header
+@param key     Header key
+@param value Header value
+]]
+
+---[[
+Set the mime type of following content data.
+
+@class function
+@name prepare_content
+@param mime    Mimetype of following content
+]]
+
+---[[
+Get the RAW HTTP input source
+
+@class function
+@name source
+@return        HTTP LTN12 source
+]]
+
+---[[
+Set the HTTP status code and status message.
+
+@class function
+@name status
+@param code            Status code
+@param message Status message
+]]
+
+---[[
+Send a chunk of content data to the client.
+
+This function is as a valid LTN12 sink.
+If the content chunk is nil this function will automatically invoke close.
+@class function
+@name write
+@param content Content chunk
+@param src_err Error object from source (optional)
+@see close
+]]
+
+---[[
+Splice data from a filedescriptor to the client.
+
+@class function
+@name splice
+@param fp      File descriptor
+@param size    Bytes to splice (optional)
+]]
+
+---[[
+Redirects the client to a new URL and closes the connection.
+
+@class function
+@name redirect
+@param url     Target URL
+]]
+
+---[[
+Create a querystring out of a table of key - value pairs.
+
+@class function
+@name build_querystring
+@param table           Query string source table
+@return                        Encoded HTTP query string
+]]
+
+---[[
+Return the URL-decoded equivalent of a string.
+
+@param str             URL-encoded string
+@param no_plus Don't decode + to " "
+@return                        URL-decoded string
+@see urlencode
+]]
+
+---[[
+Return the URL-encoded equivalent of a string.
+
+@param str             Source string
+@return                        URL-encoded string
+@see urldecode
+]]
+
+---[[
+Send the given data as JSON encoded string.
+
+@class function
+@name write_json
+@param data            Data to send
+]]
+
index aeb7ea6..e9efb44 100644 (file)
@@ -1,7 +1,6 @@
 -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
---- LuCI http protocol class.
 -- This class contains several functions useful for http message- and content
 -- decoding and to retrive form data from raw http messages.
 module("luci.http.protocol", package.seeall)
@@ -10,12 +9,7 @@ local ltn12 = require("luci.ltn12")
 
 HTTP_MAX_CONTENT      = 1024*8         -- 8 kB maximum content size
 
---- Decode an urlencoded string - optionally without decoding
 -- the "+" sign to " " - and return the decoded string.
--- @param str          Input string in x-www-urlencoded format
--- @param no_plus      Don't decode "+" signs to spaces
--- @return                     The decoded string
--- @see                                urlencode
 function urldecode( str, no_plus )
 
        local function __chrdec( hex )
@@ -33,15 +27,10 @@ function urldecode( str, no_plus )
        return str
 end
 
---- Extract and split urlencoded data pairs, separated bei either "&" or ";"
 -- from given url or string. Returns a table with urldecoded values.
 -- Simple parameters are stored as string values associated with the parameter
 -- name within the table. Parameters with multiple values are stored as array
 -- containing the corresponding values.
--- @param url  The url or string which contains x-www-urlencoded form data
--- @param tbl  Use the given table for storing values (optional)
--- @return             Table containing the urldecoded parameters
--- @see                        urlencode_params
 function urldecode_params( url, tbl )
 
        local params = tbl or { }
@@ -73,10 +62,6 @@ function urldecode_params( url, tbl )
        return params
 end
 
---- Encode given string to x-www-urlencoded format.
--- @param str  String to encode
--- @return             String containing the encoded data
--- @see                        urldecode
 function urlencode( str )
 
        local function __chrenc( chr )
@@ -95,12 +80,8 @@ function urlencode( str )
        return str
 end
 
---- Encode each key-value-pair in given table to x-www-urlencoded format,
 -- separated by "&". Tables are encoded as parameters with multiple values by
 -- repeating the parameter name with each value.
--- @param tbl  Table with the values
--- @return             String containing encoded values
--- @see                        urldecode_params
 function urlencode_params( tbl )
        local enc = ""
 
@@ -122,9 +103,6 @@ end
 -- (Internal function)
 -- Initialize given parameter and coerce string into table when the parameter
 -- already exists.
--- @param tbl  Table where parameter should be created
--- @param key  Parameter name
--- @return             Always nil
 local function __initval( tbl, key )
        if tbl[key] == nil then
                tbl[key] = ""
@@ -138,11 +116,6 @@ end
 -- (Internal function)
 -- Append given data to given parameter, either by extending the string value
 -- or by appending it to the last string in the parameter's value table.
--- @param tbl  Table containing the previously initialized parameter value
--- @param key  Parameter name
--- @param chunk        String containing the data to append
--- @return             Always nil
--- @see                        __initval
 local function __appendval( tbl, key, chunk )
        if type(tbl[key]) == "table" then
                tbl[key][#tbl[key]] = tbl[key][#tbl[key]] .. chunk
@@ -155,12 +128,6 @@ end
 -- Finish the value of given parameter, either by transforming the string value
 -- or - in the case of multi value parameters - the last element in the
 -- associated values table.
--- @param tbl          Table containing the previously initialized parameter value
--- @param key          Parameter name
--- @param handler      Function which transforms the parameter value
--- @return                     Always nil
--- @see                                __initval
--- @see                                __appendval
 local function __finishval( tbl, key, handler )
        if handler then
                if type(tbl[key]) == "table" then
@@ -259,10 +226,7 @@ process_states['headers'] = function( msg, chunk )
 end
 
 
---- Creates a ltn12 source from the given socket. The source will return it's
 -- data line by line with the trailing \r\n stripped of.
--- @param sock Readable network socket
--- @return             Ltn12 source function
 function header_source( sock )
        return ltn12.source.simplify( function()
 
@@ -289,7 +253,6 @@ function header_source( sock )
        end )
 end
 
---- Decode a mime encoded http message body with multipart/form-data
 -- Content-Type. Stores all extracted data associated with its parameter name
 -- in the params table withing the given message object. Multiple parameter
 -- values are stored as tables, ordinary ones as strings.
@@ -300,12 +263,6 @@ end
 --  o Table containing decoded (name, file) and raw (headers) mime header data
 --  o String value containing a chunk of the file data
 --  o Boolean which indicates wheather the current chunk is the last one (eof)
--- @param src          Ltn12 source function
--- @param msg          HTTP message object
--- @param filecb       File callback function (optional)
--- @return                     Value indicating successful operation (not nil means "ok")
--- @return                     String containing the error if unsuccessful
--- @see                                parse_message_header
 function mimedecode_message_body( src, msg, filecb )
 
        if msg and msg.env.CONTENT_TYPE then
@@ -449,15 +406,9 @@ function mimedecode_message_body( src, msg, filecb )
        return ltn12.pump.all( src, snk )
 end
 
---- Decode an urlencoded http message body with application/x-www-urlencoded
 -- Content-Type. Stores all extracted data associated with its parameter name
 -- in the params table withing the given message object. Multiple parameter
 -- values are stored as tables, ordinary ones as strings.
--- @param src  Ltn12 source function
--- @param msg  HTTP message object
--- @return             Value indicating successful operation (not nil means "ok")
--- @return             String containing the error if unsuccessful
--- @see                        parse_message_header
 function urldecode_message_body( src, msg )
 
        local tlen   = 0
@@ -507,12 +458,8 @@ function urldecode_message_body( src, msg )
        return ltn12.pump.all( src, snk )
 end
 
---- Try to extract an http message header including information like protocol
 -- version, message headers and resulting CGI environment variables from the
 -- given ltn12 source.
--- @param src  Ltn12 source function
--- @return             HTTP message object
--- @see                        parse_message_body
 function parse_message_header( src )
 
        local ok   = true
@@ -582,19 +529,12 @@ function parse_message_header( src )
        return msg
 end
 
---- Try to extract and decode a http message body from the given ltn12 source.
 -- This function will examine the Content-Type within the given message object
 -- to select the appropriate content decoder.
 -- Currently the application/x-www-urlencoded and application/form-data
 -- mime types are supported. If the encountered content encoding can't be
 -- handled then the whole message body will be stored unaltered as "content"
 -- property within the given message object.
--- @param src          Ltn12 source function
--- @param msg          HTTP message object
--- @param filecb       File data callback (optional, see mimedecode_message_body())
--- @return                     Value indicating successful operation (not nil means "ok")
--- @return                     String containing the error if unsuccessful
--- @see                                parse_message_header
 function parse_message_body( src, msg, filecb )
        -- Is it multipart/mime ?
        if msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE and
@@ -655,8 +595,6 @@ function parse_message_body( src, msg, filecb )
        end
 end
 
---- Table containing human readable messages for several http status codes.
--- @class table
 statusmsg = {
        [200] = "OK",
        [206] = "Partial Content",
diff --git a/modules/luci-base/luasrc/http/protocol.luadoc b/modules/luci-base/luasrc/http/protocol.luadoc
new file mode 100644 (file)
index 0000000..67a60d9
--- /dev/null
@@ -0,0 +1,142 @@
+---[[
+LuCI http protocol class.
+
+This class contains several functions useful for http message- and content
+decoding and to retrive form data from raw http messages.
+]]
+module "luci.http.protocol"
+
+---[[
+Decode an urlencoded string - optionally without decoding
+
+the "+" sign to " " - and return the decoded string.
+@class function
+@name urldecode
+@param str             Input string in x-www-urlencoded format
+@param no_plus Don't decode "+" signs to spaces
+@return                        The decoded string
+@see                           urlencode
+]]
+
+---[[
+Extract and split urlencoded data pairs, separated bei either "&" or ";"
+
+from given url or string. Returns a table with urldecoded values.
+Simple parameters are stored as string values associated with the parameter
+name within the table. Parameters with multiple values are stored as array
+containing the corresponding values.
+@class function
+@name urldecode_params
+@param url     The url or string which contains x-www-urlencoded form data
+@param tbl     Use the given table for storing values (optional)
+@return                Table containing the urldecoded parameters
+@see                   urlencode_params
+]]
+
+---[[
+Encode given string to x-www-urlencoded format.
+
+@class function
+@name urlencode
+@param str     String to encode
+@return                String containing the encoded data
+@see                   urldecode
+]]
+
+---[[
+Encode each key-value-pair in given table to x-www-urlencoded format,
+
+separated by "&". Tables are encoded as parameters with multiple values by
+repeating the parameter name with each value.
+@class function
+@name urlencode_params
+@param tbl     Table with the values
+@return                String containing encoded values
+@see                   urldecode_params
+]]
+
+---[[
+Creates a ltn12 source from the given socket. The source will return it's
+
+data line by line with the trailing \r\n stripped of.
+@class function
+@name header_source
+@param sock    Readable network socket
+@return                Ltn12 source function
+]]
+
+---[[
+Decode a mime encoded http message body with multipart/form-data
+
+Content-Type. Stores all extracted data associated with its parameter name
+in the params table withing the given message object. Multiple parameter
+values are stored as tables, ordinary ones as strings.
+If an optional file callback function is given then it is feeded with the
+file contents chunk by chunk and only the extracted file name is stored
+within the params table. The callback function will be called subsequently
+with three arguments:
+ o Table containing decoded (name, file) and raw (headers) mime header data
+ o String value containing a chunk of the file data
+ o Boolean which indicates wheather the current chunk is the last one (eof)
+@class function
+@name mimedecode_message_body
+@param src             Ltn12 source function
+@param msg             HTTP message object
+@param filecb  File callback function (optional)
+@return                        Value indicating successful operation (not nil means "ok")
+@return                        String containing the error if unsuccessful
+@see                           parse_message_header
+]]
+
+---[[
+Decode an urlencoded http message body with application/x-www-urlencoded
+
+Content-Type. Stores all extracted data associated with its parameter name
+in the params table withing the given message object. Multiple parameter
+values are stored as tables, ordinary ones as strings.
+@class function
+@name urldecode_message_body
+@param src     Ltn12 source function
+@param msg     HTTP message object
+@return                Value indicating successful operation (not nil means "ok")
+@return                String containing the error if unsuccessful
+@see                   parse_message_header
+]]
+
+---[[
+Try to extract an http message header including information like protocol
+
+version, message headers and resulting CGI environment variables from the
+given ltn12 source.
+@class function
+@name parse_message_header
+@param src     Ltn12 source function
+@return                HTTP message object
+@see                   parse_message_body
+]]
+
+---[[
+Try to extract and decode a http message body from the given ltn12 source.
+
+This function will examine the Content-Type within the given message object
+to select the appropriate content decoder.
+Currently the application/x-www-urlencoded and application/form-data
+mime types are supported. If the encountered content encoding can't be
+handled then the whole message body will be stored unaltered as "content"
+property within the given message object.
+@class function
+@name parse_message_body
+@param src             Ltn12 source function
+@param msg             HTTP message object
+@param filecb  File data callback (optional, see mimedecode_message_body())
+@return                        Value indicating successful operation (not nil means "ok")
+@return                        String containing the error if unsuccessful
+@see                           parse_message_header
+]]
+
+---[[
+Table containing human readable messages for several http status codes.
+
+@class table
+]]
+
index 1d40425..d31a4e3 100644 (file)
@@ -1,7 +1,6 @@
 -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
---- LuCI http protocol implementation - HTTP/1.1 bits.
 -- This class provides basic ETag handling and implements most of the
 -- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
 module("luci.http.protocol.conditionals", package.seeall)
@@ -9,22 +8,14 @@ module("luci.http.protocol.conditionals", package.seeall)
 local date = require("luci.http.protocol.date")
 
 
---- Implement 14.19 / ETag.
--- @param stat A file.stat structure
--- @return             String containing the generated tag suitable for ETag headers
 function mk_etag( stat )
        if stat ~= nil then
                return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime )
        end
 end
 
---- 14.24 / If-Match
 -- Test whether the given message object contains an "If-Match" header and
 -- compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
 function if_match( req, stat )
        local h    = req.headers
        local etag = mk_etag( stat )
@@ -43,14 +34,8 @@ function if_match( req, stat )
        return true
 end
 
---- 14.25 / If-Modified-Since
 -- Test whether the given message object contains an "If-Modified-Since" header
 -- and compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
--- @return             Table containing extra HTTP headers if the precondition failed
 function if_modified_since( req, stat )
        local h = req.headers
 
@@ -72,14 +57,8 @@ function if_modified_since( req, stat )
        return true
 end
 
---- 14.26 / If-None-Match
 -- Test whether the given message object contains an "If-None-Match" header and
 -- compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
--- @return             Table containing extra HTTP headers if the precondition failed
 function if_none_match( req, stat )
        local h      = req.headers
        local etag   = mk_etag( stat )
@@ -105,26 +84,16 @@ function if_none_match( req, stat )
        return true
 end
 
---- 14.27 / If-Range
 -- The If-Range header is currently not implemented due to the lack of general
 -- byte range stuff in luci.http.protocol . This function will always return
 -- false, 412 to indicate a failed precondition.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
 function if_range( req, stat )
        -- Sorry, no subranges (yet)
        return false, 412
 end
 
---- 14.28 / If-Unmodified-Since
 -- Test whether the given message object contains an "If-Unmodified-Since"
 -- header and compare it against the given stat object.
--- @param req  HTTP request message object
--- @param stat A file.stat object
--- @return             Boolean indicating whether the precondition is ok
--- @return             Alternative status code if the precondition failed
 function if_unmodified_since( req, stat )
        local h = req.headers
 
diff --git a/modules/luci-base/luasrc/http/protocol/conditionals.luadoc b/modules/luci-base/luasrc/http/protocol/conditionals.luadoc
new file mode 100644 (file)
index 0000000..9cfe02d
--- /dev/null
@@ -0,0 +1,85 @@
+---[[
+LuCI http protocol implementation - HTTP/1.1 bits.
+
+This class provides basic ETag handling and implements most of the
+conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 .
+]]
+module "luci.http.protocol.conditionals"
+
+---[[
+Implement 14.19 / ETag.
+
+@class function
+@name mk_etag
+@param stat    A file.stat structure
+@return                String containing the generated tag suitable for ETag headers
+]]
+
+---[[
+14.24 / If-Match
+
+Test whether the given message object contains an "If-Match" header and
+compare it against the given stat object.
+@class function
+@name if_match
+@param req     HTTP request message object
+@param stat    A file.stat object
+@return                Boolean indicating whether the precondition is ok
+@return                Alternative status code if the precondition failed
+]]
+
+---[[
+14.25 / If-Modified-Since
+
+Test whether the given message object contains an "If-Modified-Since" header
+and compare it against the given stat object.
+@class function
+@name if_modified_since
+@param req     HTTP request message object
+@param stat    A file.stat object
+@return                Boolean indicating whether the precondition is ok
+@return                Alternative status code if the precondition failed
+@return                Table containing extra HTTP headers if the precondition failed
+]]
+
+---[[
+14.26 / If-None-Match
+
+Test whether the given message object contains an "If-None-Match" header and
+compare it against the given stat object.
+@class function
+@name if_none_match
+@param req     HTTP request message object
+@param stat    A file.stat object
+@return                Boolean indicating whether the precondition is ok
+@return                Alternative status code if the precondition failed
+@return                Table containing extra HTTP headers if the precondition failed
+]]
+
+---[[
+14.27 / If-Range
+
+The If-Range header is currently not implemented due to the lack of general
+byte range stuff in luci.http.protocol . This function will always return
+false, 412 to indicate a failed precondition.
+@class function
+@name if_range
+@param req     HTTP request message object
+@param stat    A file.stat object
+@return                Boolean indicating whether the precondition is ok
+@return                Alternative status code if the precondition failed
+]]
+
+---[[
+14.28 / If-Unmodified-Since
+
+Test whether the given message object contains an "If-Unmodified-Since"
+header and compare it against the given stat object.
+@class function
+@name if_unmodified_since
+@param req     HTTP request message object
+@param stat    A file.stat object
+@return                Boolean indicating whether the precondition is ok
+@return                Alternative status code if the precondition failed
+]]
+
index 3105f37..e440219 100644 (file)
@@ -1,7 +1,6 @@
 -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
---- LuCI http protocol implementation - date helper class.
 -- This class contains functions to parse, compare and format http dates.
 module("luci.http.protocol.date", package.seeall)
 
@@ -13,9 +12,6 @@ MONTHS = {
        "Sep", "Oct", "Nov", "Dec"
 }
 
---- Return the time offset in seconds between the UTC and given time zone.
--- @param tz   Symbolic or numeric timezone specifier
--- @return             Time offset to UTC in seconds
 function tz_offset(tz)
 
        if type(tz) == "string" then
@@ -39,9 +35,6 @@ function tz_offset(tz)
        return 0
 end
 
---- Parse given HTTP date string and convert it to unix epoch time.
--- @param data String containing the date
--- @return             Unix epoch time
 function to_unix(date)
 
        local wd, day, mon, yr, hr, min, sec, tz = date:match(
@@ -75,19 +68,10 @@ function to_unix(date)
        return 0
 end
 
---- Convert the given unix epoch time to valid HTTP date string.
--- @param time Unix epoch time
--- @return             String containing the formatted date
 function to_http(time)
        return os.date( "%a, %d %b %Y %H:%M:%S GMT", time )
 end
 
---- Compare two dates which can either be unix epoch times or HTTP date strings.
--- @param d1   The first date or epoch time to compare
--- @param d2   The first date or epoch time to compare
--- @return             -1  -  if d1 is lower then d2
--- @return             0   -  if both dates are equal
--- @return             1   -  if d1 is higher then d2
 function compare(d1, d2)
 
        if d1:match("[^0-9]") then d1 = to_unix(d1) end
diff --git a/modules/luci-base/luasrc/http/protocol/date.luadoc b/modules/luci-base/luasrc/http/protocol/date.luadoc
new file mode 100644 (file)
index 0000000..d6f1c8d
--- /dev/null
@@ -0,0 +1,46 @@
+---[[
+LuCI http protocol implementation - date helper class.
+
+This class contains functions to parse, compare and format http dates.
+]]
+module "luci.http.protocol.date"
+
+---[[
+Return the time offset in seconds between the UTC and given time zone.
+
+@class function
+@name tz_offset
+@param tz      Symbolic or numeric timezone specifier
+@return                Time offset to UTC in seconds
+]]
+
+---[[
+Parse given HTTP date string and convert it to unix epoch time.
+
+@class function
+@name to_unix
+@param data    String containing the date
+@return                Unix epoch time
+]]
+
+---[[
+Convert the given unix epoch time to valid HTTP date string.
+
+@class function
+@name to_http
+@param time    Unix epoch time
+@return                String containing the formatted date
+]]
+
+---[[
+Compare two dates which can either be unix epoch times or HTTP date strings.
+
+@class function
+@name compare
+@param d1      The first date or epoch time to compare
+@param d2      The first date or epoch time to compare
+@return                -1  -  if d1 is lower then d2
+@return                0   -  if both dates are equal
+@return                1   -  if d1 is higher then d2
+]]
+
index 15da15c..2b99d8e 100644 (file)
@@ -1,15 +1,12 @@
 -- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
---- LuCI http protocol implementation - mime helper class.
 -- This class provides functions to guess mime types from file extensions and
 -- vice versa.
 module("luci.http.protocol.mime", package.seeall)
 
 require("luci.util")
 
---- MIME mapping table containg extension - mimetype relations.
--- @class table
 MIME_TYPES = {
     ["txt"]   = "text/plain";
     ["js"]    = "text/javascript";
@@ -54,10 +51,7 @@ MIME_TYPES = {
     ["avi"]   = "video/x-msvideo";
 }
 
---- Extract extension from a filename and return corresponding mime-type or
 -- "application/octet-stream" if the extension is unknown.
--- @param filename     The filename for which the mime type is guessed
--- @return                     String containign the determined mime type
 function to_mime(filename)
        if type(filename) == "string" then
                local ext = filename:match("[^%.]+$")
@@ -70,10 +64,7 @@ function to_mime(filename)
        return "application/octet-stream"
 end
 
---- Return corresponding extension for a given mime type or nil if the
 -- given mime-type is unknown.
--- @param mimetype     The mimetype to retrieve the extension from
--- @return                     String with the extension or nil for unknown type
 function to_ext(mimetype)
        if type(mimetype) == "string" then
                for ext, type in luci.util.kspairs( MIME_TYPES ) do
diff --git a/modules/luci-base/luasrc/http/protocol/mime.luadoc b/modules/luci-base/luasrc/http/protocol/mime.luadoc
new file mode 100644 (file)
index 0000000..195b5fc
--- /dev/null
@@ -0,0 +1,34 @@
+---[[
+LuCI http protocol implementation - mime helper class.
+
+This class provides functions to guess mime types from file extensions and
+vice versa.
+]]
+module "luci.http.protocol.mime"
+
+---[[
+MIME mapping table containg extension - mimetype relations.
+
+@class table
+]]
+
+---[[
+Extract extension from a filename and return corresponding mime-type or
+
+"application/octet-stream" if the extension is unknown.
+@class function
+@name to_mime
+@param filename        The filename for which the mime type is guessed
+@return                        String containign the determined mime type
+]]
+
+---[[
+Return corresponding extension for a given mime type or nil if the
+
+given mime-type is unknown.
+@class function
+@name to_ext
+@param mimetype        The mimetype to retrieve the extension from
+@return                        String with the extension or nil for unknown type
+]]
+
index dd84a59..bcb16d5 100644 (file)
@@ -1,7 +1,6 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
 -- Licensed to the public under the Apache License 2.0.
 
---- LuCI translation library.
 module("luci.i18n", package.seeall)
 require("luci.util")
 
@@ -13,27 +12,16 @@ loaded  = {}
 context = luci.util.threadlocal()
 default = "en"
 
---- Clear the translation table.
 function clear()
 end
 
---- Load a translation and copy its data into the translation table.
--- @param file Language file
--- @param lang Two-letter language code
--- @param force        Force reload even if already loaded (optional)
--- @return             Success status
 function load(file, lang, force)
 end
 
---- Load a translation file using the default translation language.
 -- Alternatively load the translation of the fallback language.
--- @param file Language file
--- @param force        Force reload even if already loaded (optional)
 function loadc(file, force)
 end
 
---- Set the context default translation language.
--- @param lang Two-letter language code
 function setlanguage(lang)
        context.lang   = lang:gsub("_", "-")
        context.parent = (context.lang:match("^([a-z][a-z])_"))
@@ -46,36 +34,22 @@ function setlanguage(lang)
        return context.lang
 end
 
---- Return the translated value for a specific translation key.
--- @param key  Default translation text
--- @return             Translated string
 function translate(key)
        return tparser.translate(key) or key
 end
 
---- Return the translated value for a specific translation key and use it as sprintf pattern.
--- @param key          Default translation text
--- @param ...          Format parameters
--- @return                     Translated and formatted string
 function translatef(key, ...)
        return tostring(translate(key)):format(...)
 end
 
---- Return the translated value for a specific translation key
 -- and ensure that the returned value is a Lua string value.
 -- This is the same as calling <code>tostring(translate(...))</code>
--- @param key          Default translation text
--- @return                     Translated string
 function string(key)
        return tostring(translate(key))
 end
 
---- Return the translated value for a specific translation key and use it as sprintf pattern.
 -- Ensure that the returned value is a Lua string value.
 -- This is the same as calling <code>tostring(translatef(...))</code>
--- @param key          Default translation text
--- @param ...          Format parameters
--- @return                     Translated and formatted string
 function stringf(key, ...)
        return tostring(translate(key)):format(...)
 end
diff --git a/modules/luci-base/luasrc/i18n.luadoc b/modules/luci-base/luasrc/i18n.luadoc
new file mode 100644 (file)
index 0000000..aa38841
--- /dev/null
@@ -0,0 +1,84 @@
+---[[
+LuCI translation library.
+]]
+module "luci.i18n"
+
+---[[
+Clear the translation table.
+
+
+@class function
+@name clear
+]]
+
+---[[
+Load a translation and copy its data into the translation table.
+
+@class function
+@name load
+@param file    Language file
+@param lang    Two-letter language code
+@param force   Force reload even if already loaded (optional)
+@return                Success status
+]]
+
+---[[
+Load a translation file using the default translation language.
+
+Alternatively load the translation of the fallback language.
+@class function
+@name loadc
+@param file    Language file
+@param force   Force reload even if already loaded (optional)
+]]
+
+---[[
+Set the context default translation language.
+
+@class function
+@name setlanguage
+@param lang    Two-letter language code
+]]
+
+---[[
+Return the translated value for a specific translation key.
+
+@class function
+@name translate
+@param key     Default translation text
+@return                Translated string
+]]
+
+---[[
+Return the translated value for a specific translation key and use it as sprintf pattern.
+
+@class function
+@name translatef
+@param key             Default translation text
+@param ...             Format parameters
+@return                        Translated and formatted string
+]]
+
+---[[
+Return the translated value for a specific translation key
+
+and ensure that the returned value is a Lua string value.
+This is the same as calling <code>tostring(translate(...))</code>
+@class function
+@name string
+@param key             Default translation text
+@return                        Translated string
+]]
+
+---[[
+Return the translated value for a specific translation key and use it as sprintf pattern.
+
+Ensure that the returned value is a Lua string value.
+This is the same as calling <code>tostring(translatef(...))</code>
+@class function
+@name stringf
+@param key             Default translation text
+@param ...             Format parameters
+@return                        Translated and formatted string
+]]
+
diff --git a/modules/luci-base/luasrc/ip.lua b/modules/luci-base/luasrc/ip.lua
deleted file mode 100644 (file)
index d8aaea9..0000000
+++ /dev/null
@@ -1,661 +0,0 @@
--- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
--- Copyright 2008 Steven Barth <steven@midlink.org>
--- Licensed to the public under the Apache License 2.0.
-
---- LuCI IP calculation library.
-module( "luci.ip", package.seeall )
-
-require "nixio"
-local bit  = nixio.bit
-local util = require "luci.util"
-
---- Boolean; true if system is little endian
-LITTLE_ENDIAN = not util.bigendian()
-
---- Boolean; true if system is big endian
-BIG_ENDIAN    = not LITTLE_ENDIAN
-
---- Specifier for IPv4 address family
-FAMILY_INET4  = 0x04
-
---- Specifier for IPv6 address family
-FAMILY_INET6  = 0x06
-
-
-local function __bless(x)
-       return setmetatable( x, {
-               __index = luci.ip.cidr,
-               __add   = luci.ip.cidr.add,
-               __sub   = luci.ip.cidr.sub,
-               __lt    = luci.ip.cidr.lower,
-               __eq    = luci.ip.cidr.equal,
-               __le    =
-                       function(...)
-                               return luci.ip.cidr.equal(...) or luci.ip.cidr.lower(...)
-                       end
-       } )
-end
-
-local function __array16( x, family )
-       local list
-
-       if type(x) == "number" then
-               list = { bit.rshift(x, 16), bit.band(x, 0xFFFF) }
-
-       elseif type(x) == "string" then
-               if x:find(":") then x = IPv6(x) else x = IPv4(x) end
-               if x then
-                       assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
-                       list = { unpack(x[2]) }
-               end
-
-       elseif type(x) == "table" and type(x[2]) == "table" then
-               assert( x[1] == family, "Can't mix IPv4 and IPv6 addresses" )
-               list = { unpack(x[2]) }
-
-       elseif type(x) == "table" then
-               list = { unpack(x) }
-       end
-
-       assert( list, "Invalid operand" )
-
-       return list
-end
-
-local function __mask16(bits)
-       return bit.lshift( bit.rshift( 0xFFFF, 16 - bits % 16 ), 16 - bits % 16 )
-end
-
-local function __not16(bits)
-       return bit.band( bit.bnot( __mask16(bits) ), 0xFFFF )
-end
-
-local function __maxlen(family)
-       return ( family == FAMILY_INET4 ) and 32 or 128
-end
-
-local function __sublen(family)
-       return ( family == FAMILY_INET4 ) and 30 or 127
-end
-
-
---- Convert given short value to network byte order on little endian hosts
--- @param x    Unsigned integer value between 0x0000 and 0xFFFF
--- @return     Byte-swapped value
--- @see                htonl
--- @see                ntohs
-function htons(x)
-       if LITTLE_ENDIAN then
-               return bit.bor(
-                       bit.rshift( x, 8 ),
-                       bit.band( bit.lshift( x, 8 ), 0xFF00 )
-               )
-       else
-               return x
-       end
-end
-
---- Convert given long value to network byte order on little endian hosts
--- @param x    Unsigned integer value between 0x00000000 and 0xFFFFFFFF
--- @return     Byte-swapped value
--- @see                htons
--- @see                ntohl
-function htonl(x)
-       if LITTLE_ENDIAN then
-               return bit.bor(
-                       bit.lshift( htons( bit.band( x, 0xFFFF ) ), 16 ),
-                       htons( bit.rshift( x, 16 ) )
-               )
-       else
-               return x
-       end
-end
-
---- Convert given short value to host byte order on little endian hosts
--- @class      function
--- @name       ntohs
--- @param x    Unsigned integer value between 0x0000 and 0xFFFF
--- @return     Byte-swapped value
--- @see                htonl
--- @see                ntohs
-ntohs = htons
-
---- Convert given short value to host byte order on little endian hosts
--- @class      function
--- @name       ntohl
--- @param x    Unsigned integer value between 0x00000000 and 0xFFFFFFFF
--- @return     Byte-swapped value
--- @see                htons
--- @see                ntohl
-ntohl = htonl
-
-
---- Parse given IPv4 address in dotted quad or CIDR notation. If an optional
--- netmask is given as second argument and the IP address is encoded in CIDR
--- notation then the netmask parameter takes precedence. If neither a CIDR
--- encoded prefix nor a netmask parameter is given, then a prefix length of
--- 32 bit is assumed.
--- @param address      IPv4 address in dotted quad or CIDR notation
--- @param netmask      IPv4 netmask in dotted quad notation (optional)
--- @return                     luci.ip.cidr instance or nil if given address was invalid
--- @see                                IPv6
--- @see                                Hex
-function IPv4(address, netmask)
-       address = address or "0.0.0.0/0"
-
-       local obj = __bless({ FAMILY_INET4 })
-
-       local data = {}
-       local prefix = address:match("/(.+)")
-       address = address:gsub("/.+","")
-       address = address:gsub("^%[(.*)%]$", "%1"):upper():gsub("^::FFFF:", "")
-
-       if netmask then
-               prefix = obj:prefix(netmask)
-       elseif prefix then
-               prefix = tonumber(prefix)
-               if not prefix or prefix < 0 or prefix > 32 then return nil end
-       else
-               prefix = 32
-       end
-
-       local b1, b2, b3, b4 = address:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")
-
-       b1 = tonumber(b1)
-       b2 = tonumber(b2)
-       b3 = tonumber(b3)
-       b4 = tonumber(b4)
-
-       if b1 and b1 <= 255 and
-          b2 and b2 <= 255 and
-          b3 and b3 <= 255 and
-          b4 and b4 <= 255 and
-          prefix
-       then
-               table.insert(obj, { b1 * 256 + b2, b3 * 256 + b4 })
-               table.insert(obj, prefix)
-               return obj
-       end
-end
-
---- Parse given IPv6 address in full, compressed, mixed or CIDR notation.
--- If an optional netmask is given as second argument and the IP address is
--- encoded in CIDR notation then the netmask parameter takes precedence.
--- If neither a CIDR encoded prefix nor a netmask parameter is given, then a
--- prefix length of 128 bit is assumed.
--- @param address      IPv6 address in full/compressed/mixed or CIDR notation
--- @param netmask      IPv6 netmask in full/compressed/mixed notation (optional)
--- @return                     luci.ip.cidr instance or nil if given address was invalid
--- @see                                IPv4
--- @see                                Hex
-function IPv6(address, netmask)
-       address = address or "::/0"
-
-       local obj = __bless({ FAMILY_INET6 })
-
-       local data = {}
-       local prefix = address:match("/(.+)")
-       address = address:gsub("/.+","")
-       address = address:gsub("^%[(.*)%]$", "%1")
-
-       if netmask then
-               prefix = obj:prefix(netmask)
-       elseif prefix then
-               prefix = tonumber(prefix)
-               if not prefix or prefix < 0 or prefix > 128 then return nil end
-       else
-               prefix = 128
-       end
-
-       local borderl = address:sub(1, 1) == ":" and 2 or 1
-       local borderh, zeroh, chunk, block, i
-
-       if #address > 45 then return nil end
-
-       repeat
-               borderh = address:find(":", borderl, true)
-               if not borderh then break end
-
-               block = tonumber(address:sub(borderl, borderh - 1), 16)
-               if block and block <= 0xFFFF then
-                       data[#data+1] = block
-               else
-                       if zeroh or borderh - borderl > 1 then return nil end
-                       zeroh = #data + 1
-               end
-
-               borderl = borderh + 1
-       until #data == 7
-
-       chunk = address:sub(borderl)
-       if #chunk > 0 and #chunk <= 4 then
-               block = tonumber(chunk, 16)
-               if not block or block > 0xFFFF then return nil end
-
-               data[#data+1] = block
-       elseif #chunk > 4 then
-               if #data == 7 or #chunk > 15 then return nil end
-               borderl = 1
-               for i=1, 4 do
-                       borderh = chunk:find(".", borderl, true)
-                       if not borderh and i < 4 then return nil end
-                       borderh = borderh and borderh - 1
-
-                       block = tonumber(chunk:sub(borderl, borderh))
-                       if not block or block > 255 then return nil end
-
-                       if i == 1 or i == 3 then
-                               data[#data+1] = block * 256
-                       else
-                               data[#data] = data[#data] + block
-                       end
-
-                       borderl = borderh and borderh + 2
-               end
-       end
-
-       if zeroh then
-               if #data == 8 then return nil end
-               while #data < 8 do
-                       table.insert(data, zeroh, 0)
-               end
-       end
-
-       if #data == 8 and prefix then
-               table.insert(obj, data)
-               table.insert(obj, prefix)
-               return obj
-       end
-end
-
---- Transform given hex-encoded value to luci.ip.cidr instance of specified
--- address family.
--- @param hex          String containing hex encoded value
--- @param prefix       Prefix length of CIDR instance (optional, default is 32/128)
--- @param family       Address family, either luci.ip.FAMILY_INET4 or FAMILY_INET6
--- @param swap         Bool indicating whether to swap byteorder on low endian host
--- @return                     luci.ip.cidr instance or nil if given value was invalid
--- @see                                IPv4
--- @see                                IPv6
-function Hex( hex, prefix, family, swap )
-       family = ( family ~= nil ) and family or FAMILY_INET4
-       swap   = ( swap   == nil ) and true   or swap
-       prefix = prefix or __maxlen(family)
-
-       local len  = __maxlen(family)
-       local tmp  = ""
-       local data = { }
-       local i
-
-       for i = 1, (len/4) - #hex do tmp = tmp .. '0' end
-
-       if swap and LITTLE_ENDIAN then
-               for i = #hex, 1, -2 do tmp = tmp .. hex:sub( i - 1, i ) end
-       else
-               tmp = tmp .. hex
-       end
-
-       hex = tmp
-
-       for i = 1, ( len / 4 ), 4 do
-               local n = tonumber( hex:sub( i, i+3 ), 16 )
-               if n then
-                       data[#data+1] = n
-               else
-                       return nil
-               end
-       end
-
-       return __bless({ family, data, prefix })
-end
-
-
---- LuCI IP Library / CIDR instances
--- @class      module
--- @cstyle     instance
--- @name       luci.ip.cidr
-cidr = util.class()
-
---- Test whether the instance is a IPv4 address.
--- @return     Boolean indicating a IPv4 address type
--- @see                cidr.is6
-function cidr.is4( self )
-       return self[1] == FAMILY_INET4
-end
-
---- Test whether this instance is an IPv4 RFC1918 private address
--- @return     Boolean indicating whether this instance is an RFC1918 address
-function cidr.is4rfc1918( self )
-       if self[1] == FAMILY_INET4 then
-               return ((self[2][1] >= 0x0A00) and (self[2][1] <= 0x0AFF)) or
-                      ((self[2][1] >= 0xAC10) and (self[2][1] <= 0xAC1F)) or
-                       (self[2][1] == 0xC0A8)
-       end
-       return false
-end
-
---- Test whether this instance is an IPv4 link-local address (Zeroconf)
--- @return     Boolean indicating whether this instance is IPv4 link-local
-function cidr.is4linklocal( self )
-       if self[1] == FAMILY_INET4 then
-               return (self[2][1] == 0xA9FE)
-       end
-       return false
-end
-
---- Test whether the instance is a IPv6 address.
--- @return     Boolean indicating a IPv6 address type
--- @see                cidr.is4
-function cidr.is6( self )
-       return self[1] == FAMILY_INET6
-end
-
---- Test whether this instance is an IPv6 link-local address
--- @return     Boolean indicating whether this instance is IPv6 link-local
-function cidr.is6linklocal( self )
-       if self[1] == FAMILY_INET6 then
-               return (self[2][1] >= 0xFE80) and (self[2][1] <= 0xFEBF)
-       end
-       return false
-end
-
---- Return a corresponding string representation of the instance.
--- If the prefix length is lower then the maximum possible prefix length for the
--- corresponding address type then the address is returned in CIDR notation,
--- otherwise the prefix will be left out.
-function cidr.string( self )
-       local str
-       if self:is4() then
-               str = string.format(
-                       "%d.%d.%d.%d",
-                       bit.rshift(self[2][1], 8), bit.band(self[2][1], 0xFF),
-                       bit.rshift(self[2][2], 8), bit.band(self[2][2], 0xFF)
-               )
-               if self[3] < 32 then
-                       str = str .. "/" .. self[3]
-               end
-       elseif self:is6() then
-               str = string.format( "%X:%X:%X:%X:%X:%X:%X:%X", unpack(self[2]) )
-               if self[3] < 128 then
-                       str = str .. "/" .. self[3]
-               end
-       end
-       return str
-end
-
---- Test whether the value of the instance is lower then the given address.
--- This function will throw an exception if the given address has a different
--- family than this instance.
--- @param addr A luci.ip.cidr instance to compare against
--- @return             Boolean indicating whether this instance is lower
--- @see                        cidr.higher
--- @see                        cidr.equal
-function cidr.lower( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-       local i
-       for i = 1, #self[2] do
-               if self[2][i] ~= addr[2][i] then
-                       return self[2][i] < addr[2][i]
-               end
-       end
-       return false
-end
-
---- Test whether the value of the instance is higher then the given address.
--- This function will throw an exception if the given address has a different
--- family than this instance.
--- @param addr A luci.ip.cidr instance to compare against
--- @return             Boolean indicating whether this instance is higher
--- @see                        cidr.lower
--- @see                        cidr.equal
-function cidr.higher( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-       local i
-       for i = 1, #self[2] do
-               if self[2][i] ~= addr[2][i] then
-                       return self[2][i] > addr[2][i]
-               end
-       end
-       return false
-end
-
---- Test whether the value of the instance is equal to the given address.
--- This function will throw an exception if the given address is a different
--- family than this instance.
--- @param addr A luci.ip.cidr instance to compare against
--- @return             Boolean indicating whether this instance is equal
--- @see                        cidr.lower
--- @see                        cidr.higher
-function cidr.equal( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-       local i
-       for i = 1, #self[2] do
-               if self[2][i] ~= addr[2][i] then
-                       return false
-               end
-       end
-       return true
-end
-
---- Return the prefix length of this CIDR instance.
--- @param mask Override instance prefix with given netmask (optional)
--- @return             Prefix length in bit
-function cidr.prefix( self, mask )
-       local prefix = self[3]
-
-       if mask then
-               prefix = 0
-
-               local stop = false
-               local obj = type(mask) ~= "table"
-                       and ( self:is4() and IPv4(mask) or IPv6(mask) ) or mask
-
-               if not obj then return nil end
-
-               local _, word
-               for _, word in ipairs(obj[2]) do
-                       if word == 0xFFFF then
-                               prefix = prefix + 16
-                       else
-                               local bitmask = bit.lshift(1, 15)
-                               while bit.band(word, bitmask) == bitmask do
-                                       prefix  = prefix + 1
-                                       bitmask = bit.lshift(1, 15 - (prefix % 16))
-                               end
-
-                               break
-                       end
-               end
-       end
-
-       return prefix
-end
-
---- Return a corresponding CIDR representing the network address of this
--- instance.
--- @param bits Override prefix length of this instance (optional)
--- @return             CIDR instance containing the network address
--- @see                        cidr.host
--- @see                        cidr.broadcast
--- @see                        cidr.mask
-function cidr.network( self, bits )
-       local data = { }
-       bits = bits or self[3]
-
-       local i
-       for i = 1, math.floor( bits / 16 ) do
-               data[#data+1] = self[2][i]
-       end
-
-       if #data < #self[2] then
-               data[#data+1] = bit.band( self[2][1+#data], __mask16(bits) )
-
-               for i = #data + 1, #self[2] do
-                       data[#data+1] = 0
-               end
-       end
-
-       return __bless({ self[1], data, __maxlen(self[1]) })
-end
-
---- Return a corresponding CIDR representing the host address of this
--- instance. This is intended to extract the host address from larger subnet.
--- @return             CIDR instance containing the network address
--- @see                        cidr.network
--- @see                        cidr.broadcast
--- @see                        cidr.mask
-function cidr.host( self )
-       return __bless({ self[1], self[2], __maxlen(self[1]) })
-end
-
---- Return a corresponding CIDR representing the netmask of this instance.
--- @param bits Override prefix length of this instance (optional)
--- @return             CIDR instance containing the netmask
--- @see                        cidr.network
--- @see                        cidr.host
--- @see                        cidr.broadcast
-function cidr.mask( self, bits )
-       local data = { }
-       bits = bits or self[3]
-
-       for i = 1, math.floor( bits / 16 ) do
-               data[#data+1] = 0xFFFF
-       end
-
-       if #data < #self[2] then
-               data[#data+1] = __mask16(bits)
-
-               for i = #data + 1, #self[2] do
-                       data[#data+1] = 0
-               end
-       end
-
-       return __bless({ self[1], data, __maxlen(self[1]) })
-end
-
---- Return CIDR containing the broadcast address of this instance.
--- @return             CIDR instance containing the netmask, always nil for IPv6
--- @see                        cidr.network
--- @see                        cidr.host
--- @see                        cidr.mask
-function cidr.broadcast( self )
-       -- IPv6 has no broadcast addresses (XXX: assert() instead?)
-       if self[1] == FAMILY_INET4 then
-               local data   = { unpack(self[2]) }
-               local offset = math.floor( self[3] / 16 ) + 1
-
-               if offset <= #data then
-                       data[offset] = bit.bor( data[offset], __not16(self[3]) )
-                       for i = offset + 1, #data do data[i] = 0xFFFF end
-
-                       return __bless({ self[1], data, __maxlen(self[1]) })
-               end
-       end
-end
-
---- Test whether this instance fully contains the given CIDR instance.
--- @param addr CIDR instance to test against
--- @return             Boolean indicating whether this instance contains the given CIDR
-function cidr.contains( self, addr )
-       assert( self[1] == addr[1], "Can't compare IPv4 and IPv6 addresses" )
-
-       if self:prefix() <= addr:prefix() then
-               return self:network() == addr:network(self:prefix())
-       end
-
-       return false
-end
-
---- Add specified amount of hosts to this instance.
--- @param amount       Number of hosts to add to this instance
--- @param inplace      Boolen indicating whether to alter values inplace (optional)
--- @return                     CIDR representing the new address or nil on overflow error
--- @see                                cidr.sub
-function cidr.add( self, amount, inplace )
-       local pos
-       local data   = { unpack(self[2]) }
-       local shorts = __array16( amount, self[1] )
-
-       for pos = #data, 1, -1 do
-               local add = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
-               if ( data[pos] + add ) > 0xFFFF then
-                       data[pos] = ( data[pos] + add ) % 0xFFFF
-                       if pos > 1 then
-                               data[pos-1] = data[pos-1] + ( add - data[pos] )
-                       else
-                               return nil
-                       end
-               else
-                       data[pos] = data[pos] + add
-               end
-       end
-
-       if inplace then
-               self[2] = data
-               return self
-       else
-               return __bless({ self[1], data, self[3] })
-       end
-end
-
---- Substract specified amount of hosts from this instance.
--- @param amount       Number of hosts to substract from this instance
--- @param inplace      Boolen indicating whether to alter values inplace (optional)
--- @return                     CIDR representing the new address or nil on underflow error
--- @see                                cidr.add
-function cidr.sub( self, amount, inplace )
-       local pos
-       local data   = { unpack(self[2]) }
-       local shorts = __array16( amount, self[1] )
-
-       for pos = #data, 1, -1 do
-               local sub = ( #shorts > 0 ) and table.remove( shorts, #shorts ) or 0
-               if ( data[pos] - sub ) < 0 then
-                       data[pos] = ( sub - data[pos] ) % 0xFFFF
-                       if pos > 1 then
-                               data[pos-1] = data[pos-1] - ( sub + data[pos] )
-                       else
-                               return nil
-                       end
-               else
-                       data[pos] = data[pos] - sub
-               end
-       end
-
-       if inplace then
-               self[2] = data
-               return self
-       else
-               return __bless({ self[1], data, self[3] })
-       end
-end
-
---- Return CIDR containing the lowest available host address within this subnet.
--- @return             CIDR containing the host address, nil if subnet is too small
--- @see                        cidr.maxhost
-function cidr.minhost( self )
-       if self[3] <= __sublen(self[1]) then
-               -- 1st is Network Address in IPv4 and Subnet-Router Anycast Adresse in IPv6
-               return self:network():add(1, true)
-       end
-end
-
---- Return CIDR containing the highest available host address within the subnet.
--- @return             CIDR containing the host address, nil if subnet is too small
--- @see                        cidr.minhost
-function cidr.maxhost( self )
-       if self[3] <= __sublen(self[1]) then
-               local i
-               local data   = { unpack(self[2]) }
-               local offset = math.floor( self[3] / 16 ) + 1
-
-               data[offset] = bit.bor( data[offset], __not16(self[3]) )
-               for i = offset + 1, #data do data[i] = 0xFFFF end
-               data = __bless({ self[1], data, __maxlen(self[1]) })
-
-               -- Last address in reserved for Broadcast Address in IPv4
-               if data[1] == FAMILY_INET4 then data:sub(1, true) end
-
-               return data
-       end
-end
index b59fb8c..3a7268c 100644 (file)
@@ -39,7 +39,6 @@ local string = require("string")
 local table = require("table")
 local base = _G
 
---- Diego Nehab's LTN12 - Filters, sources, sinks and pumps.
 -- See http://lua-users.org/wiki/FiltersSourcesAndSinks for design concepts 
 module("luci.ltn12")
 
@@ -56,16 +55,8 @@ _VERSION = "LTN12 1.0.1"
 -- Filter stuff
 -----------------------------------------------------------------------------
 
---- LTN12 Filter constructors
--- @class module
--- @name luci.ltn12.filter
 
---- Return a high level filter that cycles a low-level filter
 -- by passing it each chunk and updating a context between calls. 
--- @param low   Low-level filter
--- @param ctx   Context
--- @param extra Extra argument passed to the low-level filter
--- @return LTN12 filter
 function filter.cycle(low, ctx, extra)
     base.assert(low)
     return function(chunk)
@@ -75,10 +66,7 @@ function filter.cycle(low, ctx, extra)
     end
 end
 
---- Chain a bunch of filters together.
 -- (thanks to Wim Couwenberg)
--- @param ... filters to be chained
--- @return LTN12 filter
 function filter.chain(...)
     local n = table.getn(arg)
     local top, index = 1, 1
@@ -112,34 +100,22 @@ end
 -- Source stuff
 -----------------------------------------------------------------------------
 
---- LTN12 Source constructors
--- @class module
--- @name luci.ltn12.source
 
 -- create an empty source
 local function empty()
     return nil
 end
 
---- Create an empty source.
--- @return LTN12 source
 function source.empty()
     return empty
 end
 
---- Return a source that just outputs an error.
--- @param err Error object
--- @return LTN12 source
 function source.error(err)
     return function()
         return nil, err
     end
 end
 
---- Create a file source.
--- @param handle File handle ready for reading
--- @param io_err IO error object
--- @return LTN12 source
 function source.file(handle, io_err)
     if handle then
         return function()
@@ -151,9 +127,6 @@ function source.file(handle, io_err)
     else return source.error(io_err or "unable to open file") end
 end
 
---- Turn a fancy source into a simple source.
--- @param src fancy source
--- @return LTN12 source
 function source.simplify(src)
     base.assert(src)
     return function()
@@ -164,9 +137,6 @@ function source.simplify(src)
     end
 end
 
---- Create a string source.
--- @param s Data
--- @return LTN12 source
 function source.string(s)
     if s then
         local i = 1
@@ -179,9 +149,6 @@ function source.string(s)
     else return source.empty() end
 end
 
---- Creates rewindable source.
--- @param src LTN12 source to be made rewindable
--- @return LTN12 source
 function source.rewind(src)
     base.assert(src)
     local t = {}
@@ -196,10 +163,6 @@ function source.rewind(src)
     end
 end
 
---- Chain a source and a filter together.
--- @param src LTN12 source
--- @param f LTN12 filter
--- @return LTN12 source
 function source.chain(src, f)
     base.assert(src and f)
     local last_in, last_out = "", ""
@@ -247,11 +210,8 @@ function source.chain(src, f)
     end
 end
 
---- Create a source that produces contents of several sources.
 -- Sources will be used one after the other, as if they were concatenated
 -- (thanks to Wim Couwenberg)
--- @param ... LTN12 sources
--- @return LTN12 source
 function source.cat(...)
     local src = table.remove(arg, 1)
     return function()
@@ -268,13 +228,7 @@ end
 -- Sink stuff
 -----------------------------------------------------------------------------
 
---- LTN12 sink constructors
--- @class module
--- @name luci.ltn12.sink
 
---- Create a sink that stores into a table.
--- @param t output table to store into
--- @return LTN12 sink
 function sink.table(t)
     t = t or {}
     local f = function(chunk, err)
@@ -284,9 +238,6 @@ function sink.table(t)
     return f, t
 end
 
---- Turn a fancy sink into a simple sink.
--- @param snk fancy sink
--- @return LTN12 sink
 function sink.simplify(snk)
     base.assert(snk)
     return function(chunk, err)
@@ -297,10 +248,6 @@ function sink.simplify(snk)
     end
 end
 
---- Create a file sink.
--- @param handle file handle to write to
--- @param io_err IO error
--- @return LTN12 sink
 function sink.file(handle, io_err)
     if handle then
         return function(chunk, err)
@@ -317,25 +264,16 @@ local function null()
     return 1
 end
 
---- Create a sink that discards data.
--- @return LTN12 sink
 function sink.null()
     return null
 end
 
---- Create a sink that just returns an error.
--- @param err Error object
--- @return LTN12 sink
 function sink.error(err)
     return function()
         return nil, err
     end
 end
 
---- Chain a sink with a filter.
--- @param f LTN12 filter
--- @param snk LTN12 sink
--- @return LTN12 sink
 function sink.chain(f, snk)
     base.assert(f and snk)
     return function(chunk, err)
@@ -356,15 +294,7 @@ end
 -- Pump stuff
 -----------------------------------------------------------------------------
 
---- LTN12 pump functions
--- @class module
--- @name luci.ltn12.pump
 
---- Pump one chunk from the source to the sink.
--- @param src LTN12 source
--- @param snk LTN12 sink
--- @return Chunk of data or nil if an error occured
--- @return Error object
 function pump.step(src, snk)
     local chunk, src_err = src()
     local ret, snk_err = snk(chunk, src_err)
@@ -372,12 +302,6 @@ function pump.step(src, snk)
     else return nil, src_err or snk_err end
 end
 
---- Pump all data from a source to a sink, using a step function.
--- @param src LTN12 source
--- @param snk LTN12 sink
--- @param step step function (optional)
--- @return 1 if the operation succeeded otherwise nil
--- @return Error object
 function pump.all(src, snk, step)
     base.assert(src and snk)
     step = step or pump.step
index 216caa5..5876372 100644 (file)
@@ -15,7 +15,6 @@ local table = table
 local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
 local icfg = "/etc/opkg.conf"
 
---- LuCI OPKG call abstraction library
 module "luci.model.ipkg"
 
 
@@ -93,54 +92,31 @@ local function _lookup(act, pkg)
 end
 
 
---- Return information about installed and available packages.
--- @param pkg Limit output to a (set of) packages
--- @return Table containing package information
 function info(pkg)
        return _lookup("info", pkg)
 end
 
---- Return the package status of one or more packages.
--- @param pkg Limit output to a (set of) packages
--- @return Table containing package status information
 function status(pkg)
        return _lookup("status", pkg)
 end
 
---- Install one or more packages.
--- @param ... List of packages to install
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function install(...)
        return _action("install", ...)
 end
 
---- Determine whether a given package is installed.
--- @param pkg Package
--- @return Boolean
 function installed(pkg)
        local p = status(pkg)[pkg]
        return (p and p.Status and p.Status.installed)
 end
 
---- Remove one or more packages.
--- @param ... List of packages to install
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function remove(...)
        return _action("remove", ...)
 end
 
---- Update package lists.
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function update()
        return _action("update")
 end
 
---- Upgrades all installed packages.
--- @return Boolean indicating the status of the action
--- @return OPKG return code, STDOUT and STDERR
 function upgrade()
        return _action("upgrade")
 end
@@ -174,33 +150,19 @@ function _list(action, pat, cb)
        end
 end
 
---- List all packages known to opkg.
--- @param pat  Only find packages matching this pattern, nil lists all packages
--- @param cb   Callback function invoked for each package, receives name, version and description as arguments
--- @return     nothing
 function list_all(pat, cb)
        _list("list", pat, cb)
 end
 
---- List installed packages.
--- @param pat  Only find packages matching this pattern, nil lists all packages
--- @param cb   Callback function invoked for each package, receives name, version and description as arguments
--- @return     nothing
 function list_installed(pat, cb)
        _list("list_installed", pat, cb)
 end
 
---- Find packages that match the given pattern.
--- @param pat  Find packages whose names or descriptions match this pattern, nil results in zero results
--- @param cb   Callback function invoked for each patckage, receives name, version and description as arguments
--- @return     nothing
 function find(pat, cb)
        _list("find", pat, cb)
 end
 
 
---- Determines the overlay root used by opkg.
--- @return             String containing the directory path of the overlay root.
 function overlay_root()
        local od = "/"
        local fd = io.open(icfg, "r")
diff --git a/modules/luci-base/luasrc/model/ipkg.luadoc b/modules/luci-base/luasrc/model/ipkg.luadoc
new file mode 100644 (file)
index 0000000..cf0985f
--- /dev/null
@@ -0,0 +1,109 @@
+---[[
+LuCI OPKG call abstraction library
+
+module "luci.model.ipkg"
+]]
+
+---[[
+Return information about installed and available packages.
+
+@class function
+@name info
+@param pkg Limit output to a (set of) packages
+@return Table containing package information
+]]
+
+---[[
+Return the package status of one or more packages.
+
+@class function
+@name status
+@param pkg Limit output to a (set of) packages
+@return Table containing package status information
+]]
+
+---[[
+Install one or more packages.
+
+@class function
+@name install
+@param ... List of packages to install
+@return Boolean indicating the status of the action
+@return OPKG return code, STDOUT and STDERR
+]]
+
+---[[
+Determine whether a given package is installed.
+
+@class function
+@name installed
+@param pkg Package
+@return Boolean
+]]
+
+---[[
+Remove one or more packages.
+
+@class function
+@name remove
+@param ... List of packages to install
+@return Boolean indicating the status of the action
+@return OPKG return code, STDOUT and STDERR
+]]
+
+---[[
+Update package lists.
+
+@class function
+@name update
+@return Boolean indicating the status of the action
+@return OPKG return code, STDOUT and STDERR
+]]
+
+---[[
+Upgrades all installed packages.
+
+@class function
+@name upgrade
+@return Boolean indicating the status of the action
+@return OPKG return code, STDOUT and STDERR
+]]
+
+---[[
+List all packages known to opkg.
+
+@class function
+@name list_all
+@param pat     Only find packages matching this pattern, nil lists all packages
+@param cb      Callback function invoked for each package, receives name, version and description as arguments
+@return        nothing
+]]
+
+---[[
+List installed packages.
+
+@class function
+@name list_installed
+@param pat     Only find packages matching this pattern, nil lists all packages
+@param cb      Callback function invoked for each package, receives name, version and description as arguments
+@return        nothing
+]]
+
+---[[
+Find packages that match the given pattern.
+
+@class function
+@name find
+@param pat     Find packages whose names or descriptions match this pattern, nil results in zero results
+@param cb      Callback function invoked for each patckage, receives name, version and description as arguments
+@return        nothing
+]]
+
+---[[
+Determines the overlay root used by opkg.
+
+@class function
+@name overlay_root
+@return                String containing the directory path of the overlay root.
+]]
+
index 8ac8277..1659137 100644 (file)
@@ -12,26 +12,18 @@ local require, getmetatable = require, getmetatable
 local error, pairs, ipairs = error, pairs, ipairs
 local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
 
---- LuCI UCI model library.
 -- The typical workflow for UCI is:  Get a cursor instance from the
 -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
 -- save the changes to the staging area via Cursor.save and finally
 -- Cursor.commit the data to the actual config files.
 -- LuCI then needs to Cursor.apply the changes so deamons etc. are
 -- reloaded.
--- @cstyle     instance
 module "luci.model.uci"
 
---- Create a new UCI-Cursor.
--- @class function
--- @name cursor
--- @return     UCI-Cursor
 cursor = uci.cursor
 
 APIVERSION = uci.APIVERSION
 
---- Create a new Cursor initialized to the state directory.
--- @return UCI cursor
 function cursor_state()
        return cursor(nil, "/var/state")
 end
@@ -42,9 +34,6 @@ inst_state = cursor_state()
 
 local Cursor = getmetatable(inst)
 
---- Applies UCI configuration changes
--- @param configlist           List of UCI configurations
--- @param command                      Don't apply only return the command
 function Cursor.apply(self, configlist, command)
        configlist = self:_affected(configlist)
        if command then
@@ -56,10 +45,6 @@ function Cursor.apply(self, configlist, command)
 end
 
 
---- Delete all sections of a given type that match certain criteria.
--- @param config               UCI config
--- @param type                 UCI section type
--- @param comparator   Function that will be called for each section and
 -- returns a boolean whether to delete the current section (optional)
 function Cursor.delete_all(self, config, stype, comparator)
        local del = {}
@@ -90,12 +75,6 @@ function Cursor.delete_all(self, config, stype, comparator)
        end
 end
 
---- Create a new section and initialize it with data.
--- @param config       UCI config
--- @param type         UCI section type
--- @param name         UCI section name (optional)
--- @param values       Table of key - value pairs to initialize the section with
--- @return                     Name of created section
 function Cursor.section(self, config, type, name, values)
        local stat = true
        if name then
@@ -112,10 +91,6 @@ function Cursor.section(self, config, type, name, values)
        return stat and name
 end
 
---- Updated the data of a section using data from a table.
--- @param config       UCI config
--- @param section      UCI section name (optional)
--- @param values       Table of key - value pairs to update the section with
 function Cursor.tset(self, config, section, values)
        local stat = true
        for k, v in pairs(values) do
@@ -126,21 +101,11 @@ function Cursor.tset(self, config, section, values)
        return stat
 end
 
---- Get a boolean option and return it's value as true or false.
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option
--- @return                     Boolean
 function Cursor.get_bool(self, ...)
        local val = self:get(...)
        return ( val == "1" or val == "true" or val == "yes" or val == "on" )
 end
 
---- Get an option or list and return values as table.
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option
--- @return                     UCI value
 function Cursor.get_list(self, config, section, option)
        if config and section and option then
                local val = self:get(config, section, option)
@@ -149,12 +114,6 @@ function Cursor.get_list(self, config, section, option)
        return nil
 end
 
---- Get the given option from the first section with the given type.
--- @param config       UCI config
--- @param type         UCI section type
--- @param option       UCI option (optional)
--- @param default      Default value (optional)
--- @return                     UCI value
 function Cursor.get_first(self, conf, stype, opt, def)
        local rv = def
 
@@ -178,12 +137,6 @@ function Cursor.get_first(self, conf, stype, opt, def)
        return rv
 end
 
---- Set given values as list.
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option
--- @param value                UCI value
--- @return                     Boolean whether operation succeeded
 function Cursor.set_list(self, config, section, option, value)
        if config and section and option then
                return self:set(
@@ -238,10 +191,8 @@ function Cursor._affected(self, configlist)
        return reloadlist
 end
 
---- Create a sub-state of this cursor. The sub-state is tied to the parent
 -- curser, means it the parent unloads or loads configs, the sub state will
 -- do so as well.
--- @return                     UCI state cursor tied to the parent cursor
 function Cursor.substate(self)
        Cursor._substates = Cursor._substates or { }
        Cursor._substates[self] = Cursor._substates[self] or cursor_state()
@@ -265,118 +216,18 @@ function Cursor.unload(self, ...)
 end
 
 
---- Add an anonymous section.
--- @class function
--- @name Cursor.add
--- @param config       UCI config
--- @param type         UCI section type
--- @return                     Name of created section
-
---- Get a table of saved but uncommitted changes.
--- @class function
--- @name Cursor.changes
--- @param config       UCI config
--- @return                     Table of changes
--- @see Cursor.save
-
---- Commit saved changes.
--- @class function
--- @name Cursor.commit
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.revert
--- @see Cursor.save
-
---- Deletes a section or an option.
--- @class function
--- @name Cursor.delete
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option (optional)
--- @return                     Boolean whether operation succeeded
-
---- Call a function for every section of a certain type.
--- @class function
--- @name Cursor.foreach
--- @param config       UCI config
--- @param type         UCI section type
--- @param callback     Function to be called
--- @return                     Boolean whether operation succeeded
-
---- Get a section type or an option
--- @class function
--- @name Cursor.get
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option (optional)
--- @return                     UCI value
-
---- Get all sections of a config or all values of a section.
--- @class function
--- @name Cursor.get_all
--- @param config       UCI config
--- @param section      UCI section name (optional)
--- @return                     Table of UCI sections or table of UCI values
-
---- Manually load a config.
--- @class function
--- @name Cursor.load
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.save
--- @see Cursor.unload
-
---- Revert saved but uncommitted changes.
--- @class function
--- @name Cursor.revert
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.commit
--- @see Cursor.save
-
---- Saves changes made to a config to make them committable.
--- @class function
--- @name Cursor.save
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.load
--- @see Cursor.unload
-
---- Set a value or create a named section.
--- @class function
--- @name Cursor.set
--- @param config       UCI config
--- @param section      UCI section name
--- @param option       UCI option or UCI section type
--- @param value                UCI value or nil if you want to create a section
--- @return                     Boolean whether operation succeeded
-
---- Get the configuration directory.
--- @class function
--- @name Cursor.get_confdir
--- @return                     Configuration directory
-
---- Get the directory for uncomitted changes.
--- @class function
--- @name Cursor.get_savedir
--- @return                     Save directory
-
---- Set the configuration directory.
--- @class function
--- @name Cursor.set_confdir
--- @param directory    UCI configuration directory
--- @return                     Boolean whether operation succeeded
-
---- Set the directory for uncommited changes.
--- @class function
--- @name Cursor.set_savedir
--- @param directory    UCI changes directory
--- @return                     Boolean whether operation succeeded
-
---- Discard changes made to a config.
--- @class function
--- @name Cursor.unload
--- @param config       UCI config
--- @return                     Boolean whether operation succeeded
--- @see Cursor.load
--- @see Cursor.save
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/luci-base/luasrc/model/uci.luadoc b/modules/luci-base/luasrc/model/uci.luadoc
new file mode 100644 (file)
index 0000000..1c20866
--- /dev/null
@@ -0,0 +1,291 @@
+---[[
+LuCI UCI model library.
+
+The typical workflow for UCI is:  Get a cursor instance from the
+cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
+save the changes to the staging area via Cursor.save and finally
+Cursor.commit the data to the actual config files.
+LuCI then needs to Cursor.apply the changes so deamons etc. are
+reloaded.
+@cstyle        instance
+module "luci.model.uci"
+]]
+
+---[[
+Create a new UCI-Cursor.
+
+@class function
+@name cursor
+@return        UCI-Cursor
+]]
+
+---[[
+Create a new Cursor initialized to the state directory.
+
+@class function
+@name cursor_state
+@return UCI cursor
+]]
+
+---[[
+Applies UCI configuration changes
+
+@class function
+@name Cursor.apply
+@param configlist              List of UCI configurations
+@param command                 Don't apply only return the command
+]]
+
+---[[
+Delete all sections of a given type that match certain criteria.
+
+@class function
+@name Cursor.delete_all
+@param config          UCI config
+@param type                    UCI section type
+@param comparator      Function that will be called for each section and
+returns a boolean whether to delete the current section (optional)
+]]
+
+---[[
+Create a new section and initialize it with data.
+
+@class function
+@name Cursor.section
+@param config  UCI config
+@param type            UCI section type
+@param name            UCI section name (optional)
+@param values  Table of key - value pairs to initialize the section with
+@return                        Name of created section
+]]
+
+---[[
+Updated the data of a section using data from a table.
+
+@class function
+@name Cursor.tset
+@param config  UCI config
+@param section UCI section name (optional)
+@param values  Table of key - value pairs to update the section with
+]]
+
+---[[
+Get a boolean option and return it's value as true or false.
+
+@class function
+@name Cursor.get_bool
+@param config  UCI config
+@param section UCI section name
+@param option  UCI option
+@return                        Boolean
+]]
+
+---[[
+Get an option or list and return values as table.
+
+@class function
+@name Cursor.get_list
+@param config  UCI config
+@param section UCI section name
+@param option  UCI option
+@return                        UCI value
+]]
+
+---[[
+Get the given option from the first section with the given type.
+
+@class function
+@name Cursor.get_first
+@param config  UCI config
+@param type            UCI section type
+@param option  UCI option (optional)
+@param default Default value (optional)
+@return                        UCI value
+]]
+
+---[[
+Set given values as list.
+
+@class function
+@name Cursor.set_list
+@param config  UCI config
+@param section UCI section name
+@param option  UCI option
+@param value           UCI value
+@return                        Boolean whether operation succeeded
+]]
+
+---[[
+Create a sub-state of this cursor. The sub-state is tied to the parent
+
+curser, means it the parent unloads or loads configs, the sub state will
+do so as well.
+@class function
+@name Cursor.substate
+@return                        UCI state cursor tied to the parent cursor
+]]
+
+---[[
+Add an anonymous section.
+
+@class function
+@name Cursor.add
+@param config  UCI config
+@param type            UCI section type
+@return                        Name of created section
+]]
+
+---[[
+Get a table of saved but uncommitted changes.
+
+@class function
+@name Cursor.changes
+@param config  UCI config
+@return                        Table of changes
+@see Cursor.save
+]]
+
+---[[
+Commit saved changes.
+
+@class function
+@name Cursor.commit
+@param config  UCI config
+@return                        Boolean whether operation succeeded
+@see Cursor.revert
+@see Cursor.save
+]]
+
+---[[
+Deletes a section or an option.
+
+@class function
+@name Cursor.delete
+@param config  UCI config
+@param section UCI section name
+@param option  UCI option (optional)
+@return                        Boolean whether operation succeeded
+]]
+
+---[[
+Call a function for every section of a certain type.
+
+@class function
+@name Cursor.foreach
+@param config  UCI config
+@param type            UCI section type
+@param callback        Function to be called
+@return                        Boolean whether operation succeeded
+]]
+
+---[[
+Get a section type or an option
+
+@class function
+@name Cursor.get
+@param config  UCI config
+@param section UCI section name
+@param option  UCI option (optional)
+@return                        UCI value
+]]
+
+---[[
+Get all sections of a config or all values of a section.
+
+@class function
+@name Cursor.get_all
+@param config  UCI config
+@param section UCI section name (optional)
+@return                        Table of UCI sections or table of UCI values
+]]
+
+---[[
+Manually load a config.
+
+@class function
+@name Cursor.load
+@param config  UCI config
+@return                        Boolean whether operation succeeded
+@see Cursor.save
+@see Cursor.unload
+]]
+
+---[[
+Revert saved but uncommitted changes.
+
+@class function
+@name Cursor.revert
+@param config  UCI config
+@return                        Boolean whether operation succeeded
+@see Cursor.commit
+@see Cursor.save
+]]
+
+---[[
+Saves changes made to a config to make them committable.
+
+@class function
+@name Cursor.save
+@param config  UCI config
+@return                        Boolean whether operation succeeded
+@see Cursor.load
+@see Cursor.unload
+]]
+
+---[[
+Set a value or create a named section.
+
+@class function
+@name Cursor.set
+@param config  UCI config
+@param section UCI section name
+@param option  UCI option or UCI section type
+@param value           UCI value or nil if you want to create a section
+@return                        Boolean whether operation succeeded
+]]
+
+---[[
+Get the configuration directory.
+
+@class function
+@name Cursor.get_confdir
+@return                        Configuration directory
+]]
+
+---[[
+Get the directory for uncomitted changes.
+
+@class function
+@name Cursor.get_savedir
+@return                        Save directory
+]]
+
+---[[
+Set the configuration directory.
+
+@class function
+@name Cursor.set_confdir
+@param directory       UCI configuration directory
+@return                        Boolean whether operation succeeded
+]]
+
+---[[
+Set the directory for uncommited changes.
+
+@class function
+@name Cursor.set_savedir
+@param directory       UCI changes directory
+@return                        Boolean whether operation succeeded
+]]
+
+---[[
+Discard changes made to a config.
+
+@class function
+@name Cursor.unload
+@param config  UCI config
+@return                        Boolean whether operation succeeded
+@see Cursor.load
+@see Cursor.save
+]]
+
index 1e594e1..3977da3 100644 (file)
@@ -16,27 +16,14 @@ local tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
        tonumber, ipairs, pairs, pcall, type, next, setmetatable, require, select
 
 
---- LuCI Linux and POSIX system utilities.
 module "luci.sys"
 
---- Execute a given shell command and return the error code
--- @class              function
--- @name               call
--- @param              ...             Command to call
--- @return             Error code of the command
 function call(...)
        return os.execute(...) / 256
 end
 
---- Execute a given shell command and capture its standard output
--- @class              function
--- @name               exec
--- @param command      Command to call
--- @return                     String containg the return the output of the command
 exec = luci.util.exec
 
---- Retrieve information about currently mounted file systems.
--- @return     Table containing mount information
 function mounts()
        local data = {}
        local k = {"fs", "blocks", "used", "available", "percent", "mountpoint"}
@@ -82,20 +69,11 @@ function mounts()
        return data
 end
 
---- Retrieve environment variables. If no variable is given then a table
 -- containing the whole environment is returned otherwise this function returns
 -- the corresponding string value for the given name or nil if no such variable
 -- exists.
--- @class              function
--- @name               getenv
--- @param var  Name of the environment variable to retrieve (optional)
--- @return             String containg the value of the specified variable
--- @return             Table containing all variables if no variable name is given
 getenv = nixio.getenv
 
---- Get or set the current hostname.
--- @param              String containing a new hostname to set (optional)
--- @return             String containing the system hostname
 function hostname(newname)
        if type(newname) == "string" and #newname > 0 then
                fs.writefile( "/proc/sys/kernel/hostname", newname )
@@ -105,11 +83,6 @@ function hostname(newname)
        end
 end
 
---- Returns the contents of a documented referred by an URL.
--- @param url   The URL to retrieve
--- @param stream Return a stream instead of a buffer
--- @param target Directly write to target file name
--- @return             String containing the contents of given the URL
 function httpget(url, stream, target)
        if not target then
                local source = stream and io.popen or luci.util.exec
@@ -120,46 +93,30 @@ function httpget(url, stream, target)
        end
 end
 
---- Initiate a system reboot.
--- @return     Return value of os.execute()
 function reboot()
        return os.execute("reboot >/dev/null 2>&1")
 end
 
---- Retrieves the output of the "logread" command.
--- @return     String containing the current log buffer
 function syslog()
        return luci.util.exec("logread")
 end
 
---- Retrieves the output of the "dmesg" command.
--- @return     String containing the current log buffer
 function dmesg()
        return luci.util.exec("dmesg")
 end
 
---- Generates a random id with specified length.
--- @param bytes        Number of bytes for the unique id
--- @return             String containing hex encoded id
 function uniqueid(bytes)
        local rand = fs.readfile("/dev/urandom", bytes)
        return rand and nixio.bin.hexlify(rand)
 end
 
---- Returns the current system uptime stats.
--- @return     String containing total uptime in seconds
 function uptime()
        return nixio.sysinfo().uptime
 end
 
 
---- LuCI system utilities / network related functions.
--- @class      module
--- @name       luci.sys.net
 net = {}
 
---- Returns the current arp-table entries as two-dimensional table.
--- @return     Table of table containing the current arp entries.
 --                     The following fields are defined for arp entry objects:
 --                     { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
 function net.arptable(callback)
@@ -269,8 +226,6 @@ local function _nethints(what, callback)
        end
 end
 
---- Returns a two-dimensional table of mac address hints.
--- @return  Table of table containing known hosts from various sources.
 --          Each entry contains the values in the following order:
 --          [ "mac", "name" ]
 function net.mac_hints(callback)
@@ -293,8 +248,6 @@ function net.mac_hints(callback)
        end
 end
 
---- Returns a two-dimensional table of IPv4 address hints.
--- @return  Table of table containing known hosts from various sources.
 --          Each entry contains the values in the following order:
 --          [ "ip", "name" ]
 function net.ipv4_hints(callback)
@@ -317,8 +270,6 @@ function net.ipv4_hints(callback)
        end
 end
 
---- Returns a two-dimensional table of IPv6 address hints.
--- @return  Table of table containing known hosts from various sources.
 --          Each entry contains the values in the following order:
 --          [ "ip", "name" ]
 function net.ipv6_hints(callback)
@@ -341,8 +292,6 @@ function net.ipv6_hints(callback)
        end
 end
 
---- Returns conntrack information
--- @return     Table with the currently tracked IP connections
 function net.conntrack(callback)
        local connt = {}
        if fs.access("/proc/net/nf_conntrack", "r") then
@@ -387,57 +336,6 @@ function net.conntrack(callback)
        return connt
 end
 
---- Determine the current IPv4 default route. If multiple default routes exist,
--- return the one with the lowest metric.
--- @return     Table with the properties of the current default route.
---                     The following fields are defined:
---                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
---                       "flags", "device" }
-function net.defaultroute()
-       local route
-
-       net.routes(function(rt)
-               if rt.dest:prefix() == 0 and (not route or route.metric > rt.metric) then
-                       route = rt
-               end
-       end)
-
-       return route
-end
-
---- Determine the current IPv6 default route. If multiple default routes exist,
--- return the one with the lowest metric.
--- @return     Table with the properties of the current default route.
---                     The following fields are defined:
---                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
---                       "flags", "device" }
-function net.defaultroute6()
-       local route
-
-       net.routes6(function(rt)
-               if rt.dest:prefix() == 0 and rt.device ~= "lo" and
-                  (not route or route.metric > rt.metric)
-               then
-                       route = rt
-               end
-       end)
-
-       if not route then
-               local global_unicast = luci.ip.IPv6("2000::/3")
-               net.routes6(function(rt)
-                       if rt.dest:equal(global_unicast) and
-                          (not route or route.metric > rt.metric)
-                       then
-                               route = rt
-                       end
-               end)
-       end
-
-       return route
-end
-
---- Determine the names of available network interfaces.
--- @return     Table containing all current interface names
 function net.devices()
        local devs = {}
        for k, v in ipairs(nixio.getifaddrs()) do
@@ -449,8 +347,6 @@ function net.devices()
 end
 
 
---- Return information about available network interfaces.
--- @return     Table containing all current interface names and their information
 function net.deviceinfo()
        local devs = {}
        for k, v in ipairs(nixio.getifaddrs()) do
@@ -479,21 +375,6 @@ function net.deviceinfo()
 end
 
 
--- Determine the MAC address belonging to the given IP address.
--- @param ip   IPv4 address
--- @return             String containing the MAC address or nil if it cannot be found
-function net.ip4mac(ip)
-       local mac = nil
-       net.arptable(function(e)
-               if e["IP address"] == ip then
-                       mac = e["HW address"]
-               end
-       end)
-       return mac
-end
-
---- Returns the current kernel routing table entries.
--- @return     Table of tables with properties of the corresponding routes.
 --                     The following fields are defined for route entry tables:
 --                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
 --                       "flags", "device" }
@@ -539,8 +420,6 @@ function net.routes(callback)
        return routes
 end
 
---- Returns the current ipv6 kernel routing table entries.
--- @return     Table of tables with properties of the corresponding routes.
 --                     The following fields are defined for route entry tables:
 --                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
 --                       "flags", "device" }
@@ -602,30 +481,18 @@ function net.routes6(callback)
        end
 end
 
---- Tests whether the given host responds to ping probes.
--- @param host String containing a hostname or IPv4 address
--- @return             Number containing 0 on success and >= 1 on error
 function net.pingtest(host)
        return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1")
 end
 
 
---- LuCI system utilities / process related functions.
--- @class      module
--- @name       luci.sys.process
 process = {}
 
---- Get the current process id.
--- @class function
--- @name  process.info
--- @return     Number containing the current pid
 function process.info(key)
        local s = {uid = nixio.getuid(), gid = nixio.getgid()}
        return not key and s or s[key]
 end
 
---- Retrieve information about currently running processes.
--- @return     Table containing process information
 function process.list()
        local data = {}
        local k
@@ -658,51 +525,22 @@ function process.list()
        return data
 end
 
---- Set the gid of a process identified by given pid.
--- @param gid  Number containing the Unix group id
--- @return             Boolean indicating successful operation
--- @return             String containing the error message if failed
--- @return             Number containing the error code if failed
 function process.setgroup(gid)
        return nixio.setgid(gid)
 end
 
---- Set the uid of a process identified by given pid.
--- @param uid  Number containing the Unix user id
--- @return             Boolean indicating successful operation
--- @return             String containing the error message if failed
--- @return             Number containing the error code if failed
 function process.setuser(uid)
        return nixio.setuid(uid)
 end
 
---- Send a signal to a process identified by given pid.
--- @class function
--- @name  process.signal
--- @param pid  Number containing the process id
--- @param sig  Signal to send (default: 15 [SIGTERM])
--- @return             Boolean indicating successful operation
--- @return             Number containing the error code if failed
 process.signal = nixio.kill
 
 
---- LuCI system utilities / user related functions.
--- @class      module
--- @name       luci.sys.user
 user = {}
 
---- Retrieve user informations for given uid.
--- @class              function
--- @name               getuser
--- @param uid  Number containing the Unix user id
--- @return             Table containing the following fields:
 --                             { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
 user.getuser = nixio.getpw
 
---- Retrieve the current user password hash.
--- @param username     String containing the username to retrieve the password for
--- @return                     String containing the hash or nil if no password is set.
--- @return                     Password database entry
 function user.getpasswd(username)
        local pwe = nixio.getsp and nixio.getsp(username) or nixio.getpw(username)
        local pwh = pwe and (pwe.pwdp or pwe.passwd)
@@ -713,10 +551,6 @@ function user.getpasswd(username)
        end
 end
 
---- Test whether given string matches the password of a given system user.
--- @param username     String containing the Unix user name
--- @param pass         String containing the password to compare
--- @return                     Boolean indicating wheather the passwords are equal
 function user.checkpasswd(username, pass)
        local pwh, pwe = user.getpasswd(username)
        if pwe then
@@ -725,10 +559,6 @@ function user.checkpasswd(username, pass)
        return false
 end
 
---- Change the password of given user.
--- @param username     String containing the Unix user name
--- @param password     String containing the password to compare
--- @return                     Number containing 0 on success and >= 1 on error
 function user.setpasswd(username, password)
        if password then
                password = password:gsub("'", [['"'"']])
@@ -745,14 +575,8 @@ function user.setpasswd(username, password)
 end
 
 
---- LuCI system utilities / wifi related functions.
--- @class      module
--- @name       luci.sys.wifi
 wifi = {}
 
---- Get wireless information for given interface.
--- @param ifname        String containing the interface name
--- @return              A wrapped iwinfo object instance
 function wifi.getiwinfo(ifname)
        local stat, iwinfo = pcall(require, "iwinfo")
 
@@ -798,14 +622,9 @@ function wifi.getiwinfo(ifname)
 end
 
 
---- LuCI system utilities / init related functions.
--- @class      module
--- @name       luci.sys.init
 init = {}
 init.dir = "/etc/init.d/"
 
---- Get the names of all installed init scripts
--- @return     Table containing the names of all inistalled init scripts
 function init.names()
        local names = { }
        for name in fs.glob(init.dir.."*") do
@@ -814,9 +633,6 @@ function init.names()
        return names
 end
 
---- Get the index of he given init script
--- @param name Name of the init script
--- @return             Numeric index value
 function init.index(name)
        if fs.access(init.dir..name) then
                return call("env -i sh -c 'source %s%s enabled; exit ${START:-255}' >/dev/null"
@@ -830,37 +646,22 @@ local function init_action(action, name)
        end
 end
 
---- Test whether the given init script is enabled
--- @param name Name of the init script
--- @return             Boolean indicating whether init is enabled
 function init.enabled(name)
        return (init_action("enabled", name) == 0)
 end
 
---- Enable the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
 function init.enable(name)
        return (init_action("enable", name) == 1)
 end
 
---- Disable the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
 function init.disable(name)
        return (init_action("disable", name) == 0)
 end
 
---- Start the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
 function init.start(name)
        return (init_action("start", name) == 0)
 end
 
---- Stop the given init script
--- @param name Name of the init script
--- @return             Boolean indicating success
 function init.stop(name)
        return (init_action("stop", name) == 0)
 end
diff --git a/modules/luci-base/luasrc/sys.luadoc b/modules/luci-base/luasrc/sys.luadoc
new file mode 100644 (file)
index 0000000..72a16a1
--- /dev/null
@@ -0,0 +1,396 @@
+---[[
+LuCI Linux and POSIX system utilities.
+
+module "luci.sys"
+]]
+
+---[[
+Execute a given shell command and return the error code
+
+@class         function
+@name          call
+@param                 ...             Command to call
+@return                Error code of the command
+]]
+
+---[[
+Execute a given shell command and capture its standard output
+
+@class         function
+@name          exec
+@param command Command to call
+@return                        String containg the return the output of the command
+]]
+
+---[[
+Retrieve information about currently mounted file systems.
+
+@class function
+@name mounts
+@return        Table containing mount information
+]]
+
+---[[
+Retrieve environment variables. If no variable is given then a table
+
+containing the whole environment is returned otherwise this function returns
+the corresponding string value for the given name or nil if no such variable
+exists.
+@class         function
+@name          getenv
+@param var     Name of the environment variable to retrieve (optional)
+@return                String containg the value of the specified variable
+@return                Table containing all variables if no variable name is given
+]]
+
+---[[
+Get or set the current hostname.
+
+@class function
+@name hostname
+@param         String containing a new hostname to set (optional)
+@return                String containing the system hostname
+]]
+
+---[[
+Returns the contents of a documented referred by an URL.
+
+@class function
+@name httpget
+@param url      The URL to retrieve
+@param stream Return a stream instead of a buffer
+@param target Directly write to target file name
+@return                String containing the contents of given the URL
+]]
+
+---[[
+Initiate a system reboot.
+
+@class function
+@name reboot
+@return        Return value of os.execute()
+]]
+
+---[[
+Retrieves the output of the "logread" command.
+
+@class function
+@name syslog
+@return        String containing the current log buffer
+]]
+
+---[[
+Retrieves the output of the "dmesg" command.
+
+@class function
+@name dmesg
+@return        String containing the current log buffer
+]]
+
+---[[
+Generates a random id with specified length.
+
+@class function
+@name uniqueid
+@param bytes   Number of bytes for the unique id
+@return                String containing hex encoded id
+]]
+
+---[[
+Returns the current system uptime stats.
+
+@class function
+@name uptime
+@return        String containing total uptime in seconds
+]]
+
+---[[
+LuCI system utilities / network related functions.
+
+@class module
+@name  luci.sys.net
+]]
+
+---[[
+Returns the current arp-table entries as two-dimensional table.
+
+@class function
+@name net.arptable
+@return        Table of table containing the current arp entries.
+--                     The following fields are defined for arp entry objects:
+--                     { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" }
+]]
+
+---[[
+Returns a two-dimensional table of mac address hints.
+
+@class function
+@name net.mac_hints
+@return  Table of table containing known hosts from various sources.
+         Each entry contains the values in the following order:
+         [ "mac", "name" ]
+]]
+
+---[[
+Returns a two-dimensional table of IPv4 address hints.
+
+@class function
+@name net.ipv4_hints
+@return  Table of table containing known hosts from various sources.
+         Each entry contains the values in the following order:
+         [ "ip", "name" ]
+]]
+
+---[[
+Returns a two-dimensional table of IPv6 address hints.
+
+@class function
+@name net.ipv6_hints
+@return  Table of table containing known hosts from various sources.
+         Each entry contains the values in the following order:
+         [ "ip", "name" ]
+]]
+
+---[[
+Returns conntrack information
+
+@class function
+@name net.conntrack
+@return        Table with the currently tracked IP connections
+]]
+
+---[[
+Determine the names of available network interfaces.
+
+@class function
+@name net.devices
+@return        Table containing all current interface names
+]]
+
+---[[
+Return information about available network interfaces.
+
+@class function
+@name net.deviceinfo
+@return        Table containing all current interface names and their information
+]]
+
+---[[
+Returns the current kernel routing table entries.
+
+@class function
+@name net.routes
+@return        Table of tables with properties of the corresponding routes.
+--                     The following fields are defined for route entry tables:
+--                     { "dest", "gateway", "metric", "refcount", "usecount", "irtt",
+--                       "flags", "device" }
+]]
+
+---[[
+Returns the current ipv6 kernel routing table entries.
+
+@class function
+@name net.routes6
+@return        Table of tables with properties of the corresponding routes.
+--                     The following fields are defined for route entry tables:
+--                     { "source", "dest", "nexthop", "metric", "refcount", "usecount",
+--                       "flags", "device" }
+]]
+
+---[[
+Tests whether the given host responds to ping probes.
+
+@class function
+@name net.pingtest
+@param host    String containing a hostname or IPv4 address
+@return                Number containing 0 on success and >= 1 on error
+]]
+
+---[[
+LuCI system utilities / process related functions.
+
+@class module
+@name  luci.sys.process
+]]
+
+---[[
+Get the current process id.
+
+@class function
+@name  process.info
+@return        Number containing the current pid
+]]
+
+---[[
+Retrieve information about currently running processes.
+
+@class function
+@name process.list
+@return        Table containing process information
+]]
+
+---[[
+Set the gid of a process identified by given pid.
+
+@class function
+@name process.setgroup
+@param gid     Number containing the Unix group id
+@return                Boolean indicating successful operation
+@return                String containing the error message if failed
+@return                Number containing the error code if failed
+]]
+
+---[[
+Set the uid of a process identified by given pid.
+
+@class function
+@name process.setuser
+@param uid     Number containing the Unix user id
+@return                Boolean indicating successful operation
+@return                String containing the error message if failed
+@return                Number containing the error code if failed
+]]
+
+---[[
+Send a signal to a process identified by given pid.
+
+@class function
+@name  process.signal
+@param pid     Number containing the process id
+@param sig     Signal to send (default: 15 [SIGTERM])
+@return                Boolean indicating successful operation
+@return                Number containing the error code if failed
+]]
+
+---[[
+LuCI system utilities / user related functions.
+
+@class module
+@name  luci.sys.user
+]]
+
+---[[
+Retrieve user informations for given uid.
+
+@class         function
+@name          getuser
+@param uid     Number containing the Unix user id
+@return                Table containing the following fields:
+--                             { "uid", "gid", "name", "passwd", "dir", "shell", "gecos" }
+]]
+
+---[[
+Retrieve the current user password hash.
+
+@class function
+@name user.getpasswd
+@param username        String containing the username to retrieve the password for
+@return                        String containing the hash or nil if no password is set.
+@return                        Password database entry
+]]
+
+---[[
+Test whether given string matches the password of a given system user.
+
+@class function
+@name user.checkpasswd
+@param username        String containing the Unix user name
+@param pass            String containing the password to compare
+@return                        Boolean indicating wheather the passwords are equal
+]]
+
+---[[
+Change the password of given user.
+
+@class function
+@name user.setpasswd
+@param username        String containing the Unix user name
+@param password        String containing the password to compare
+@return                        Number containing 0 on success and >= 1 on error
+]]
+
+---[[
+LuCI system utilities / wifi related functions.
+
+@class module
+@name  luci.sys.wifi
+]]
+
+---[[
+Get wireless information for given interface.
+
+@class function
+@name wifi.getiwinfo
+@param ifname        String containing the interface name
+@return              A wrapped iwinfo object instance
+]]
+
+---[[
+LuCI system utilities / init related functions.
+
+@class module
+@name  luci.sys.init
+]]
+
+---[[
+Get the names of all installed init scripts
+
+@class function
+@name init.names
+@return        Table containing the names of all inistalled init scripts
+]]
+
+---[[
+Get the index of he given init script
+
+@class function
+@name init.index
+@param name    Name of the init script
+@return                Numeric index value
+]]
+
+---[[
+Test whether the given init script is enabled
+
+@class function
+@name init.enabled
+@param name    Name of the init script
+@return                Boolean indicating whether init is enabled
+]]
+
+---[[
+Enable the given init script
+
+@class function
+@name init.enable
+@param name    Name of the init script
+@return                Boolean indicating success
+]]
+
+---[[
+Disable the given init script
+
+@class function
+@name init.disable
+@param name    Name of the init script
+@return                Boolean indicating success
+]]
+
+---[[
+Start the given init script
+
+@class function
+@name init.start
+@param name    Name of the init script
+@return                Boolean indicating success
+]]
+
+---[[
+Stop the given init script
+
+@class function
+@name init.stop
+@param name    Name of the init script
+@return                Boolean indicating success
+]]
+
index 6715937..2b81e0e 100644 (file)
@@ -21,15 +21,8 @@ luci.ip     = require "luci.ip"
 
 local tonumber, ipairs, table = tonumber, ipairs, table
 
---- LuCI iptables parser and query library
--- @cstyle     instance
 module("luci.sys.iptparser")
 
---- Create a new iptables parser object.
--- @class      function
--- @name       IptParser
--- @param      family  Number specifying the address family. 4 for IPv4, 6 for IPv6
--- @return     IptParser instance
 IptParser = luci.util.class()
 
 function IptParser.__init__( self, family )
@@ -50,7 +43,6 @@ function IptParser.__init__( self, family )
        self:_parse_rules()
 end
 
---- Find all firewall rules that match the given criteria. Expects a table with
 -- search criteria as only argument. If args is nil or an empty table then all
 -- rules will be returned.
 --
@@ -108,8 +100,6 @@ end
 -- This will match all rules with target "-j REJECT",
 -- protocol "-p tcp" (or "-p all")
 -- and the option "--reject-with tcp-reset".
--- @params args                Table containing the search arguments (optional)
--- @return                     Table of matching rule tables
 function IptParser.find( self, args )
 
        local args = args or { }
@@ -205,9 +195,7 @@ function IptParser.find( self, args )
 end
 
 
---- Rebuild the internal lookup table, for example when rules have changed
 -- through external commands.
--- @return     nothing
 function IptParser.resync( self )
        self._rules = { }
        self._chain = nil
@@ -215,16 +203,11 @@ function IptParser.resync( self )
 end
 
 
---- Find the names of all tables.
--- @return             Table of table names.
 function IptParser.tables( self )
        return self._tables
 end
 
 
---- Find the names of all chains within the given table name.
--- @param table        String containing the table name
--- @return             Table of chain names in the order they occur.
 function IptParser.chains( self, table )
        local lookup = { }
        local chains = { }
@@ -238,19 +221,12 @@ function IptParser.chains( self, table )
 end
 
 
---- Return the given firewall chain within the given table name.
--- @param table        String containing the table name
--- @param chain        String containing the chain name
--- @return             Table containing the fields "policy", "packets", "bytes"
 --                             and "rules". The "rules" field is a table of rule tables.
 function IptParser.chain( self, table, chain )
        return self._chains[table:lower()] and self._chains[table:lower()][chain]
 end
 
 
---- Test whether the given target points to a custom chain.
--- @param target       String containing the target action
--- @return                     Boolean indicating whether target is a custom chain.
 function IptParser.is_custom_target( self, target )
        for _, r in ipairs(self._rules) do
                if r.chain == target then
diff --git a/modules/luci-base/luasrc/sys/iptparser.luadoc b/modules/luci-base/luasrc/sys/iptparser.luadoc
new file mode 100644 (file)
index 0000000..071e7d5
--- /dev/null
@@ -0,0 +1,69 @@
+---[[
+LuCI iptables parser and query library
+
+@cstyle        instance
+]]
+module "luci.sys.iptparser"
+
+---[[
+Create a new iptables parser object.
+
+@class function
+@name  IptParser
+@param family  Number specifying the address family. 4 for IPv4, 6 for IPv6
+@return        IptParser instance
+]]
+
+---[[
+Find all firewall rules that match the given criteria. Expects a table with
+
+search criteria as only argument. If args is nil or an empty table then all
+rules will be returned.
+]]
+
+---[[
+Rebuild the internal lookup table, for example when rules have changed
+
+through external commands.
+@class function
+@name IptParser.resync
+@return        nothing
+]]
+
+---[[
+Find the names of all tables.
+
+@class function
+@name IptParser.tables
+@return                Table of table names.
+]]
+
+---[[
+Find the names of all chains within the given table name.
+
+@class function
+@name IptParser.chains
+@param table   String containing the table name
+@return                Table of chain names in the order they occur.
+]]
+
+---[[
+Return the given firewall chain within the given table name.
+
+@class function
+@name IptParser.chain
+@param table   String containing the table name
+@param chain   String containing the chain name
+@return                Table containing the fields "policy", "packets", "bytes"
+--                             and "rules". The "rules" field is a table of rule tables.
+]]
+
+---[[
+Test whether the given target points to a custom chain.
+
+@class function
+@name IptParser.is_custom_target
+@param target  String containing the target action
+@return                        Boolean indicating whether target is a custom chain.
+]]
+
index 6337496..97a3608 100644 (file)
@@ -86,7 +86,7 @@ TZ = {
        { 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' },
        { 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' },
        { 'America/Campo Grande', 'AMT4AMST,M10.3.0/0,M2.3.0/0' },
-       { 'America/Cancun', 'CST6CDT,M4.1.0,M10.5.0' },
+       { 'America/Cancun', 'EST5' },
        { 'America/Caracas', 'VET4:30' },
        { 'America/Cayenne', 'GFT3' },
        { 'America/Cayman', 'EST5' },
@@ -178,7 +178,7 @@ TZ = {
        { 'America/Rio Branco', 'ACT5' },
        { 'America/Santa Isabel', 'PST8PDT,M4.1.0,M10.5.0' },
        { 'America/Santarem', 'BRT3' },
-       { 'America/Santiago', 'CLT4CLST,M9.1.6/24,M4.4.6/24' },
+       { 'America/Santiago', 'CLT3' },
        { 'America/Santo Domingo', 'AST4' },
        { 'America/Sao Paulo', 'BRT3BRST,M10.3.0/0,M2.3.0/0' },
        { 'America/Scoresbysund', 'EGT1EGST,M3.5.0/0,M10.5.0/1' },
@@ -207,7 +207,7 @@ TZ = {
        { 'Antarctica/Macquarie', 'MIST-11' },
        { 'Antarctica/Mawson', 'MAWT-5' },
        { 'Antarctica/McMurdo', 'NZST-12NZDT,M9.5.0,M4.1.0/3' },
-       { 'Antarctica/Palmer', 'CLT4CLST,M9.1.6/24,M4.4.6/24' },
+       { 'Antarctica/Palmer', 'CLT3' },
        { 'Antarctica/Rothera', 'ROTT3' },
        { 'Antarctica/Syowa', 'SYOT-3' },
        { 'Antarctica/Troll', 'UTC0CEST-2,M3.5.0/1,M10.5.0/3' },
@@ -384,7 +384,7 @@ TZ = {
        { 'Pacific/Bougainville', 'BST-11' },
        { 'Pacific/Chatham', 'CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45' },
        { 'Pacific/Chuuk', 'CHUT-10' },
-       { 'Pacific/Easter', 'EAST6EASST,M9.1.6/22,M4.4.6/22' },
+       { 'Pacific/Easter', 'EAST5' },
        { 'Pacific/Efate', 'VUT-11' },
        { 'Pacific/Enderbury', 'PHOT-13' },
        { 'Pacific/Fakaofo', 'TKT-13' },
index facfd0c..c8c908b 100644 (file)
@@ -45,8 +45,7 @@ OFFSET = {
        uyst  =  -7200, -- UYST
        fnt   =  -7200, -- FNT
        srt   = -10800, -- SRT
-       clt   = -14400, -- CLT
-       clst  = -10800, -- CLST
+       clt   = -10800, -- CLT
        egt   =  -3600, -- EGT
        egst  =      0, -- EGST
        nst   = -12600, -- NST
@@ -135,8 +134,7 @@ OFFSET = {
        chast =  45900, -- CHAST
        chadt =  49500, -- CHADT
        chut  =  36000, -- CHUT
-       east  = -21600, -- EAST
-       easst = -18000, -- EASST
+       east  = -18000, -- EAST
        vut   =  39600, -- VUT
        phot  =  46800, -- PHOT
        tkt   =  46800, -- TKT
index 9adac29..8273175 100644 (file)
@@ -1,11 +1,12 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
+-- Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
 module("luci.tools.webadmin", package.seeall)
-local uci = require("luci.model.uci")
-require("luci.sys")
-require("luci.ip")
+
+local util = require "luci.util"
+local uci  = require "luci.model.uci"
+local ip   = require "luci.ip"
 
 function byte_format(byte)
        local suff = {"B", "KB", "MB", "GB", "TB"}
@@ -47,49 +48,6 @@ function date_format(secs)
        end
 end
 
-function network_get_addresses(net)
-       local state = uci.cursor_state()
-       state:load("network")
-       local addr = {}
-       local ipv4 = state:get("network", net, "ipaddr")
-       local mav4 = state:get("network", net, "netmask")
-       local ipv6 = state:get("network", net, "ip6addr")
-       
-       if ipv4 and #ipv4 > 0 then
-               if mav4 and #mav4 == 0 then mav4 = nil end
-
-               ipv4 = luci.ip.IPv4(ipv4, mav4)
-               
-               if ipv4 then 
-                       table.insert(addr, ipv4:string())
-               end
-       end
-
-       if ipv6 then
-               table.insert(addr, ipv6)
-       end
-       
-       state:foreach("network", "alias",
-               function (section)
-                       if section.interface == net then
-                               if section.ipaddr and section.netmask then
-                                       local ipv4 = luci.ip.IPv4(section.ipaddr, section.netmask)
-                                       
-                                       if ipv4 then
-                                               table.insert(addr, ipv4:string())
-                                       end
-                               end
-                               
-                               if section.ip6addr then
-                                       table.insert(addr, section.ip6addr)
-                               end
-                       end
-               end
-       )
-       
-       return addr
-end
-
 function cbi_add_networks(field)
        uci.cursor():foreach("network", "interface",
                function (section)
@@ -102,29 +60,12 @@ function cbi_add_networks(field)
 end
 
 function cbi_add_knownips(field)
-       for i, dataset in ipairs(luci.sys.net.arptable()) do
-               field:value(dataset["IP address"])
-       end
-end
-
-function network_get_zones(net)
-       local state = uci.cursor_state()
-       if not state:load("firewall") then
-               return nil
-       end
-       
-       local zones = {}
-       
-       state:foreach("firewall", "zone", 
-               function (section)
-                       local znet = section.network or section.name
-                       if luci.util.contains(luci.util.split(znet, " "), net) then
-                               table.insert(zones, section.name)
-                       end
+       local _, n
+       for _, n in ipairs(ip.neighbors({ family = 4 })) do
+               if n.dest then
+                       field:value(n.dest:string())
                end
-       )
-       
-       return zones
+       end
 end
 
 function firewall_find_zone(name)
@@ -142,21 +83,23 @@ function firewall_find_zone(name)
 end
 
 function iface_get_network(iface)
-       local state = uci.cursor_state()
-       state:load("network")
-       local net
-       
-       state:foreach("network", "interface",
-               function (section)
-                       local ifname = state:get(
-                               "network", section[".name"], "ifname"
-                       )
-                       
-                       if iface == ifname then
-                               net = section[".name"]
+       local link = ip.link(tostring(iface))
+       if link.master then
+               iface = link.master
+       end
+
+       local cur = uci.cursor()
+       local dump = util.ubus("network.interface", "dump", { })
+       if dump then
+               local _, net
+               for _, net in ipairs(dump.interface) do
+                       if net.l3_device == iface or net.device == iface then
+                               -- cross check with uci to filter out @name style aliases
+                               local uciname = cur:get("network", net.interface, "ifname")
+                               if not uciname or uciname:sub(1, 1) ~= "@" then
+                                       return net.interface
+                               end
                        end
                end
-       )
-       
-       return net
+       end
 end
index 42de3dd..8b28b17 100644 (file)
@@ -20,7 +20,6 @@ local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
 local require, pcall, xpcall = require, pcall, xpcall
 local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
 
---- LuCI utility functions.
 module "luci.util"
 
 --
@@ -54,7 +53,6 @@ local function _instantiate(class, ...)
        return inst
 end
 
---- Create a Class object (Python-style object model).
 -- The class object can be instantiated by calling itself.
 -- Any class functions or shared parameters can be attached to this object.
 -- Attaching a table to the class object makes this table shared between
@@ -64,10 +62,6 @@ end
 -- to the __init__ function of this class - if such a function exists.
 -- The __init__ function must be used to set any object parameters that are not shared
 -- with other objects of this class. Any return values will be ignored.
--- @param base The base class to inherit from (optional)
--- @return             A class object
--- @see                        instanceof
--- @see                        clone
 function class(base)
        return setmetatable({}, {
                __call  = _instantiate,
@@ -75,12 +69,6 @@ function class(base)
        })
 end
 
---- Test whether the given object is an instance of the given class.
--- @param object       Object instance
--- @param class                Class object to test against
--- @return                     Boolean indicating whether the object is an instance
--- @see                                class
--- @see                                clone
 function instanceof(object, class)
        local meta = getmetatable(object)
        while meta and meta.__index do
@@ -117,10 +105,8 @@ local tl_meta = {
        end
 }
 
---- Create a new or get an already existing thread local store associated with
 -- the current active coroutine. A thread local store is private a table object
 -- whose values can't be accessed from outside of the running coroutine.
--- @return     Table value representing the corresponding thread local store
 function threadlocal(tbl)
        return setmetatable(tbl or {}, tl_meta)
 end
@@ -130,17 +116,10 @@ end
 -- Debugging routines
 --
 
---- Write given object to stderr.
--- @param obj  Value to write to stderr
--- @return             Boolean indicating whether the write operation was successful
 function perror(obj)
        return io.stderr:write(tostring(obj) .. "\n")
 end
 
---- Recursively dumps a table to stdout, useful for testing and debugging.
--- @param t    Table value to dump
--- @param maxdepth     Maximum depth
--- @return     Always nil
 function dumptable(t, maxdepth, i, seen)
        i = i or 0
        seen = seen or setmetatable({}, {__mode="k"})
@@ -163,31 +142,19 @@ end
 -- String and data manipulation routines
 --
 
---- Create valid XML PCDATA from given string.
--- @param value        String value containing the data to escape
--- @return             String value containing the escaped data
 function pcdata(value)
        return value and tparser.pcdata(tostring(value))
 end
 
---- Strip HTML tags from given string.
--- @param value        String containing the HTML text
--- @return     String with HTML tags stripped of
 function striptags(value)
        return value and tparser.striptags(tostring(value))
 end
 
---- Splits given string on a defined separator sequence and return a table
 -- containing the resulting substrings. The optional max parameter specifies
 -- the number of bytes to process, regardless of the actual length of the given
 -- string. The optional last parameter, regex, specifies whether the separator
 -- sequence is interpreted as regular expression.
--- @param str          String value containing the data to split up
--- @param pat          String with separator pattern (optional, defaults to "\n")
--- @param max          Maximum times to split (optional)
--- @param regex        Boolean indicating whether to interpret the separator
 --                                     pattern as regular expression (optional, default is false)
--- @return                     Table containing the resulting substrings
 function split(str, pat, max, regex)
        pat = pat or "\n"
        max = max or #str
@@ -221,29 +188,19 @@ function split(str, pat, max, regex)
        return t
 end
 
---- Remove leading and trailing whitespace from given string value.
--- @param str  String value containing whitespace padded data
--- @return             String value with leading and trailing space removed
 function trim(str)
        return (str:gsub("^%s*(.-)%s*$", "%1"))
 end
 
---- Count the occurences of given substring in given string.
--- @param str          String to search in
--- @param pattern      String containing pattern to find
--- @return                     Number of found occurences
 function cmatch(str, pat)
        local count = 0
        for _ in str:gmatch(pat) do count = count + 1 end
        return count
 end
 
---- Return a matching iterator for the given value. The iterator will return
 -- one token per invocation, the tokens are separated by whitespace. If the
 -- input value is a table, it is transformed into a string first. A nil value
 -- will result in a valid interator which aborts with the first invocation.
--- @param val          The value to scan (table, string or nil)
--- @return                     Iterator which returns one token per call
 function imatch(v)
        if type(v) == "table" then
                local k = nil
@@ -268,7 +225,6 @@ function imatch(v)
        return function() end
 end
 
---- Parse certain units from the given string and return the canonical integer
 -- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
 -- Recognized units are:
 --     o "y"   - one year   (60*60*24*366)
@@ -283,8 +239,6 @@ end
 --  o "kib" - one si kilobyte (1000)
 --  o "mib"    - one si megabyte (1000*1000)
 --  o "gib"    - one si gigabyte (1000*1000*1000)
--- @param ustr String containing a numerical value with trailing unit
--- @return             Number containing the canonical value
 function parse_units(ustr)
 
        local val = 0
@@ -336,10 +290,6 @@ string.cmatch      = cmatch
 string.parse_units = parse_units
 
 
---- Appends numerically indexed tables or single objects to a given table.
--- @param src  Target table
--- @param ...  Objects to insert
--- @return             Target table
 function append(src, ...)
        for i, a in ipairs({...}) do
                if type(a) == "table" then
@@ -353,19 +303,10 @@ function append(src, ...)
        return src
 end
 
---- Combines two or more numerically indexed tables and single objects into one table.
--- @param tbl1 Table value to combine
--- @param tbl2 Table value to combine
--- @param ...  More tables to combine
--- @return             Table value containing all values of given tables
 function combine(...)
        return append({}, ...)
 end
 
---- Checks whether the given table contains the given value.
--- @param table        Table value
--- @param value        Value to search within the given table
--- @return             Boolean indicating whether the given value occurs within table
 function contains(table, value)
        for k, v in pairs(table) do
                if value == v then
@@ -375,20 +316,13 @@ function contains(table, value)
        return false
 end
 
---- Update values in given table with the values from the second given table.
 -- Both table are - in fact - merged together.
--- @param t                    Table which should be updated
--- @param updates      Table containing the values to update
--- @return                     Always nil
 function update(t, updates)
        for k, v in pairs(updates) do
                t[k] = v
        end
 end
 
---- Retrieve all keys of given associative table.
--- @param t    Table to extract keys from
--- @return     Sorted table containing the keys
 function keys(t)
        local keys = { }
        if t then
@@ -399,10 +333,6 @@ function keys(t)
        return keys
 end
 
---- Clones the given object and return it's copy.
--- @param object       Table value to clone
--- @param deep         Boolean indicating whether to do recursive cloning
--- @return                     Cloned table value
 function clone(object, deep)
        local copy = {}
 
@@ -417,8 +347,6 @@ function clone(object, deep)
 end
 
 
---- Create a dynamic table which automatically creates subtables.
--- @return     Dynamic Table
 function dtable()
         return setmetatable({}, { __index =
                 function(tbl, key)
@@ -457,12 +385,7 @@ function _serialize_table(t, seen)
        return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
 end
 
---- Recursively serialize given data to lua code, suitable for restoring
 -- with loadstring().
--- @param val  Value containing the data to serialize
--- @return             String value containing the serialized code
--- @see                        restore_data
--- @see                        get_bytecode
 function serialize_data(val, seen)
        seen = seen or setmetatable({}, {__mode="k"})
 
@@ -483,11 +406,6 @@ function serialize_data(val, seen)
        end
 end
 
---- Restore data previously serialized with serialize_data().
--- @param str  String containing the data to restore
--- @return             Value containing the restored data structure
--- @see                        serialize_data
--- @see                        get_bytecode
 function restore_data(str)
        return loadstring("return " .. str)()
 end
@@ -497,10 +415,7 @@ end
 -- Byte code manipulation routines
 --
 
---- Return the current runtime bytecode of the given data. The byte code
 -- will be stripped before it is returned.
--- @param val  Value to return as bytecode
--- @return             String value containing the bytecode of the given data
 function get_bytecode(val)
        local code
 
@@ -513,11 +428,8 @@ function get_bytecode(val)
        return code -- and strip_bytecode(code)
 end
 
---- Strips unnescessary lua bytecode from given string. Information like line
 -- numbers and debugging numbers will be discarded. Original version by
 -- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
--- @param code String value containing the original lua byte code
--- @return             String value containing the stripped lua byte code
 function strip_bytecode(code)
        local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
        local subint
@@ -607,27 +519,17 @@ function _sortiter( t, f )
        end
 end
 
---- Return a key, value iterator which returns the values sorted according to
 -- the provided callback function.
--- @param t    The table to iterate
--- @param f A callback function to decide the order of elements
--- @return     Function value containing the corresponding iterator
 function spairs(t,f)
        return _sortiter( t, f )
 end
 
---- Return a key, value iterator for the given table.
 -- The table pairs are sorted by key.
--- @param t    The table to iterate
--- @return     Function value containing the corresponding iterator
 function kspairs(t)
        return _sortiter( t )
 end
 
---- Return a key, value iterator for the given table.
 -- The table pairs are sorted by value.
--- @param t    The table to iterate
--- @return     Function value containing the corresponding iterator
 function vspairs(t)
        return _sortiter( t, function (a,b) return t[a] < t[b] end )
 end
@@ -637,15 +539,10 @@ end
 -- System utility functions
 --
 
---- Test whether the current system is operating in big endian mode.
--- @return     Boolean value indicating whether system is big endian
 function bigendian()
        return string.byte(string.dump(function() end), 7) == 0
 end
 
---- Execute given commandline and gather stdout.
--- @param command      String containing command to execute
--- @return                     String containing the command's stdout
 function exec(command)
        local pp   = io.popen(command)
        local data = pp:read("*a")
@@ -654,9 +551,6 @@ function exec(command)
        return data
 end
 
---- Return a line-buffered iterator over the output of given command.
--- @param command      String containing the command to execute
--- @return                     Iterator
 function execi(command)
        local pp = io.popen(command)
 
@@ -687,11 +581,6 @@ function execl(command)
        return data
 end
 
---- Issue an ubus call.
--- @param object               String containing the ubus object to call
--- @param method               String containing the ubus method to call
--- @param values               Table containing the values to pass
--- @return                     Table containin the ubus result
 function ubus(object, method, data)
        if not _ubus_connection then
                _ubus_connection = _ubus.connect()
@@ -710,8 +599,60 @@ function ubus(object, method, data)
        end
 end
 
---- Returns the absolute path to LuCI base directory.
--- @return             String containing the directory path
+function serialize_json(x, cb)
+       local rv, push = nil, cb
+       if not push then
+               rv = { }
+               push = function(tok) rv[#rv+1] = tok end
+       end
+
+       if x == nil then
+               push("null")
+       elseif type(x) == "table" then
+               -- test if table is array like
+               local k, v
+               local n1, n2 = 0, 0
+               for k in pairs(x) do n1 = n1 + 1 end
+               for k in ipairs(x) do n2 = n2 + 1 end
+
+               if n1 == n2 and n1 > 0 then
+                       push("[")
+                       for k = 1, n2 do
+                               if k > 1 then
+                                       push(",")
+                               end
+                               serialize_json(x[k], push)
+                       end
+                       push("]")
+               else
+                       push("{")
+                       for k, v in pairs(x) do
+                               push("%q:" % tostring(k))
+                               serialize_json(v, push)
+                               if next(x, k) then
+                                       push(",")
+                               end
+                       end
+                       push("}")
+               end
+       elseif type(x) == "number" or type(x) == "boolean" then
+               if (x ~= x) then
+                       -- NaN is the only value that doesn't equal to itself.
+                       push("Number.NaN")
+               else
+                       push(tostring(x))
+               end
+       else
+               push('"%s"' % tostring(x):gsub('["%z\1-\31]',
+                       function(c) return '\\u%04x' % c:byte(1) end))
+       end
+
+       if not cb then
+               return table.concat(rv, "")
+       end
+end
+
+
 function libpath()
        return require "nixio.fs".dirname(ldebug.__file__)
 end
@@ -751,11 +692,6 @@ local function copcall_id(trace, ...)
   return ...
 end
 
---- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
--- @param f            Lua function to be called protected
--- @param err  Custom error handler
--- @param ...  Parameters passed to the function
--- @return             A boolean whether the function call succeeded and the return
 --                             values of either the function or the error handler
 function coxpcall(f, err, ...)
        local res, co = oldpcall(coroutine.create, f)
@@ -770,10 +706,6 @@ function coxpcall(f, err, ...)
        return performResume(err, co, ...)
 end
 
---- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
--- @param f            Lua function to be called protected
--- @param ...  Parameters passed to the function
--- @return             A boolean whether the function call succeeded and the returns
 --                             values of the function or the error object
 function copcall(f, ...)
        return coxpcall(f, copcall_id, ...)
diff --git a/modules/luci-base/luasrc/util.luadoc b/modules/luci-base/luasrc/util.luadoc
new file mode 100644 (file)
index 0000000..1c09b7a
--- /dev/null
@@ -0,0 +1,378 @@
+---[[
+LuCI utility functions.
+
+module "luci.util"
+]]
+
+---[[
+Create a Class object (Python-style object model).
+
+The class object can be instantiated by calling itself.
+Any class functions or shared parameters can be attached to this object.
+Attaching a table to the class object makes this table shared between
+all instances of this class. For object parameters use the __init__ function.
+Classes can inherit member functions and values from a base class.
+Class can be instantiated by calling them. All parameters will be passed
+to the __init__ function of this class - if such a function exists.
+The __init__ function must be used to set any object parameters that are not shared
+with other objects of this class. Any return values will be ignored.
+@class function
+@name class
+@param base    The base class to inherit from (optional)
+@return                A class object
+@see                   instanceof
+@see                   clone
+]]
+
+---[[
+Test whether the given object is an instance of the given class.
+
+@class function
+@name instanceof
+@param object  Object instance
+@param class           Class object to test against
+@return                        Boolean indicating whether the object is an instance
+@see                           class
+@see                           clone
+]]
+
+---[[
+Create a new or get an already existing thread local store associated with
+
+the current active coroutine. A thread local store is private a table object
+whose values can't be accessed from outside of the running coroutine.
+@class function
+@name threadlocal
+@return        Table value representing the corresponding thread local store
+]]
+
+---[[
+Write given object to stderr.
+
+@class function
+@name perror
+@param obj     Value to write to stderr
+@return                Boolean indicating whether the write operation was successful
+]]
+
+---[[
+Recursively dumps a table to stdout, useful for testing and debugging.
+
+@class function
+@name dumptable
+@param t       Table value to dump
+@param maxdepth        Maximum depth
+@return        Always nil
+]]
+
+---[[
+Create valid XML PCDATA from given string.
+
+@class function
+@name pcdata
+@param value   String value containing the data to escape
+@return                String value containing the escaped data
+]]
+
+---[[
+Strip HTML tags from given string.
+
+@class function
+@name striptags
+@param value   String containing the HTML text
+@return        String with HTML tags stripped of
+]]
+
+---[[
+Splits given string on a defined separator sequence and return a table
+
+containing the resulting substrings. The optional max parameter specifies
+the number of bytes to process, regardless of the actual length of the given
+string. The optional last parameter, regex, specifies whether the separator
+sequence is interpreted as regular expression.
+@class function
+@name split
+@param str             String value containing the data to split up
+@param pat             String with separator pattern (optional, defaults to "\n")
+@param max             Maximum times to split (optional)
+@param regex   Boolean indicating whether to interpret the separator
+--                                     pattern as regular expression (optional, default is false)
+@return                        Table containing the resulting substrings
+]]
+
+---[[
+Remove leading and trailing whitespace from given string value.
+
+@class function
+@name trim
+@param str     String value containing whitespace padded data
+@return                String value with leading and trailing space removed
+]]
+
+---[[
+Count the occurences of given substring in given string.
+
+@class function
+@name cmatch
+@param str             String to search in
+@param pattern String containing pattern to find
+@return                        Number of found occurences
+]]
+
+---[[
+Return a matching iterator for the given value. The iterator will return
+
+one token per invocation, the tokens are separated by whitespace. If the
+input value is a table, it is transformed into a string first. A nil value
+will result in a valid interator which aborts with the first invocation.
+@class function
+@name imatch
+@param val             The value to scan (table, string or nil)
+@return                        Iterator which returns one token per call
+]]
+
+---[[
+Parse certain units from the given string and return the canonical integer
+
+value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
+Recognized units are:
+--     o "y"   - one year   (60*60*24*366)
+ o "m" - one month  (60*60*24*31)
+ o "w" - one week   (60*60*24*7)
+ o "d" - one day    (60*60*24)
+ o "h" - one hour       (60*60)
+ o "min"       - one minute (60)
+ o "kb"  - one kilobyte (1024)
+ o "mb"        - one megabyte (1024*1024)
+ o "gb"        - one gigabyte (1024*1024*1024)
+ o "kib" - one si kilobyte (1000)
+ o "mib"       - one si megabyte (1000*1000)
+ o "gib"       - one si gigabyte (1000*1000*1000)
+@class function
+@name parse_units
+@param ustr    String containing a numerical value with trailing unit
+@return                Number containing the canonical value
+]]
+
+---[[
+Appends numerically indexed tables or single objects to a given table.
+
+@class function
+@name append
+@param src     Target table
+@param ...     Objects to insert
+@return                Target table
+]]
+
+---[[
+Combines two or more numerically indexed tables and single objects into one table.
+
+@class function
+@name combine
+@param tbl1    Table value to combine
+@param tbl2    Table value to combine
+@param ...     More tables to combine
+@return                Table value containing all values of given tables
+]]
+
+---[[
+Checks whether the given table contains the given value.
+
+@class function
+@name contains
+@param table   Table value
+@param value   Value to search within the given table
+@return                Boolean indicating whether the given value occurs within table
+]]
+
+---[[
+Update values in given table with the values from the second given table.
+
+Both table are - in fact - merged together.
+@class function
+@name update
+@param t                       Table which should be updated
+@param updates Table containing the values to update
+@return                        Always nil
+]]
+
+---[[
+Retrieve all keys of given associative table.
+
+@class function
+@name keys
+@param t       Table to extract keys from
+@return        Sorted table containing the keys
+]]
+
+---[[
+Clones the given object and return it's copy.
+
+@class function
+@name clone
+@param object  Table value to clone
+@param deep            Boolean indicating whether to do recursive cloning
+@return                        Cloned table value
+]]
+
+---[[
+Create a dynamic table which automatically creates subtables.
+
+@class function
+@name dtable
+@return        Dynamic Table
+]]
+
+---[[
+Recursively serialize given data to lua code, suitable for restoring
+
+with loadstring().
+@class function
+@name serialize_data
+@param val     Value containing the data to serialize
+@return                String value containing the serialized code
+@see                   restore_data
+@see                   get_bytecode
+]]
+
+---[[
+Restore data previously serialized with serialize_data().
+
+@class function
+@name restore_data
+@param str     String containing the data to restore
+@return                Value containing the restored data structure
+@see                   serialize_data
+@see                   get_bytecode
+]]
+
+---[[
+Return the current runtime bytecode of the given data. The byte code
+
+will be stripped before it is returned.
+@class function
+@name get_bytecode
+@param val     Value to return as bytecode
+@return                String value containing the bytecode of the given data
+]]
+
+---[[
+Strips unnescessary lua bytecode from given string. Information like line
+
+numbers and debugging numbers will be discarded. Original version by
+Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
+@class function
+@name strip_bytecode
+@param code    String value containing the original lua byte code
+@return                String value containing the stripped lua byte code
+]]
+
+---[[
+Return a key, value iterator which returns the values sorted according to
+
+the provided callback function.
+@class function
+@name spairs
+@param t       The table to iterate
+@param f A callback function to decide the order of elements
+@return        Function value containing the corresponding iterator
+]]
+
+---[[
+Return a key, value iterator for the given table.
+
+The table pairs are sorted by key.
+@class function
+@name kspairs
+@param t       The table to iterate
+@return        Function value containing the corresponding iterator
+]]
+
+---[[
+Return a key, value iterator for the given table.
+
+The table pairs are sorted by value.
+@class function
+@name vspairs
+@param t       The table to iterate
+@return        Function value containing the corresponding iterator
+]]
+
+---[[
+Test whether the current system is operating in big endian mode.
+
+@class function
+@name bigendian
+@return        Boolean value indicating whether system is big endian
+]]
+
+---[[
+Execute given commandline and gather stdout.
+
+@class function
+@name exec
+@param command String containing command to execute
+@return                        String containing the command's stdout
+]]
+
+---[[
+Return a line-buffered iterator over the output of given command.
+
+@class function
+@name execi
+@param command String containing the command to execute
+@return                        Iterator
+]]
+
+---[[
+Issue an ubus call.
+
+@class function
+@name ubus
+@param object          String containing the ubus object to call
+@param method          String containing the ubus method to call
+@param values          Table containing the values to pass
+@return                        Table containin the ubus result
+]]
+
+---[[
+Convert data structure to JSON
+
+@class function
+@name serialize_json
+@param data            The data to serialize
+@param writer  A function to write a chunk of JSON data (optional)
+@return                        String containing the JSON if called without write callback
+]]
+
+---[[
+Returns the absolute path to LuCI base directory.
+
+@class function
+@name libpath
+@return                String containing the directory path
+]]
+
+---[[
+This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
+
+@class function
+@name coxpcall
+@param f               Lua function to be called protected
+@param err     Custom error handler
+@param ...     Parameters passed to the function
+@return                A boolean whether the function call succeeded and the return
+--                             values of either the function or the error handler
+]]
+
+---[[
+This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
+
+@class function
+@name copcall
+@param f               Lua function to be called protected
+@param ...     Parameters passed to the function
+@return                A boolean whether the function call succeeded and the returns
+--                             values of the function or the error object
+]]
+
index 055142b..52e347d 100644 (file)
@@ -178,13 +178,7 @@ function action_flashops()
        local image_tmp   = "/tmp/firmware.img"
 
        local function image_supported()
-               -- XXX: yay...
-               return ( 0 == os.execute(
-                       ". /lib/functions.sh; " ..
-                       "include /lib/upgrade; " ..
-                       "platform_check_image %q >/dev/null"
-                               % image_tmp
-               ) )
+               return (os.execute("sysupgrade -T %q >/dev/null" % image_tmp) == 0)
        end
 
        local function image_checksum()
index 88e81bb..997a927 100644 (file)
@@ -1,7 +1,7 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
 -- Licensed to the public under the Apache License 2.0.
 
-local sys = require "luci.sys"
+local ipc = require "luci.ip"
 
 m = Map("dhcp", translate("DHCP and DNS"),
        translate("Dnsmasq is a combined <abbr title=\"Dynamic Host Configuration Protocol" ..
@@ -232,12 +232,11 @@ ip.datatype = "or(ip4addr,'ignore')"
 
 hostid = s:option(Value, "hostid", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Suffix (hex)"))
 
-sys.net.arptable(function(entry)
-       ip:value(entry["IP address"])
-       mac:value(
-               entry["HW address"],
-               entry["HW address"] .. " (" .. entry["IP address"] .. ")"
-       )
+ipc.neighbors({ family = 4 }, function(n)
+       if n.mac and n.dest then
+               ip:value(n.dest:string())
+               mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() })
+       end
 end)
 
 function ip.validate(self, value, section)
index da7c118..fafacf3 100644 (file)
@@ -1,9 +1,9 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
--- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
+-- Copyright 2010-2015 Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
-require("luci.sys")
-require("luci.util")
+local ipc = require "luci.ip"
+
 m = Map("dhcp", translate("Hostnames"))
 
 s = m:section(TypedSection, "domain", translate("Host entries"))
@@ -19,12 +19,10 @@ ip = s:option(Value, "ip", translate("IP address"))
 ip.datatype = "ipaddr"
 ip.rmempty  = true
 
-local arptable = luci.sys.net.arptable() or {}
-for i, dataset in ipairs(arptable) do
-       ip:value(
-               dataset["IP address"],
-               "%s (%s)" %{ dataset["IP address"], dataset["HW address"] }
-       )
-end
+ipc.neighbors({ }, function(n)
+       if n.mac and n.dest and not n.dest:is6linklocal() then
+               ip:value(n.dest:string(), "%s (%s)" %{ n.dest:string(), n.mac })
+       end
+end)
 
 return m
index eabb257..c2e5c72 100644 (file)
@@ -14,6 +14,7 @@ local has_dnsmasq  = fs.access("/etc/config/dhcp")
 local has_firewall = fs.access("/etc/config/firewall")
 
 m = Map("network", translate("Interfaces") .. " - " .. arg[1]:upper(), translate("On this page you can configure the network interfaces. You can bridge several interfaces by ticking the \"bridge interfaces\" field and enter the names of several network interfaces separated by spaces. You can also use <abbr title=\"Virtual Local Area Network\">VLAN</abbr> notation <samp>INTERFACE.VLANNR</samp> (<abbr title=\"for example\">e.g.</abbr>: <samp>eth0.1</samp>)."))
+m.redirect = luci.dispatcher.build_url("admin", "network", "network")
 m:chain("wireless")
 
 if has_firewall then
index 01580f1..ac02b15 100644 (file)
@@ -1,14 +1,14 @@
 -- Copyright 2008 Steven Barth <steven@midlink.org>
 -- Licensed to the public under the Apache License 2.0.
 
-require("luci.tools.webadmin")
+local wa = require "luci.tools.webadmin"
+local fs = require "nixio.fs"
+
 m = Map("network",
        translate("Routes"),
        translate("Routes specify over which interface and gateway a certain host or network " ..
                "can be reached."))
 
-local routes6 = luci.sys.net.routes6()
-
 s = m:section(TypedSection, "route", translate("Static IPv4 Routes"))
 s.addremove = true
 s.anonymous = true
@@ -16,7 +16,7 @@ s.anonymous = true
 s.template  = "cbi/tblsection"
 
 iface = s:option(ListValue, "interface", translate("Interface"))
-luci.tools.webadmin.cbi_add_networks(iface)
+wa.cbi_add_networks(iface)
 
 t = s:option(Value, "target", translate("Target"), translate("Host-<abbr title=\"Internet Protocol Address\">IP</abbr> or Network"))
 t.datatype = "ip4addr"
@@ -41,7 +41,7 @@ mtu.placeholder = 1500
 mtu.datatype = "range(64,9000)"
 mtu.rmempty = true
 
-if routes6 then
+if fs.access("/proc/net/ipv6_route") then
        s = m:section(TypedSection, "route6", translate("Static IPv6 Routes"))
        s.addremove = true
        s.anonymous = true
@@ -49,7 +49,7 @@ if routes6 then
        s.template  = "cbi/tblsection"
 
        iface = s:option(ListValue, "interface", translate("Interface"))
-       luci.tools.webadmin.cbi_add_networks(iface)
+       wa.cbi_add_networks(iface)
 
        t = s:option(Value, "target", translate("Target"), translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Address or Network (CIDR)"))
        t.datatype = "ip6addr"
index ea60a7f..b7c44f9 100644 (file)
@@ -1,12 +1,12 @@
 <%#
  Copyright 2008-2009 Steven Barth <steven@midlink.org>
- Copyright 2008-2013 Jo-Philipp Wich <jow@openwrt.org>
+ Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
  Licensed to the public under the Apache License 2.0.
 -%>
 
 <%-
 
-       local sys = require "luci.sys"
+       local ip = require "luci.ip"
        local fs = require "nixio.fs"
        local utl = require "luci.util"
        local uci = require "luci.model.uci".cursor()
@@ -90,7 +90,9 @@
 
        local devices  = ntm:get_wifidevs()
        local arpcache = { }
-       sys.net.arptable(function(e) arpcache[e["HW address"]:upper()] = e["IP address"] end)
+       ip.neighbors({ family = 4 }, function(n)
+               if n.mac and n.dest then arpcache[n.mac:upper()] = n.dest:string() end
+       end)
 
        local netlist = { }
        local netdevs = { }
index 2d9a4a3..82dd3a7 100644 (file)
@@ -1,15 +1,33 @@
 <%#
  Copyright 2008-2009 Steven Barth <steven@midlink.org>
- Copyright 2008-2009 Jo-Philipp Wich <jow@openwrt.org>
+ Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
  Licensed to the public under the Apache License 2.0.
 -%>
 
 <%-
-       require "luci.sys"
        require "luci.tools.webadmin"
        require "nixio.fs"
 
+       local ip = require "luci.ip"
        local style = true
+       local _, v
+
+       local rtn = {
+               [255] = "local",
+               [254] = "main",
+               [253] = "default",
+               [0]   = "unspec"
+       }
+
+       if nixio.fs.access("/etc/iproute2/rt_tables") then
+               local ln
+               for ln in io.lines("/etc/iproute2/rt_tables") do
+                       local i, n = ln:match("^(%d+)%s+(%S+)")
+                       if i and n then
+                               rtn[tonumber(i)] = n
+                       end
+               end
+       end
 -%>
 
 <%+header%>
@@ -18,7 +36,7 @@
        <h2><a id="content" name="content"><%:Routes%></a></h2>
        <div class="cbi-map-descr"><%:The following rules are currently active on this system.%></div>
 
-       <fieldset class="cbi-section" id="cbi-table-table">
+       <fieldset class="cbi-section">
                <legend>ARP</legend>
                <div class="cbi-section-node">
                        <table class="cbi-section-table">
                                        <th class="cbi-section-table-cell"><%:Interface%></th>
                                </tr>
 
-                               <% luci.sys.net.arptable(function(e) %>
+                               <%
+                                       for _, v in ipairs(ip.neighbors({ family = 4 })) do
+                                               if v.mac then
+                               %>
                                <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>">
-                                       <td class="cbi-value-field"><%=e["IP address"]%></td>
-                                       <td class="cbi-value-field"><%=e["HW address"]%></td>
-                                       <td class="cbi-value-field"><%=e["Device"]%></td>
+                                       <td class="cbi-value-field"><%=v.dest%></td>
+                                       <td class="cbi-value-field"><%=v.mac%></td>
+                                       <td class="cbi-value-field"><%=v.dev%></td>
                                </tr>
-                               <% style = not style; end) %>
+                               <%
+                                                       style = not style
+                                               end
+                                       end
+                               %>
                        </table>
                </div>
        </fieldset>
        <br />
 
-       <fieldset class="cbi-section" id="cbi-table-table">
+       <fieldset class="cbi-section">
                <legend><%_Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes%></legend>
 
                <div class="cbi-section-node">
                                        <th class="cbi-section-table-cell"><%:Target%></th>
                                        <th class="cbi-section-table-cell"><%_<abbr title="Internet Protocol Version 4">IPv4</abbr>-Gateway%></th>
                                        <th class="cbi-section-table-cell"><%:Metric%></th>
+                                       <th class="cbi-section-table-cell"><%:Table%></th>
                                </tr>
-                               <% luci.sys.net.routes(function(rt) %>
+                               <% for _, v in ipairs(ip.routes({ family = 4, type = 1 })) do %>
                                <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>">
-                                       <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(rt.device) or rt.device%></td>
-                                       <td class="cbi-value-field"><%=rt.dest:string()%></td>
-                                       <td class="cbi-value-field"><%=rt.gateway:string()%></td>
-                                       <td class="cbi-value-field"><%=rt.metric%></td>
+                                       <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(v.dev) or v.dev%></td>
+                                       <td class="cbi-value-field"><%=v.dest%></td>
+                                       <td class="cbi-value-field"><%=v.gw%></td>
+                                       <td class="cbi-value-field"><%=v.metric or 0%></td>
+                                       <td class="cbi-value-field"><%=rtn[v.table] or v.table%></td>
                                </tr>
-                               <% style = not style; end) %>
+                               <% style = not style end %>
                        </table>
                </div>
        </fieldset>
        <br />
 
-       <% if nixio.fs.access("/proc/net/ipv6_route") then
-               style = true
-               fe80 = luci.ip.IPv6("fe80::/10")
+       <%
+               if nixio.fs.access("/proc/net/ipv6_route") then
+                       style = true
        %>
-       <fieldset class="cbi-section" id="cbi-table-table">
+       <fieldset class="cbi-section">
                <legend><%_Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes%></legend>
 
                <div class="cbi-section-node">
                                <tr class="cbi-section-table-titles">
                                        <th class="cbi-section-table-cell"><%:Network%></th>
                                        <th class="cbi-section-table-cell"><%:Target%></th>
-                                       <th class="cbi-section-table-cell"><%_<abbr title="Internet Protocol Version 6">IPv6</abbr>-Gateway%></th>
+                                       <th class="cbi-section-table-cell"><%:Source%></th>
                                        <th class="cbi-section-table-cell"><%:Metric%></th>
+                                       <th class="cbi-section-table-cell"><%:Table%></th>
+                               </tr>
+                               <%
+                                       for _, v in ipairs(ip.routes({ family = 6, type = 1 })) do
+                                               if v.dest and not v.dest:is6linklocal() then
+                               %>
+                               <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>">
+                                       <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></td>
+                                       <td class="cbi-value-field"><%=v.dest%></td>
+                                       <td class="cbi-value-field"><%=v.from%></td>
+                                       <td class="cbi-value-field"><%=v.metric or 0%></td>
+                                       <td class="cbi-value-field"><%=rtn[v.table] or v.table%></td>
+                               </tr>
+                               <%
+                                                       style = not style
+                                               end
+                                       end
+                               %>
+                       </table>
+               </div>
+       </fieldset>
+       <br />
+
+       <fieldset class="cbi-section">
+               <legend><%:IPv6 Neighbours%></legend>
+
+               <div class="cbi-section-node">
+                       <table class="cbi-section-table">
+                               <tr class="cbi-section-table-titles">
+                                       <th class="cbi-section-table-cell"><%:IPv6-Address%></th>
+                                       <th class="cbi-section-table-cell"><%:MAC-Address%></th>
+                                       <th class="cbi-section-table-cell"><%:Interface%></th>
                                </tr>
-                               <% luci.sys.net.routes6(function(rt) if fe80:contains(rt.dest) then return end %>
+                               <%
+                                       for _, v in ipairs(ip.neighbors({ family = 6 })) do
+                                               if v.dest and not v.dest:is6linklocal() and v.mac then
+                               %>
                                <tr class="cbi-section-table-row cbi-rowstyle-<%=(style and 1 or 2)%>">
-                                       <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(rt.device) or '(' .. rt.device .. ')'%></td>
-                                       <td class="cbi-value-field"><%=rt.dest:string()%></td>
-                                       <td class="cbi-value-field"><%=rt.source:string()%></td>
-                                       <td class="cbi-value-field"><%=rt.metric_raw:upper()%></td>
+                                       <td class="cbi-value-field"><%=v.dest%></td>
+                                       <td class="cbi-value-field"><%=v.mac%></td>
+                                       <td class="cbi-value-field"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></td>
                                </tr>
-                               <% style = not style; end) %>
+                               <%
+                                                       style = not style
+                                               end
+                                       end
+                               %>
                        </table>
                </div>
        </fieldset>
index 5d3d8ad..9a1c1fe 100644 (file)
@@ -3,7 +3,7 @@
 -- Licensed to the public under the Apache License 2.0.
 
 local uci = require "luci.model.uci".cursor()
-local sys = require "luci.sys"
+local ipc = require "luci.ip"
 local wa  = require "luci.tools.webadmin"
 local fs  = require "nixio.fs"
 
@@ -86,12 +86,12 @@ s2.template = "cbi/tblsection"
 name = s2:option(Value, "name", translate("Hostname"))
 mac = s2:option(Value, "mac", translate("<abbr title=\"Media Access Control\">MAC</abbr>-Address"))
 ip = s2:option(Value, "ip", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address"))
-sys.net.arptable(function(entry)
-       ip:value(entry["IP address"])
-       mac:value(
-               entry["HW address"],
-               entry["HW address"] .. " (" .. entry["IP address"] .. ")"
-       )
+
+ipc.neighbors({ family = 4 }, function(n)
+       if n.mac and n.dest then
+               ip:value(n.dest:string())
+               mac:value(n.mac, "%s (%s)" %{ n.mac, n.dest:string() })
+       end
 end)
 
 return m
index 715ac75..72bd136 100644 (file)
@@ -2,6 +2,7 @@
 local utl = require "luci.util"
 local sys = require "luci.sys"
 local twa = require "luci.tools.webadmin"
+local ip  = require "luci.ip"
 
 -- System
 
@@ -48,27 +49,6 @@ for _, dev in ipairs(devices) do
 end
 local has_iwinfo = pcall(require, "iwinfo")
 
--- Routes
-local defroutev4 = sys.net.defaultroute()
-local defroutev6 = sys.net.defaultroute6()
-
-if defroutev4 then
-       defroutev4.dest = defroutev4.dest:string()
-       defroutev4.gateway = defroutev4.gateway:string()
-else
-       -- probably policy routing activated, try olsr-default table
-       local dr4 = sys.exec("ip r s t olsr-default")
-       if dr4 then
-               defroutev4 = { }
-               defroutev4.dest, defroutev4.gateway, defroutev4.device, defroutev4.metric = dr4:match("^(%w+) via (%d+.%d+.%d+.%d+) dev ([%w-]+) +metric (%d+)")
-       end
-end
-
-if defroutev6 then
-       defroutev6.dest = defroutev6.dest:string()
-       defroutev6.nexthop = defroutev6.nexthop:string()
-end
-
 if luci.http.formvalue("status") == "1" then
        local rv = { }
        for dev in pairs(netdevs) do
@@ -86,22 +66,29 @@ if luci.http.formvalue("status") == "1" then
                rv[#rv+1] = j
        end
 
-       if defroutev6 then
-               def6 = {
-                       gateway = defroutev6.nexthop,
-                       dest = defroutev6.dest,
-                       dev = defroutev6.device,
-                       metr = defroutev6.metric
+
+       -- Find default routes
+
+       local _, r, def4, def6
+
+       for _, r in ipairs(ip.routes({ type = 1, dest_exact = "0.0.0.0/0" })) do
+               def4 = {
+                       gateway = r.gw:string(),
+                       dest = r.dest:string(),
+                       dev = r.dev,
+                       metr = r.metric or 0
                }
+               break
        end
 
-       if defroutev4 then
-               def4 = {
-                       gateway = defroutev4.gateway,
-                       dest = defroutev4.dest,
-                       dev = defroutev4.device,
-                       metr = defroutev4.metric
+       for _, r in ipairs(ip.routes({ type = 1, dest_exact = "::/0" })) do
+               def6 = {
+                       gateway = r.gw:string(),
+                       dest = r.dest:string(),
+                       dev = r.dev,
+                       metr = r.metric or 0
                }
+               break
        end
 
        rv[#rv+1] = {
index f0ca605..e749bc9 100644 (file)
@@ -6,7 +6,7 @@
 
 include $(TOPDIR)/rules.mk
 
-LUCI_TITLE:=Support for DHCPv6/6in4/6to4/6rd/DS-Lite
+LUCI_TITLE:=Support for DHCPv6/6in4/6to4/6rd/DS-Lite/aiccu
 LUCI_DEPENDS:=
 
 include ../../luci.mk
diff --git a/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua b/protocols/luci-proto-ipv6/luasrc/model/cbi/admin_network/proto_aiccu.lua
new file mode 100644 (file)
index 0000000..e3ae020
--- /dev/null
@@ -0,0 +1,136 @@
+-- Copyright 2015 Paul Oranje <por@xs4all.nl>
+-- Licensed to the public under the Apache License 2.0.
+
+local map, section, net = ...
+
+-- config read by /lib/netifd/proto/aiccu.sh
+local username, password, protocol, server, tunnelid, ip6prefix, requiretls, nat, heartbeat,
+       verbose, ntpsynctimeout, ip6addr, sourcerouting, defaultroute
+
+-- generic parameters
+local metric, ttl, mtu
+
+
+username = section:taboption("general", Value, "username",
+       translate("Server username"),
+       translate("SIXXS-handle[/Tunnel-ID]"))
+username.datatype = "string"
+
+password = section:taboption("general", Value, "password",
+       translate("Server password"),
+       translate("Server password, enter the specific password of the tunnel when the username contains the tunnel ID"))
+password.datatype = "string"
+password.password = true
+
+
+--[[ SIXXS supports only TIC as tunnel broker protocol, no use setting it.
+protocol = section:taboption("general", ListValue, "protocol",
+       translate("Tunnel broker protocol"),
+       translate("SIXXS supports TIC only, for static tunnels using IP protocol 41 (RFC4213) use 6in4 instead"))
+
+protocol:value("tic", "TIC")
+protocol:value("tsp", "TSP")
+protocol:value("l2tp", "L2TP")
+protocol.default = "tic"
+protocol.optional = true
+--]]
+
+
+server = section:taboption("general", Value, "server",
+       translate("Tunnel setup server"),
+       translate("Optional, specify to override default server (tic.sixxs.net)"))
+server.datatype = "host"
+server.optional = true
+
+
+tunnelid = section:taboption("general", Value, "tunnelid",
+       translate("Tunnel ID"),
+       translate("Optional, use when the SIXXS account has more than one tunnel"))
+tunnelid.datatype = "string"
+tunnelid.optional = true
+
+
+local ip6prefix = section:taboption("general", Value, "ip6prefix",
+       translate("IPv6 prefix"),
+       translate("Routed IPv6 prefix for downstream interfaces"))
+ip6prefix.datatype = "ip6addr"
+ip6prefix.optional = true
+
+
+heartbeat = s:taboption("general", ListValue, "heartbeat",
+        translate("Tunnel type"),
+       translate("Also see <a href=\"https://www.sixxs.net/faq/connectivity/?faq=comparison\">Tunneling Comparison</a> on SIXXS"))
+heartbeat:value("0", translate("AYIYA"))
+heartbeat:value("1", translate("Heartbeat"))
+heartbeat.default = "0"
+
+
+nat = section:taboption("general", Flag, "nat",
+       translate("Behind NAT"),
+       translate("The tunnel end-point is behind NAT, defaults to disabled and only applies to AYIYA"))
+nat.optional = true
+nat.default = disabled
+
+
+requiretls = section:taboption("general", Flag, "requiretls",
+       translate("Require TLS"),
+       translate("Connection to server fails when TLS cannot be used"))
+requiretls.optional = true
+requiretls.default = requiretls.enabled
+
+
+verbose = section:taboption("advanced", Flag, "verbose",
+       translate("Verbose"),
+       translate("Verbose logging by aiccu daemon"))
+verbose.optional = true
+verbose.default = disabled
+
+
+ntpsynctimeout = section:taboption("advanced", Value, "ntpsynctimeout",
+       translate("NTP sync time-out"),
+       translate("Wait for NTP sync that many seconds, seting to 0 disables waiting (optional)"))
+ntpsynctimeout.datatype = "uinteger"
+ntpsynctimeout.placeholder = "90"
+ntpsynctimeout.optional = true
+
+
+ip6addr = section:taboption("advanced", Value, "ip6addr",
+       translate("Local IPv6 address"),
+       translate("IPv6 address delegated to the local tunnel endpoint (optional)"))
+ip6addr.datatype = "ip6addr"
+ip6addr.optional = true
+
+
+defaultroute = section:taboption("advanced", Flag, "defaultroute",
+       translate("Default route"),
+       translate("Whether to create an IPv6 default route over the tunnel"))
+defaultroute.default = defaultroute.enabled
+defaultroute.optional = true
+
+
+sourcerouting = section:taboption("advanced", Flag, "sourcerouting",
+       translate("Source routing"),
+       translate("Whether to route only packets from delegated prefixes"))
+sourcerouting.default = sourcerouting.enabled
+sourcerouting.optional = true
+
+
+metric = section:taboption("advanced", Value, "metric",
+       translate("Use gateway metric"))
+metric.datatype = "uinteger"
+metric.placeholder = "0"
+metric:depends("defaultroute", defaultroute.enabled)
+
+
+ttl = section:taboption("advanced", Value, "ttl",
+       translate("Use TTL on tunnel interface"))
+ttl.datatype = "range(1,255)"
+ttl.placeholder = "64"
+
+
+mtu = section:taboption("advanced", Value, "mtu",
+       translate("Use MTU on tunnel interface"),
+        translate("minimum 1280, maximum 1480"))
+mtu.datatype = "range(1280,1480)"
+mtu.placeholder = "1280"
+
diff --git a/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua b/protocols/luci-proto-ipv6/luasrc/model/network/proto_aiccu.lua
new file mode 100644 (file)
index 0000000..5896a27
--- /dev/null
@@ -0,0 +1,49 @@
+-- Copyright 2015 Paul Oranje <por@xs4all.nl>
+-- Licensed to the public under GPLv2
+
+local netmod = luci.model.network
+local interface = luci.model.network.interface
+
+local proto = netmod:register_protocol("aiccu")
+
+function proto.get_i18n(self)
+       return luci.i18n.translate("AICCU (SIXXS)")
+end
+
+function proto.ifname(self)
+       return "aiccu-" .. self.sid
+end
+
+function proto.get_interface(self)
+       return interface(self:ifname(), self)
+end
+
+function proto.is_installed(self)
+       return nixio.fs.access("/lib/netifd/proto/aiccu.sh")
+end
+
+function proto.opkg_package(self)
+       return "aiccu"
+end
+
+function proto.is_floating(self)
+       return true
+end
+
+function proto.is_virtual(self)
+       return true
+end
+
+function proto.get_interfaces(self)
+       return nil
+end
+
+function proto.contains_interface(self, ifname)
+       if self:is_floating() then
+               return (netmod:ifnameof(ifc) == self:ifname())
+       else
+               return netmod.protocol.contains_interface(self, ifc)
+       end
+end
+
+netmod:register_pattern_virtual("^aiccu-%%w")
index e0e63d8..7b21312 100644 (file)
@@ -19,6 +19,9 @@ port = section:taboption("general", Value, "port", translate("VPN Server port"))
 port.placeholder = "443"
 port.datatype    = "port"
 
+ifname = section:taboption("general", Value, "interface", translate("Output Interface"))
+ifname.template = "cbi/network_netlist"
+
 section:taboption("general", Value, "serverhash", translate("VPN Server's certificate SHA1 hash"))
 
 section:taboption("general", Value, "authgroup", translate("AuthGroup"))