summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-base/src/template_lmo.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/luci-base/src/template_lmo.c')
-rw-r--r--modules/luci-base/src/template_lmo.c307
1 files changed, 301 insertions, 6 deletions
diff --git a/modules/luci-base/src/template_lmo.c b/modules/luci-base/src/template_lmo.c
index f7a118c9bb..8634bc4bf3 100644
--- a/modules/luci-base/src/template_lmo.c
+++ b/modules/luci-base/src/template_lmo.c
@@ -17,6 +17,7 @@
*/
#include "template_lmo.h"
+#include "plural_formula.h"
/*
* Hash function from http://www.azillionmonkeys.com/qed/hash.html
@@ -69,17 +70,51 @@ uint32_t sfh_hash(const char *data, int len)
return hash;
}
-uint32_t lmo_canon_hash(const char *str, int len)
+uint32_t lmo_canon_hash(const char *str, int len,
+ const char *ctx, int ctxlen, int plural)
{
char res[4096];
- char *ptr, prev;
+ char *ptr, *end, prev;
int off;
- if (!str || len >= sizeof(res))
+ if (!str)
return 0;
- for (prev = ' ', ptr = res, off = 0; off < len; prev = *str, off++, str++)
+ ptr = res;
+ end = res + sizeof(res);
+
+ if (ctx)
+ {
+ for (prev = ' ', off = 0; off < ctxlen; prev = *ctx, off++, ctx++)
+ {
+ if (ptr >= end)
+ return 0;
+
+ if (isspace(*ctx))
+ {
+ if (!isspace(prev))
+ *ptr++ = ' ';
+ }
+ else
+ {
+ *ptr++ = *ctx;
+ }
+ }
+
+ if ((ptr > res) && isspace(*(ptr-1)))
+ ptr--;
+
+ if (ptr >= end)
+ return 0;
+
+ *ptr++ = '\1';
+ }
+
+ for (prev = ' ', off = 0; off < len; prev = *str, off++, str++)
{
+ if (ptr >= end)
+ return 0;
+
if (isspace(*str))
{
if (!isspace(prev))
@@ -94,6 +129,14 @@ uint32_t lmo_canon_hash(const char *str, int len)
if ((ptr > res) && isspace(*(ptr-1)))
ptr--;
+ if (plural > -1)
+ {
+ if (plural >= 100 || ptr + 3 >= end)
+ return 0;
+
+ ptr += snprintf(ptr, 3, "\2%d", plural);
+ }
+
return sfh_hash(res, ptr - res);
}
@@ -277,8 +320,194 @@ static lmo_entry_t * lmo_find_entry(lmo_archive_t *ar, uint32_t hash)
return NULL;
}
+void *pluralParseAlloc(void *(*)(size_t));
+void pluralParse(void *, int, int, void *);
+void pluralParseFree(void *, void (*)(void *));
+
+static int lmo_eval_plural(const char *expr, int len, int val)
+{
+ struct { int num; int res; } s = { .num = val, .res = -1 };
+ const char *p = NULL;
+ void *pParser = NULL;
+ int t, n;
+ char c;
+
+ while (len > 7) {
+ if (*expr == 'p') {
+ if (!strncmp(expr, "plural=", 7)) {
+ p = expr + 7;
+ len -= 7;
+ break;
+ }
+ }
+
+ expr++;
+ len--;
+ }
+
+ if (!p)
+ goto out;
+
+ pParser = pluralParseAlloc(malloc);
+
+ if (!pParser)
+ goto out;
+
+ while (len-- > 0) {
+ c = *p++;
+ t = -1;
+ n = 0;
+
+ switch (c) {
+ case ' ':
+ case '\t':
+ continue;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ t = T_NUM;
+ n = c - '0';
+
+ while (*p >= '0' && *p <= '9') {
+ n *= 10;
+ n += *p - '0';
+ p++;
+ }
+
+ break;
+
+ case '=':
+ if (*p == '=') {
+ t = T_EQ;
+ p++;
+ }
+
+ break;
+
+ case '!':
+ if (*p == '=') {
+ t = T_NE;
+ p++;
+ }
+ else {
+ t = T_NOT;
+ }
+
+ break;
+
+ case '&':
+ if (*p == '&') {
+ t = T_AND;
+ p++;
+ }
+
+ break;
+
+ case '|':
+ if (*p == '|') {
+ t = T_OR;
+ p++;
+ }
+
+ break;
+
+ case '<':
+ if (*p == '=') {
+ t = T_LE;
+ p++;
+ }
+ else {
+ t = T_LT;
+ }
+
+ break;
+
+ case '>':
+ if (*p == '=') {
+ t = T_GE;
+ p++;
+ }
+ else {
+ t = T_GT;
+ }
+
+ break;
+
+ case '*':
+ t = T_MUL;
+ break;
+
+ case '/':
+ t = T_DIV;
+ break;
+
+ case '%':
+ t = T_MOD;
+ break;
+
+ case '+':
+ t = T_ADD;
+ break;
+
+ case '-':
+ t = T_SUB;
+ break;
+
+ case 'n':
+ t = T_N;
+ break;
+
+ case '?':
+ t = T_QMARK;
+ break;
+
+ case ':':
+ t = T_COLON;
+ break;
+
+ case '(':
+ t = T_LPAREN;
+ break;
+
+ case ')':
+ t = T_RPAREN;
+ break;
+
+ case ';':
+ case '\n':
+ case '\0':
+ t = 0;
+ break;
+ }
+
+ /* syntax error */
+ if (t < 0)
+ goto out;
+
+ pluralParse(pParser, t, n, &s);
+
+ /* eof */
+ if (t == 0)
+ break;
+ }
+
+ pluralParse(pParser, 0, 0, &s);
+
+out:
+ pluralParseFree(pParser, free);
+
+ return s.res;
+}
+
int lmo_translate(const char *key, int keylen, char **out, int *outlen)
{
+ return lmo_translate_ctxt(key, keylen, NULL, 0, out, outlen);
+}
+
+int lmo_translate_ctxt(const char *key, int keylen,
+ const char *ctx, int ctxlen,
+ char **out, int *outlen)
+{
uint32_t hash;
lmo_entry_t *e;
lmo_archive_t *ar;
@@ -286,7 +515,62 @@ int lmo_translate(const char *key, int keylen, char **out, int *outlen)
if (!key || !_lmo_active_catalog)
return -2;
- hash = lmo_canon_hash(key, keylen);
+ hash = lmo_canon_hash(key, keylen, ctx, ctxlen, -1);
+
+ if (hash > 0)
+ {
+ 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;
+}
+
+int lmo_translate_plural(int n, const char *skey, int skeylen,
+ const char *pkey, int pkeylen,
+ char **out, int *outlen)
+{
+ return lmo_translate_plural_ctxt(n, skey, skeylen, pkey, pkeylen,
+ NULL, 0, out, outlen);
+}
+
+int lmo_translate_plural_ctxt(int n, const char *skey, int skeylen,
+ const char *pkey, int pkeylen,
+ const char *ctx, int ctxlen,
+ char **out, int *outlen)
+{
+ int pid = -1;
+ uint32_t hash;
+ lmo_entry_t *e;
+ lmo_archive_t *ar;
+ const char *plural_formula;
+
+ if (!skey || !pkey || !_lmo_active_catalog)
+ return -2;
+
+ for (ar = _lmo_active_catalog->archives; ar; ar = ar->next) {
+ e = lmo_find_entry(ar, 0);
+
+ if (e != NULL) {
+ pid = lmo_eval_plural(ar->mmap + ntohl(e->offset), ntohl(e->length), n);
+ break;
+ }
+ }
+
+ if (pid == -1)
+ pid = (n != 1);
+
+ hash = lmo_canon_hash(skey, skeylen, ctx, ctxlen, pid);
+
+ if (hash == 0)
+ return -1;
for (ar = _lmo_active_catalog->archives; ar; ar = ar->next)
{
@@ -298,7 +582,18 @@ int lmo_translate(const char *key, int keylen, char **out, int *outlen)
}
}
- return -1;
+ if (n != 1)
+ {
+ *out = (char *)pkey;
+ *outlen = pkeylen;
+ }
+ else
+ {
+ *out = (char *)skey;
+ *outlen = skeylen;
+ }
+
+ return 0;
}
void lmo_iterate(lmo_iterate_cb_t cb, void *priv)