luci-0.11: merge outstanding trunk changes
authorJo-Philipp Wich <jow@openwrt.org>
Tue, 18 Dec 2012 13:58:22 +0000 (13:58 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Tue, 18 Dec 2012 13:58:22 +0000 (13:58 +0000)
390 files changed:
Makefile
applications/luci-ahcp/luasrc/controller/ahcp.lua
applications/luci-asterisk/luasrc/controller/asterisk.lua
applications/luci-commands/Makefile [new file with mode: 0644]
applications/luci-commands/luasrc/controller/commands.lua [new file with mode: 0644]
applications/luci-commands/luasrc/model/cbi/commands.lua [new file with mode: 0644]
applications/luci-commands/luasrc/view/commands.htm [new file with mode: 0644]
applications/luci-coovachilli/luasrc/controller/coovachilli.lua
applications/luci-ddns/luasrc/controller/ddns.lua
applications/luci-ddns/luasrc/model/cbi/ddns/ddns.lua
applications/luci-diag-core/luasrc/controller/luci_diag.lua
applications/luci-diag-devinfo/luasrc/controller/luci_diag/devinfo_common.lua
applications/luci-diag-devinfo/luasrc/controller/luci_diag/luci_diag_devinfo.lua
applications/luci-diag-devinfo/luasrc/controller/luci_diag/netdiscover_common.lua
applications/luci-diag-devinfo/luasrc/controller/luci_diag/smap_common.lua
applications/luci-firewall/luasrc/controller/firewall.lua
applications/luci-freifunk-policyrouting/luasrc/controller/freifunk/policy-routing.lua
applications/luci-freifunk-policyrouting/luasrc/model/cbi/freifunk/policyrouting.lua
applications/luci-hd-idle/luasrc/controller/hd_idle.lua
applications/luci-ltqtapi/luasrc/controller/ltqtapi.lua
applications/luci-minidlna/luasrc/controller/minidlna.lua
applications/luci-minidlna/luasrc/view/minidlna_status.htm
applications/luci-mmc-over-gpio/luasrc/controller/mmc_over_gpio.lua
applications/luci-multiwan/luasrc/controller/multiwan.lua
applications/luci-ntpc/luasrc/controller/ntpc.lua
applications/luci-ntpc/luasrc/model/cbi/ntpc/ntpc.lua
applications/luci-ntpc/luasrc/model/cbi/ntpc/ntpcmini.lua
applications/luci-olsr/luasrc/controller/olsr.lua
applications/luci-olsr/luasrc/tools/olsr.lua
applications/luci-olsr/luasrc/view/status-olsr/neighbors.htm
applications/luci-openvpn/luasrc/controller/openvpn.lua
applications/luci-p2pblock/luasrc/controller/ff_p2pblock.lua
applications/luci-p910nd/luasrc/controller/p910nd.lua
applications/luci-p910nd/luasrc/model/cbi/p910nd.lua
applications/luci-polipo/luasrc/controller/polipo.lua
applications/luci-qos/luasrc/controller/qos.lua
applications/luci-qos/luasrc/model/cbi/qos/qos.lua
applications/luci-qos/luasrc/model/cbi/qos/qosmini.lua
applications/luci-radvd/luasrc/controller/radvd.lua
applications/luci-samba/luasrc/controller/samba.lua
applications/luci-splash/luasrc/controller/splash/splash.lua
applications/luci-splash/luasrc/model/cbi/splash/splash.lua
applications/luci-splash/luasrc/model/cbi/splash/splashtext.lua
applications/luci-splash/luasrc/view/admin_status/splash.htm
applications/luci-statistics/luasrc/controller/luci_statistics/luci_statistics.lua
applications/luci-statistics/luasrc/model/cbi/luci_statistics/collectd.lua
applications/luci-statistics/luasrc/model/cbi/luci_statistics/nut.lua [new file with mode: 0644]
applications/luci-statistics/luasrc/statistics/i18n.lua
applications/luci-statistics/luasrc/statistics/rrdtool.lua
applications/luci-statistics/luasrc/statistics/rrdtool/definitions/cpu.lua
applications/luci-statistics/luasrc/statistics/rrdtool/definitions/load.lua
applications/luci-statistics/luasrc/statistics/rrdtool/definitions/nut.lua [new file with mode: 0644]
applications/luci-statistics/root/etc/config/luci_statistics
applications/luci-statistics/root/usr/bin/stat-genconfig
applications/luci-tinyproxy/luasrc/controller/tinyproxy.lua
applications/luci-transmission/luasrc/controller/transmission.lua
applications/luci-upnp/luasrc/controller/upnp.lua
applications/luci-upnp/luasrc/view/upnp_status.htm
applications/luci-ushare/luasrc/controller/ushare.lua
applications/luci-vnstat/luasrc/controller/vnstat.lua
applications/luci-voice-core/luasrc/controller/luci_voice.lua
applications/luci-voice-diag/luasrc/controller/luci_voice/luci_voice_diag.lua
applications/luci-wol/luasrc/controller/wol.lua
build/i18n-init.sh
contrib/package/community-profiles/files/etc/config/profile_hamburg [deleted file]
contrib/package/freifunk-p2pblock/Makefile
contrib/package/freifunk-p2pblock/files/freifunk-p2pblock.init
contrib/package/luci/Makefile
i18n/polish/root/etc/uci-defaults/luci-i18n-polish
libs/core/luasrc/model/firewall.lua
libs/core/luasrc/model/network.lua
libs/core/luasrc/util.lua
libs/ipkg/luasrc/model/ipkg.lua
libs/lmo/Makefile [deleted file]
libs/lmo/src/lmo.h [deleted file]
libs/lmo/src/lmo_core.c [deleted file]
libs/lmo/src/lmo_hash.c [deleted file]
libs/lmo/src/lmo_lookup.c [deleted file]
libs/lmo/src/lmo_lualib.c [deleted file]
libs/lmo/src/lmo_lualib.h [deleted file]
libs/lmo/src/lmo_po2lmo.c [deleted file]
libs/lmo/standalone.mk [deleted file]
libs/web/Makefile
libs/web/htdocs/luci-static/resources/cbi.js
libs/web/luasrc/cbi.lua
libs/web/luasrc/http.lua
libs/web/luasrc/i18n.lua
libs/web/luasrc/template.lua
libs/web/luasrc/view/cbi/apply_xhr.htm
libs/web/luasrc/view/cbi/footer.htm
libs/web/src/po2lmo.c [new file with mode: 0644]
libs/web/src/template_lmo.c [new file with mode: 0644]
libs/web/src/template_lmo.h [new file with mode: 0644]
libs/web/src/template_lualib.c
libs/web/src/template_lualib.h
libs/web/src/template_parser.c
libs/web/src/template_parser.h
libs/web/src/template_utils.c
libs/web/src/template_utils.h
libs/web/standalone.mk [new file with mode: 0644]
modules/admin-core/luasrc/controller/admin/servicectl.lua
modules/admin-core/luasrc/tools/status.lua
modules/admin-full/luasrc/controller/admin/network.lua
modules/admin-full/luasrc/controller/admin/status.lua
modules/admin-full/luasrc/model/cbi/admin_network/hosts.lua
modules/admin-full/luasrc/model/cbi/admin_network/vlan.lua
modules/admin-full/luasrc/model/cbi/admin_network/wifi.lua
modules/admin-full/luasrc/view/admin_network/diagnostics.htm
modules/admin-full/luasrc/view/admin_network/switch_status.htm
modules/admin-full/luasrc/view/admin_network/wifi_overview.htm
modules/admin-full/luasrc/view/admin_status/connections.htm
modules/admin-full/luasrc/view/admin_status/index.htm
modules/admin-full/luasrc/view/admin_status/load.htm
modules/admin-full/luasrc/view/admin_status/wireless.htm
modules/admin-full/luasrc/view/admin_system/packages.htm
modules/admin-full/src/luci-bwc.c
modules/freifunk/luasrc/controller/freifunk/freifunk.lua
modules/freifunk/luasrc/model/cbi/freifunk/basics.lua
modules/freifunk/luasrc/model/cbi/freifunk/contact.lua
modules/freifunk/luasrc/model/cbi/freifunk/profile.lua
modules/freifunk/luasrc/model/cbi/freifunk/profile_expert.lua
modules/freifunk/luasrc/model/cbi/freifunk/user_index.lua
modules/freifunk/luasrc/view/freifunk-map/frame.htm
modules/freifunk/luasrc/view/freifunk-map/map.htm
modules/freifunk/luasrc/view/freifunk/adminindex.htm
po/ca/base.po
po/ca/commands.po [new file with mode: 0644]
po/ca/ddns.po
po/ca/diag_devinfo.po
po/ca/freifunk.po
po/ca/meshwizard.po
po/ca/statistics.po
po/cs/base.po
po/cs/commands.po [new file with mode: 0644]
po/cs/ddns.po
po/cs/diag_devinfo.po
po/cs/freifunk.po
po/cs/meshwizard.po
po/cs/statistics.po
po/de/base.po
po/de/commands.po [new file with mode: 0644]
po/de/ddns.po
po/de/diag_devinfo.po
po/de/freifunk-policyrouting.po
po/de/freifunk.po
po/de/meshwizard.po
po/de/mmc_over_gpio.po
po/de/olsr.po
po/de/openvpn.po
po/de/splash.po
po/de/statistics.po
po/de/tinyproxy.po
po/de/upnp.po
po/el/base.po
po/el/commands.po [new file with mode: 0644]
po/el/ddns.po
po/el/diag_devinfo.po
po/el/freifunk.po
po/el/meshwizard.po
po/el/statistics.po
po/en/base.po
po/en/commands.po [new file with mode: 0644]
po/en/ddns.po
po/en/diag_devinfo.po
po/en/freifunk.po
po/en/meshwizard.po
po/en/statistics.po
po/es/ahcp.po
po/es/base.po
po/es/commands.po [new file with mode: 0644]
po/es/ddns.po
po/es/diag_devinfo.po
po/es/freifunk.po
po/es/meshwizard.po
po/es/minidlna.po
po/es/ntpc.po
po/es/olsr.po
po/es/p2pblock.po
po/es/pbx.po
po/es/polipo.po
po/es/radvd.po
po/es/statistics.po
po/es/tinyproxy.po
po/fr/ahcp.po
po/fr/base.po
po/fr/commands.po [new file with mode: 0644]
po/fr/coovachilli.po
po/fr/ddns.po
po/fr/diag_core.po
po/fr/diag_devinfo.po
po/fr/freifunk.po
po/fr/hd_idle.po
po/fr/meshwizard.po
po/fr/mmc_over_gpio.po
po/fr/multiwan.po
po/fr/ntpc.po
po/fr/qos.po
po/fr/statistics.po
po/fr/tinyproxy.po
po/fr/upnp.po
po/fr/voice_core.po
po/he/base.po
po/he/commands.po [new file with mode: 0644]
po/he/ddns.po
po/he/diag_devinfo.po
po/he/freifunk.po
po/he/meshwizard.po
po/he/statistics.po
po/hu/base.po
po/hu/commands.po [new file with mode: 0644]
po/hu/ddns.po
po/hu/diag_devinfo.po
po/hu/freifunk.po
po/hu/meshwizard.po
po/hu/qos.po
po/hu/statistics.po
po/it/base.po
po/it/commands.po [new file with mode: 0644]
po/it/ddns.po
po/it/diag_devinfo.po
po/it/freifunk.po
po/it/meshwizard.po
po/it/minidlna.po
po/it/pbx.po
po/it/statistics.po
po/it/tinyproxy.po
po/ja/base.po
po/ja/commands.po [new file with mode: 0644]
po/ja/ddns.po
po/ja/diag_devinfo.po
po/ja/firewall.po
po/ja/freifunk.po
po/ja/hd_idle.po
po/ja/meshwizard.po
po/ja/ntpc.po
po/ja/olsr.po
po/ja/statistics.po
po/ja/watchcat.po
po/ms/base.po
po/ms/commands.po [new file with mode: 0644]
po/ms/ddns.po
po/ms/diag_devinfo.po
po/ms/freifunk.po
po/ms/meshwizard.po
po/ms/statistics.po
po/no/base.po
po/no/commands.po [new file with mode: 0644]
po/no/ddns.po
po/no/diag_devinfo.po
po/no/freifunk.po
po/no/meshwizard.po
po/no/statistics.po
po/pl/base.po
po/pl/commands.po [new file with mode: 0644]
po/pl/ddns.po
po/pl/diag_devinfo.po
po/pl/ffwizard.po
po/pl/freifunk.po
po/pl/meshwizard.po
po/pl/minidlna.po
po/pl/multiwan.po
po/pl/olsr.po
po/pl/pbx.po
po/pl/splash.po
po/pl/statistics.po
po/pl/tinyproxy.po
po/pl/transmission.po
po/pt/base.po
po/pt/commands.po [new file with mode: 0644]
po/pt/ddns.po
po/pt/diag_devinfo.po
po/pt/freifunk.po
po/pt/meshwizard.po
po/pt/statistics.po
po/pt_BR/base.po
po/pt_BR/commands.po [new file with mode: 0644]
po/pt_BR/ddns.po
po/pt_BR/diag_devinfo.po
po/pt_BR/freifunk.po
po/pt_BR/meshwizard.po
po/pt_BR/multiwan.po
po/pt_BR/olsr.po
po/pt_BR/pbx.po
po/pt_BR/statistics.po
po/pt_BR/transmission.po
po/ro/base.po
po/ro/commands.po [new file with mode: 0644]
po/ro/coovachilli.po
po/ro/ddns.po
po/ro/diag_devinfo.po
po/ro/freifunk.po
po/ro/meshwizard.po
po/ro/ntpc.po
po/ro/statistics.po
po/ru/ahcp.po
po/ru/base.po
po/ru/commands.po [new file with mode: 0644]
po/ru/coovachilli.po
po/ru/ddns.po
po/ru/diag_core.po
po/ru/diag_devinfo.po
po/ru/ffwizard.po
po/ru/freifunk-policyrouting.po
po/ru/freifunk.po
po/ru/hd_idle.po
po/ru/meshwizard.po
po/ru/minidlna.po
po/ru/mmc_over_gpio.po
po/ru/multiwan.po
po/ru/ntpc.po
po/ru/olsr.po
po/ru/openvpn.po
po/ru/p2pblock.po
po/ru/p910nd.po
po/ru/pbx-voicemail.po
po/ru/polipo.po
po/ru/qos.po
po/ru/radvd.po
po/ru/samba.po
po/ru/splash.po
po/ru/statistics.po
po/ru/tinyproxy.po
po/ru/transmission.po
po/ru/upnp.po
po/ru/ushare.po
po/ru/uvc_streamer.po
po/ru/vnstat.po
po/ru/voice_core.po
po/ru/voice_diag.po
po/ru/watchcat.po
po/ru/wol.po
po/ru/wshaper.po
po/templates/base.pot
po/templates/commands.pot [new file with mode: 0644]
po/templates/ddns.pot
po/templates/diag_devinfo.pot
po/templates/freifunk.pot
po/templates/meshwizard.pot
po/templates/olsr.pot
po/templates/statistics.pot
po/tr/base.po
po/tr/commands.po [new file with mode: 0644]
po/tr/ddns.po
po/tr/diag_devinfo.po
po/tr/freifunk.po
po/tr/meshwizard.po
po/tr/statistics.po
po/uk/base.po
po/uk/commands.po [new file with mode: 0644]
po/uk/ddns.po
po/uk/diag_devinfo.po
po/uk/freifunk.po
po/uk/meshwizard.po
po/uk/statistics.po
po/vi/base.po
po/vi/commands.po [new file with mode: 0644]
po/vi/ddns.po
po/vi/diag_devinfo.po
po/vi/freifunk.po
po/vi/meshwizard.po
po/vi/statistics.po
po/zh_CN/base.po
po/zh_CN/commands.po [new file with mode: 0644]
po/zh_CN/ddns.po
po/zh_CN/diag_devinfo.po
po/zh_CN/firewall.po
po/zh_CN/freifunk.po
po/zh_CN/meshwizard.po
po/zh_CN/minidlna.po
po/zh_CN/ntpc.po
po/zh_CN/p910nd.po
po/zh_CN/splash.po
po/zh_CN/statistics.po
po/zh_CN/tinyproxy.po
po/zh_CN/ushare.po
po/zh_CN/uvc_streamer.po
po/zh_CN/watchcat.po
po/zh_CN/wol.po
protocols/ppp/luasrc/model/network/proto_ppp.lua
themes/base/htdocs/luci-static/resources/Dropdowns.js [deleted file]
themes/base/htdocs/luci-static/resources/VarType.js [deleted file]
themes/base/htdocs/luci-static/resources/XHTML1.js [deleted file]
themes/bootstrap/htdocs/luci-static/bootstrap/cascade.css
themes/bootstrap/luasrc/view/themes/bootstrap/header.htm
themes/freifunk-bno/htdocs/luci-static/freifunk-bno/cascade.css
themes/freifunk-bno/luasrc/view/themes/freifunk-bno/header.htm
themes/freifunk-generic/htdocs/luci-static/freifunk-generic/cascade.css
themes/freifunk-generic/luasrc/view/themes/freifunk-generic/header.htm
themes/openwrt/htdocs/luci-static/openwrt.org/cascade.css
themes/openwrt/luasrc/view/themes/openwrt.org/header.htm

index efccd79..7dc9fa1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ all: build
 build: gccbuild luabuild
 
 gccbuild:
-       make -C libs/lmo CC="cc" CFLAGS="" LDFLAGS="" SDK="$(shell test -f .running-sdk && echo 1)" host-install
+       make -C libs/web CC="cc" CFLAGS="" LDFLAGS="" SDK="$(shell test -f .running-sdk && echo 1)" host-install
        for i in $(MODULES); do \
                make -C$$i SDK="$(shell test -f .running-sdk && echo 1)" compile || { \
                        echo "*** Compilation of $$i failed!"; \
index 5667bb5..704ec4f 100644 (file)
@@ -19,7 +19,7 @@ function index()
                return
        end
 
-       entry({"admin", "network", "ahcpd"}, cbi("ahcp"), _("AHCP Server"), 90).i18n = "ahcp"
+       entry({"admin", "network", "ahcpd"}, cbi("ahcp"), _("AHCP Server"), 90)
        entry({"admin", "network", "ahcpd", "status"}, call("ahcp_status"))
 end
 
index ab05339..54b9525 100644 (file)
@@ -17,7 +17,7 @@ module("luci.controller.asterisk", package.seeall)
 
 function index()
 
-       entry({"admin", "services", "asterisk"},                                  cbi("asterisk"),                        "Asterisk",                           80).i18n = "asterisk"
+       entry({"admin", "services", "asterisk"},                                  cbi("asterisk"),                        "Asterisk",                           80)
 
        entry({"admin", "services", "asterisk", "voice"},         cbi("asterisk-voice"),      "Voice Functions",        1)
        entry({"admin", "services", "asterisk", "meetme"},        cbi("asterisk-meetme"),     "Meetme Conferences",     2)
diff --git a/applications/luci-commands/Makefile b/applications/luci-commands/Makefile
new file mode 100644 (file)
index 0000000..64801c2
--- /dev/null
@@ -0,0 +1,4 @@
+PO = commands
+
+include ../../build/config.mk
+include ../../build/module.mk
diff --git a/applications/luci-commands/luasrc/controller/commands.lua b/applications/luci-commands/luasrc/controller/commands.lua
new file mode 100644 (file)
index 0000000..b9f0ce8
--- /dev/null
@@ -0,0 +1,237 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2012 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
+
+]]--
+
+module("luci.controller.commands", package.seeall)
+
+function index()
+       entry({"admin", "system", "commands"}, firstchild(), _("Custom Commands"), 80)
+       entry({"admin", "system", "commands", "dashboard"}, template("commands"), _("Dashboard"), 1)
+       entry({"admin", "system", "commands", "config"}, cbi("commands"), _("Configure"), 2)
+       entry({"admin", "system", "commands", "run"}, call("action_run"), nil, 3).leaf = true
+       entry({"admin", "system", "commands", "download"}, call("action_download"), nil, 3).leaf = true
+
+       entry({"command"}, call("action_public"), nil, 1).leaf = true
+end
+
+--- Decode a given string into arguments following shell quoting rules
+--- [[abc \def "foo\"bar" abc'def']] -> [[abc def]] [[foo"bar]] [[abcdef]]
+local function parse_args(str)
+       local args = { }
+
+       local function isspace(c)
+               if c == 9 or c == 10 or c == 11 or c == 12 or c == 13 or c == 32 then
+                       return c
+               end
+       end
+
+       local function isquote(c)
+               if c == 34 or c == 39 or c == 96 then
+                       return c
+               end
+       end
+
+       local function isescape(c)
+               if c == 92 then
+                       return c
+               end
+       end
+
+       local function ismeta(c)
+               if c == 36 or c == 92 or c == 96 then
+                       return c
+               end
+       end
+
+       --- Convert given table of byte values into a Lua string and append it to
+       --- the "args" table. Segment byte value sequence into chunks of 256 values
+       --- to not trip over the parameter limit for string.char()
+       local function putstr(bytes)
+               local chunks = { }
+               local csz = 256
+               local upk = unpack
+               local chr = string.char
+               local min = math.min
+               local len = #bytes
+               local off
+
+               for off = 1, len, csz do
+                       chunks[#chunks+1] = chr(upk(bytes, off, min(off + csz - 1, len)))
+               end
+
+               args[#args+1] = table.concat(chunks)
+       end
+
+       --- Scan substring defined by the indexes [s, e] of the string "str",
+       --- perform unquoting and de-escaping on the fly and store the result in
+       --- a table of byte values which is passed to putstr()
+       local function unquote(s, e)
+               local off, esc, quote
+               local res = { }
+
+               for off = s, e do
+                       local byte = str:byte(off)
+                       local q = isquote(byte)
+                       local e = isescape(byte)
+                       local m = ismeta(byte)
+
+                       if e then
+                               esc = true
+                       elseif esc then
+                               if m then res[#res+1] = 92 end
+                               res[#res+1] = byte
+                               esc = false
+                       elseif q and quote and q == quote then
+                               quote = nil
+                       elseif q and not quote then
+                               quote = q
+                       else
+                               if m then res[#res+1] = 92 end
+                               res[#res+1] = byte
+                       end
+               end
+
+               putstr(res)
+       end
+
+       --- Find substring boundaries in "str". Ignore escaped or quoted
+       --- whitespace, pass found start- and end-index for each substring
+       --- to unquote()
+       local off, esc, start, quote
+       for off = 1, #str + 1 do
+               local byte = str:byte(off)
+               local q = isquote(byte)
+               local s = isspace(byte) or (off > #str)
+               local e = isescape(byte)
+
+               if esc then
+                       esc = false
+               elseif e then
+                       esc = true
+               elseif q and quote and q == quote then
+                       quote = nil
+               elseif q and not quote then
+                       start = start or off
+                       quote = q
+               elseif s and not quote then
+                       if start then
+                               unquote(start, off - 1)
+                               start = nil
+                       end
+               else
+                       start = start or off
+               end
+       end
+
+       --- If the "quote" is still set we encountered an unfinished string
+       if quote then
+               unquote(start, #str)
+       end
+
+       return args
+end
+
+local function parse_cmdline(cmdid, args)
+       local uci = require "luci.model.uci".cursor()
+       if uci:get("luci", cmdid) == "command" then
+               local cmd = uci:get_all("luci", cmdid)
+               local argv = parse_args(cmd.command)
+               local i, v
+
+               if cmd.param == "1" and args then
+                       for i, v in ipairs(parse_args(luci.http.urldecode(args))) do
+                               argv[#argv+1] = v
+                       end
+               end
+
+               for i, v in ipairs(argv) do
+                       if v:match("[^%w%.%-i/]") then
+                               argv[i] = '"%s"' % v:gsub('"', '\\"')
+                       end
+               end
+
+               return argv
+       end
+end
+
+function action_run(...)
+       local fs   = require "nixio.fs"
+       local argv = parse_cmdline(...)
+       if argv then
+               local outfile = os.tmpname()
+               local errfile = os.tmpname()
+
+               local rv = os.execute(table.concat(argv, " ") .. " >%s 2>%s" %{ outfile, errfile })
+               local stdout = fs.readfile(outfile, 1024 * 512) or ""
+               local stderr = fs.readfile(errfile, 1024 * 512) or ""
+
+               fs.unlink(outfile)
+               fs.unlink(errfile)
+
+               local binary = not not (stdout:match("[%z\1-\8\14-\31]"))
+
+               luci.http.prepare_content("application/json")
+               luci.http.write_json({
+                       command  = table.concat(argv, " "),
+                       stdout   = not binary and stdout,
+                       stderr   = stderr,
+                       exitcode = rv,
+                       binary   = binary
+               })
+       else
+               luci.http.status(404, "No such command")
+       end
+end
+
+function action_download(...)
+       local fs   = require "nixio.fs"
+       local argv = parse_cmdline(...)
+       if argv then
+               local fd = io.popen(table.concat(argv, " ") .. " 2>/dev/null")
+               if fd then
+                       local chunk = fd:read(4096) or ""
+                       local name
+                       if chunk:match("[%z\1-\8\14-\31]") then
+                               luci.http.header("Content-Disposition", "attachment; filename=%s"
+                                                % fs.basename(argv[1]):gsub("%W+", ".") .. ".bin")
+                               luci.http.prepare_content("application/octet-stream")
+                       else
+                               luci.http.header("Content-Disposition", "attachment; filename=%s"
+                                                % fs.basename(argv[1]):gsub("%W+", ".") .. ".txt")
+                               luci.http.prepare_content("text/plain")
+                       end
+
+                       while chunk do
+                               luci.http.write(chunk)
+                               chunk = fd:read(4096)
+                       end
+
+                       fd:close()
+               else
+                       luci.http.status(500, "Failed to execute command")
+               end
+       else
+               luci.http.status(404, "No such command")
+       end
+end
+
+function action_public(cmdid, args)
+       local uci = require "luci.model.uci".cursor()
+       if cmdid and
+          uci:get("luci", cmdid) == "command" and
+          uci:get("luci", cmdid, "public") == "1"
+       then
+               action_download(cmdid, args)
+       else
+               luci.http.status(403, "Access to command denied")
+       end
+end
diff --git a/applications/luci-commands/luasrc/model/cbi/commands.lua b/applications/luci-commands/luasrc/model/cbi/commands.lua
new file mode 100644 (file)
index 0000000..1359eb2
--- /dev/null
@@ -0,0 +1,37 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2012 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
+
+]]--
+
+local m, s
+
+m = Map("luci", translate("Custom Commands"),
+       translate("This page allows you to configure custom shell commands which can be easily invoked from the web interface."))
+
+s = m:section(TypedSection, "command", "")
+s.template = "cbi/tblsection"
+s.anonymous = true
+s.addremove = true
+
+
+s:option(Value, "name", translate("Description"),
+         translate("A short textual description of the configured command"))
+
+s:option(Value, "command", translate("Command"),
+         translate("Command line to execute"))
+
+s:option(Flag, "param", translate("Custom arguments"),
+         translate("Allow the user to provide additional command line arguments"))
+
+s:option(Flag, "public", translate("Public access"),
+         translate("Allow executing the command and downloading its output without prior authentication"))
+
+return m
diff --git a/applications/luci-commands/luasrc/view/commands.htm b/applications/luci-commands/luasrc/view/commands.htm
new file mode 100644 (file)
index 0000000..83792a9
--- /dev/null
@@ -0,0 +1,176 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2012 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
+
+-%>
+
+<% css = [[
+
+.commandbox {
+       height: 12em;
+       width: 30%;
+       float: left;
+       height: 12em;
+       margin: 5px;
+       position: relative;
+}
+
+.commandbox h3 {
+       font-size: 1.5em !important;
+       line-height: 2em !important;
+       margin: 0 !important;
+}
+
+.commandbox input[type="text"] {
+       width: 50% !important;
+}
+
+.commandbox div {
+       position: absolute;
+       left: 0;
+       bottom: 1.5em;
+}
+
+]] -%>
+
+<%+header%>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript">//<![CDATA[
+       var stxhr = new XHR();
+
+       function command_run(id)
+       {
+               var args;
+               var field = document.getElementById(id);
+               if (field)
+                       args = encodeURIComponent(field.value);
+
+               var legend = document.getElementById('command-rc-legend');
+               var output = document.getElementById('command-rc-output');
+
+               if (legend && output)
+               {
+                       output.innerHTML =
+                               '<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> ' +
+                               '<%:Waiting for command to complete...%>'
+                       ;
+
+                       legend.parentNode.style.display = 'block';
+                       legend.style.display = 'inline';
+
+                       stxhr.get('<%=luci.dispatcher.build_url("admin", "system", "commands", "run")%>/' + id + (args ? '/' + args : ''), null,
+                               function(x, st)
+                               {
+                                       if (st)
+                                       {
+                                               if (st.binary)
+                                                       st.stdout = '[<%:Binary data not displayed, download instead.%>]';
+
+                                               legend.style.display = 'none';
+                                               output.innerHTML = String.format(
+                                                       '<pre><strong># %h\n</strong>%h<span style="color:red">%h</span></pre>' +
+                                                       '<div class="alert-message warning">%s (<%:Code:%> %d)</div>',
+                                                       st.command, st.stdout, st.stderr,
+                                                       (st.exitcode == 0) ? '<%:Command successful%>' : '<%:Command failed%>',
+                                                       st.exitcode);
+                                       }
+                                       else
+                                       {
+                                               legend.style.display = 'none';
+                                               output.innerHTML = '<span class="error"><%:Failed to execute command!%></span>';
+                                       }
+
+                                       location.hash = '#output';
+                               }
+                       );
+               }
+       }
+
+       function command_download(id)
+       {
+               var args;
+               var field = document.getElementById(id);
+               if (field)
+                       args = encodeURIComponent(field.value);
+
+               location.href = '<%=luci.dispatcher.build_url("admin", "system", "commands", "download")%>/' + id + (args ? '/' + args : '');
+       }
+
+       function command_link(id)
+       {
+               var legend = document.getElementById('command-rc-legend');
+               var output = document.getElementById('command-rc-output');
+
+               var args;
+               var field = document.getElementById(id);
+               if (field)
+                       args = encodeURIComponent(field.value);
+
+               if (legend && output)
+               {
+                       var link = location.protocol + '//' + location.hostname +
+                                  (location.port ? ':' + location.port : '') +
+                                          location.pathname.split(';')[0] + 'command/' +
+                                          id + (args ? '/' + args : '');
+
+                       legend.style.display = 'none';
+                       output.parentNode.style.display = 'block';
+                       output.innerHTML = String.format(
+                               '<div class="alert-message"><%:Access command with%> <a href="%s">%s</a></div>',
+                               link, link
+                       );
+
+                       location.hash = '#output';
+               }
+       }
+
+//]]></script>
+
+<%
+       local uci = require "luci.model.uci".cursor()
+       local commands = { }
+
+       uci:foreach("luci", "command", function(s) commands[#commands+1] = s end)
+%>
+
+<form method="get" action="<%=pcdata(luci.http.getenv("REQUEST_URI"))%>">
+       <div class="cbi-map">
+               <h2><a id="content" name="content"><%:Custom Commands%></a></h2>
+
+               <fieldset class="cbi-section">
+                       <% local _, command; for _, command in ipairs(commands) do %>
+                       <div class="commandbox">
+                               <h3><%=pcdata(command.name)%></h3>
+                               <p><%:Command:%> <code><%=pcdata(command.command)%></code></p>
+                               <% if command.param == "1" then %>
+                                       <p><%:Arguments:%> <input type="text" id="<%=command['.name']%>" /></p>
+                               <% end %>
+                               <div>
+                                       <input type="button" value="<%:Run%>" class="cbi-button cbi-button-apply" onclick="command_run('<%=command['.name']%>')" />
+                                       <input type="button" value="<%:Download%>" class="cbi-button cbi-button-download" onclick="command_download('<%=command['.name']%>')" />
+                                       <% if command.public == "1" then %>
+                                               <input type="button" value="<%:Link%>" class="cbi-button cbi-button-link" onclick="command_link('<%=command['.name']%>')" />
+                                       <% end %>
+                               </div>
+                       </div>
+                       <% end %>
+
+                       <br style="clear:both" /><br />
+                       <a name="output"></a>
+               </fieldset>
+       </div>
+
+       <fieldset class="cbi-section" style="display:none">
+               <legend id="command-rc-legend"><%:Collecting data...%></legend>
+               <span id="command-rc-output"></span>
+       </fieldset>
+</form>
+
+<%+footer%>
index 73a2c51..aa0a5cc 100644 (file)
@@ -18,7 +18,6 @@ function index()
        local cc
 
        cc = entry( { "admin", "services", "coovachilli" },       cbi("coovachilli"),         _("CoovaChilli"),                90)
-       cc.i18n = "coovachilli"
        cc.subindex = true
 
        entry( { "admin", "services", "coovachilli", "network" }, cbi("coovachilli_network"), _("Network Configuration"),      10)
index e2f786b..0c7293d 100644 (file)
@@ -23,10 +23,8 @@ function index()
        local page
 
        page = entry({"admin", "services", "ddns"}, cbi("ddns/ddns"), _("Dynamic DNS"), 60)
-       page.i18n = "ddns"
        page.dependent = true
 
        page = entry({"mini", "network", "ddns"}, cbi("ddns/ddns", {autoapply=true}), _("Dynamic DNS"), 60)
-       page.i18n = "ddns"
        page.dependent = true
 end
index 42fb4a6..e477a24 100644 (file)
@@ -112,14 +112,19 @@ else
 end
 
 
-s:option(Value, "check_interval",
-       translate("Check for changed IP every")).default = 10
+ci = s:option(Value, "check_interval", translate("Check for changed IP every"))
+ci.datatype = "and(uinteger,min(1))"
+ci.default = 10
+
 unit = s:option(ListValue, "check_unit", translate("Check-time unit"))
 unit.default = "minutes"
 unit:value("minutes", translate("min"))
 unit:value("hours", translate("h"))
 
-s:option(Value, "force_interval", translate("Force update every")).default = 72
+fi = s:option(Value, "force_interval", translate("Force update every"))
+fi.datatype = "and(uinteger,min(1))"
+fi.default = 72
+
 unit = s:option(ListValue, "force_unit", translate("Force-time unit"))
 unit.default = "hours"
 unit:value("minutes", translate("min"))
index c123994..4a8a020 100644 (file)
@@ -11,8 +11,6 @@ You may obtain a copy of the License at
 
 ]]--
 
-require("luci.i18n")
-
 module("luci.controller.luci_diag", package.seeall)
 
 function index()
@@ -20,11 +18,9 @@ function index()
 
        e = entry({"admin", "network", "diag_config"}, template("diag/network_config_index") , _("Configure Diagnostics"), 120)
        e.index = true
-       e.i18n = "diag_core"
        e.dependent = true
 
        e = entry({"mini", "diag"}, template("diag/index"), _("Diagnostics"), 120)
        e.index = true
-       e.i18n = "diag_core"
        e.dependent = true
 end
index 009054a..c124cd3 100644 (file)
@@ -38,8 +38,6 @@ end
 function parse_output(devmap, outnets, haslink, type, mini, debug)
    local curnet = next(outnets, nil)
 
-   luci.i18n.loadc("diag_devinfo")
-
    while (curnet) do
       local output = outnets[curnet]["output"]
       local subnet = outnets[curnet]["subnet"]
index e8a2f50..7a2fbd9 100644 (file)
@@ -19,49 +19,38 @@ function index()
    e = entry({"admin", "voice", "diag", "phones"}, arcombine(cbi("luci_diag/smap_devinfo"), cbi("luci_diag/smap_devinfo_config")), _("Phones"), 10)
    e.leaf = true
    e.subindex = true
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"admin", "voice", "diag", "phones", "config"}, cbi("luci_diag/smap_devinfo_config"), _("Configure"), 10)
-   e.i18n = "diag_devinfo"
 
    e = entry({"admin", "status", "smap_devinfo"}, cbi("luci_diag/smap_devinfo"), _("SIP Devices on Network"), 120)
    e.leaf = true
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"admin", "network", "diag_config", "netdiscover_devinfo_config"}, cbi("luci_diag/netdiscover_devinfo_config"), _("Network Device Scan"), 100)
    e.leaf = true
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"admin", "network", "diag_config", "smap_devinfo_config"}, cbi("luci_diag/smap_devinfo_config"), _("SIP Device Scan"))
    e.leaf = true
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"admin", "status", "netdiscover_devinfo"}, cbi("luci_diag/netdiscover_devinfo"), _("Devices on Network"), 90)
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"admin", "network", "mactodevinfo"}, cbi("luci_diag/mactodevinfo"), _("MAC Device Info Overrides"), 190)
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"mini", "diag", "phone_scan"}, cbi("luci_diag/smap_devinfo_mini"), _("Phone Scan"), 100)
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"mini", "voice", "phones", "phone_scan_config"}, cbi("luci_diag/smap_devinfo_config_mini"), _("Config Phone Scan"), 90)
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"mini", "diag", "netdiscover_devinfo"}, cbi("luci_diag/netdiscover_devinfo_mini"), _("Network Device Scan"), 10)
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
    e = entry({"mini", "network", "netdiscover_devinfo_config"}, cbi("luci_diag/netdiscover_devinfo_config_mini"), _("Device Scan Config"))
