diff --git a/Makefile b/Makefile
index a67a558..6335022 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-cd8021x
-PKG_VERSION=1.2.1
+PKG_VERSION=1.2.2
PKG_RELEASE:=1
PKG_LICENSE:=GPLv3
@@ -18,6 +18,7 @@ define Package/luci-app-cd8021x
SUBMENU:=3. Applications
TITLE:=a wired IEEE 802.1x client for Luci
PKGARCH:=all
+ DEPENDS:=+wpad
endef
define Package/luci-app-cd8021x/description
@@ -25,6 +26,8 @@ define Package/luci-app-cd8021x/description
endef
define Build/Prepare
+ $(foreach po,$(wildcard ${CURDIR}/files/root/usr/lib/lua/luci/i18n/*.po), \
+ po2lmo $(po) $(PKG_BUILD_DIR)/$(patsubst %.po,%.lmo,$(notdir $(po)));)
endef
define Build/Configure
@@ -38,11 +41,13 @@ define Package/luci-app-cd8021x/install
$(INSTALL_DIR) $(1)/etc/init.d
$(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/cd8021x $(1)/etc/config/cd8021x
$(INSTALL_BIN) ./files/root/etc/init.d/cd8021x $(1)/etc/init.d/cd8021x
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/model/cbi/cd8021x.lua $(1)/usr/lib/lua/luci/model/cbi/cd8021x.lua
$(INSTALL_DATA) ./files/root/usr/lib/lua/luci/controller/cd8021x.lua $(1)/usr/lib/lua/luci/controller/cd8021x.lua
+ $(INSTALL_DATA) $(PKG_BUILD_DIR)/cd8021x.*.lmo $(1)/usr/lib/lua/luci/i18n/
endef
$(eval $(call BuildPackage,luci-app-cd8021x))
diff --git a/README.md b/README.md
index 1477681..da2eeca 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@
Configure 802.1x wired authentication on OpenWrt/LEDE
-![screenshot](https://raw.githubusercontent.com/max0y/luci-app-cd8021x/master/screenshot.png)
+![screenshot](https://raw.githubusercontent.com/max0y/luci-app-cd8021x/master/screenshot_en.png)
## Install
-1. This package depends on *wpad*, you need to remove *wpad-mini* first
+1. This package depends on *wpad*, you need to remove *wpad-mini* first, or you can manually download the *wpad* package from [OpenWrt packages site](https://downloads.openwrt.org/releases/17.01.4/packages/).
```bash
opkg update
opkg remove wpad-mini
@@ -29,6 +29,11 @@ cd openwrt-sdk-ar71xx-*
# clone this repo
git clone https://github.com/max0y/luci-app-cd8021x.git package/luci-app-cd8021x
+# complie po2lmo (if you don't have po2lmo)
+pushd package/luci-app-cd8021x/tool/po2lmo
+make && sudo make install
+popd
+
# run make menuconfig, and choose LuCI -> 3. Applications
make menuconfig
diff --git a/README_zh.md b/README_zh.md
index 6b05129..5f4ac84 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -6,7 +6,7 @@ OpenWrt 802.1x 有线认证拨号界面
![screenshot](https://raw.githubusercontent.com/max0y/luci-app-cd8021x/master/screenshot_zh.png)
## 安装
-1. 这个包的功能实现依赖于*wpad*,需要先卸载*wpad-mini*,再安装*wpad*
+1. 这个包的功能实现依赖于*wpad*,需要先卸载*wpad-mini*,再安装*wpad*,也可以手动到OpenWrt[官方源](https://downloads.openwrt.org/releases/17.01.4/packages/)处下载*wpad*安装包
```bash
opkg update
opkg remove wpad-mini
@@ -29,6 +29,11 @@ cd openwrt-sdk-ar71xx-*
# Clone 项目
git clone https://github.com/max0y/luci-app-cd8021x.git package/luci-app-cd8021x
+# 编译 po2lmo (如果已安装po2lmo可跳过)
+pushd package/luci-app-cd8021x/tool/po2lmo
+make && sudo make install
+popd
+
# 运行make menuconfig,选择要编译的包 LuCI -> 3. Applications
make menuconfig
diff --git a/files/root/usr/lib/lua/luci/controller/cd8021x.lua b/files/root/usr/lib/lua/luci/controller/cd8021x.lua
index 87e4429..46a1808 100644
--- a/files/root/usr/lib/lua/luci/controller/cd8021x.lua
+++ b/files/root/usr/lib/lua/luci/controller/cd8021x.lua
@@ -4,5 +4,5 @@
module("luci.controller.cd8021x", package.seeall)
function index()
- entry({"admin", "network", "cd8021x"}, cbi("cd8021x"), _("cd802.1x client"), 100).dependent = true
+ entry({"admin", "network", "cd8021x"}, cbi("cd8021x"), _("802.1x Client"), 100).dependent = true
end
diff --git a/files/root/usr/lib/lua/luci/i18n/cd8021x.zh-cn.po b/files/root/usr/lib/lua/luci/i18n/cd8021x.zh-cn.po
new file mode 100644
index 0000000..d84777d
--- /dev/null
+++ b/files/root/usr/lib/lua/luci/i18n/cd8021x.zh-cn.po
@@ -0,0 +1,14 @@
+
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8\n"
+
+msgid "802.1x Client"
+msgstr "802.1x 客户端"
+
+msgid ""
+"Configure IEEE 802.1x wired authentication, you may need to edit your WAN interface "
+"protocol as DHCP client here"
+msgstr ""
+"IEEE 802.1x 有线拨号客户端, 使用前需要将WAN口"
+"协议修改为DHCP客户端. 点击这里"
+
diff --git a/files/root/usr/lib/lua/luci/model/cbi/cd8021x.lua b/files/root/usr/lib/lua/luci/model/cbi/cd8021x.lua
index b283a4f..2948c70 100644
--- a/files/root/usr/lib/lua/luci/model/cbi/cd8021x.lua
+++ b/files/root/usr/lib/lua/luci/model/cbi/cd8021x.lua
@@ -9,8 +9,8 @@ local eap_list = {
"MSCHAPV2",
}
-m = Map("cd8021x", translate("cd802.1x client"),
- translate("Configure IEEE 802.1x wired authentication, you may need to edit your WAN interface protocol as DHCP client here."))
+m = Map("cd8021x", translate("802.1x Client"),
+ translate("Configure IEEE 802.1x wired authentication, you may need to edit your WAN interface protocol as DHCP client here"))
s = m:section(TypedSection, "login", "")
s.addremove = false
diff --git a/screenshot.png b/screenshot.png
deleted file mode 100644
index 23d92a8..0000000
Binary files a/screenshot.png and /dev/null differ
diff --git a/screenshot_en.png b/screenshot_en.png
new file mode 100644
index 0000000..e39a6e9
Binary files /dev/null and b/screenshot_en.png differ
diff --git a/screenshot_zh.png b/screenshot_zh.png
index e8df648..8985d67 100644
Binary files a/screenshot_zh.png and b/screenshot_zh.png differ
diff --git a/tool/po2lmo/Makefile b/tool/po2lmo/Makefile
new file mode 100644
index 0000000..924bd9f
--- /dev/null
+++ b/tool/po2lmo/Makefile
@@ -0,0 +1,13 @@
+
+INSTALL = install
+PREFIX = /usr/local/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/tool/po2lmo/src/po2lmo.c b/tool/po2lmo/src/po2lmo.c
new file mode 100644
index 0000000..b431ae2
--- /dev/null
+++ b/tool/po2lmo/src/po2lmo.c
@@ -0,0 +1,248 @@
+/*
+ * 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/tool/po2lmo/src/template_lmo.c b/tool/po2lmo/src/template_lmo.c
new file mode 100644
index 0000000..7752f45
--- /dev/null
+++ b/tool/po2lmo/src/template_lmo.c
@@ -0,0 +1,329 @@
+/*
+ * 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/tool/po2lmo/src/template_lmo.h b/tool/po2lmo/src/template_lmo.h
new file mode 100644
index 0000000..e6b839c
--- /dev/null
+++ b/tool/po2lmo/src/template_lmo.h
@@ -0,0 +1,93 @@
+/*
+ * 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
+