diff --git a/README.md b/README.md
index e5b5d13e60..807f909e5e 100755
--- a/README.md
+++ b/README.md
@@ -42,7 +42,8 @@ OpenAppFilter source: [destan19/OpenAppFilter](https://github.com/destan19/OpenA
luci-app-smartinfo source: [animefansxj/luci-app-smartinfo](https://github.com/animefansxj/luci-app-smartinfo).
luci-app-koolproxy source: [Baozisoftware/luci-app-koolproxy](https://github.com/Baozisoftware/luci-app-koolproxy).
luci-app-k3screenctrl source: [Hill-98/luci-app-k3screenctrl](https://github.com/Hill-98/luci-app-k3screenctrl).
-luci-app-smstool source: [f8q8/luci-app-smstool-master](https://github.com/f8q8/luci-app-smstool-master).
+luci-app-smstool source: [f8q8/luci-app-smstool-master](https://github.com/f8q8/luci-app-smstool-master).
+luci-app-airwhu source: [KyleRicardo/luci-app-airwhu](https://github.com/KyleRicardo/luci-app-airwhu).
# License
### [GPL v3](https://www.gnu.org/licenses/gpl-3.0.html).
diff --git a/package/cnsztl/luci-app-airwhu/Makefile b/package/cnsztl/luci-app-airwhu/Makefile
new file mode 100755
index 0000000000..1b98b587d5
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/Makefile
@@ -0,0 +1,51 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-airwhu
+PKG_VERSION=1.0
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/luci-app-airwhu
+ SECTION:=luci
+ CATEGORY:=LuCI
+ SUBMENU:=3. Applications
+ DEPENDS:=+mentohust +kmod-ipt-nat6
+ TITLE:=luci-app-airwhu
+ PKGARCH:=all
+endef
+
+define Package/luci-app-airwhu/description
+ LuCI web-interface for Ruijie 802.1X Client with IPv6 NAT.
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+ $(CP) ./* $(PKG_BUILD_DIR)/
+ po2lmo $(PKG_BUILD_DIR)/po/airwhu.zh-cn.po $(PKG_BUILD_DIR)/po/airwhu.zh-cn.lmo
+endef
+
+define Build/Compile
+endef
+
+define Package/luci-app-airwhu/install
+ $(INSTALL_DIR) $(1)/etc/config
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_DIR) $(1)/etc/hotplug.d/iface
+ $(INSTALL_DIR) $(1)/bin
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
+ $(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
+
+ $(INSTALL_CONF) ./files/root/etc/config/airwhu $(1)/etc/config/airwhu
+ $(INSTALL_BIN) ./files/root/etc/init.d/mentohust $(1)/etc/init.d/mentohust
+ $(INSTALL_BIN) ./files/root/etc/hotplug.d/iface/99-ipv6nat $(1)/etc/hotplug.d/iface/99-ipv6nat
+ $(INSTALL_BIN) ./files/root/bin/ipv6masq.sh $(1)/bin/ipv6masq.sh
+ $(INSTALL_DATA) ./files/root/usr/lib/lua/luci/model/cbi/airwhu.lua $(1)/usr/lib/lua/luci/model/cbi/airwhu.lua
+ $(INSTALL_DATA) ./files/root/usr/lib/lua/luci/controller/airwhu.lua $(1)/usr/lib/lua/luci/controller/airwhu.lua
+ $(INSTALL_DATA) $(PKG_BUILD_DIR)/po/airwhu.zh-cn.lmo $(1)/usr/lib/lua/luci/i18n/airwhu.zh-cn.lmo
+endef
+
+$(eval $(call BuildPackage,luci-app-airwhu))
diff --git a/package/cnsztl/luci-app-airwhu/files/root/bin/ipv6masq.sh b/package/cnsztl/luci-app-airwhu/files/root/bin/ipv6masq.sh
new file mode 100755
index 0000000000..c04bb7c716
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/files/root/bin/ipv6masq.sh
@@ -0,0 +1,41 @@
+#!/bin/sh /etc/rc.common
+#Author:KyleRicardo
+#Email:shaoyz714@126.com
+
+action=$1
+
+if [ "$action"x = "install"x ]; then
+ uci delete network.globals.ula_prefix
+
+ uci set network.wan6.peerdns='0'
+ uci set network.wan6.dns='2001:da8:202:10::36'
+
+ uci delete network.lan.ip6assign
+ uci set network.lan.ip6addr='BABE:BABE:BABE:BABE::1/64'
+ uci set network.lan.ip6prefix='BABE:BABE:BABE:BABE::/64'
+
+ uci set dhcp.lan.ra_management='1'
+ uci set dhcp.lan.ra_default='1'
+
+ uci set $(uci show firewall|grep Allow-ICMPv6-Forward|sed -e 's/.name[^ ]*'//).enabled='0'
+
+ uci commit
+
+
+elif [ "$action"x = "uninstall"x ]; then
+ uci set network.globals.ula_prefix='fdad:91b7:54bb::/48'
+
+ uci delete network.wan6.peerdns='0'
+ uci delete network.wan6.dns='2001:da8:202:10::36'
+
+ uci set network.lan.ip6assign='60'
+ uci delete network.lan.ip6addr='BABE:BABE:BABE:BABE::1/64'
+ uci delete network.lan.ip6prefix='BABE:BABE:BABE:BABE::/64'
+
+ uci delete dhcp.lan.ra_management='1'
+ uci delete dhcp.lan.ra_default='1'
+
+ uci delete $(uci show firewall|grep Allow-ICMPv6-Forward|sed -e 's/.name[^ ]*'//).enabled
+
+ uci commit
+fi
diff --git a/package/cnsztl/luci-app-airwhu/files/root/etc/config/airwhu b/package/cnsztl/luci-app-airwhu/files/root/etc/config/airwhu
new file mode 100755
index 0000000000..734dc8539a
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/files/root/etc/config/airwhu
@@ -0,0 +1,25 @@
+
+config switch
+ option enableauth
+ option startwithboot
+ option enableipv6
+config auth
+ option Username
+ option Password
+ option ifname
+ option IP
+ option Mask
+ option Gateway
+ option DNS
+ option PingHost
+ option Timeout
+ option EchoInterval
+ option RestartWait
+ option MaxFail
+ option StartMode
+ option DHCPMode
+ option DaemonMode
+ option ShowNotify
+ option Version
+ option DataFile
+ option DHCPScript
diff --git a/package/cnsztl/luci-app-airwhu/files/root/etc/hotplug.d/iface/99-ipv6nat b/package/cnsztl/luci-app-airwhu/files/root/etc/hotplug.d/iface/99-ipv6nat
new file mode 100755
index 0000000000..2d7230b37b
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/files/root/etc/hotplug.d/iface/99-ipv6nat
@@ -0,0 +1,56 @@
+#!/bin/sh /etc/rc.common
+#Author:KyleRicardo
+#Email:shaoyz714@126.com
+
+
+[ "$ACTION" = ifup ] || exit 0
+iface=wan6
+[ -z "$iface" -o "$INTERFACE" = "$iface" ] || exit 0
+ip -6 route add `ip -6 route show default|sed -e 's/from [^ ]* //'`
+logger -t IPv6 "Add IPv6 default route."
+
+MAX_TRIES=99
+
+WAN6_NAME="wan6"
+
+WAN6_INTERFACE=$(uci get "network.$WAN6_NAME.ifname")
+#eth0.2 by default
+
+LAN_IP6PREFIX=$(uci get network.lan.ip6prefix)
+#e.g. aaaa:bbbb:cccc:dddd::/64
+
+#LAN_ULA_PREFIX=$(uci get network.globals.ula_prefix)
+#e.g. ddc2:d512:65f5::/48
+
+PROBE=0
+
+COUNT=1
+
+while [ $PROBE -eq 0 ]
+do
+if [ $COUNT -gt $MAX_TRIES ]
+then
+logger -t NAT6 "No IPv6 route found (reached retry limit $MAX_TRIES times)" && exit 1
+fi
+sleep 5
+logger -t NAT6 "Probing IPv6 route ($COUNT time)"
+COUNT=$((COUNT+1))
+PROBE=$(route -A inet6 | grep -c '::/0')
+done
+
+#ip6tables -t nat -I POSTROUTING -s "$LAN_ULA_PREFIX" -o "$WAN6_INTERFACE" -j MASQUERADE
+ip6tables -t nat -I POSTROUTING -s "$LAN_IP6PREFIX" -o "$WAN6_INTERFACE" -j MASQUERADE
+
+#WAN6_GATEWAY=$(route -A inet6 -e | grep "$WAN6_INTERFACE" | awk '/::\/0/{print $2; exit}')
+#get gateway from routing table. !!!Caution!!! May not work !
+WAN6_GATEWAY=$(ifconfig eth0.2 | grep 'Global' | awk '{print $3}'| awk -F':' '{print $1":"$2":"$3":"$4"::1"}')
+#caculate gateway from wan ipv6
+#WAN6_GATEWAY=$(tracepath6 -n tv.byr.cn | grep ' 1: ' | awk 'NR==1 {print $2}')
+#opkg install iputils-tracepath6 . change tv.byr.cn for faster site. e.g. ipv6.bjtu.edu.cn
+
+#route -A inet6 add 2000::/3 gw "$WAN6_GATEWAY" dev "$WAN6_INTERFACE"
+route -A inet6 add default gw "$WAN6_GATEWAY" dev "$WAN6_INTERFACE"
+
+logger -t NAT6 "Done with IPv6 settings"
+
+
diff --git a/package/cnsztl/luci-app-airwhu/files/root/etc/init.d/mentohust b/package/cnsztl/luci-app-airwhu/files/root/etc/init.d/mentohust
new file mode 100755
index 0000000000..cf622808ee
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/files/root/etc/init.d/mentohust
@@ -0,0 +1,49 @@
+#!/bin/sh /etc/rc.common
+#Author:KyleRicardo
+#Email:shaoyz714@126.com
+
+START=65
+STOP=10
+
+start()
+{
+if [ $(uci get airwhu.@switch[0].enableauth) ] ;
+then
+ local Username=$(uci get airwhu.@auth[0].Username)
+ local Password=$(uci get airwhu.@auth[0].Password)
+ local ifname=$(uci get airwhu.@auth[0].ifname)
+ local IP=$(uci get airwhu.@auth[0].IP)
+ local Mask=$(uci get airwhu.@auth[0].Mask)
+ local Gateway=$(uci get airwhu.@auth[0].Gateway)
+ local DNS=$(uci get airwhu.@auth[0].DNS)
+ local PingHost=$(uci get airwhu.@auth[0].PingHost)
+ local Timeout=$(uci get airwhu.@auth[0].Timeout)
+ local EchoInterval=$(uci get airwhu.@auth[0].EchoInterval)
+ local RestartWait=$(uci get airwhu.@auth[0].RestartWait)
+ local MaxFail=$(uci get airwhu.@auth[0].MaxFail)
+ local StartMode=$(uci get airwhu.@auth[0].StartMode)
+ local DHCPMode=$(uci get airwhu.@auth[0].DHCPMode)
+ local DaemonMode=$(uci get airwhu.@auth[0].DaemonMode)
+ local ShowNotify=$(uci get airwhu.@auth[0].ShowNotify)
+ local Version=$(uci get airwhu.@auth[0].Version)
+ local DataFile=$(uci get airwhu.@auth[0].DataFile)
+ local DHCPScript=$(uci get airwhu.@auth[0].DHCPScript)
+ mentohust -u$Username -p$Password -n$ifname -b$DaemonMode -a$StartMode -d$DaemonMode -c"$DHCPScript" -w>/dev/null 2>&1
+ #Temporarily designed only for whu. To be continued...
+else
+ stop
+fi
+
+}
+
+stop()
+{
+ sync
+ mentohust -k >/dev/null 2>&1
+}
+
+restart()
+{
+ stop
+ start
+}
diff --git a/package/cnsztl/luci-app-airwhu/files/root/usr/lib/lua/luci/controller/airwhu.lua b/package/cnsztl/luci-app-airwhu/files/root/usr/lib/lua/luci/controller/airwhu.lua
new file mode 100755
index 0000000000..15d9239680
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/files/root/usr/lib/lua/luci/controller/airwhu.lua
@@ -0,0 +1,5 @@
+module("luci.controller.airwhu", package.seeall)
+
+function index()
+ entry({"admin", "services", "AirWHU"}, cbi("airwhu"), _("AirWHU"), 100)
+ end
diff --git a/package/cnsztl/luci-app-airwhu/files/root/usr/lib/lua/luci/model/cbi/airwhu.lua b/package/cnsztl/luci-app-airwhu/files/root/usr/lib/lua/luci/model/cbi/airwhu.lua
new file mode 100644
index 0000000000..d3cdbde505
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/files/root/usr/lib/lua/luci/model/cbi/airwhu.lua
@@ -0,0 +1,115 @@
+--[[
+
+LuCI - Lua Configuration Interface
+LICENSE under GPLv3.
+Copyright 2017 KyleRicardo[W.B.L.E. TeAm]
+
+]]
+--
+require("luci.sys")
+require("luci.tools.webadmin")
+
+local IsOnAir = (luci.sys.call("pidof mentohust > /dev/null") == 0)
+if IsOnAir then
+ state_msg = "" .. translate("Running") .. ""
+else
+ state_msg = "" .. translate("Not Running") .. ""
+end
+
+m = Map("airwhu", translate("AirWHU"), translate("Configure Ruijie 802.1X client with IPv6 NAT based on Masquerade.") .. "
" .. translate("Status") .. " : " .. state_msg)
+
+s = m:section(TypedSection, "switch", translate("Global Switch"), translate("Configure global 802.1X Authentication and IPv6-NAT on-off."))
+s.addremove = false
+s.anonymous = true
+
+s:option(Flag, "enableauth", translate("Enable 802.1X Auth"), translate("Enable or disable Ruijie 802.1X authentication."))
+tmp = s:option(Flag, "startwithboot", translate("Start with boot"), translate("Start Ruijie 802.1X Authentication based on MentoHUST when the router is booting."))
+tmp:depends("enableauth","1")
+s:option(Flag, "enableipv6", translate("Enable IPv6 NAT"), translate("Enable IPv6 NAT pass-through based on ip6tables MASQUERADE."))
+
+s = m:section(TypedSection, "auth", translate("Config Authentication"), translate("The options below are all of MentoHUST's arguments."))
+s.anonymous = true
+s.addremove = false
+
+ur = s:option(Value, "Username", translate("Username"))
+ur.rmempty = false
+
+pw = s:option(Value, "Password", translate("Password"))
+pw.password = true
+pw.rmempty = false
+
+wan_dev = luci.sys.exec("uci get network.wan.ifname")
+wan_dev = string.sub(wan_dev,1,string.len(wan_dev)-1)
+ifname = s:option(ListValue, "ifname", translate("Interfaces"))
+ifname:value(wan_dev)
+
+s:option(Value, "IP", translate("IP"), translate("default to localhost's IP")).default = "0.0.0.0"
+
+s:option(Value, "Mask", translate("Netmask"), translate("default to localhost's netmask")).default = "255.255.255.0"
+
+s:option(Value, "Gateway", translate("Gateway"), translate("default to 0.0.0.0")).default = "0.0.0.0"
+
+s:option(Value, "DNS", translate("DNS"), translate("default to 0.0.0.0")).default = "0.0.0.0"
+
+s:option(Value, "PingHost", translate("Ping host"), translate("default to 0.0.0.0,i.e. disable this function")).default = "0.0.0.0"
+
+s:option(Value, "Timeout", translate("Authenticate timeout(s)"), translate("default to 8s")).default = "8"
+
+s:option(Value, "EchoInterval", translate("Heartbeat timeout(s)"), translate("default to 30s")).default = "30"
+
+s:option(Value, "RestartWait", translate("Timeout on failure(s)"), translate("default to 15s")).default = "15"
+
+s:option(Value, "MaxFail", translate("Max failure times"), translate("0 means no limit,default to 8")).default = "8"
+
+t = s:option(ListValue, "StartMode", translate("mulcast address"), translate("default to 1"))
+t:value("0", translate("0(standard)"))
+t:value("1", translate("1(ruijjie)"))
+t:value("2", translate("2(saier)"))
+t.default = "1"
+
+t = s:option(ListValue, "DHCPMode", translate("DHCP type"), translate("default to 1"))
+t:value("0", translate("0(not in used)"))
+t:value("1", translate("1(secondary authenticate)"))
+t:value("2", translate("2(post authenticate)"))
+t:value("3", translate("3(pre authenticate)"))
+t.default = "1"
+
+t = s:option(ListValue, "DaemonMode", translate("run in daemon mode"), translate("default to 3"))
+t:value("0", translate("0(no)"))
+t:value("1", translate("1(yes,turn off output)"))
+t:value("2", translate("2(yes,persevere output)"))
+t:value("3", translate("3(yes,output to file)"))
+t.default = "3"
+
+s:option(Value, "ShowNotify", translate("display notification"), translate("0(no),1-20(yes),default to 0")).default = "0"
+
+s:option(Value, "Version", translate("client version"), translate("default to 0.00,compatible with xrgsu")).default = "0.00"
+
+tmp = s:option(Value, "DataFile", translate("customized data file"), translate("not in used by default"))
+tmp.rmempty = true
+
+script = s:option(Value, "DHCPScript", translate("DHCP script"), translate("use dhclient by default"))
+script.default = "udhcpc -i "..wan_dev
+
+local apply = luci.http.formvalue("cbi.apply")
+if apply then
+ if luci.sys.exec("uci get airwhu.@switch[0].enableauth") then
+ luci.sys.exec("/etc/init.d/mentohust restart")
+ else
+ luci.sys.exec("/etc/init.d/mentohust stop")
+ end
+
+ if luci.sys.exec("uci get airwhu.@switch[0].startwithboot") then
+ luci.sys.exec("/etc/init.d/mentohust enable")
+ else
+ luci.sys.exec("/etc/init.d/mentohust disable")
+ end
+
+ if luci.sys.exec("uci get airwhu.@switch[0].enableipv6") then
+ luci.sys.exec("sh /bin/ipv6masq.sh install")
+ else
+ luci.sys.exec("sh /bin/ipv6masq.sh uninstall")
+ end
+end
+
+return m
diff --git a/package/cnsztl/luci-app-airwhu/po/airwhu.zh-cn.po b/package/cnsztl/luci-app-airwhu/po/airwhu.zh-cn.po
new file mode 100644
index 0000000000..8cc68762cc
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/po/airwhu.zh-cn.po
@@ -0,0 +1,258 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: airwhu\n"
+"POT-Creation-Date: 2017-05-11 19:39+0800\n"
+"PO-Revision-Date: 2017-05-11 19:40+0800\n"
+"Last-Translator: KyleRicardo \n"
+"Language-Team: [W.B.L.E. TeAm] \n"
+"Language: en_US\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.8\n"
+"X-Poedit-Basepath: ../files\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-KeywordsList: translate\n"
+"X-Poedit-SearchPath-0: .\n"
+
+#: root/usr/lib/lua/luci/controller/airwhu.lua:4
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
+msgid "AirWHU"
+msgstr "AirWHU"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:14
+msgid "Running"
+msgstr "运行中"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:16
+msgid "Not Running"
+msgstr "未运行"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
+msgid "Configure Ruijie 802.1X client with IPv6 NAT based on Masquerade."
+msgstr "配置基于IPv6 Masquerade技术的锐捷802.1X认证网络。"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:19
+msgid "Status"
+msgstr "状态"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:21
+msgid "Global Switch"
+msgstr "全局开关"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:21
+msgid "Configure global 802.1X Authentication and IPv6-NAT on-off."
+msgstr "配置全局802.1X认证与IPv6 NAT开关。"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:25
+msgid "Enable 802.1X Auth"
+msgstr "开启锐捷认证"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:25
+msgid "Enable or disable Ruijie 802.1X authentication."
+msgstr "打开或关闭锐捷802.1X认证。"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:26
+msgid "Start with boot"
+msgstr "开机启动"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:26
+msgid ""
+"Start Ruijie 802.1X Authentication based on MentoHUST when the router is "
+"booting."
+msgstr "开机自动启动基于MentoHUST的锐捷802.1X认证客户端。"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:28
+msgid "Enable IPv6 NAT"
+msgstr "开启IPv6 NAT"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:28
+msgid "Enable IPv6 NAT pass-through based on ip6tables MASQUERADE."
+msgstr "开启基于ip6tables Masquerade技术的IPv6 NAT穿透。"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:30
+msgid "Config Authentication"
+msgstr "配置认证参数"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:30
+msgid "The options below are all of MentoHUST's arguments."
+msgstr "以下是全部的MentoHUST认证参数。"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:34
+msgid "Username"
+msgstr "用户名"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:36
+msgid "Password"
+msgstr "密码"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:41
+msgid "Interfaces"
+msgstr "网卡"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:51
+msgid "IP"
+msgstr "IP地址"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:51
+msgid "default to localhost's IP"
+msgstr "默认为localhost的IP地址"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:53
+msgid "Netmask"
+msgstr "掩码"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:53
+msgid "default to localhost's netmask"
+msgstr "默认为localhost的掩码"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:55
+msgid "Gateway"
+msgstr "网关"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:55
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:57
+msgid "default to 0.0.0.0"
+msgstr "默认为0.0.0.0"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:57
+msgid "DNS"
+msgstr "DNS服务器"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:59
+msgid "Ping host"
+msgstr "Ping主机"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:59
+msgid "default to 0.0.0.0,i.e. disable this function"
+msgstr "默认为0.0.0.0,即关闭该功能"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:61
+msgid "Authenticate timeout(s)"
+msgstr "认证超时(秒)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:61
+msgid "default to 8s"
+msgstr "默认为8秒"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:63
+msgid "Heartbeat timeout(s)"
+msgstr "心跳包超时(秒)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:63
+msgid "default to 30s"
+msgstr "默认为30秒"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:65
+msgid "Timeout on failure(s)"
+msgstr "失败等待(秒)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:65
+msgid "default to 15s"
+msgstr "默认为15秒"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:67
+msgid "Max failure times"
+msgstr "允许失败次数"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:67
+msgid "0 means no limit,default to 8"
+msgstr "0表示无限制,默认为8"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:69
+msgid "mulcast address"
+msgstr "多播地址"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:69
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:75
+msgid "default to 1"
+msgstr "默认为1"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:70
+msgid "0(standard)"
+msgstr "0(标准)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:71
+msgid "1(ruijjie)"
+msgstr "1(锐捷)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:72
+msgid "2(saier)"
+msgstr "2(赛尔)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:75
+msgid "DHCP type"
+msgstr "DHCP方式"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:76
+msgid "0(not in used)"
+msgstr "0(不使用)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:77
+msgid "1(secondary authenticate)"
+msgstr "1(二次认证)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:78
+msgid "2(post authenticate)"
+msgstr "3(认证后)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:79
+msgid "3(pre authenticate)"
+msgstr "3(认证前)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:82
+msgid "run in daemon mode"
+msgstr "后台运行模式"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:82
+msgid "default to 3"
+msgstr "默认为3"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:83
+msgid "0(no)"
+msgstr "0(不使用后台模式)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:84
+msgid "1(yes,turn off output)"
+msgstr "1(后台模式,关闭输出)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:85
+msgid "2(yes,persevere output)"
+msgstr "2(后台模式,保留输出)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:86
+msgid "3(yes,output to file)"
+msgstr "3(后台模式,输出到日志文件)"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:89
+msgid "display notification"
+msgstr "显示通知"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:89
+msgid "0(no),1-20(yes),default to 0"
+msgstr "0(不使用),1-20(使用),默认为0"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:91
+msgid "client version"
+msgstr "客户端版本号"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:91
+msgid "default to 0.00,compatible with xrgsu"
+msgstr "默认为0.00,表示兼容xrgsu"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:93
+msgid "customized data file"
+msgstr "自定义数据文件"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:93
+msgid "not in used by default"
+msgstr "默认不使用"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:96
+msgid "DHCP script"
+msgstr "DHCP脚本"
+
+#: root/usr/lib/lua/luci/model/cbi/airwhu.lua:96
+msgid "use dhclient by default"
+msgstr "默认使用dhclient"
diff --git a/package/cnsztl/luci-app-airwhu/tools/po2lmo/Makefile b/package/cnsztl/luci-app-airwhu/tools/po2lmo/Makefile
new file mode 100644
index 0000000000..ad2c133207
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/tools/po2lmo/Makefile
@@ -0,0 +1,12 @@
+
+INSTALL = install
+PREFIX = /usr/bin
+
+po2lmo: src/po2lmo.o src/template_lmo.o
+ $(CC) $(LDFLAGS) -o src/po2lmo src/po2lmo.o src/template_lmo.o
+
+install:
+ $(INSTALL) -m 755 src/po2lmo $(PREFIX)
+
+clean:
+ $(RM) src/po2lmo src/*.o
diff --git a/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/po2lmo b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/po2lmo
new file mode 100755
index 0000000000..74c9146dc7
Binary files /dev/null and b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/po2lmo differ
diff --git a/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/po2lmo.c b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/po2lmo.c
new file mode 100644
index 0000000000..0da792b680
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/po2lmo.c
@@ -0,0 +1,247 @@
+/*
+ * lmo - Lua Machine Objects - PO to LMO conversion tool
+ *
+ * Copyright (C) 2009-2012 Jo-Philipp Wich
+ *
+ * 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 = ((const lmo_entry_t *)a)->key_id;
+ uint32_t y = ((const lmo_entry_t *)b)->key_id;
+
+ if (x < y)
+ return -1;
+ else if (x > y)
+ return 1;
+
+ return 0;
+}
+
+static void print_uint32(uint32_t x, FILE *out)
+{
+ uint32_t y = htonl(x);
+ print(&y, sizeof(uint32_t), 1, out);
+}
+
+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_uint32(e->key_id, out);
+ print_uint32(e->val_id, out);
+ print_uint32(e->offset, out);
+ print_uint32(e->length, 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 = key_id;
+ entry->val_id = val_id;
+ entry->offset = offset;
+ entry->length = 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 )
+ {
+ print_uint32(offset, out);
+ fsync(fileno(out));
+ fclose(out);
+ }
+ else
+ {
+ fclose(out);
+ unlink(argv[2]);
+ }
+
+ fclose(in);
+ return(0);
+}
diff --git a/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/template_lmo.c b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/template_lmo.c
new file mode 100644
index 0000000000..27205a7228
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/template_lmo.c
@@ -0,0 +1,328 @@
+/*
+ * lmo - Lua Machine Objects - Base functions
+ *
+ * Copyright (C) 2009-2010 Jo-Philipp Wich
+ *
+ * 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 = ntohl(*((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;
+ uint32_t k;
+
+ l = 0;
+ r = ar->length - 1;
+
+ while (1)
+ {
+ m = l + ((r - l) / 2);
+
+ if (r < l)
+ break;
+
+ k = ntohl(ar->index[m].key_id);
+
+ if (k == hash)
+ return &ar->index[m];
+
+ if (k > 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 = 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 + ntohl(e->offset);
+ *outlen = ntohl(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/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/template_lmo.h b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/template_lmo.h
new file mode 100644
index 0000000000..57f59aa56b
--- /dev/null
+++ b/package/cnsztl/luci-app-airwhu/tools/po2lmo/src/template_lmo.h
@@ -0,0 +1,92 @@
+/*
+ * lmo - Lua Machine Objects - General header
+ *
+ * Copyright (C) 2009-2012 Jo-Philipp Wich
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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