-   e.i18n = "diag_devinfo"
    e.dependent = true
 
 end
index 95859ae..592ca1e 100644 (file)
@@ -83,7 +83,6 @@ function command_function(outnets, i)
 end
 
 function action_links(netdiscovermap, mini) 
-   luci.i18n.loadc("diag_devinfo")
    s = netdiscovermap:section(SimpleSection, "", translate("Actions")) 
    b = s:option(DummyValue, "_config", translate("Configure Scans"))
    b.value = ""
index 13ac91f..e9aeaa6 100644 (file)
@@ -94,7 +94,6 @@ function command_function(outnets, i)
 end
 
 function action_links(smapmap, mini) 
-   luci.i18n.loadc("diag_devinfo")
    s = smapmap:section(SimpleSection, "", translate("Actions")) 
    b = s:option(DummyValue, "_config", translate("Configure Scans"))
    b.value = ""
index c0149f8..5a6ab0a 100644 (file)
@@ -3,7 +3,7 @@ module("luci.controller.firewall", package.seeall)
 function index()
        entry({"admin", "network", "firewall"},
                alias("admin", "network", "firewall", "zones"),
-               _("Firewall"), 60).i18n = "firewall"
+               _("Firewall"), 60)
 
        entry({"admin", "network", "firewall", "zones"},
                arcombine(cbi("firewall/zones"), cbi("firewall/zone-details")),
index e8aca9c..ec087d9 100644 (file)
@@ -14,5 +14,5 @@ module "luci.controller.freifunk.policy-routing"
 
 function index()
        entry({"admin", "freifunk", "policyrouting"}, cbi("freifunk/policyrouting"),
-               _("Policy Routing"), 60).i18n = "freifunk-policyrouting"
+               _("Policy Routing"), 60)
 end
index 4361eb7..f7f0467 100644 (file)
@@ -10,7 +10,6 @@ You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0
 ]]--
 
