From 23fd9cd0f9ea4bbd1d3b28ad5ec42206e30870a2 Mon Sep 17 00:00:00 2001
From: Matthias Schiffer <mschiffer@universe-factory.net>
Date: Sat, 23 Nov 2019 14:35:45 +0100
Subject: [PATCH] gluon-web: import po2lmo tool from luci-base

The code is slightly cleaned up to fix compiler warnings.
---
 package/gluon-web/Makefile           |  15 ++
 package/gluon-web/src/Makefile       |   2 +
 package/gluon-web/src/gluon-po2lmo.c | 256 +++++++++++++++++++++++++++
 package/gluon-web/src/template_lmo.c |  11 +-
 package/gluon-web/src/template_lmo.h |   9 +
 package/gluon.mk                     |   3 +-
 6 files changed, 285 insertions(+), 11 deletions(-)
 create mode 100644 package/gluon-web/src/gluon-po2lmo.c

diff --git a/package/gluon-web/Makefile b/package/gluon-web/Makefile
index 8585a493a..decc9c84d 100644
--- a/package/gluon-web/Makefile
+++ b/package/gluon-web/Makefile
@@ -6,6 +6,7 @@ PKG_VERSION:=1
 PKG_INSTALL:=1
 
 include ../gluon.mk
+include $(INCLUDE_DIR)/host-build.mk
 
 define Package/gluon-web
   TITLE:=Minimal Lua web framework derived from LuCI
@@ -24,4 +25,18 @@ define Package/gluon-web/config
 $(foreach lang,$(GLUON_SUPPORTED_LANGS),$(call lang-config,$(lang)))
 endef
 
+define Host/Prepare
+	$(CP) ./src/* $(HOST_BUILD_DIR)
+endef
+
+define Host/Compile
+	$(call Host/Compile/Default,gluon-po2lmo)
+endef
+
+define Host/Install
+	$(INSTALL_DIR) $(1)/bin
+	$(INSTALL_BIN) $(HOST_BUILD_DIR)/gluon-po2lmo $(1)/bin/
+endef
+
 $(eval $(call BuildPackageGluon,gluon-web))
+$(eval $(call HostBuild))
diff --git a/package/gluon-web/src/Makefile b/package/gluon-web/src/Makefile
index 90b952f03..b8b66ef12 100644
--- a/package/gluon-web/src/Makefile
+++ b/package/gluon-web/src/Makefile
@@ -9,6 +9,8 @@ clean:
 parser.so: template_parser.o template_utils.o template_lmo.o template_lualib.o
 	$(CC) $(LDFLAGS) -shared -o $@ $^
 
+gluon-po2lmo: gluon-po2lmo.o template_lmo.o
+
 compile: parser.so
 
 install: compile
diff --git a/package/gluon-web/src/gluon-po2lmo.c b/package/gluon-web/src/gluon-po2lmo.c
new file mode 100644
index 000000000..259651c99
--- /dev/null
+++ b/package/gluon-web/src/gluon-po2lmo.c
@@ -0,0 +1,256 @@
+/*
+ * lmo - Lua Machine Objects - PO to LMO conversion tool
+ *
+ *   Copyright (C) 2009-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
+ *
+ *  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"
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+__attribute__((noreturn))
+static void die(const char *msg)
+{
+	fprintf(stderr, "Error: %s\n", msg);
+	exit(1);
+}
+
+__attribute__((noreturn))
+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 ssize_t extract_string(const char *src, char *dest, size_t len)
+{
+	size_t 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) ? (ssize_t) 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/gluon-web/src/template_lmo.c b/package/gluon-web/src/template_lmo.c
index 0a86bbc97..062497b30 100644
--- a/package/gluon-web/src/template_lmo.c
+++ b/package/gluon-web/src/template_lmo.c
@@ -25,20 +25,11 @@
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <stdio.h>
-#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 
-struct lmo_entry {
-	uint32_t key_id;
-	uint32_t val_id;
-	uint32_t offset;
-	uint32_t length;
-} __attribute__((packed));
-
-
 static inline uint16_t get_le16(const void *data) {
 	const uint8_t *d = data;
 	return (((uint16_t)d[1]) << 8) | d[0];
@@ -56,7 +47,7 @@ static inline uint32_t get_be32(const void *data) {
  * Hash function from http://www.azillionmonkeys.com/qed/hash.html
  * Copyright (C) 2004-2008 by Paul Hsieh
  */
-static uint32_t sfh_hash(const void *input, size_t len)
+uint32_t sfh_hash(const void *input, size_t len)
 {
 	const uint8_t *data = input;
 	uint32_t hash = len, tmp;
diff --git a/package/gluon-web/src/template_lmo.h b/package/gluon-web/src/template_lmo.h
index 4af6cac17..f381c7c19 100644
--- a/package/gluon-web/src/template_lmo.h
+++ b/package/gluon-web/src/template_lmo.h
@@ -22,8 +22,15 @@
 
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
 
 
+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;
 
 
@@ -37,6 +44,8 @@ struct lmo_catalog {
 typedef struct lmo_catalog lmo_catalog_t;
 
 
+uint32_t sfh_hash(const void *input, size_t len);
+
 bool lmo_load(lmo_catalog_t *cat, const char *file);
 void lmo_unload(lmo_catalog_t *cat);
 bool lmo_translate(const lmo_catalog_t *cat, const char *key, size_t keylen, const char **out, size_t *outlen);
diff --git a/package/gluon.mk b/package/gluon.mk
index 90aeabc94..4aae8f71a 100644
--- a/package/gluon.mk
+++ b/package/gluon.mk
@@ -31,6 +31,7 @@ GLUON_I18N_CONFIG := $(foreach lang,$(GLUON_SUPPORTED_LANGS),CONFIG_GLUON_WEB_LA
 GLUON_ENABLED_LANGS := en $(foreach lang,$(GLUON_SUPPORTED_LANGS),$(if $(CONFIG_GLUON_WEB_LANG_$(lang)),$(lang)))
 
 ifneq ($(wildcard ./i18n/.),)
+  PKG_BUILD_DEPENDS += gluon-web/host
   PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
 endif
 
@@ -40,7 +41,7 @@ define GluonBuildI18N
 	for lang in $$(GLUON_ENABLED_LANGS); do \
 		if [ -e $(1)/$$$$lang.po ]; then \
 			rm -f $$(PKG_BUILD_DIR)/i18n/$$$$lang.lmo; \
-			po2lmo $(1)/$$$$lang.po $$(PKG_BUILD_DIR)/i18n/$$$$lang.lmo; \
+			gluon-po2lmo $(1)/$$$$lang.po $$(PKG_BUILD_DIR)/i18n/$$$$lang.lmo; \
 		fi; \
 	done
 endef
-- 
GitLab