-luci.i18n.loadc("freifunk")
 local uci = require "luci.model.uci".cursor()
 
 m = Map("freifunk-policyrouting", translate("Policy Routing"), translate("These pages can be used to setup policy routing for certain firewall zones. "..
index e28983f..a6f4adc 100644 (file)
@@ -23,6 +23,5 @@ function index()
        local page
 
        page = entry({"admin", "services", "hd_idle"}, cbi("hd_idle"), _("hd-idle"), 60)
-       page.i18n = "hd_idle"
        page.dependent = true
 end
index 81baf39..abce09c 100644 (file)
@@ -22,7 +22,6 @@ function index()
        page.target = firstchild()
        page.title  = _("VoIP")
        page.order  = 90
-       page.i18n = "telephony"
 
        entry({"admin", "telephony", "account"}, cbi("luci_ltqtapi/account") , _("Account"), 10)
        entry({"admin", "telephony", "contact"}, cbi("luci_ltqtapi/contact") , _("Contacts"), 20)
index 1bd5617..500c90a 100644 (file)
@@ -22,7 +22,6 @@ function index()
        local page
 
        page = entry({"admin", "services", "minidlna"}, cbi("minidlna"), _("miniDLNA"))
-       page.i18n = "minidlna"
        page.dependent = true
 
        entry({"admin", "services", "minidlna_status"}, call("minidlna_status"))
index a2336f7..098a72a 100644 (file)
@@ -1,5 +1,3 @@
-<%- luci.i18n.loadc("minidlna") -%>
-
 <script type="text/javascript">//<![CDATA[
        XHR.poll(5, '<%=luci.dispatcher.build_url("admin/services/minidlna_status")%>', null,
                function(x, st)
index 5abceab..b9984cb 100644 (file)
@@ -23,6 +23,5 @@ function index()
        local page
 
        page = entry({"admin", "system", "mmc_over_gpio"}, cbi("mmc_over_gpio"), _("MMC/SD driver configuration"), 60)
-       page.i18n = "mmc_over_gpio"
        page.dependent = true
 end
index 6aa4cf4..b596ef7 100644 (file)
@@ -9,13 +9,11 @@ function index()
        local page
 
        page = entry({"admin", "network", "multiwan"}, cbi("multiwan/multiwan"), _("Multi-WAN"))
-       page.i18n = "multiwan"
        page.dependent = true
        
        entry({"admin", "network", "multiwan", "status"}, call("multiwan_status"))
 
        page = entry({"mini", "network", "multiwan"}, cbi("multiwan/multiwanmini", {autoapply=true}), _("Multi-WAN"))
-       page.i18n = "multiwan"
        page.dependent = true
 end
 function multiwan_status()
index 782e216..b7f4cdf 100644 (file)
@@ -23,10 +23,8 @@ function index()
        local page
 
        page = entry({"admin", "system", "ntpc"}, cbi("ntpc/ntpc"), _("Time Synchronisation"), 50)
-       page.i18n = "ntpc"
        page.dependent = true
 
        page = entry({"mini", "system", "ntpc"}, cbi("ntpc/ntpcmini", {autoapply=true}), _("Time Synchronisation"), 50)
-       page.i18n = "ntpc"
        page.dependent = true
 end
index ab7f73e..5265359 100644 (file)
@@ -20,14 +20,21 @@ s.addremove = false
 
 s:option(DummyValue, "_time", translate("Current system time")).value = os.date("%c")
 
-s:option(Value, "interval", translate("Update interval (in seconds)")).rmempty = true
-s:option(Value, "count", translate("Count of time measurements"), translate("empty = infinite")).rmempty = true
+interval = s:option(Value, "interval", translate("Update interval (in seconds)"))
+interval.datatype = "and(uinteger,min(1))"
+interval.rmempty = true
 
+count = s:option(Value, "count", translate("Count of time measurements"), translate("empty = infinite"))
+count.datatype = "and(uinteger,min(1))"
+count.rmempty = true
 
 s2 = m:section(TypedSection, "ntpdrift", translate("Clock Adjustment"))
 s2.anonymous = true
 s2.addremove = false
-s2:option(Value, "freq", translate("Offset frequency")).rmempty = true
+
+freq = s2:option(Value, "freq", translate("Offset frequency"))
+freq.datatype = "integer"
+freq.rmempty = true
 
 s3 = m:section(TypedSection, "ntpserver", translate("Time Servers"))
 s3.anonymous = true
@@ -35,6 +42,8 @@ s3.addremove = true
 s3.template = "cbi/tblsection"
 
 s3:option(Value, "hostname", translate("Hostname"))
-s3:option(Value, "port", translate("Port")).rmempty = true
+port = s3:option(Value, "port", translate("Port"))
+port.datatype = "port"
+port.rmempty = true
 
 return m
index 9b56337..2a6c415 100644 (file)
@@ -21,8 +21,9 @@ s.addremove = false
 
 s:option(DummyValue, "_time", translate("Current system time")).value = os.date("%c")
 
-s:option(Value, "interval", translate("Update interval (in seconds)")).rmempty = true
-
+interval = s:option(Value, "interval", translate("Update interval (in seconds)"))
+interval.datatype = "and(uinteger,min(1))"
+interval.rmempty = true
 
 s3 = m:section(TypedSection, "ntpserver", translate("Time Server"))
 s3.anonymous = true
@@ -30,6 +31,8 @@ s3.addremove = true
 s3.template = "cbi/tblsection"
 
 s3:option(Value, "hostname", translate("Hostname"))
-s3:option(Value, "port", translate("Port")).rmempty = true
+port = s3:option(Value, "port", translate("Port"))
+port.datatype = "port"
+port.rmempty = true
 
 return m
index 371bad8..0342bde 100644 (file)
@@ -8,7 +8,6 @@ function index()
        local page  = node("admin", "status", "olsr")
        page.target = template("status-olsr/overview")
        page.title  = _("OLSR")
-       page.i18n   = "olsr"
        page.subindex = true
 
        local page  = node("admin", "status", "olsr", "neighbors")
@@ -51,7 +50,6 @@ function index()
                {"admin", "services", "olsrd"},
                cbi("olsr/olsrd"), "OLSR"
        )
-       ol.i18n = "olsr"
        ol.subindex = true
 
        entry(
@@ -74,7 +72,6 @@ function index()
                cbi("olsr/olsrddisplay"), _("Display")
                )
 
-       oplg.i18n = "olsr"
        oplg.leaf = true
        oplg.subindex = true
 
index 4550102..425d09a 100644 (file)
@@ -16,11 +16,11 @@ function etx_color(etx)
        local color = "#bb3333"
        if etx == 0 then
                color = "#bb3333"
-       elseif etx < 4 then
+       elseif etx < 2 then
                color = "#00cc00"
-       elseif etx < 10 then
+       elseif etx < 4 then
                color = "#ffcb05"
-       elseif etx < 100 then
+       elseif etx < 10 then
                color = "#ff6600"
        end
        return color
index 029f585..669ddc2 100644 (file)
@@ -147,6 +147,10 @@ end
        <li><strong>LQ: </strong><%:Success rate of packages received from the neighbour%></li>
        <li><strong>NLQ: </strong><%:Success rate of packages sent to the neighbour%></li>
        <li><strong>ETX: </strong><%:Expected retransmission count%></li>
+       <li><strong><span style="color:#00cc00"><%:Green%></span></strong>:<%:Very good (ETX < 2)%></li> 
+       <li><strong><span style="color:#ffcb05"><%:Yellow%></span></strong>:<%:Good (2 < ETX < 4)%></li> 
+       <li><strong><span style="color:#ff6600"><%:Orange%></span></strong>:<%:Still usable (4 < ETX < 10)%></li> 
+       <li><strong><span style="color:#bb3333"><%:Red%></span></strong>:<%:Bad (ETX > 10)%></li> 
 </ul>
 </fieldset>
 <%+footer%>
index 691ce17..8695dae 100644 (file)
@@ -16,7 +16,7 @@ $Id$
 module("luci.controller.openvpn", package.seeall)
 
 function index()
-       entry( {"admin", "services", "openvpn"}, cbi("openvpn"), _("OpenVPN") ).i18n = "openvpn"
+       entry( {"admin", "services", "openvpn"}, cbi("openvpn"), _("OpenVPN") )
        entry( {"admin", "services", "openvpn", "basic"},    cbi("openvpn-basic"),    nil ).leaf = true
        entry( {"admin", "services", "openvpn", "advanced"}, cbi("openvpn-advanced"), nil ).leaf = true
 end
index 50c9e7a..887f187 100644 (file)
@@ -16,5 +16,5 @@ module("luci.controller.ff_p2pblock", package.seeall)
 
 function index()
        entry({"admin", "network", "firewall", "p2pblock"}, cbi("luci_fw/p2pblock"),
-               _("P2P-Block"), 40).i18n = "p2pblock"
+               _("P2P-Block"), 40)
 end
index bc38c2b..4efaef8 100644 (file)
@@ -23,6 +23,5 @@ function index()
        local page
 
        page = entry({"admin", "services", "p910nd"}, cbi("p910nd"), _("p910nd - Printer server"), 60)
-       page.i18n = "p910nd"
        page.dependent = true
 end
index e450a62..7580a3e 100644 (file)
@@ -2,6 +2,7 @@
 
 LuCI p910nd
 (c) 2008 Yanira <forum-2008@email.de>
+(c) 2012 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.
@@ -9,15 +10,17 @@ You may obtain a copy of the License at
 
         http://www.apache.org/licenses/LICENSE-2.0
 
-$Id$
-
 ]]--
 
 local uci = luci.model.uci.cursor_state()
+local net = require "luci.model.network"
+local m, s, p, b
 
 m = Map("p910nd", translate("p910nd - Printer server"),
         translatef("First you have to install the packages to get support for USB (kmod-usb-printer) or parallel port (kmod-lp)."))
 
+net = net.init(m.uci)
+
 s = m:section(TypedSection, "p910nd", translate("Settings"))
 s.addremove = true
 s.anonymous = true
@@ -26,6 +29,25 @@ s:option(Flag, "enabled", translate("enable"))
 
 s:option(Value, "device", translate("Device")).rmempty = true
 
+b = s:option(Value, "bind", translate("Interface"), translate("Specifies the interface to listen on."))
+b.template = "cbi/network_netlist"
+b.nocreate = true
+b.unspecified = true
+
+function b.cfgvalue(...)
+       local v = Value.cfgvalue(...)
+       if v then
+               return (net:get_status_by_address(v))
+       end
+end
+
+function b.write(self, section, value)
+       local n = net:get_network(value)
+       if n and n:ipaddr() then
+               Value.write(self, section, n:ipaddr())
+       end
+end
+
 p = s:option(ListValue, "port", translate("Port"), translate("TCP listener port."))
 p.rmempty = true
 for i=0,9 do
index d07eaeb..1113f8f 100644 (file)
@@ -19,7 +19,7 @@ function index()
                return
        end
 
-       entry({"admin", "services", "polipo"}, alias("admin", "services", "polipo", "config"), _("Polipo")).i18n = "polipo"
+       entry({"admin", "services", "polipo"}, alias("admin", "services", "polipo", "config"), _("Polipo"))
        entry({"admin", "services", "polipo", "status"}, template("polipo_status"), _("Status"))
        entry({"admin", "services", "polipo", "config"}, cbi("polipo"), _("Configuration"))
 end
index c9eb9e5..e356599 100644 (file)
@@ -22,10 +22,8 @@ function index()
        local page
 
        page = entry({"admin", "network", "qos"}, cbi("qos/qos"), _("QoS"))
-       page.i18n = "qos"
        page.dependent = true
 
        page = entry({"mini", "network", "qos"}, cbi("qos/qosmini", {autoapply=true}), _("QoS"))
-       page.i18n = "qos"
        page.dependent = true
 end
index 98e9a25..1af3bdb 100644 (file)
@@ -35,9 +35,11 @@ s:option(Flag, "overhead", translate("Calculate overhead"))
 
 s:option(Flag, "halfduplex", translate("Half-duplex"))
 
-s:option(Value, "download", translate("Download speed (kbit/s)"))
+dl = s:option(Value, "download", translate("Download speed (kbit/s)"))
+dl.datatype = "and(uinteger,min(1))"
 
-s:option(Value, "upload", translate("Upload speed (kbit/s)"))
+ul = s:option(Value, "upload", translate("Upload speed (kbit/s)"))
+ul.datatype = "and(uinteger,min(1))"
 
 s = m:section(TypedSection, "classify", translate("Classification Rules"))
 s.template = "cbi/tblsection"
index 9ffefec..0c5766f 100644 (file)
@@ -21,8 +21,12 @@ m = Map("qos")
 s = m:section(NamedSection, "wan", "interface", translate("Internet Connection"))
 
 s:option(Flag, "enabled", translate("Quality of Service"))
-s:option(Value, "download", translate("Downlink"), "kbit/s")
-s:option(Value, "upload", translate("Uplink"), "kbit/s")
+
+dl = s:option(Value, "download", translate("Downlink"), "kbit/s")
+dl.datatype = "and(uinteger,min(1))"
+
+ul = s:option(Value, "upload", translate("Uplink"), "kbit/s")
+ul.datatype = "and(uinteger,min(1))"
 
 s = m:section(TypedSection, "classify")
 s.template = "cbi/tblsection"
index 09e7646..58dcbb2 100644 (file)
@@ -19,7 +19,7 @@ function index()
                return
        end
 
-       entry({"admin", "network", "radvd"}, cbi("radvd"), _("Radvd"), 61).i18n = "radvd"
+       entry({"admin", "network", "radvd"}, cbi("radvd"), _("Radvd"), 61)
        entry({"admin", "network", "radvd", "interface"}, cbi("radvd/interface"), nil).leaf = true
        entry({"admin", "network", "radvd", "prefix"}, cbi("radvd/prefix"), nil).leaf = true
        entry({"admin", "network", "radvd", "route"}, cbi("radvd/route"), nil).leaf = true
index 7d53133..ee7d44a 100644 (file)
@@ -23,6 +23,5 @@ function index()
        local page
 
        page = entry({"admin", "services", "samba"}, cbi("samba"), _("Network Shares"))
-       page.i18n = "samba"
        page.dependent = true
 end
index aceeb4a..7358458 100644 (file)
@@ -1,28 +1,25 @@
 module("luci.controller.splash.splash", package.seeall)
-luci.i18n.loadc("splash")
 
 local uci = luci.model.uci.cursor()
 local util = require "luci.util"
 
 function index()
-       entry({"admin", "services", "splash"}, cbi("splash/splash"), _("Client-Splash"), 90).i18n = "freifunk"
+       entry({"admin", "services", "splash"}, cbi("splash/splash"), _("Client-Splash"), 90)
        entry({"admin", "services", "splash", "splashtext" }, form("splash/splashtext"), _("Splashtext"), 10)
 
        local e
        
        e = node("splash")
        e.target = call("action_dispatch")
-       e.i18n = "freifunk"
 
        node("splash", "activate").target = call("action_activate")
        node("splash", "splash").target   = template("splash_splash/splash")
        node("splash", "blocked").target  = template("splash/blocked")
 
-       entry({"admin", "status", "splash"}, call("action_status_admin"), _("Client-Splash")).i18n = "freifunk"
+       entry({"admin", "status", "splash"}, call("action_status_admin"), _("Client-Splash"))
 
        local page  = node("splash", "publicstatus")
        page.target = call("action_status_public")
-       page.i18n   = "freifunk"
        page.leaf   = true
 end
 
index c03cd45..47d7695 100644 (file)
@@ -9,7 +9,6 @@ You may obtain a copy of the License at
 ]]--
 
 require("luci.model.uci")
-luci.i18n.loadc("splash")
 
 m = Map("luci_splash", translate("Client-Splash"), translate("Client-Splash is a hotspot authentification system for wireless mesh networks."))
 
index 5b744e1..8748a93 100644 (file)
@@ -13,7 +13,6 @@ You may obtain a copy of the License at
 ]]--
 
 local fs = require "nixio.fs"
-luci.i18n.loadc("splash")
 
 local splashtextfile = "/usr/lib/luci-splash/splashtext.html" 
 
index d979219..56cbe01 100644 (file)
@@ -19,7 +19,6 @@ local ipt = require "luci.sys.iptparser".IptParser()
 local uci = require "luci.model.uci".cursor_state()
 local wat = require "luci.tools.webadmin"
 local fs  = require "nixio.fs"
-luci.i18n.loadc("splash")
 
 local clients = { }
 local leasetime = tonumber(uci:get("luci_splash", "general", "leasetime") or 1) * 60 * 60
index 2e25154..869384c 100644 (file)
@@ -50,6 +50,7 @@ function index()
                memory          = _("Memory"),
                netlink         = _("Netlink"),
                network         = _("Network"),
+               nut                     = _("UPS"),
                olsrd           = _("OLSRd"),
                ping            = _("Ping"),
                processes       = _("Processes"),
@@ -61,13 +62,12 @@ function index()
        -- our collectd menu
        local collectd_menu = {
                output  = { "csv", "network", "rrdtool", "unixsock" },
-               system  = { "cpu", "df", "disk", "email", "exec", "irq", "load", "memory", "processes" },
+               system  = { "cpu", "df", "disk", "email", "exec", "irq", "load", "memory", "nut", "processes" },
                network = { "conntrack", "dns", "interface", "iptables", "netlink", "olsrd", "ping", "tcpconns", "iwinfo" }
        }
 
        -- create toplevel menu nodes
        local st = entry({"admin", "statistics"}, template("admin_statistics/index"), _("Statistics"), 80)
-       st.i18n = "statistics"
        st.index = true
        
        entry({"admin", "statistics", "collectd"}, cbi("luci_statistics/collectd"), _("Collectd"), 10).subindex = true
@@ -82,7 +82,6 @@ function index()
                )
 
                e.index = true
-               e.i18n  = "rrdtool"
 
                for j, plugin in luci.util.vspairs( plugins ) do
                        _entry(
@@ -97,7 +96,6 @@ function index()
 
        -- output views
        local page = entry( { "admin", "statistics", "graph" }, template("admin_statistics/index"), _("Graphs"), 80)
-             page.i18n     = "statistics"
              page.setuser  = "nobody"
              page.setgroup = "nogroup"
 
index c1fd7a3..2343854 100644 (file)
@@ -19,7 +19,7 @@ require("luci.sys")
 m = Map("luci_statistics",
        translate("Collectd Settings"),
        translate(
-               "Collectd is a small daeomon for collecting data from " ..
+               "Collectd is a small daemon for collecting data from " ..
                "various sources through different plugins. On this page " ..
                "you can change general settings for the collectd daemon."
        ))
diff --git a/applications/luci-statistics/luasrc/model/cbi/luci_statistics/nut.lua b/applications/luci-statistics/luasrc/model/cbi/luci_statistics/nut.lua
new file mode 100644 (file)
index 0000000..ad03071
--- /dev/null
@@ -0,0 +1,28 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright © 2011 Manuel Munz <freifunk at somakoma dot de>
+Copyright © 2012 David Woodhouse <dwmw2@infradead.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
+]]--
+
+m = Map("luci_statistics",
+       translate("UPS Plugin Configuration"),
+       translate("The NUT plugin reads information about Uninterruptible Power Supplies."))
+
+s = m:section(NamedSection, "collectd_nut", "luci_statistics" )
+
+enable = s:option(Flag, "enable", translate("Enable this plugin"))
+enable.default = 0
+
+host = s:option(Value, "UPS", translate("UPS"), translate("UPS name in NUT ups@host format"))
+host.placeholder = "myupsname"
+host.datatype = "string"
+host.rmempty = true
+
+return m
index a279e91..a1a2fa9 100644 (file)
@@ -25,9 +25,6 @@ Instance = luci.util.class()
 function Instance.__init__( self, graph )
        self.i18n  = luci.i18n
        self.graph = graph
-
-       self.i18n.loadc("rrdtool")
-       self.i18n.loadc("statistics")
 end
 
 function Instance._subst( self, str, val )
index 844c157..dbcae9d 100644 (file)
@@ -156,11 +156,14 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
 
                if not ds or ds:len() == 0 then ds = "value" end
 
-               _tif( _args, "DEF:%s_avg=%s:%s:AVERAGE", inst, rrd, ds )
+               _tif( _args, "DEF:%s_avg_raw=%s:%s:AVERAGE", inst, rrd, ds )
+               _tif( _args, "CDEF:%s_avg=%s_avg_raw,%s", inst, inst, source.transform_rpn )
 
                if not self.opts.rrasingle then
-                       _tif( _args, "DEF:%s_min=%s:%s:MIN", inst, rrd, ds )
-                       _tif( _args, "DEF:%s_max=%s:%s:MAX", inst, rrd, ds )
+                       _tif( _args, "DEF:%s_min_raw=%s:%s:MIN", inst, rrd, ds )
+                       _tif( _args, "CDEF:%s_min=%s_min_raw,%s", inst, inst, source.transform_rpn )
+                       _tif( _args, "DEF:%s_max_raw=%s:%s:MAX", inst, rrd, ds )
+                       _tif( _args, "CDEF:%s_max=%s_max_raw,%s", inst, inst, source.transform_rpn )
                end
 
                _tif( _args, "CDEF:%s_nnl=%s_avg,UN,0,%s_avg,IF", inst, inst, inst )
@@ -180,20 +183,23 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
 
                -- is first source in stack or overlay source: source_stk = source_nnl
                if not prev or source.overlay then
-                       -- create cdef statement
+                       -- create cdef statement for cumulative stack (no NaNs) and also
+                        -- for display (preserving NaN where no points should be displayed)
                        _tif( _args, "CDEF:%s_stk=%s_nnl", source.sname, source.sname )
+                       _tif( _args, "CDEF:%s_plot=%s_avg", source.sname, source.sname )
 
                -- is subsequent source without overlay: source_stk = source_nnl + previous_stk
                else
                        -- create cdef statement
                        _tif( _args, "CDEF:%s_stk=%s_nnl,%s_stk,+", source.sname, source.sname, prev )
+                       _tif( _args, "CDEF:%s_plot=%s_avg,%s_stk,+", source.sname, source.sname, prev )
                end
 
                -- create multiply by minus one cdef if flip is enabled
                if source.flip then
 
                        -- create cdef statement: source_stk = source_stk * -1
-                       _tif( _args, "CDEF:%s_neg=%s_stk,-1,*", source.sname, source.sname )
+                       _tif( _args, "CDEF:%s_neg=%s_plot,-1,*", source.sname, source.sname )
 
                        -- push to negative stack if overlay is disabled
                        if not source.overlay then
@@ -253,11 +259,11 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
                -- derive area background color from line color
                area_color = self.colors:to_string( self.colors:faded( area_color ) )
 
-               -- choose source_stk or source_neg variable depending on flip state
+               -- choose source_plot or source_neg variable depending on flip state
                if source.flip then
                        var = "neg"
                else
-                       var = "stk"
+                       var = "plot"
                end
 
                -- create legend
@@ -400,6 +406,7 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
                                        flip     = dopts.flip    or false,
                                        total    = dopts.total   or false,
                                        overlay  = dopts.overlay or false,
+                                       transform_rpn = dopts.transform_rpn or "0,+",
                                        noarea   = dopts.noarea  or false,
                                        title    = dopts.title   or nil,
                                        ds       = dsource,
@@ -450,6 +457,18 @@ function Graph._generic( self, opts, plugin, plugin_instance, dtype, index )
                _ti( _args, self.i18n:title( plugin, plugin_instance, _sources[1].type, instance, opts.title ) )
                _ti( _args, "-v" )
                _ti( _args, self.i18n:label( plugin, plugin_instance, _sources[1].type, instance, opts.vlabel ) )
+               if opts.y_max then
+                       _ti ( _args, "-u" )
+                       _ti ( _args, opts.y_max )
+               end
+               if opts.y_min then
+                       _ti ( _args, "-l" )
+                       _ti ( _args, opts.y_min )
+               end
+               if opts.units_exponent then
+                       _ti ( _args, "-X" )
+                       _ti ( _args, opts.units_exponent )
+               end
 
                -- store additional rrd options
                if opts.rrdopts then
index adc3f28..c0e8624 100644 (file)
@@ -19,6 +19,7 @@ function rrdargs( graph, plugin, plugin_instance, dtype )
 
        return {
                title = "%H: Processor usage on core #%pi",
+               y_min = "0",
                vlabel = "Percent",
                number_format = "%5.1lf%%",
                data = {
index 8052696..4cb4795 100644 (file)
@@ -19,6 +19,8 @@ function rrdargs( graph, plugin, plugin_instance, dtype )
 
        return {
                title = "%H: Load", vlabel = "Load",
+               y_min = "0",
+               units_exponent = "0",
                number_format = "%5.2lf", data = {
                        sources = {
                                load = { "shortterm", "midterm", "longterm" }
diff --git a/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/nut.lua b/applications/luci-statistics/luasrc/statistics/rrdtool/definitions/nut.lua
new file mode 100644 (file)
index 0000000..69f1ae3
--- /dev/null
@@ -0,0 +1,106 @@
+--[[
+
+Luci statistics - ups plugin diagram definition
+Copyright © 2008 Freifunk Leipzig / Jo-Philipp Wich <xm@leipzig.freifunk.net>
+Copyright © 2012 David Woodhouse <dwmw2@infradead.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
+
+]]--
+
+
+module("luci.statistics.rrdtool.definitions.nut",package.seeall)
+
+function rrdargs( graph, plugin, plugin_instance, dtype )
+
+       local voltages = {
+               title = "%H: Voltages on UPS \"%pi\"",
+               vlabel = "V",
+               number_format = "%5.1lfV",
+               data = {
+                       instances = {
+                               voltage = { "battery", "input", "output" }
+                       },
+
+                       options = {
+                               voltage_output  = { color = "00e000", title = "Output voltage", noarea=true, overlay=true },
+                               voltage_battery = { color = "0000ff", title = "Battery voltage", noarea=true, overlay=true },
+                               voltage_input   = { color = "ffb000", title = "Input voltage", noarea=true, overlay=true }
+                       }
+               }
+       }
+
+       local currents = {
+               title = "%H: Current on UPS \"%pi\"",
+               vlabel = "A",
+               number_format = "%5.3lfA",
+               data = {
+                       instances = {
+                               current = { "battery", "output" }
+                       },
+
+                       options = {
+                               current_output  = { color = "00e000", title = "Output current", noarea=true, overlay=true },
+                               current_battery = { color = "0000ff", title = "Battery current", noarea=true, overlay=true },
+                       }
+               }
+       }
+
+       local percentage = {
+               title = "%H: Battery charge on UPS \"%pi\"",
+               vlabel = "Percent",
+               y_min = "0",
+               y_max = "100",
+               number_format = "%5.1lf%%",
+               data = {
+                       sources = {
+                               percent = { "percent" }
+                       },
+                       instances = {
+                               percent = "charge"
+                       },
+                       options = {
+                               percent_charge = { color = "00ff00", title = "Charge level"  }
+                       }
+               }
+       }
+
+       -- Note: This is in ISO8859-1 for rrdtool. Welcome to the 20th century.
+       local temperature = {
+               title = "%H: Battery temperature on UPS \"%pi\"",
+               vlabel = "\176C",
+               number_format = "%5.1lf\176C",
+               data = {
+                       instances = {
+                               temperature = "battery"
+                       },
+
+                       options = {
+                               temperature_battery = { color = "ffb000", title = "Battery temperature" }
+                       }
+               }
+       }
+
+       local timeleft = {
+               title = "%H: Time left on UPS \"%pi\"",
+               vlabel = "Minutes",
+               number_format = "%.1lfm",
+               data = {
+                       sources = {
+                               timeleft = { "timeleft" }
+                       },
+                       instances = {
+                               timeleft = { "battery" }
+                       },
+                       options = {
+                               timeleft_battery = { color = "0000ff", title = "Time left", transform_rpn = "60,/" }
+                       }
+               }
+       }
+
+       return { voltages, currents, percentage, temperature, timeleft }
+end
index a1e6a6b..5267a02 100644 (file)
@@ -131,3 +131,7 @@ config 'statistics' 'collectd_olsrd'
 
 config 'statistics' 'collectd_iwinfo'
        option 'enable' '1'
+
+config 'statistics' 'collectd_nut'
+       option 'enable' '0'
+       option 'UPS' 'myupsname'
index 4cf3946..0a9fa1a 100755 (executable)
@@ -357,6 +357,12 @@ plugins = {
 
        network = config_network,
 
+       nut = {
+               { "UPS" },
+               { },
+               { }
+       },
+
        olsrd = {
                { "Host", "Port", "CollectLinks","CollectRoutes","CollectTopology"},
                { },
index 1dcb20c..0b81b90 100644 (file)
@@ -20,7 +20,7 @@ function index()
                return
        end
 
-       entry({"admin", "services", "tinyproxy"}, alias("admin", "services", "tinyproxy", "config"), _("Tinyproxy")).i18n = "tinyproxy"
+       entry({"admin", "services", "tinyproxy"}, alias("admin", "services", "tinyproxy", "config"), _("Tinyproxy"))
        entry({"admin", "services", "tinyproxy", "status"}, template("tinyproxy_status"), _("Status"))
        entry({"admin", "services", "tinyproxy", "config"}, cbi("tinyproxy"), _("Configuration"))
 end
index a5bcd64..2c4df46 100644 (file)
@@ -15,14 +15,11 @@ $Id$
 module("luci.controller.transmission", package.seeall)
 
 function index()
-       require("luci.i18n")
-       luci.i18n.loadc("transmission")
        if not nixio.fs.access("/etc/config/transmission") then
                return
        end
 
        local page = entry({"admin", "services", "transmission"}, cbi("transmission"), _("Transmission"))
-       page.i18n = "transmission"
        page.dependent = true
 
 end
index c40493d..7acd029 100644 (file)
@@ -23,11 +23,9 @@ function index()
        local page
 
        page = entry({"admin", "services", "upnp"}, cbi("upnp/upnp"), _("UPNP"))
-       page.i18n = "upnp"
        page.dependent = true
 
        page = entry({"mini", "network", "upnp"}, cbi("upnp/upnpmini", {autoapply=true}), _("UPNP"))
-       page.i18n = "upnp"
        page.dependent = true
 
        entry({"admin", "services", "upnp", "status"}, call("act_status")).leaf = true
@@ -69,10 +67,8 @@ function act_status()
        end
 end
 
-function act_delete()
-       local path = luci.dispatcher.context.requestpath
-       local idx = tonumber(path[#path])
-
+function act_delete(idx)
+       idx = tonumber(idx)
        if idx and idx > 0 then
                luci.sys.call("iptables -t filter -D MINIUPNPD %d 2>/dev/null" % idx)
                luci.sys.call("iptables -t nat -D MINIUPNPD %d 2>/dev/null" % idx)
index 11c72a5..d50ed0c 100644 (file)
@@ -1,5 +1,3 @@
-<%- luci.i18n.loadc("upnp") -%>
-
 <script type="text/javascript">//<![CDATA[
        function upnp_delete_fwd(idx) {
                XHR.get('<%=luci.dispatcher.build_url("admin", "services", "upnp", "delete")%>/' + idx, null,
index 7bbd2dd..eed8532 100644 (file)
@@ -23,6 +23,5 @@ function index()
        local page
 
        page = entry({"admin", "services", "ushare"}, cbi("ushare"), _("uShare"), 60)
-       page.i18n = "ushare"
        page.dependent = true
 end
index 4c10e2a..3871fa2 100644 (file)
@@ -1,11 +1,11 @@
 module("luci.controller.vnstat", package.seeall)
 
 function index()
-       entry({"admin", "status", "vnstat"}, alias("admin", "status", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90).i18n = "vnstat"
+       entry({"admin", "status", "vnstat"}, alias("admin", "status", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90)
        entry({"admin", "status", "vnstat", "graphs"}, template("vnstat"), _("Graphs"), 1)
        entry({"admin", "status", "vnstat", "config"}, cbi("vnstat"), _("Configuration"), 2)
 
-       entry({"mini", "network", "vnstat"}, alias("mini", "network", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90).i18n = "vnstat"
+       entry({"mini", "network", "vnstat"}, alias("mini", "network", "vnstat", "graphs"), _("VnStat Traffic Monitor"), 90)
        entry({"mini", "network", "vnstat", "graphs"}, template("vnstat"), _("Graphs"), 1)
        entry({"mini", "network", "vnstat", "config"}, cbi("vnstat"), _("Configuration"), 2)
 end
index dc1482e..728afb6 100644 (file)
@@ -18,18 +18,14 @@ function index()
 
    e = entry({"admin", "voice"}, template("luci_voice/index") , _("Voice"), 90)
    e.index = true
-   e.i18n = "voice_core"
 
    e = entry({"mini", "voice"}, template("luci_voice/index"), _("Voice"), 90)
    e.index = true
-   e.i18n = "voice_core"
 
    e = entry({"mini", "voice", "phones"}, template("luci_voice/phone_index"), _("Phones"), 90)
    e.index = true
-   e.i18n = "voice_core"
 
    e = entry({"admin", "voice", "phones"}, template("luci_voice/phone_index"), _("Phones"), 90)
    e.index = true
-   e.i18n = "voice_core"
 
 end
index 8fba53b..7c3f341 100644 (file)
@@ -18,6 +18,5 @@ function index()
 
        e = entry({"admin", "voice", "diag"}, template("luci_voice/diag_index"), _("Diagnostics"), 90)
        e.index = true
-       e.i18n = "voice_diag"
        e.dependent = true
 end
index bb98b6d..73a9594 100644 (file)
@@ -1,6 +1,6 @@
 module("luci.controller.wol", package.seeall)
 
 function index()
-       entry({"admin", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90).i18n = "wol"
-       entry({"mini", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90).i18n = "wol"
+       entry({"admin", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90)
+       entry({"mini", "network", "wol"}, cbi("wol"), _("Wake on LAN"), 90)
 end
index 9a206bb..b20fbc3 100755 (executable)
@@ -4,7 +4,8 @@ PATTERN=$1
 SCM=
 
 [ -d .svn ] && SCM="svn"
-[ -d .git ] && SCM="git"
+git=$( which git 2>/dev/null )
+[ "$git" ] && "$git" status >/dev/null && SCM="git"
 
 [ -z "$SCM" ] && {
        echo "Unsupported SCM tool" >&2
diff --git a/contrib/package/community-profiles/files/etc/config/profile_hamburg b/contrib/package/community-profiles/files/etc/config/profile_hamburg
deleted file mode 100644 (file)
index ba1dd21..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-config 'community' 'profile'
-       option 'name' 'Freifunk Hamburg'
-       option 'homepage' 'http://hamburg.piratenpartei.de'
-       option 'ssid' 'hamburg.freifunk.net'
-       option 'mesh_network' '10.112.0.0/12'
-       option 'splash_network' '10.104.0.0/16'
-       option 'splash_prefix' '27'
-       option 'latitude' '53.56262'
-       option 'longitude' '10.01069'
-
-config 'defaults' 'interface'
-       option 'netmask' '255.240.0.0'
-
-config 'defaults' 'wifi_device'
-       option 'channel' '1'
index 2dbe1fa..6bf3a96 100644 (file)
@@ -1,5 +1,6 @@
 #
 # Copyright (C) 2009 Andreas Seidler <tetzlav@subsignal.org>
+# Copyright (C) 2012 Jo-Philipp Wich <xm@subsignal.org>
 #
 # This is free software, licensed under the GNU General Public License v2.
 # See /LICENSE for more information.
@@ -8,7 +9,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=freifunk-p2pblock
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
 
index aa8b1c8..5ca60c8 100644 (file)
@@ -26,6 +26,7 @@ start() {
                        config_get ipp2p p2pblock ipp2p
                        config_get portrange p2pblock portrange
                        config_get blocktime p2pblock blocktime
+                       config_get whitelist p2pblock whitelist
 
                        # load modules
                        insmod ipt_ipp2p 2>&-
index 2b8bbfe..33267b6 100644 (file)
@@ -177,14 +177,13 @@ $(eval $(call library,fastindex,Fastindex indexing module,+PACKAGE_luci-lib-fast
 $(eval $(call library,httpclient,HTTP(S) client library,+luci-lib-web +luci-lib-nixio))
 $(eval $(call library,ipkg,LuCI IPKG/OPKG call abstraction library))
 $(eval $(call library,json,LuCI JSON library))
-$(eval $(call library,lmo,LuCI LMO I18N library))
 $(eval $(call library,lucid,LuCId Full-Stack Webserver,+luci-lib-nixio +luci-lib-web +luci-lib-px5g))
 $(eval $(call library,lucid-http,LuCId HTTP Backend,+luci-lib-lucid))
 $(eval $(call library,lucid-rpc,LuCId RPC Backend,+luci-lib-lucid))
 $(eval $(call library,nixio,NIXIO POSIX library,+PACKAGE_luci-lib-nixio_openssl:libopenssl +PACKAGE_luci-lib-nixio_cyassl:libcyassl))
 $(eval $(call library,px5g,RSA/X.509 Key Generator (required for LuCId SSL support),+luci-lib-nixio))
 $(eval $(call library,sys,LuCI Linux/POSIX system library))
-$(eval $(call library,web,MVC Webframework,+luci-lib-sys +luci-lib-nixio +luci-lib-core +luci-sgi-cgi +luci-lib-lmo))
+$(eval $(call library,web,MVC Webframework,+luci-lib-sys +luci-lib-nixio +luci-lib-core +luci-sgi-cgi))
 
 
 ### Protocols ###
@@ -327,7 +326,7 @@ endef
 $(eval $(call application,siitwizard,SIIT IPv4-over-IPv6 configuration wizard,\
        +PACKAGE_luci-app-siitwizard:kmod-siit))
 
-$(eval $(call application,firewall,Firmware and Portforwarding application,\
+$(eval $(call application,firewall,Firewall and Portforwarding application,\
        +PACKAGE_luci-app-firewall:firewall))
 
 $(eval $(call application,freifunk-policyrouting,Policy routing for mesh traffic,\
@@ -380,6 +379,8 @@ $(eval $(call application,voice-core,LuCI Voice Software (Core)))
 $(eval $(call application,voice-diag,LuCI Voice Software (Diagnostics),\
        luci-app-diag-devinfo))
 
+$(eval $(call application,commands,LuCI Shell Command Module))
+
 $(eval $(call application,upnp,Universal Plug & Play configuration module,\
        +PACKAGE_luci-app-upnp:miniupnpd))
 
index 785a166..0183c9e 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
 uci batch <<-EOF
-       set luci.languages.pl='Język polski'
+       set luci.languages.pl='Polski'
        commit luci
 EOF
index 0c574e0..a9f6fdb 100644 (file)
@@ -20,7 +20,7 @@ limitations under the License.
 local type, pairs, ipairs, table, luci, math
        = type, pairs, ipairs, table, luci, math
 
-local lmo = require "lmo"
+local tpl = require "luci.template.parser"
 local utl = require "luci.util"
 local uci = require "luci.model.uci"
 
@@ -478,7 +478,7 @@ function zone.get_color(self)
        elseif self and self:name() == "wan" then
                return "#f09090"
        elseif self then
-               math.randomseed(lmo.hash(self:name()))
+               math.randomseed(tpl.hash(self:name()))
 
                local r   = math.random(128)
                local g   = math.random(128)
index cc57229..56f1751 100644 (file)
@@ -623,6 +623,32 @@ function get_status_by_route(self, addr, mask)
        end
 end
 
+function get_status_by_address(self, addr)
+       local _, object
+       for _, object in ipairs(_ubus:objects()) do
+               local net = object:match("^network%.interface%.(.+)")
+               if net then
+                       local s = _ubus:call(object, "status", {})
+                       if s and s['ipv4-address'] then
+                               local a
+                               for _, a in ipairs(s['ipv4-address']) do
+                                       if a.address == addr then
+                                               return net, s
+                                       end
+                               end
+                       end
+                       if s and s['ipv6-address'] then
+                               local a
+                               for _, a in ipairs(s['ipv6-address']) do
+                                       if a.address == addr then
+                                               return net, s
+                                       end
+                               end
+                       end
+               end
+       end
+end
+
 function get_wannet(self)
        local net = self:get_status_by_route("0.0.0.0", 0)
        return net and network(net)
index bde803f..e285777 100644 (file)
@@ -182,27 +182,18 @@ end
 -- String and data manipulation routines
 --
 
---- Escapes all occurrences of the given character in given string.
--- @param s    String value containing unescaped characters
--- @param c    String value with character to escape (optional, defaults to "\")
--- @return     String value with each occurrence of character escaped with "\"
-function escape(s, c)
-       c = c or "\\"
-       return s:gsub(c, "\\" .. c)
-end
-
 --- 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.sanitize_pcdata(tostring(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(s)
-       return pcdata(tostring(s):gsub("</?[A-Za-z][A-Za-z0-9:_%-]*[^>]*>", " "):gsub("%s+", " "))
+function striptags(value)
+       return value and tparser.striptags(tostring(value))
 end
 
 --- Splits given string on a defined separator sequence and return a table
@@ -356,7 +347,6 @@ function parse_units(ustr)
 end
 
 -- also register functions above in the central string class for convenience
-string.escape      = escape
 string.pcdata      = pcdata
 string.striptags   = striptags
 string.split       = split
index d0d9788..c927e71 100644 (file)
@@ -10,7 +10,6 @@ You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
-$Id$
 ]]--
 
 local os   = require "os"
@@ -23,7 +22,7 @@ local pairs = pairs
 local error = error
 local table = table
 
-local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite"
+local ipkg = "opkg --force-removal-of-dependent-packages --force-overwrite --nocase"
 local icfg = "/etc/opkg.conf"
 
 --- LuCI OPKG call abstraction library
@@ -159,7 +158,7 @@ end
 -- List helper
 function _list(action, pat, cb)
        local fd = io.popen(ipkg .. " " .. action ..
-               (pat and (" '%s'" % pat:gsub("'", "")) or "")) -- .. " | grep -vE '^ '")
+               (pat and (" '%s'" % pat:gsub("'", "")) or ""))
 
        if fd then
                local name, version, desc
@@ -167,20 +166,18 @@ function _list(action, pat, cb)
                        local line = fd:read("*l")
                        if not line then break end
 
-                       if line:sub(1,1) ~= " " then
-                               name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
+                       name, version, desc = line:match("^(.-) %- (.-) %- (.+)")
 
-                               if not name then
-                                       name, version = line:match("^(.-) %- (.+)")
-                                       desc = ""
-                               end
+                       if not name then
+                               name, version = line:match("^(.-) %- (.+)")
+                               desc = ""
+                       end
 
-                               cb(name, version, desc)
+                       cb(name, version, desc)
 
-                               name    = nil
-                               version = nil
-                               desc    = nil
-                       end
+                       name    = nil
+                       version = nil
+                       desc    = nil
                end
 
                fd:close()
@@ -203,6 +200,15 @@ 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()
diff --git a/libs/lmo/Makefile b/libs/lmo/Makefile
deleted file mode 100644 (file)
index a15390c..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-ifneq (,$(wildcard ../../build/config.mk))
-include ../../build/config.mk
-include ../../build/module.mk
-include ../../build/gccconfig.mk
-else
-include standalone.mk
-endif
-
-LMO_LDFLAGS    =
-LMO_CFLAGS     =
-LMO_SO         = lmo.so
-LMO_PO2LMO     = po2lmo
-LMO_LOOKUP     = lookup
-LMO_COMMON_OBJ = src/lmo_core.o src/lmo_hash.o
-LMO_PO2LMO_OBJ = src/lmo_po2lmo.o
-LMO_LOOKUP_OBJ = src/lmo_lookup.o
-LMO_LUALIB_OBJ = src/lmo_lualib.o
-
-%.o: %.c
-       $(COMPILE) $(LMO_CFLAGS) $(LUA_CFLAGS) $(FPIC) -c -o $@ $< 
-
-compile: build-clean $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ) $(LMO_LOOKUP_OBJ) $(LMO_LUALIB_OBJ)
-       $(LINK) $(SHLIB_FLAGS) $(LMO_LDFLAGS) -o src/$(LMO_SO) \
-               $(LMO_COMMON_OBJ) $(LMO_LUALIB_OBJ)
-       $(LINK) $(LMO_LDFLAGS) -o src/$(LMO_PO2LMO) $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ)
-       $(LINK) $(LMO_LDFLAGS) -o src/$(LMO_LOOKUP) $(LMO_COMMON_OBJ) $(LMO_LOOKUP_OBJ)
-       mkdir -p dist$(LUA_LIBRARYDIR)
-       cp src/$(LMO_SO) dist$(LUA_LIBRARYDIR)/$(LMO_SO)
-
-install: build
-       cp -pR dist$(LUA_LIBRARYDIR)/* $(LUA_LIBRARYDIR)
-
-clean: build-clean
-
-build-clean:
-       rm -f src/*.o src/lookup src/po2lmo src/lmo.so
-
-host-compile: build-clean host-clean $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ)
-       $(LINK) $(LMO_LDFLAGS) -o src/$(LMO_PO2LMO) $(LMO_COMMON_OBJ) $(LMO_PO2LMO_OBJ)
-
-host-install: host-compile
-       cp src/$(LMO_PO2LMO) ../../build/$(LMO_PO2LMO)
-
-host-clean:
-       rm -f ../../build/$(LMO_PO2LMO)
-
diff --git a/libs/lmo/src/lmo.h b/libs/lmo/src/lmo.h
deleted file mode 100644 (file)
index ab17e87..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * lmo - Lua Machine Objects - General header
- *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.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.
- */
-
-#ifndef _LMO_H_
-#define _LMO_H_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <errno.h>
-
-
-#if (defined(__GNUC__) && defined(__i386__))
-#define sfh_get16(d) (*((const uint16_t *) (d)))
-#else
-#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
-                                          +(uint32_t)(((const uint8_t *)(d))[0]) )
-#endif
-
-
-struct lmo_entry {
-       uint32_t key_id;
-       uint32_t val_id;
-       uint32_t offset;
-       uint32_t length;
-       struct lmo_entry *next;
-} __attribute__((packed));
-
-typedef struct lmo_entry lmo_entry_t;
-
-
-struct lmo_archive {
-       int         fd;
-       uint32_t    length;
-       lmo_entry_t *index;
-       char        *mmap;
-};
-
-typedef struct lmo_archive lmo_archive_t;
-
-
-uint32_t sfh_hash(const char * data, int len);
-
-char _lmo_error[1024];
-const char * lmo_error(void);
-
-lmo_archive_t * lmo_open(const char *file);
-int lmo_lookup(lmo_archive_t *ar, const char *key, char *dest, int len);
-void lmo_close(lmo_archive_t *ar);
-
-#endif
diff --git a/libs/lmo/src/lmo_core.c b/libs/lmo/src/lmo_core.c
deleted file mode 100644 (file)
index 0814138..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * lmo - Lua Machine Objects - Base functions
- *
- *   Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.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.
- */
-
-#include "lmo.h"
-
-extern char _lmo_error[1024];
-
-static int lmo_read32( int fd, uint32_t *val )
-{
-       if( read(fd, val, 4) < 4 )
-               return -1;
-
-       *val = ntohl(*val);
-
-       return 4;
-}
-
-static char * error(const char *message, int add_errno)
-{
-       memset(_lmo_error, 0, sizeof(_lmo_error));
-
-       if( add_errno )
-               snprintf(_lmo_error, sizeof(_lmo_error),
-                       "%s: %s", message, strerror(errno));
-       else
-               snprintf(_lmo_error, sizeof(_lmo_error), "%s", message);
-
-       return NULL;
-}
-
-const char * lmo_error(void)
-{
-       return _lmo_error;
-}
-
-lmo_archive_t * lmo_open(const char *file)
-{
-       int in = -1;
-       uint32_t idx_offset = 0;
-       uint32_t i;
-       struct stat s;
-
-       lmo_archive_t *ar    = NULL;
-       lmo_entry_t   *head  = NULL;
-       lmo_entry_t   *entry = NULL;
-
-       if( stat(file, &s) == -1 )
-       {
-               error("Can not stat file", 1);
-               goto cleanup;
-       }
-
-       if( (in = open(file, O_RDONLY)) == -1 )
-       {
-               error("Can not open file", 1);
-               goto cleanup;
-       }
-
-       if( lseek(in, -sizeof(uint32_t), SEEK_END) == -1 )
-       {
-               error("Can not seek to eof", 1);
-               goto cleanup;
-       }
-
-       if( lmo_read32(in, &idx_offset) != 4 )
-       {
-               error("Unexpected EOF while reading index offset", 0);
-               goto cleanup;
-       }
-
-       if( lseek(in, (off_t)idx_offset, SEEK_SET) == -1 )
-       {
-               error("Can not seek to index offset", 1);
-               goto cleanup;
-       }
-
-       if( (ar = (lmo_archive_t *) malloc(sizeof(lmo_archive_t))) != NULL )
-       {
-               ar->fd     = in;
-               ar->length = idx_offset;
-
-               fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
-
-               for( i = idx_offset;
-                    i < (s.st_size - sizeof(uint32_t));
-                    i += (4 * sizeof(uint32_t))
-               ) {
-                       if( (entry = (lmo_entry_t *) malloc(sizeof(lmo_entry_t))) != NULL )
-                       {
-                               if( (lmo_read32(ar->fd, &entry->key_id) == 4) &&
-                                   (lmo_read32(ar->fd, &entry->val_id) == 4) &&
-                                   (lmo_read32(ar->fd, &entry->offset) == 4) &&
-                                   (lmo_read32(ar->fd, &entry->length) == 4)
-                               ) {
-                                       entry->next = head;
-                                       head = entry;
-                               }
-                               else
-                               {
-                                       error("Unexpected EOF while reading index entry", 0);
-                                       goto cleanup;
-                               }
-                       }
-                       else
-                       {
-                               error("Out of memory", 0);
-                               goto cleanup;
-                       }
-               }
-
-               ar->index = head;
-
-               if( lseek(ar->fd, 0, SEEK_SET) == -1 )
-               {
-                       error("Can not seek to start", 1);
-                       goto cleanup;
-               }
-
-               if( (ar->mmap = mmap(NULL, ar->length, PROT_READ, MAP_PRIVATE, ar->fd, 0)) == MAP_FAILED )
-               {
-                       error("Failed to memory map archive contents", 1);
-                       goto cleanup;
-               }
-
-               return ar;
-       }
-       else
-       {
-               error("Out of memory", 0);
-               goto cleanup;
-       }
-
-
-       cleanup:
-
-       if( in > -1 )
-               close(in);
-
-       if( head != NULL )
-       {
-               entry = head;
-
-               while( entry != NULL )
-               {
-                       head = entry->next;
-                       free(entry);
-                       entry = head;
-               }
-
-               head = entry = NULL;
-       }
-
-       if( ar != NULL )
-       {
-               if( (ar->mmap != NULL) && (ar->mmap != MAP_FAILED) )
-                       munmap(ar->mmap, ar->length);
-
-               free(ar);
-               ar = NULL;
-       }
-
-       return NULL;
-}
-
-void lmo_close(lmo_archive_t *ar)
-{
-       lmo_entry_t *head  = NULL;
-       lmo_entry_t *entry = NULL;
-
-       if( ar != NULL )
-       {
-               entry = ar->index;
-
-               while( entry != NULL )
-               {
-                       head = entry->next;
-                       free(entry);
-                       entry = head;
-               }
-
-               head = entry = NULL;
-
-               if( (ar->mmap != NULL) && (ar->mmap != MAP_FAILED) )
-                       munmap(ar->mmap, ar->length);
-
-               close(ar->fd);
-               free(ar);
-
-               ar = NULL;
-       }
-}
-
-int lmo_lookup(lmo_archive_t *ar, const char *key, char *dest, int len)
-{
-       uint32_t look_key = sfh_hash(key, strlen(key));
-       int copy_len = -1;
-       lmo_entry_t *entry;
-
-       if( !ar )
-               return copy_len;
-
-       entry = ar->index;
-
-       while( entry != NULL )
-       {
-               if( entry->key_id == look_key )
-               {
-                       copy_len = ((len - 1) > entry->length) ? entry->length : (len - 1);
-                       memcpy(dest, &ar->mmap[entry->offset], copy_len);
-                       dest[copy_len] = '\0';
-
-                       break;
-               }
-
-               entry = entry->next;
-       }
-
-       return copy_len;
-}
diff --git a/libs/lmo/src/lmo_hash.c b/libs/lmo/src/lmo_hash.c
deleted file mode 100644 (file)
index bc8e6fe..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Hash function from http://www.azillionmonkeys.com/qed/hash.html
- * Copyright (C) 2004-2008 by Paul Hsieh
- */
-
-#include "lmo.h"
-
-uint32_t sfh_hash(const char * data, int len)
-{
-       uint32_t hash = len, tmp;
-       int rem;
-
-       if (len <= 0 || data == NULL) return 0;
-
-       rem = len & 3;
-       len >>= 2;
-
-       /* Main loop */
-       for (;len > 0; len--) {
-               hash  += sfh_get16(data);
-               tmp    = (sfh_get16(data+2) << 11) ^ hash;
-               hash   = (hash << 16) ^ tmp;
-               data  += 2*sizeof(uint16_t);
-               hash  += hash >> 11;
-       }
-
-       /* Handle end cases */
-       switch (rem) {
-               case 3: hash += sfh_get16(data);
-                       hash ^= hash << 16;
-                       hash ^= data[sizeof(uint16_t)] << 18;
-                       hash += hash >> 11;
-                       break;
-               case 2: hash += sfh_get16(data);
-                       hash ^= hash << 11;
-                       hash += hash >> 17;
-                       break;
-               case 1: hash += *data;
-                       hash ^= hash << 10;
-                       hash += hash >> 1;
-       }
-
-       /* Force "avalanching" of final 127 bits */
-       hash ^= hash << 3;
-       hash += hash >> 5;
-       hash ^= hash << 4;
-       hash += hash >> 17;
-       hash ^= hash << 25;
-       hash += hash >> 6;
-
-       return hash;
-}
-
diff --git a/libs/lmo/src/lmo_lookup.c b/libs/lmo/src/lmo_lookup.c
deleted file mode 100644 (file)
index 8b48f7f..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * lmo - Lua Machine Objects - Lookup utility
- *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.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.
- */
-
-#include "lmo.h"
-
-extern char _lmo_error[1024];
-
-static void die(const char *msg)
-{
-       printf("Error: %s\n", msg);
-       exit(1);
-}
-
-static void usage(const char *name)
-{
-       printf("Usage: %s input.lmo key\n", name);
-       exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-       char val[4096];
-       lmo_archive_t *ar = NULL;
-
-       if( argc != 3 )
-               usage(argv[0]);
-
-       if( (ar = (lmo_archive_t *) lmo_open(argv[1])) != NULL )
-       {
-               if( lmo_lookup(ar, argv[2], val, sizeof(val)) > -1 )
-               {
-                       printf("%s\n", val);
-               }
-
-               lmo_close(ar);
-       }
-       else
-       {
-               die(lmo_error());
-       }
-
-       return 0;
-}
diff --git a/libs/lmo/src/lmo_lualib.c b/libs/lmo/src/lmo_lualib.c
deleted file mode 100644 (file)
index 59d88a1..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * lmo - Lua Machine Objects - Lua binding
- *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.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.
- */
-
-#include "lmo_lualib.h"
-
-extern char _lmo_error[1024];
-
-
-static int lmo_L_open(lua_State *L) {
-       const char *filename = luaL_checklstring(L, 1, NULL);
-       lmo_archive_t *ar, **udata;
-
-       if( (ar = lmo_open(filename)) != NULL )
-       {
-               if( (udata = lua_newuserdata(L, sizeof(lmo_archive_t *))) != NULL )
-               {
-                       *udata = ar;
-                       luaL_getmetatable(L, LMO_ARCHIVE_META);
-                       lua_setmetatable(L, -2);
-                       return 1;
-               }
-
-               lmo_close(ar);
-               lua_pushnil(L);
-               lua_pushstring(L, "out of memory");
-               return 2;
-       }
-
-       lua_pushnil(L);
-       lua_pushstring(L, lmo_error());
-       return 2;
-}
-
-static int lmo_L_hash(lua_State *L) {
-       const char *data = luaL_checkstring(L, 1);
-       uint32_t hash = sfh_hash(data, strlen(data));
-       lua_pushinteger(L, (lua_Integer)hash);
-       return 1;
-}
-
-static lmo_luaentry_t *_lmo_push_entry(lua_State *L) {
-       lmo_luaentry_t *le;
-
-       if( (le = lua_newuserdata(L, sizeof(lmo_luaentry_t))) != NULL )
-       {
-               luaL_getmetatable(L, LMO_ENTRY_META);
-               lua_setmetatable(L, -2);
-
-               return le;
-       }
-
-       return NULL;
-}
-
-static int _lmo_lookup(lua_State *L, lmo_archive_t *ar, uint32_t hash) {
-       lmo_entry_t *e = ar->index;
-       lmo_luaentry_t *le = NULL;
-
-       while( e != NULL )
-       {
-               if( e->key_id == hash )
-               {
-                       if( (le = _lmo_push_entry(L)) != NULL )
-                       {
-                               le->archive = ar;
-                               le->entry   = e;
-                               return 1;
-                       }
-                       else
-                       {
-                               lua_pushnil(L);
-                               lua_pushstring(L, "out of memory");
-                               return 2;
-                       }
-               }
-
-               e = e->next;
-       }
-
-       lua_pushnil(L);
-       return 1;
-}
-
-static int lmo_L_get(lua_State *L) {
-       lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
-       uint32_t hash = (uint32_t) luaL_checkinteger(L, 2);
-       return _lmo_lookup(L, *ar, hash);
-}
-
-static int lmo_L_lookup(lua_State *L) {
-       lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
-       const char *key = luaL_checkstring(L, 2);
-       uint32_t hash = sfh_hash(key, strlen(key));
-       return _lmo_lookup(L, *ar, hash);
-}
-
-static int lmo_L_foreach(lua_State *L) {
-       lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
-       lmo_entry_t *e = (*ar)->index;
-
-       if( lua_isfunction(L, 2) )
-       {
-               while( e != NULL )
-               {
-                       lua_pushvalue(L, 2);
-                       lua_pushinteger(L, e->key_id);
-                       lua_pushlstring(L, &(*ar)->mmap[e->offset], e->length);
-                       lua_pcall(L, 2, 0, 0);
-                       e = e->next;
-               }
-       }
-
-       return 0;
-}
-
-static int lmo_L__gc(lua_State *L) {
-       lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
-
-       if( (*ar) != NULL )
-               lmo_close(*ar);
-
-       *ar = NULL;
-
-       return 0;
-}
-
-static int lmo_L__tostring(lua_State *L) {
-       lmo_archive_t **ar = luaL_checkudata(L, 1, LMO_ARCHIVE_META);
-       lua_pushfstring(L, "LMO Archive (%d bytes)", (*ar)->length);
-       return 1;
-}
-
-
-static int _lmo_convert_entry(lua_State *L, int idx) {
-       lmo_luaentry_t *le = luaL_checkudata(L, idx, LMO_ENTRY_META);
-
-       lua_pushlstring(L,
-               &le->archive->mmap[le->entry->offset],
-               le->entry->length
-       );
-
-       return 1;
-}
-
-static int lmo_L_entry__tostring(lua_State *L) {
-       return _lmo_convert_entry(L, 1);
-}
-
-static int lmo_L_entry__concat(lua_State *L) {
-       if( lua_isuserdata(L, 1) )
-               _lmo_convert_entry(L, 1);
-       else
-               lua_pushstring(L, lua_tostring(L, 1));
-
-       if( lua_isuserdata(L, 2) )
-               _lmo_convert_entry(L, 2);
-       else
-               lua_pushstring(L, lua_tostring(L, 2));
-
-       lua_concat(L, 2);
-
-       return 1;
-}
-
-static int lmo_L_entry__len(lua_State *L) {
-       lmo_luaentry_t *le = luaL_checkudata(L, 1, LMO_ENTRY_META);
-       lua_pushinteger(L, le->entry->length);
-       return 1;
-}
-
-static int lmo_L_entry__gc(lua_State *L) {
-       lmo_luaentry_t *le = luaL_checkudata(L, 1, LMO_ENTRY_META);
-       le->archive = NULL;
-       le->entry   = NULL;
-       return 0;
-}
-
-
-/* lmo method table */
-static const luaL_reg M[] = {
-       {"close",               lmo_L__gc},
-       {"get",                 lmo_L_get},
-       {"lookup",              lmo_L_lookup},
-       {"foreach",             lmo_L_foreach},
-       {"__tostring",  lmo_L__tostring},
-       {"__gc",                lmo_L__gc},
-       {NULL,                  NULL}
-};
-
-/* lmo.entry method table */
-static const luaL_reg E[] = {
-       {"__tostring",  lmo_L_entry__tostring},
-       {"__concat",    lmo_L_entry__concat},
-       {"__len",               lmo_L_entry__len},
-       {"__gc",                lmo_L_entry__gc},
-       {NULL,                  NULL}
-};
-
-/* module table */
-static const luaL_reg R[] = {
-       {"open",        lmo_L_open},
-       {"hash",        lmo_L_hash},
-       {NULL,          NULL}
-};
-
-LUALIB_API int luaopen_lmo(lua_State *L) {
-       luaL_newmetatable(L, LMO_ARCHIVE_META);
-       luaL_register(L, NULL, M);
-       lua_pushvalue(L, -1);
-       lua_setfield(L, -2, "__index");
-       lua_setglobal(L, LMO_ARCHIVE_META);
-
-       luaL_newmetatable(L, LMO_ENTRY_META);
-       luaL_register(L, NULL, E);
-       lua_pushvalue(L, -1);
-       lua_setfield(L, -2, "__index");
-       lua_setglobal(L, LMO_ENTRY_META);       
-
-       luaL_register(L, LMO_LUALIB_META, R);
-
-       return 1;
-}
diff --git a/libs/lmo/src/lmo_lualib.h b/libs/lmo/src/lmo_lualib.h
deleted file mode 100644 (file)
index 6435117..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * lmo - Lua Machine Objects - Lua library header
- *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.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.
- */
-
-#ifndef _LMO_LUALIB_H_
-#define _LMO_LUALIB_H_
-
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-
-#include "lmo.h"
-
-#define LMO_LUALIB_META  "lmo"
-#define LMO_ARCHIVE_META "lmo.archive"
-#define LMO_ENTRY_META   "lmo.entry"
-
-struct lmo_luaentry {
-       lmo_archive_t *archive;  
-       lmo_entry_t   *entry;
-};
-
-typedef struct lmo_luaentry lmo_luaentry_t;
-
-
-LUALIB_API int luaopen_lmo(lua_State *L);
-
-#endif
diff --git a/libs/lmo/src/lmo_po2lmo.c b/libs/lmo/src/lmo_po2lmo.c
deleted file mode 100644 (file)
index 380f18d..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * lmo - Lua Machine Objects - PO to LMO conversion tool
- *
- *   Copyright (C) 2009-2011 Jo-Philipp Wich <xm@subsignal.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.
- */
-
-#include "lmo.h"
-
-static void die(const char *msg)
-{
-       fprintf(stderr, "Error: %s\n", msg);
-       exit(1);
-}
-
-static void usage(const char *name)
-{
-       fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
-       exit(1);
-}
-
-static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
-{
-       if( fwrite(ptr, size, nmemb, stream) == 0 )
-               die("Failed to write stdout");
-}
-
-static int extract_string(const char *src, char *dest, int len)
-{
-       int pos = 0;
-       int esc = 0;
-       int off = -1;
-
-       for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
-       {
-               if( (off == -1) && (src[pos] == '"') )
-               {
-                       off = pos + 1;
-               }
-               else if( off >= 0 )
-               {
-                       if( esc == 1 )
-                       {
-                               dest[pos-off] = src[pos];
-                               esc = 0;
-                       }
-                       else if( src[pos] == '\\' )
-                       {
-                               off++;
-                               esc = 1;
-                       }
-                       else if( src[pos] != '"' )
-                       {
-                               dest[pos-off] = src[pos];
-                       }
-                       else
-                       {
-                               dest[pos-off] = '\0';
-                               break;
-                       }
-               }
-       }
-
-       return (off > -1) ? strlen(dest) : -1;
-}
-
-int main(int argc, char *argv[])
-{
-       char line[4096];
-       char key[4096];
-       char val[4096];
-       char tmp[4096];
-       int state  = 0;
-       int offset = 0;
-       int length = 0;
-       uint32_t key_id, val_id;
-
-       FILE *in;
-       FILE *out;
-
-       lmo_entry_t *head  = NULL;
-       lmo_entry_t *entry = NULL;
-
-       if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
-               usage(argv[0]);
-
-       memset(line, 0, sizeof(key));
-       memset(key, 0, sizeof(val));
-       memset(val, 0, sizeof(val));
-
-       while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
-       {
-               if( state == 0 && strstr(line, "msgid \"") == line )
-               {
-                       switch(extract_string(line, key, sizeof(key)))
-                       {
-                               case -1:
-                                       die("Syntax error in msgid");
-                               case 0:
-                                       state = 1;
-                                       break;
-                               default:
-                                       state = 2;
-                       }
-               }
-               else if( state == 1 || state == 2 )
-               {
-                       if( strstr(line, "msgstr \"") == line || state == 2 )
-                       {
-                               switch(extract_string(line, val, sizeof(val)))
-                               {
-                                       case -1:
-                                               state = 4;
-                                               break;
-                                       default:
-                                               state = 3;
-                               }
-                       }
-                       else
-                       {
-                               switch(extract_string(line, tmp, sizeof(tmp)))
-                               {
-                                       case -1:
-                                               state = 2;
-                                               break;
-                                       default:
-                                               strcat(key, tmp);
-                               }
-                       }
-               }
-               else if( state == 3 )
-               {
-                       switch(extract_string(line, tmp, sizeof(tmp)))
-                       {
-                               case -1:
-                                       state = 4;
-                                       break;
-                               default:
-                                       strcat(val, tmp);
-                       }
-               }
-
-               if( state == 4 )
-               {
-                       if( strlen(key) > 0 && strlen(val) > 0 )
-                       {
-                               key_id = sfh_hash(key, strlen(key));
-                               val_id = sfh_hash(val, strlen(val));
-
-                               if( key_id != val_id )
-                               {
-                                       if( (entry = (lmo_entry_t *) malloc(sizeof(lmo_entry_t))) != NULL )
-                                       {
-                                               memset(entry, 0, sizeof(entry));
-                                               length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
-
-                                               entry->key_id = htonl(key_id);
-                                               entry->val_id = htonl(val_id);
-                                               entry->offset = htonl(offset);
-                                               entry->length = htonl(strlen(val));
-
-                                               print(val, length, 1, out);
-                                               offset += length;
-
-                                               entry->next = head;
-                                               head = entry;
-                                       }
-                                       else
-                                       {
-                                               die("Out of memory");
-                                       }
-                               }
-                       }
-
-                       state = 0;
-                       memset(key, 0, sizeof(key));
-                       memset(val, 0, sizeof(val));
-               }
-
-               memset(line, 0, sizeof(line));
-       }
-
-       entry = head;
-       while( entry != NULL )
-       {
-               print(&entry->key_id, sizeof(uint32_t), 1, out);
-               print(&entry->val_id, sizeof(uint32_t), 1, out);
-               print(&entry->offset, sizeof(uint32_t), 1, out);
-               print(&entry->length, sizeof(uint32_t), 1, out);
-               entry = entry->next;
-       }
-
-       if( offset > 0 )
-       {
-               offset = htonl(offset);
-               print(&offset, sizeof(uint32_t), 1, out);
-               fsync(fileno(out));
-               fclose(out);
-       }
-       else
-       {
-               fclose(out);
-               unlink(argv[2]);
-       }
-
-       fclose(in);
-       return(0);
-}
diff --git a/libs/lmo/standalone.mk b/libs/lmo/standalone.mk
deleted file mode 100644 (file)
index 66a0e5a..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-LUAC = luac
-LUAC_OPTIONS = -s
-LUA_TARGET ?= source
-
-LUA_MODULEDIR = /usr/local/share/lua/5.1
-LUA_LIBRARYDIR = /usr/local/lib/lua/5.1
-
-OS ?= $(shell uname)
-
-LUA_SHLIBS = $(shell pkg-config --silence-errors --libs lua5.1 || pkg-config --silence-errors --libs lua-5.1 || pkg-config --silence-errors --libs lua)
-LUA_LIBS = $(if $(LUA_SHLIBS),$(LUA_SHLIBS),$(firstword $(wildcard /usr/lib/liblua.a /usr/local/lib/liblua.a /opt/local/lib/liblua.a)))
-LUA_CFLAGS = $(shell pkg-config --silence-errors --cflags lua5.1 || pkg-config --silence-errors --cflags lua-5.1 || pkg-config --silence-errors --cflags lua)
-
-CC = gcc
-AR = ar
-RANLIB = ranlib
-CFLAGS = -O2
-FPIC = -fPIC
-EXTRA_CFLAGS = --std=gnu99
-WFLAGS = -Wall -Werror -pedantic
-CPPFLAGS =
-COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) $(WFLAGS)
-ifeq ($(OS),Darwin)
-  SHLIB_FLAGS = -bundle -undefined dynamic_lookup
-else
-  SHLIB_FLAGS = -shared
-endif
-LINK = $(CC) $(LDFLAGS)
-
-.PHONY: all build compile luacompile luasource clean luaclean
-
-all: build
-
-build: luabuild gccbuild
-
-luabuild: lua$(LUA_TARGET)
-
-gccbuild: compile
-compile:
-
-clean: luaclean
-
-luasource:
-       mkdir -p dist$(LUA_MODULEDIR)
-       cp -pR root/* dist 2>/dev/null || true
-       cp -pR lua/* dist$(LUA_MODULEDIR) 2>/dev/null || true
-       for i in $$(find dist -name .svn); do rm -rf $$i || true; done
-
-luastrip: luasource
-       for i in $$(find dist -type f -name '*.lua'); do perl -e 'undef $$/; open( F, "< $$ARGV[0]" ) || die $$!; $$src = <F>; close F; $$src =~ s/--\[\[.*?\]\](--)?//gs; $$src =~ s/^\s*--.*?\n//gm; open( F, "> $$ARGV[0]" ) || die $$!; print F $$src; close F' $$i; done
-
-luacompile: luasource
-       for i in $$(find dist -name *.lua -not -name debug.lua); do $(LUAC) $(LUAC_OPTIONS) -o $$i $$i; done
-
-luaclean:
-       rm -rf dist
index d9f9700..1d28a3a 100644 (file)
@@ -1,19 +1,28 @@
+ifneq (,$(wildcard ../../build/config.mk))
 include ../../build/config.mk
 include ../../build/module.mk
 include ../../build/gccconfig.mk
+else
+include standalone.mk
+endif
 
 TPL_LDFLAGS    =
 TPL_CFLAGS     =
 TPL_SO         = parser.so
+TPL_PO2LMO     = po2lmo
+TPL_PO2LMO_OBJ = src/po2lmo.o
+TPL_LMO_OBJ    = src/template_lmo.o
 TPL_COMMON_OBJ = src/template_parser.o src/template_utils.o
 TPL_LUALIB_OBJ = src/template_lualib.o
 
 %.o: %.c
        $(COMPILE) $(TPL_CFLAGS) $(LUA_CFLAGS) $(FPIC) -c -o $@ $<
 
-compile: build-clean $(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ)
+compile: build-clean $(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
        $(LINK) $(SHLIB_FLAGS) $(TPL_LDFLAGS) -o src/$(TPL_SO) \
-               $(TPL_COMMON_OBJ) $(TPL_LUALIB_OBJ)
+               $(TPL_COMMON_OBJ) $(TPL_LMO_OBJ) $(TPL_LUALIB_OBJ)
+       $(LINK) -o src/$(TPL_PO2LMO) \
+               $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
        mkdir -p dist$(LUCI_LIBRARYDIR)/template
        cp src/$(TPL_SO) dist$(LUCI_LIBRARYDIR)/template/$(TPL_SO)
 
@@ -24,3 +33,12 @@ clean: build-clean
 
 build-clean:
        rm -f src/*.o src/$(TPL_SO)
+
+host-compile: build-clean host-clean $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
+       $(LINK) -o src/$(TPL_PO2LMO) $(TPL_LMO_OBJ) $(TPL_PO2LMO_OBJ)
+
+host-install: host-compile
+       cp src/$(TPL_PO2LMO) ../../build/$(TPL_PO2LMO)
+
+host-clean:
+       rm -f ../../build/$(TPL_PO2LMO)
index d8719c4..a30533b 100644 (file)
@@ -1097,216 +1097,243 @@ function cbi_tag_last(container)
        }
 }
 
-if( ! String.serialize )
-       String.serialize = function(o)
+String.prototype.serialize = function()
+{
+       var o = this;
+       switch(typeof(o))
        {
-               switch(typeof(o))
-               {
-                       case 'object':
-                               // null
-                               if( o == null )
-                               {
-                                       return 'null';
-                               }
+               case 'object':
+                       // null
+                       if( o == null )
+                       {
+                               return 'null';
+                       }
 
-                               // array
-                               else if( o.length )
-                               {
-                                       var i, s = '';
+                       // array
+                       else if( o.length )
+                       {
+                               var i, s = '';
 
-                                       for( var i = 0; i < o.length; i++ )
-                                               s += (s ? ', ' : '') + String.serialize(o[i]);
+                               for( var i = 0; i < o.length; i++ )
+                                       s += (s ? ', ' : '') + String.serialize(o[i]);
 
-                                       return '[ ' + s + ' ]';
-                               }
+                               return '[ ' + s + ' ]';
+                       }
 
-                               // object
-                               else
-                               {
-                                       var k, s = '';
+                       // object
+                       else
+                       {
+                               var k, s = '';
 
-                                       for( k in o )
-                                               s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
+                               for( k in o )
+                                       s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
 
-                                       return '{ ' + s + ' }';
-                               }
+                               return '{ ' + s + ' }';
+                       }
 
-                               break;
+                       break;
 
-                       case 'string':
-                               // complex string
-                               if( o.match(/[^a-zA-Z0-9_,.: -]/) )
-                                       return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
+               case 'string':
+                       // complex string
+                       if( o.match(/[^a-zA-Z0-9_,.: -]/) )
+                               return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
 
-                               // simple string
-                               else
-                                       return '"' + o + '"';
+                       // simple string
+                       else
+                               return '"' + o + '"';
 
-                               break;
+                       break;
 
-                       default:
-                               return o.toString();
-               }
+               default:
+                       return o.toString();
        }
+}
 
+String.prototype.format = function()
+{
+       if (!RegExp)
+               return;
 
-if( ! String.format )
-       String.format = function()
-       {
-               if (!arguments || arguments.length < 1 || !RegExp)
-                       return;
-
-               var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
-               var quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
+       var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
+       var quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
 
-               function esc(s, r) {
-                       for( var i = 0; i < r.length; i += 2 )
-                               s = s.replace(r[i], r[i+1]);
-                       return s;
-               }
+       function esc(s, r) {
+               for( var i = 0; i < r.length; i += 2 )
+                       s = s.replace(r[i], r[i+1]);
+               return s;
+       }
 
-               var str = arguments[0];
-               var out = '';
-               var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
-               var a = b = [], numSubstitutions = 0, numMatches = 0;
+       var str = this;
+       var out = '';
+       var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
+       var a = b = [], numSubstitutions = 0, numMatches = 0;
 
-               while( a = re.exec(str) )
-               {
-                       var m = a[1];
-                       var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
-                       var pPrecision = a[6], pType = a[7];
+       while( a = re.exec(str) )
+       {
+               var m = a[1];
+               var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
+               var pPrecision = a[6], pType = a[7];
 
-                       numMatches++;
+               numMatches++;
 
-                       if (pType == '%')
-                       {
-                               subst = '%';
-                       }
-                       else
+               if (pType == '%')
+               {
+                       subst = '%';
+               }
+               else
+               {
+                       if (numSubstitutions < arguments.length)
                        {
-                               if (numSubstitutions++ < arguments.length)
-                               {
-                                       var param = arguments[numSubstitutions];
+                               var param = arguments[numSubstitutions++];
 
-                                       var pad = '';
-                                       if (pPad && pPad.substr(0,1) == "'")
-                                               pad = leftpart.substr(1,1);
-                                       else if (pPad)
-                                               pad = pPad;
+                               var pad = '';
+                               if (pPad && pPad.substr(0,1) == "'")
+                                       pad = leftpart.substr(1,1);
+                               else if (pPad)
+                                       pad = pPad;
 
-                                       var justifyRight = true;
-                                       if (pJustify && pJustify === "-")
-                                               justifyRight = false;
+                               var justifyRight = true;
+                               if (pJustify && pJustify === "-")
+                                       justifyRight = false;
 
-                                       var minLength = -1;
-                                       if (pMinLength)
-                                               minLength = parseInt(pMinLength);
+                               var minLength = -1;
+                               if (pMinLength)
+                                       minLength = parseInt(pMinLength);
 
-                                       var precision = -1;
-                                       if (pPrecision && pType == 'f')
-                                               precision = parseInt(pPrecision.substring(1));
+                               var precision = -1;
+                               if (pPrecision && pType == 'f')
+                                       precision = parseInt(pPrecision.substring(1));
 
-                                       var subst = param;
+                               var subst = param;
 
-                                       switch(pType)
-                                       {
-                                               case 'b':
-                                                       subst = (parseInt(param) || 0).toString(2);
-                                                       break;
-
-                                               case 'c':
-                                                       subst = String.fromCharCode(parseInt(param) || 0);
-                                                       break;
-
-                                               case 'd':
-                                                       subst = (parseInt(param) || 0);
-                                                       break;
-
-                                               case 'u':
-                                                       subst = Math.abs(parseInt(param) || 0);
-                                                       break;
-
-                                               case 'f':
-                                                       subst = (precision > -1)
-                                                               ? ((parseFloat(param) || 0.0)).toFixed(precision)
-                                                               : (parseFloat(param) || 0.0);
-                                                       break;
-
-                                               case 'o':
-                                                       subst = (parseInt(param) || 0).toString(8);
-                                                       break;
-
-                                               case 's':
-                                                       subst = param;
-                                                       break;
-
-                                               case 'x':
-                                                       subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
-                                                       break;
-
-                                               case 'X':
-                                                       subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
-                                                       break;
-
-                                               case 'h':
-                                                       subst = esc(param, html_esc);
-                                                       break;
-
-                                               case 'q':
-                                                       subst = esc(param, quot_esc);
-                                                       break;
-
-                                               case 'j':
-                                                       subst = String.serialize(param);
-                                                       break;
-
-                                               case 't':
-                                                       var td = 0;
-                                                       var th = 0;
-                                                       var tm = 0;
-                                                       var ts = (param || 0);
-
-                                                       if (ts > 60) {
-                                                               tm = Math.floor(ts / 60);
-                                                               ts = (ts % 60);
-                                                       }
-
-                                                       if (tm > 60) {
-                                                               th = Math.floor(tm / 60);
-                                                               tm = (tm % 60);
-                                                       }
-
-                                                       if (th > 24) {
-                                                               td = Math.floor(th / 24);
-                                                               th = (th % 24);
-                                                       }
-
-                                                       subst = (td > 0)
-                                                               ? String.format('%dd %dh %dm %ds', td, th, tm, ts)
-                                                               : String.format('%dh %dm %ds', th, tm, ts);
-
-                                                       break;
-
-                                               case 'm':
-                                                       var mf = pMinLength ? parseInt(pMinLength) : 1000;
-                                                       var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
-
-                                                       var i = 0;
-                                                       var val = parseFloat(param || 0);
-                                                       var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
-
-                                                       for (i = 0; (i < units.length) && (val > mf); i++)
-                                                               val /= mf;
-
-                                                       subst = val.toFixed(pr) + ' ' + units[i];
-                                                       break;
-                                       }
+                               switch(pType)
+                               {
+                                       case 'b':
+                                               subst = (parseInt(param) || 0).toString(2);
+                                               break;
+
+                                       case 'c':
+                                               subst = String.fromCharCode(parseInt(param) || 0);
+                                               break;
+
+                                       case 'd':
+                                               subst = (parseInt(param) || 0);
+                                               break;
+
+                                       case 'u':
+                                               subst = Math.abs(parseInt(param) || 0);
+                                               break;
+
+                                       case 'f':
+                                               subst = (precision > -1)
+                                                       ? ((parseFloat(param) || 0.0)).toFixed(precision)
+                                                       : (parseFloat(param) || 0.0);
+                                               break;
+
+                                       case 'o':
+                                               subst = (parseInt(param) || 0).toString(8);
+                                               break;
+
+                                       case 's':
+                                               subst = param;
+                                               break;
+
+                                       case 'x':
+                                               subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
+                                               break;
+
+                                       case 'X':
+                                               subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
+                                               break;
+
+                                       case 'h':
+                                               subst = esc(param, html_esc);
+                                               break;
+
+                                       case 'q':
+                                               subst = esc(param, quot_esc);
+                                               break;
+
+                                       case 'j':
+                                               subst = String.serialize(param);
+                                               break;
+
+                                       case 't':
+                                               var td = 0;
+                                               var th = 0;
+                                               var tm = 0;
+                                               var ts = (param || 0);
+
+                                               if (ts > 60) {
+                                                       tm = Math.floor(ts / 60);
+                                                       ts = (ts % 60);
+                                               }
+
+                                               if (tm > 60) {
+                                                       th = Math.floor(tm / 60);
+                                                       tm = (tm % 60);
+                                               }
+
+                                               if (th > 24) {
+                                                       td = Math.floor(th / 24);
+                                                       th = (th % 24);
+                                               }
+
+                                               subst = (td > 0)
+                                                       ? String.format('%dd %dh %dm %ds', td, th, tm, ts)
+                                                       : String.format('%dh %dm %ds', th, tm, ts);
+
+                                               break;
+
+                                       case 'm':
+                                               var mf = pMinLength ? parseInt(pMinLength) : 1000;
+                                               var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
+
+                                               var i = 0;
+                                               var val = parseFloat(param || 0);
+                                               var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
+
+                                               for (i = 0; (i < units.length) && (val > mf); i++)
+                                                       val /= mf;
+
+                                               subst = val.toFixed(pr) + ' ' + units[i];
+                                               break;
                                }
                        }
-
-                       out += leftpart + subst;
-                       str = str.substr(m.length);
                }
 
-               return out + str;
+               out += leftpart + subst;
+               str = str.substr(m.length);
        }
+
+       return out + str;
+}
+
+String.prototype.nobr = function()
+{
+       return this.replace(/[\s\n]+/g, '&#160;');
+}
+
+String.serialize = function()
+{
+       var a = [ ];
+       for (var i = 1; i < arguments.length; i++)
+               a.push(arguments[i]);
+       return ''.serialize.apply(arguments[0], a);
+}
+
+String.format = function()
+{
+       var a = [ ];
+       for (var i = 1; i < arguments.length; i++)
+               a.push(arguments[i]);
+       return ''.format.apply(arguments[0], a);
+}
+
+String.nobr = function()
+{
+       var a = [ ];
+       for (var i = 1; i < arguments.length; i++)
+               a.push(arguments[i]);
+       return ''.nobr.apply(arguments[0], a);
+}
index ef45a89..ae570b1 100644 (file)
@@ -74,8 +74,6 @@ function load(cbimap, ...)
 
        assert(func, err)
 
-       luci.i18n.loadc("base")
-
        local env = {
                translate=i18n.translate,
                translatef=i18n.translatef,
index 1811250..c53307a 100644 (file)
@@ -4,9 +4,6 @@ LuCI - HTTP-Interaction
 Description:
 HTTP-Header manipulator and form variable preprocessor
 
-FileId:
-$Id$
-
 License:
 Copyright 2008 Steven Barth <steven@midlink.org>
 
@@ -334,12 +331,14 @@ function write_json(x)
                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.   
+                       -- NaN is the only value that doesn't equal to itself.
                        write("Number.NaN")
                else
                        write(tostring(x))
                end
        else
-               write("%q" % tostring(x))
+               write('"%s"' % tostring(x):gsub('["%z\1-\31]', function(c)
+                       return '\\u%04x' % c:byte(1)
+               end))
        end
 end
index 816d903..545a8ae 100644 (file)
@@ -27,7 +27,8 @@ limitations under the License.
 --- LuCI translation library.
 module("luci.i18n", package.seeall)
 require("luci.util")
-require("lmo")
+
+local tparser = require "luci.template.parser"
 
 table   = {}
 i18ndir = luci.util.libpath() .. "/i18n/"
@@ -37,7 +38,6 @@ default = "en"
 
 --- Clear the translation table.
 function clear()
-       table = {}
 end
 
 --- Load a translation and copy its data into the translation table.
@@ -46,33 +46,6 @@ end
 -- @param force        Force reload even if already loaded (optional)
 -- @return             Success status
 function load(file, lang, force)
-       lang = lang and lang:gsub("_", "-") or ""
-       if force or not loaded[lang] or not loaded[lang][file] then
-               local f = lmo.open(i18ndir .. file .. "." .. lang .. ".lmo")
-               if f then
-                       if not table[lang] then
-                               table[lang] = { f }
-                               setmetatable(table[lang], {
-                                       __index = function(tbl, key)
-                                               for i = 1, #tbl do
-                                                       local s = rawget(tbl, i):lookup(key)
-                                                       if s then return s end
-                                               end
-                                       end
-                               })
-                       else
-                               table[lang][#table[lang]+1] = f
-                       end
-
-                       loaded[lang] = loaded[lang] or {}
-                       loaded[lang][file] = true
-                       return true
-               else
-                       return false
-               end
-       else
-               return true
-       end
 end
 
 --- Load a translation file using the default translation language.
@@ -80,9 +53,6 @@ end
 -- @param file Language file
 -- @param force        Force reload even if already loaded (optional)
 function loadc(file, force)
-       load(file, default, force)
-       if context.parent then load(file, context.parent, force) end
-       return load(file, context.lang, force)
 end
 
 --- Set the context default translation language.
@@ -90,16 +60,20 @@ end
 function setlanguage(lang)
        context.lang   = lang:gsub("_", "-")
        context.parent = (context.lang:match("^([a-z][a-z])_"))
+       if not tparser.load_catalog(context.lang, i18ndir) then
+               if context.parent then
+                       tparser.load_catalog(context.parent, i18ndir)
+                       return context.parent
+               end
+       end
+       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 (table[context.lang] and table[context.lang][key])
-               or (table[context.parent] and table[context.parent][key])
-               or (table[default] and table[default][key])
-               or key
+       return tparser.translate(key) or key
 end
 
 --- Return the translated value for a specific translation key and use it as sprintf pattern.
index 962c2ea..72127d1 100644 (file)
@@ -79,9 +79,8 @@ function Template.__init__(self, name)
                -- If we have no valid template throw error, otherwise cache the template
                if not self.template then
                        error("Failed to load template '" .. name .. "'.\n" ..
-                             "Error while parsing template '" .. sourcefile .. "'.\n" ..
-                             "A syntax error occured near '" ..
-                             (err or "(nil)"):gsub("\t", "\\t"):gsub("\n", "\\n") .. "'.")
+                             "Error while parsing template '" .. sourcefile .. "':\n" ..
+                             (err or "Unknown syntax error"))
                else
                        self.cache[name] = self.template
                end
index 78df97e..1814c93 100644 (file)
@@ -38,6 +38,6 @@
        //]]></script>
 
        <img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" />
-       <span id="cbi-apply-<%=id%>-status"><%:Waiting for router...%></span>
+       <span id="cbi-apply-<%=id%>-status"><%:Waiting for changes to be applied...%></span>
 </fieldset>
 <%-    end) %>
index ef67761..2c34028 100644 (file)
@@ -9,11 +9,15 @@
                <% if flow.skip then %>
                        <input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
                <% end %>
-               <% if not autoapply then %>
+               <% if not autoapply and not flow.hideapplybtn then %>
                        <input class="cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
                <% end %>
-               <input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" />
-               <input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
+               <% if not flow.hidesavebtn then %>
+                       <input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" />
+               <% end %>
+               <% if not flow.hideresetbtn then %>
+                       <input class="cbi-button cbi-button-reset" type="reset" value="<%:Reset%>" />
+               <% end %>
 
                <script type="text/javascript">cbi_d_update();</script>
        </div>
diff --git a/libs/web/src/po2lmo.c b/libs/web/src/po2lmo.c
new file mode 100644 (file)
index 0000000..fb607a4
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * lmo - Lua Machine Objects - PO to LMO conversion tool
+ *
+ *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.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.
+ */
+
+#include "template_lmo.h"
+
+static void die(const char *msg)
+{
+       fprintf(stderr, "Error: %s\n", msg);
+       exit(1);
+}
+
+static void usage(const char *name)
+{
+       fprintf(stderr, "Usage: %s input.po output.lmo\n", name);
+       exit(1);
+}
+
+static void print(const void *ptr, size_t size, size_t nmemb, FILE *stream)
+{
+       if( fwrite(ptr, size, nmemb, stream) == 0 )
+               die("Failed to write stdout");
+}
+
+static int extract_string(const char *src, char *dest, int len)
+{
+       int pos = 0;
+       int esc = 0;
+       int off = -1;
+
+       for( pos = 0; (pos < strlen(src)) && (pos < len); pos++ )
+       {
+               if( (off == -1) && (src[pos] == '"') )
+               {
+                       off = pos + 1;
+               }
+               else if( off >= 0 )
+               {
+                       if( esc == 1 )
+                       {
+                               switch (src[pos])
+                               {
+                               case '"':
+                               case '\\':
+                                       off++;
+                                       break;
+                               }
+                               dest[pos-off] = src[pos];
+                               esc = 0;
+                       }
+                       else if( src[pos] == '\\' )
+                       {
+                               dest[pos-off] = src[pos];
+                               esc = 1;
+                       }
+                       else if( src[pos] != '"' )
+                       {
+                               dest[pos-off] = src[pos];
+                       }
+                       else
+                       {
+                               dest[pos-off] = '\0';
+                               break;
+                       }
+               }
+       }
+
+       return (off > -1) ? strlen(dest) : -1;
+}
+
+static int cmp_index(const void *a, const void *b)
+{
+       uint32_t x = ntohl(((const lmo_entry_t *)a)->key_id);
+       uint32_t y = ntohl(((const lmo_entry_t *)b)->key_id);
+
+       if (x < y)
+               return -1;
+       else if (x > y)
+               return 1;
+
+       return 0;
+}
+
+static void print_index(void *array, int n, FILE *out)
+{
+       lmo_entry_t *e;
+
+       qsort(array, n, sizeof(*e), cmp_index);
+
+       for (e = array; n > 0; n--, e++)
+       {
+               print(&e->key_id, sizeof(uint32_t), 1, out);
+               print(&e->val_id, sizeof(uint32_t), 1, out);
+               print(&e->offset, sizeof(uint32_t), 1, out);
+               print(&e->length, sizeof(uint32_t), 1, out);
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       char line[4096];
+       char key[4096];
+       char val[4096];
+       char tmp[4096];
+       int state  = 0;
+       int offset = 0;
+       int length = 0;
+       int n_entries = 0;
+       void *array = NULL;
+       lmo_entry_t *entry = NULL;
+       uint32_t key_id, val_id;
+
+       FILE *in;
+       FILE *out;
+
+       if( (argc != 3) || ((in = fopen(argv[1], "r")) == NULL) || ((out = fopen(argv[2], "w")) == NULL) )
+               usage(argv[0]);
+
+       memset(line, 0, sizeof(key));
+       memset(key, 0, sizeof(val));
+       memset(val, 0, sizeof(val));
+
+       while( (NULL != fgets(line, sizeof(line), in)) || (state >= 2 && feof(in)) )
+       {
+               if( state == 0 && strstr(line, "msgid \"") == line )
+               {
+                       switch(extract_string(line, key, sizeof(key)))
+                       {
+                               case -1:
+                                       die("Syntax error in msgid");
+                               case 0:
+                                       state = 1;
+                                       break;
+                               default:
+                                       state = 2;
+                       }
+               }
+               else if( state == 1 || state == 2 )
+               {
+                       if( strstr(line, "msgstr \"") == line || state == 2 )
+                       {
+                               switch(extract_string(line, val, sizeof(val)))
+                               {
+                                       case -1:
+                                               state = 4;
+                                               break;
+                                       default:
+                                               state = 3;
+                               }
+                       }
+                       else
+                       {
+                               switch(extract_string(line, tmp, sizeof(tmp)))
+                               {
+                                       case -1:
+                                               state = 2;
+                                               break;
+                                       default:
+                                               strcat(key, tmp);
+                               }
+                       }
+               }
+               else if( state == 3 )
+               {
+                       switch(extract_string(line, tmp, sizeof(tmp)))
+                       {
+                               case -1:
+                                       state = 4;
+                                       break;
+                               default:
+                                       strcat(val, tmp);
+                       }
+               }
+
+               if( state == 4 )
+               {
+                       if( strlen(key) > 0 && strlen(val) > 0 )
+                       {
+                               key_id = sfh_hash(key, strlen(key));
+                               val_id = sfh_hash(val, strlen(val));
+
+                               if( key_id != val_id )
+                               {
+                                       n_entries++;
+                                       array = realloc(array, n_entries * sizeof(lmo_entry_t));
+                                       entry = (lmo_entry_t *)array + n_entries - 1;
+
+                                       if (!array)
+                                               die("Out of memory");
+
+                                       entry->key_id = htonl(key_id);
+                                       entry->val_id = htonl(val_id);
+                                       entry->offset = htonl(offset);
+                                       entry->length = htonl(strlen(val));
+
+                                       length = strlen(val) + ((4 - (strlen(val) % 4)) % 4);
+
+                                       print(val, length, 1, out);
+                                       offset += length;
+                               }
+                       }
+
+                       state = 0;
+                       memset(key, 0, sizeof(key));
+                       memset(val, 0, sizeof(val));
+               }
+
+               memset(line, 0, sizeof(line));
+       }
+
+       print_index(array, n_entries, out);
+
+       if( offset > 0 )
+       {
+               offset = htonl(offset);
+               print(&offset, sizeof(uint32_t), 1, out);
+               fsync(fileno(out));
+               fclose(out);
+       }
+       else
+       {
+               fclose(out);
+               unlink(argv[2]);
+       }
+
+       fclose(in);
+       return(0);
+}
diff --git a/libs/web/src/template_lmo.c b/libs/web/src/template_lmo.c
new file mode 100644 (file)
index 0000000..7fcd2cd
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * lmo - Lua Machine Objects - Base functions
+ *
+ *   Copyright (C) 2009-2010 Jo-Philipp Wich <xm@subsignal.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.
+ */
+
+#include "template_lmo.h"
+
+/*
+ * Hash function from http://www.azillionmonkeys.com/qed/hash.html
+ * Copyright (C) 2004-2008 by Paul Hsieh
+ */
+
+uint32_t sfh_hash(const char *data, int len)
+{
+       uint32_t hash = len, tmp;
+       int rem;
+
+       if (len <= 0 || data == NULL) return 0;
+
+       rem = len & 3;
+       len >>= 2;
+
+       /* Main loop */
+       for (;len > 0; len--) {
+               hash  += sfh_get16(data);
+               tmp    = (sfh_get16(data+2) << 11) ^ hash;
+               hash   = (hash << 16) ^ tmp;
+               data  += 2*sizeof(uint16_t);
+               hash  += hash >> 11;
+       }
+
+       /* Handle end cases */
+       switch (rem) {
+               case 3: hash += sfh_get16(data);
+                       hash ^= hash << 16;
+                       hash ^= data[sizeof(uint16_t)] << 18;
+                       hash += hash >> 11;
+                       break;
+               case 2: hash += sfh_get16(data);
+                       hash ^= hash << 11;
+                       hash += hash >> 17;
+                       break;
+               case 1: hash += *data;
+                       hash ^= hash << 10;
+                       hash += hash >> 1;
+       }
+
+       /* Force "avalanching" of final 127 bits */
+       hash ^= hash << 3;
+       hash += hash >> 5;
+       hash ^= hash << 4;
+       hash += hash >> 17;
+       hash ^= hash << 25;
+       hash += hash >> 6;
+
+       return hash;
+}
+
+uint32_t lmo_canon_hash(const char *str, int len)
+{
+       char res[4096];
+       char *ptr, prev;
+       int off;
+
+       if (!str || len >= sizeof(res))
+               return 0;
+
+       for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
+       {
+               if (isspace(*str))
+               {
+                       if (!isspace(prev))
+                               *ptr++ = ' ';
+               }
+               else
+               {
+                       *ptr++ = *str;
+               }
+       }
+
+       if ((ptr > res) && isspace(*(ptr-1)))
+               ptr--;
+
+       return sfh_hash(res, ptr - res);
+}
+
+lmo_archive_t * lmo_open(const char *file)
+{
+       int in = -1;
+       uint32_t idx_offset = 0;
+       struct stat s;
+
+       lmo_archive_t *ar = NULL;
+
+       if (stat(file, &s) == -1)
+               goto err;
+
+       if ((in = open(file, O_RDONLY)) == -1)
+               goto err;
+
+       if ((ar = (lmo_archive_t *)malloc(sizeof(*ar))) != NULL)
+       {
+               memset(ar, 0, sizeof(*ar));
+
+               ar->fd     = in;
+               ar->size = s.st_size;
+
+               fcntl(ar->fd, F_SETFD, fcntl(ar->fd, F_GETFD) | FD_CLOEXEC);
+
+               if ((ar->mmap = mmap(NULL, ar->size, PROT_READ, MAP_SHARED, ar->fd, 0)) == MAP_FAILED)
+                       goto err;
+
+               idx_offset = *((const uint32_t *)
+                                          (ar->mmap + ar->size - sizeof(uint32_t)));
+
+               if (idx_offset >= ar->size)
+                       goto err;
+
+               ar->index  = (lmo_entry_t *)(ar->mmap + idx_offset);
+               ar->length = (ar->size - idx_offset - sizeof(uint32_t)) / sizeof(lmo_entry_t);
+               ar->end    = ar->mmap + ar->size;
+
+               return ar;
+       }
+
+err:
+       if (in > -1)
+               close(in);
+
+       if (ar != NULL)
+       {
+               if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
+                       munmap(ar->mmap, ar->size);
+
+               free(ar);
+       }
+
+       return NULL;
+}
+
+void lmo_close(lmo_archive_t *ar)
+{
+       if (ar != NULL)
+       {
+               if ((ar->mmap != NULL) && (ar->mmap != MAP_FAILED))
+                       munmap(ar->mmap, ar->size);
+
+               close(ar->fd);
+               free(ar);
+
+               ar = NULL;
+       }
+}
+
+
+lmo_catalog_t *_lmo_catalogs = NULL;
+lmo_catalog_t *_lmo_active_catalog = NULL;
+
+int lmo_load_catalog(const char *lang, const char *dir)
+{
+       DIR *dh = NULL;
+       char pattern[16];
+       char path[PATH_MAX];
+       struct dirent *de = NULL;
+
+       lmo_archive_t *ar = NULL;
+       lmo_catalog_t *cat = NULL;
+
+       if (!lmo_change_catalog(lang))
+               return 0;
+
+       if (!dir || !(dh = opendir(dir)))
+               goto err;
+
+       if (!(cat = malloc(sizeof(*cat))))
+               goto err;
+
+       memset(cat, 0, sizeof(*cat));
+
+       snprintf(cat->lang, sizeof(cat->lang), "%s", lang);
+       snprintf(pattern, sizeof(pattern), "*.%s.lmo", lang);
+
+       while ((de = readdir(dh)) != NULL)
+       {
+               if (!fnmatch(pattern, de->d_name, 0))
+               {
+                       snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
+                       ar = lmo_open(path);
+
+                       if (ar)
+                       {
+                               ar->next = cat->archives;
+                               cat->archives = ar;
+                       }
+               }
+       }
+
+       closedir(dh);
+
+       cat->next = _lmo_catalogs;
+       _lmo_catalogs = cat;
+
+       if (!_lmo_active_catalog)
+               _lmo_active_catalog = cat;
+
+       return 0;
+
+err:
+       if (dh) closedir(dh);
+       if (cat) free(cat);
+
+       return -1;
+}
+
+int lmo_change_catalog(const char *lang)
+{
+       lmo_catalog_t *cat;
+
+       for (cat = _lmo_catalogs; cat; cat = cat->next)
+       {
+               if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
+               {
+                       _lmo_active_catalog = cat;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
+{
+       unsigned int m, l, r;
+
+       l = 0;
+       r = ar->length - 1;
+
+       while (1)
+       {
+               m = l + ((r - l) / 2);
+
+               if (r < l)
+                       break;
+
+               if (ar->index[m].key_id == hash)
+                       return &ar->index[m];
+
+               if (ar->index[m].key_id > hash)
+               {
+                       if (!m)
+                               break;
+
+                       r = m - 1;
+               }
+               else
+               {
+                       l = m + 1;
+               }
+       }
+
+       return NULL;
+}
+
+int lmo_translate(const char *key, int keylen, char **out, int *outlen)
+{
+       uint32_t hash;
+       lmo_entry_t *e;
+       lmo_archive_t *ar;
+
+       if (!key || !_lmo_active_catalog)
+               return -2;
+
+       hash = htonl(lmo_canon_hash(key, keylen));
+
+       for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
+       {
+               if ((e = lmo_find_entry(ar, hash)) != NULL)
+               {
+                       *out = ar->mmap + e->offset;
+                       *outlen = e->length;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+void lmo_close_catalog(const char *lang)
+{
+       lmo_archive_t *ar, *next;
+       lmo_catalog_t *cat, *prev;
+
+       for (prev = NULL, cat = _lmo_catalogs; cat; prev = cat, cat = cat->next)
+       {
+               if (!strncmp(cat->lang, lang, sizeof(cat->lang)))
+               {
+                       if (prev)
+                               prev->next = cat->next;
+                       else
+                               _lmo_catalogs = cat->next;
+
+                       for (ar = cat->archives; ar; ar = next)
+                       {
+                               next = ar->next;
+                               lmo_close(ar);
+                       }
+
+                       free(cat);
+                       break;
+               }
+       }
+}
diff --git a/libs/web/src/template_lmo.h b/libs/web/src/template_lmo.h
new file mode 100644 (file)
index 0000000..a40d758
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * lmo - Lua Machine Objects - General header
+ *
+ *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.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.
+ */
+
+#ifndef _TEMPLATE_LMO_H_
+#define _TEMPLATE_LMO_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#if (defined(__GNUC__) && defined(__i386__))
+#define sfh_get16(d) (*((const uint16_t *) (d)))
+#else
+#define sfh_get16(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+                                          +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+
+struct lmo_entry {
+       uint32_t key_id;
+       uint32_t val_id;
+       uint32_t offset;
+       uint32_t length;
+} __attribute__((packed));
+
+typedef struct lmo_entry lmo_entry_t;
+
+
+struct lmo_archive {
+       int         fd;
+       int             length;
+       uint32_t    size;
+       lmo_entry_t *index;
+       char        *mmap;
+       char            *end;
+       struct lmo_archive *next;
+};
+
+typedef struct lmo_archive lmo_archive_t;
+
+
+struct lmo_catalog {
+       char lang[6];
+       struct lmo_archive *archives;
+       struct lmo_catalog *next;
+};
+
+typedef struct lmo_catalog lmo_catalog_t;
+
+
+uint32_t sfh_hash(const char *data, int len);
+uint32_t lmo_canon_hash(const char *data, int len);
+
+lmo_archive_t * lmo_open(const char *file);
+void lmo_close(lmo_archive_t *ar);
+
+
+extern lmo_catalog_t *_lmo_catalogs;
+extern lmo_catalog_t *_lmo_active_catalog;
+
+int lmo_load_catalog(const char *lang, const char *dir);
+int lmo_change_catalog(const char *lang);
+int lmo_translate(const char *key, int keylen, char **out, int *outlen);
+void lmo_close_catalog(const char *lang);
+
+#endif
index d3a5f89..0d43641 100644 (file)
 int template_L_parse(lua_State *L)
 {
        const char *file = luaL_checkstring(L, 1);
-       struct template_parser parser;
-       int lua_status;
+       struct template_parser *parser = template_open(file);
+       int lua_status, rv;
 
-       if( (parser.fd = open(file, O_RDONLY)) > 0 )
+       if (!parser)
        {
-               parser.flags   = 0;
-               parser.bufsize = 0;
-               parser.state   = T_STATE_TEXT_NEXT;
+               lua_pushnil(L);
+               lua_pushinteger(L, errno);
+               lua_pushstring(L, strerror(errno));
+               return 3;
+       }
 
-               lua_status = lua_load(L, template_reader, &parser, file);
+       lua_status = lua_load(L, template_reader, parser, file);
 
-               (void) close(parser.fd);
+       if (lua_status == 0)
+               rv = 1;
+       else
+               rv = template_error(L, parser);
 
+       template_close(parser);
 
-               if( lua_status == 0 )
-               {
-                       return 1;
-               }
-               else
-               {
-                       lua_pushnil(L);
-                       lua_pushinteger(L, lua_status);
-                       lua_pushlstring(L, parser.out, parser.outsize);
-                       return 3;
-               }
+       return rv;
+}
+
+int template_L_utf8(lua_State *L)
+{
+       size_t len = 0;
+       const char *str = luaL_checklstring(L, 1, &len);
+       char *res = utf8(str, len);
+
+       if (res != NULL)
+       {
+               lua_pushstring(L, res);
+               free(res);
+
+               return 1;
        }
 
-       lua_pushnil(L);
-       lua_pushinteger(L, 255);
-       lua_pushstring(L, "No such file or directory");
-       return 3;
+       return 0;
 }
 
-int template_L_sanitize_utf8(lua_State *L)
+int template_L_pcdata(lua_State *L)
 {
        size_t len = 0;
        const char *str = luaL_checklstring(L, 1, &len);
-       char *res = sanitize_utf8(str, len);
+       char *res = pcdata(str, len);
 
        if (res != NULL)
        {
@@ -71,11 +78,11 @@ int template_L_sanitize_utf8(lua_State *L)
        return 0;
 }
 
-int template_L_sanitize_pcdata(lua_State *L)
+int template_L_striptags(lua_State *L)
 {
        size_t len = 0;
        const char *str = luaL_checklstring(L, 1, &len);
-       char *res = sanitize_pcdata(str, len);
+       char *res = striptags(str, len);
 
        if (res != NULL)
        {
@@ -88,12 +95,65 @@ int template_L_sanitize_pcdata(lua_State *L)
        return 0;
 }
 
+static int template_L_load_catalog(lua_State *L) {
+       const char *lang = luaL_optstring(L, 1, "en");
+       const char *dir  = luaL_optstring(L, 2, NULL);
+       lua_pushboolean(L, !lmo_load_catalog(lang, dir));
+       return 1;
+}
+
+static int template_L_close_catalog(lua_State *L) {
+       const char *lang = luaL_optstring(L, 1, "en");
+       lmo_close_catalog(lang);
+       return 0;
+}
+
+static int template_L_change_catalog(lua_State *L) {
+       const char *lang = luaL_optstring(L, 1, "en");
+       lua_pushboolean(L, !lmo_change_catalog(lang));
+       return 1;
+}
+
+static int template_L_translate(lua_State *L) {
+       size_t len;
+       char *tr;
+       int trlen;
+       const char *key = luaL_checklstring(L, 1, &len);
+
+       switch (lmo_translate(key, len, &tr, &trlen))
+       {
+               case 0:
+                       lua_pushlstring(L, tr, trlen);
+                       return 1;
+
+               case -1:
+                       return 0;
+       }
+
+       lua_pushnil(L);
+       lua_pushstring(L, "no catalog loaded");
+       return 2;
+}
+
+static int template_L_hash(lua_State *L) {
+       size_t len;
+       const char *key = luaL_checklstring(L, 1, &len);
+       lua_pushinteger(L, sfh_hash(key, len));
+       return 1;
+}
+
 
 /* module table */
 static const luaL_reg R[] = {
        { "parse",                              template_L_parse },
-       { "sanitize_utf8",              template_L_sanitize_utf8 },
-       { "sanitize_pcdata",    template_L_sanitize_pcdata },
+       { "utf8",                               template_L_utf8 },
+       { "pcdata",                             template_L_pcdata },
+       { "striptags",                  template_L_striptags },
+       { "load_catalog",               template_L_load_catalog },
+       { "close_catalog",              template_L_close_catalog },
+       { "change_catalog",             template_L_change_catalog },
+       { "translate",                  template_L_translate },
+       { "hash",                               template_L_hash },
        { NULL,                                 NULL }
 };
 
index d628b9d..1b659be 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "template_parser.h"
 #include "template_utils.h"
+#include "template_lmo.h"
 
 #define TEMPLATE_LUALIB_META  "template.parser"
 
index a0a400b..6054451 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * LuCI Template - Parser implementation
  *
- *   Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
+ *   Copyright (C) 2009-2012 Jo-Philipp Wich <xm@subsignal.org>
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
  */
 
 #include "template_parser.h"
+#include "template_utils.h"
+#include "template_lmo.h"
 
 
 /* leading and trailing code for different types */
-const char * gen_code[7][2] = {
+const char *gen_code[9][2] = {
+       { NULL,                                 NULL                    },
        { "write(\"",                   "\")"                   },
        { NULL,                                 NULL                    },
        { "write(tostring(",    " or \"\"))"    },
        { "include(\"",                 "\")"                   },
-       { "write(pcdata(translate(\"",  "\")))" },
-       { "write(translate(\"", "\"))"                  },
-       { NULL,                                 " "                             }
+       { "write(\"",                   "\")"                   },
+       { "write(\"",                   "\")"                   },
+       { NULL,                                 " "                             },
+       { NULL,                                 NULL                    },
 };
 
 /* Simple strstr() like function that takes len arguments for both haystack and needle. */
@@ -59,407 +63,324 @@ static char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
        return NULL;
 }
 
-/*
- * Inspect current read buffer and find the number of "vague" characters at the end
- * which could indicate an opening token. Returns the number of "vague" chars.
- * The last continuous sequence of whitespace, optionally followed by a "<" is
- * treated as "vague" because whitespace may be discarded if the upcoming opening
- * token indicates pre-whitespace-removal ("<%-"). A single remaining "<" char
- * can't be differentiated from an opening token ("<%"), so it's kept to be processed
- * in the next cycle.
- */
-static int stokscan(struct template_parser *data, int off, int no_whitespace)
+struct template_parser * template_open(const char *file)
 {
-       int i;
-       int skip = 0;
-       int tokoff = data->bufsize - 1;
+       struct stat s;
+       static struct template_parser *parser;
 
-       for( i = tokoff; i >= off; i-- )
-       {
-               if( data->buf[i] == T_TOK_START[0] )
-               {
-                       skip = tokoff - i + 1;
-                       tokoff = i - 1;
-                       break;
-               }
-       }
+       if (!(parser = malloc(sizeof(*parser))))
+               goto err;
+
+       memset(parser, 0, sizeof(*parser));
+       parser->fd = -1;
+       parser->file = file;
+
+       if (stat(file, &s))
+               goto err;
+
+       if ((parser->fd = open(file, O_RDONLY)) < 0)
+               goto err;
 
-       if( !no_whitespace )
+       parser->size = s.st_size;
+       parser->mmap = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE,
+                                               parser->fd, 0);
+
+       if (parser->mmap != MAP_FAILED)
        {
-               for( i = tokoff; i >= off; i-- )
-               {
-                       if( isspace(data->buf[i]) )
-                               skip++;
-                       else
-                               break;
-               }
+               parser->off = parser->mmap;
+               parser->cur_chunk.type = T_TYPE_INIT;
+               parser->cur_chunk.s    = parser->mmap;
+               parser->cur_chunk.e    = parser->mmap;
+
+               return parser;
        }
 
-       return skip;
+err:
+       template_close(parser);
+       return NULL;
 }
 
-/*
- * Similar to stokscan() but looking for closing token indicators.
- * Matches "-", optionally followed by a "%" char.
- */
-static int etokscan(struct template_parser *data)
+void template_close(struct template_parser *parser)
 {
-       int skip = 0;
+       if (!parser)
+               return;
+
+       if (parser->gc != NULL)
+               free(parser->gc);
 
-       if( (data->bufsize > 0) && (data->buf[data->bufsize-1] == T_TOK_END[0]) )
-               skip++;
+       if ((parser->mmap != NULL) && (parser->mmap != MAP_FAILED))
+               munmap(parser->mmap, parser->size);
 
-       if( (data->bufsize > skip) && (data->buf[data->bufsize-skip-1] == T_TOK_SKIPWS[0]) )
-               skip++;
+       if (parser->fd >= 0)
+               close(parser->fd);
 
-       return skip;
+       free(parser);
 }
 
-/*
- * Generate Lua expressions from the given raw code, write it into the
- * output buffer and set the lua_Reader specific size pointer.
- * Takes parser-state, lua_Reader's size pointer and generator flags
- * as parameter. The given flags indicate whether leading or trailing
- * code should be added. Returns a pointer to the output buffer.
- */
-static const char * generate_expression(struct template_parser *data, size_t *sz, int what)
+void template_text(struct template_parser *parser, const char *e)
 {
-       char tmp[T_OUTBUFSZ];
-       int i;
-       int size = 0;
-       int start = 0;
-       int whitespace = 0;
+       const char *s = parser->off;
 
-       memset(tmp, 0, T_OUTBUFSZ);
-
-       /* Inject leading expression code (if any) */
-       if( (what & T_GEN_START) && (gen_code[data->type][0] != NULL) )
+       if (s < (parser->mmap + parser->size))
        {
-               memcpy(tmp, gen_code[data->type][0], strlen(gen_code[data->type][0]));
-               size += strlen(gen_code[data->type][0]);
-       }
-
-       /* Parse source buffer */
-       for( i = 0; i < data->outsize; i++ )
-       {
-               /* Skip leading whitespace for non-raw and non-expr chunks */
-               if( !start && isspace(data->out[i]) && (data->type == T_TYPE_I18N ||
-           data->type == T_TYPE_I18N_RAW || data->type == T_TYPE_INCLUDE) )
-                       continue;
-               else if( !start )
-                       start = 1;
-
-               /* Found whitespace after i18n key */
-               if( data->type == T_TYPE_I18N || data->type == T_TYPE_I18N_RAW )
+               if (parser->strip_after)
                {
-                       /* Is initial whitespace, insert space */
-                       if( !whitespace && isspace(data->out[i]) )
-                       {
-                               tmp[size++] = ' ';
-                               whitespace = 1;
-                       }
-
-                       /* Suppress subsequent whitespace, escape special chars */
-                       else if( !isspace(data->out[i]) )
-                       {
-                               if( data->out[i] == '\\' || data->out[i] == '"' )
-                                       tmp[size++] = '\\';
-
-                               tmp[size++] = data->out[i];
-                               whitespace = 0;
-                       }
+                       while ((s <= e) && isspace(*s))
+                               s++;
                }
 
-               /* Escape quotes, backslashes and newlines for plain and include expressions */
-               else if( (data->type == T_TYPE_TEXT || data->type == T_TYPE_INCLUDE) &&
-                   (data->out[i] == '\\' || data->out[i] == '"' || data->out[i] == '\n' || data->out[i] == '\t') )
-               {
-                       tmp[size++] = '\\';
+               parser->cur_chunk.type = T_TYPE_TEXT;
+       }
+       else
+       {
+               parser->cur_chunk.type = T_TYPE_EOF;
+       }
 
-                       switch(data->out[i])
-                       {
-                               case '\n':
-                                       tmp[size++] = 'n';
-                                       break;
+       parser->cur_chunk.line = parser->line;
+       parser->cur_chunk.s = s;
+       parser->cur_chunk.e = e;
+}
 
-                               case '\t':
-                                       tmp[size++] = 't';
-                                       break;
+void template_code(struct template_parser *parser, const char *e)
+{
+       const char *s = parser->off;
 
-                               default:
-                                       tmp[size++] = data->out[i];
-                       }
-               }
+       parser->strip_before = 0;
+       parser->strip_after = 0;
 
-               /* Normal char */
-               else
-               {
-                       tmp[size++] = data->out[i];
-               }
+       if (*s == '-')
+       {
+               parser->strip_before = 1;
+               for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++);
        }
 
-       /* Inject trailing expression code (if any) */
-       if( (what & T_GEN_END) && (gen_code[data->type][1] != NULL) )
+       if (*(e-1) == '-')
        {
-               /* Strip trailing space for i18n expressions */
-               if( data->type == T_TYPE_I18N || data->type == T_TYPE_I18N_RAW )
-                       if( (size > 0) && (tmp[size-1] == ' ') )
-                               size--;
-
-               memcpy(&tmp[size], gen_code[data->type][1], strlen(gen_code[data->type][1]));
-               size += strlen(gen_code[data->type][1]);
+               parser->strip_after = 1;
+               for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--);
        }
 
-       *sz = data->outsize = size;
-       memset(data->out, 0, T_OUTBUFSZ);
-       memcpy(data->out, tmp, size);
+       switch (*s)
+       {
+               /* comment */
+               case '#':
+                       s++;
+                       parser->cur_chunk.type = T_TYPE_COMMENT;
+                       break;
 
-       //printf("<<<%i|%i|%i|%s>>>\n", what, data->type, *sz, data->out);
+               /* include */
+               case '+':
+                       s++;
+                 &nbs