openenvutils/commandshell/shell/src/modules/zutil.c
changeset 0 2e3d3ce01487
child 4 0fdb7f6b0309
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/openenvutils/commandshell/shell/src/modules/zutil.c	Tue Feb 02 10:12:00 2010 +0200
@@ -0,0 +1,1730 @@
+#ifndef __SYMBIAN32__
+// zutil.c - misc utilities
+//
+// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved.
+//
+/*
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1999 Sven Wischnowsky
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Sven Wischnowsky and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose.  The software
+ * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+#include "zutil.mdh"
+#include "zutil.pro"
+
+#ifdef __SYMBIAN32__
+#ifdef __WINSCW__
+#pragma warn_unusedarg off
+#pragma warn_possunwant off
+#endif//__WINSCW__
+#endif//__SYMBIAN32__
+
+/* Style stuff. */
+
+typedef struct stypat *Stypat;
+typedef struct style *Style;
+
+/* A pattern and the styles for it. */
+
+struct style {
+    Style next;			/* next in stypat list */
+    Stypat pats;		/* patterns */
+    char *name;
+};
+
+struct stypat {
+    Stypat next;
+    char *pat;			/* pattern string */
+    Patprog prog;		/* compiled pattern */
+    int weight;			/* how specific is the pattern? */
+    Eprog eval;			/* eval-on-retrieve? */
+    char **vals;
+};
+    
+/* List of styles. */
+
+static Style zstyles, zlstyles;
+
+/* Memory stuff. */
+
+static void
+freestypat(Stypat p)
+{
+    zsfree(p->pat);
+    freepatprog(p->prog);
+    if (p->vals)
+	freearray(p->vals);
+    if (p->eval)
+	freeeprog(p->eval);
+    zfree(p, sizeof(*p));
+}
+
+static void
+freeallstyles(void)
+{
+    Style s, sn;
+    Stypat p, pn;
+
+    for (s = zstyles; s; s = sn) {
+	sn = s->next;
+	for (p = s->pats; p; p = pn) {
+	    pn = p->next;
+	    freestypat(p);
+	}
+	zsfree(s->name);
+	zfree(s, sizeof(*s));
+    }
+    zstyles = zlstyles = NULL;
+}
+
+/* Get the style struct for a name. */
+
+static Style
+getstyle(char *name)
+{
+    Style s;
+
+    for (s = zstyles; s; s = s->next)
+	if (!strcmp(name, s->name))
+	    return s;
+
+    return NULL;
+}
+
+/* Store a value for a style. */
+
+static int
+setstypat(Style s, char *pat, Patprog prog, char **vals, int eval)
+{
+    int weight, tmp, first;
+    char *str;
+    Stypat p, q, qq;
+    Eprog eprog = NULL;
+
+    if (eval) {
+	int ef = errflag;
+
+	eprog = parse_string(zjoin(vals, ' ', 1));
+	errflag = ef;
+
+	if (!eprog)
+	{
+	    freepatprog(prog);
+	    return 1;
+	}
+
+	eprog = dupeprog(eprog, 0);
+    }
+    for (p = s->pats; p; p = p->next)
+	if (!strcmp(pat, p->pat)) {
+
+	    /* Exists -> replace. */
+
+	    if (p->vals)
+		freearray(p->vals);
+	    if (p->eval)
+		freeeprog(p->eval);
+	    p->vals = zarrdup(vals);
+	    p->eval = eprog;
+	    freepatprog(prog);
+
+	    return 0;
+	}
+
+    /* New pattern. */
+
+    p = (Stypat) zalloc(sizeof(*p));
+    p->pat = ztrdup(pat);
+    p->prog = prog;
+    p->vals = zarrdup(vals);
+    p->eval = eprog;
+    p->next = NULL;
+
+    /* Calculate the weight. */
+
+    for (weight = 0, tmp = 2, first = 1, str = pat; *str; str++) {
+	if (first && *str == '*' && (!str[1] || str[1] == ':')) {
+	    /* Only `*' in this component. */
+	    tmp = 0;
+	    continue;
+	}
+	first = 0;
+
+	if (*str == '(' || *str == '|' || *str == '*' || *str == '[' ||
+	    *str == '<' ||  *str == '?' || *str == '#' || *str == '^')
+	    /* Is pattern. */
+	    tmp = 1;
+
+	if (*str == ':') {
+	    /* Yet another component. */
+
+	    first = 1;
+	    weight += tmp;
+	    tmp = 2;
+	}
+    }
+    p->weight = (weight += tmp);
+
+    for (qq = NULL, q = s->pats; q && q->weight >= weight;
+	 qq = q, q = q->next);
+
+    p->next = q;
+    if (qq)
+	qq->next = p;
+    else
+	s->pats = p;
+
+    return 0;
+}
+
+/* Add a new style. */
+
+static Style
+addstyle(char *name)
+{
+    Style s;
+
+    s = (Style) zalloc(sizeof(*s));
+    s->next = NULL;
+    s->pats = NULL;
+    s->name = ztrdup(name);
+
+    if (zlstyles)
+	zlstyles->next = s;
+    else
+	zstyles = s;
+    zlstyles = s;
+
+    return s;
+}
+
+static char **
+evalstyle(Stypat p)
+{
+    int ef = errflag;
+    char **ret, *str;
+
+    unsetparam("reply");
+    execode(p->eval, 1, 0);
+    if (errflag) {
+	errflag = ef;
+	return NULL;
+    }
+    errflag = ef;
+
+    queue_signals();
+    if ((ret = getaparam("reply")))
+	ret = arrdup(ret);
+    else if ((str = getsparam("reply"))) {
+	ret = (char **) hcalloc(2 * sizeof(char *));
+	ret[0] = dupstring(str);
+    }
+    unqueue_signals();
+    unsetparam("reply");
+
+    return ret;
+}
+
+/* Look up a style for a context pattern. This does the matching. */
+
+static char **
+lookupstyle(char *ctxt, char *style)
+{
+    Style s;
+    Stypat p;
+
+    for (s = zstyles; s; s = s->next)
+	if (!strcmp(s->name, style))
+	    for (p = s->pats; p; p = p->next)
+		if (pattry(p->prog, ctxt))
+		    return (p->eval ? evalstyle(p) : p->vals);
+
+    return NULL;
+}
+
+static int
+bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
+{
+    int min, max, n, add = 0, list = 0, eval = 0;
+
+    if (!args[0])
+	list = 1;
+    else if (args[0][0] == '-') {
+	char oc;
+
+	if ((oc = args[0][1]) && oc != '-') {
+	    if (args[0][2]) {
+		zwarnnam(nam, "invalid argument: %s", args[0], 0);
+		return 1;
+	    }
+	    if (oc == 'L')
+		list = 2;
+	    else if (oc == 'e') {
+		eval = add = 1;
+		args++;
+	    }
+	} else {
+	    add = 1;
+	    args++;
+	}
+    } else
+	add = 1;
+
+    if (add) {
+	Style s;
+	Patprog prog;
+	char *pat;
+
+	if (arrlen(args) < 2) {
+	    zwarnnam(nam, "not enough arguments", NULL, 0);
+	    return 1;
+	}
+	pat = dupstring(args[0]);
+	tokenize(pat);
+
+	if (!(prog = patcompile(pat, PAT_ZDUP, NULL))) {
+	    zwarnnam(nam, "invalid pattern: %s", args[0], 0);
+	    return 1;
+	}
+	if (!(s = getstyle(args[1])))
+	    s = addstyle(args[1]);
+	return setstypat(s, args[0], prog, args + 2, eval);
+    }
+    if (list) {
+	Style s;
+	Stypat p;
+	char **v;
+
+	for (s = zstyles; s; s = s->next) {
+	    if (list == 1) {
+		quotedzputs(s->name, stdout);
+		putchar('\n');
+	    }
+	    for (p = s->pats; p; p = p->next) {
+		if (list == 1)
+		    printf("%s  %s", (p->eval ? "(eval)" : "      "), p->pat);
+		else {
+		    printf("zstyle %s", (p->eval ? "-e " : ""));
+		    quotedzputs(p->pat, stdout);
+		    printf(" %s", s->name);
+		}
+		for (v = p->vals; *v; v++) {
+		    putchar(' ');
+		    quotedzputs(*v, stdout);
+		}
+		putchar('\n');
+	    }
+	}
+	return 0;
+    }
+    switch (args[0][1]) {
+    case 'd': min = 0; max = -1; break;
+    case 's': min = 3; max =  4; break;
+    case 'b': min = 3; max =  3; break;
+    case 'a': min = 3; max =  3; break;
+    case 't': min = 2; max = -1; break;
+    case 'T': min = 2; max = -1; break;
+    case 'm': min = 3; max =  3; break;
+    case 'g': min = 1; max =  3; break;
+    default:
+	zwarnnam(nam, "invalid option: %s", args[0], 0);
+	return 1;
+    }
+    n = arrlen(args) - 1;
+    if (n < min) {
+	zwarnnam(nam, "not enough arguments", NULL, 0);
+	return 1;
+    } else if (max >= 0 && n > max) {
+	zwarnnam(nam, "too many arguments", NULL, 0);
+	return 1;
+    }
+    switch (args[0][1]) {
+    case 'd':
+	{
+	    Style s;
+
+	    if (args[1]) {
+		if (args[2]) {
+		    char *pat = args[1];
+
+		    for (args += 2; *args; args++) {
+			if ((s = getstyle(*args))) {
+			    Stypat p, q;
+
+			    for (q = NULL, p = s->pats; p;
+				 q = p, p = p->next) {
+				if (!strcmp(p->pat, pat)) {
+				    if (q)
+					q->next = p->next;
+				    else
+					s->pats = p->next;
+				    freestypat(p);
+				    break;
+				}
+			    }
+			}
+		    }
+		} else {
+		    Stypat p, q;
+
+		    for (s = zstyles; s; s = s->next) {
+			for (q = NULL, p = s->pats; p; q = p, p = p->next) {
+			    if (!strcmp(p->pat, args[1])) {
+				if (q)
+				    q->next = p->next;
+				else
+				    s->pats = p->next;
+				freestypat(p);
+				break;
+			    }
+			}
+		    }
+		}
+	    } else
+		freeallstyles();
+	}
+	break;
+    case 's':
+	{
+	    char **vals, *ret;
+	    int val;
+
+	    if ((vals = lookupstyle(args[1], args[2])) && vals[0]) {
+		ret = sepjoin(vals, (args[4] ? args[4] : " "), 0);
+		val = 0;
+	    } else {
+		ret = ztrdup("");
+		val = 1;
+	    }
+	    setsparam(args[3], ret);
+
+	    return val;
+	}
+	break;
+    case 'b':
+	{
+	    char **vals, *ret;
+	    int val;
+
+	    if ((vals = lookupstyle(args[1], args[2])) &&
+		vals[0] && !vals[1] &&
+		(!strcmp(vals[0], "yes") ||
+		 !strcmp(vals[0], "true") ||
+		 !strcmp(vals[0], "on") ||
+		 !strcmp(vals[0], "1"))) {
+		ret = "yes";
+		val = 0;
+	    } else {
+		ret = "no";
+		val = 1;
+	    }
+	    setsparam(args[3], ztrdup(ret));
+
+	    return val;
+	}
+	break;
+    case 'a':
+	{
+	    char **vals, **ret;
+	    int val;
+
+	    if ((vals = lookupstyle(args[1], args[2]))) {
+		ret = zarrdup(vals);
+		val = 0;
+	    } else {
+		char *dummy = NULL;
+
+		ret = zarrdup(&dummy);
+		val = 1;
+	    }
+	    setaparam(args[3], ret);
+
+	    return val;
+	}
+	break;
+    case 't':
+    case 'T':
+	{
+	    char **vals;
+
+	    if ((vals = lookupstyle(args[1], args[2])) && vals[0]) {
+		if (args[3]) {
+		    char **ap = args + 3, **p;
+
+		    while (*ap) {
+			p = vals;
+			while (*p)
+			    if (!strcmp(*ap, *p++))
+				return 0;
+			ap++;
+		    }
+		    return 1;
+		} else
+		    return !(!strcmp(vals[0], "true") ||
+			     !strcmp(vals[0], "yes") ||
+			     !strcmp(vals[0], "on") ||
+			     !strcmp(vals[0], "1"));
+	    }
+	    return (args[0][1] == 't' ? (vals ? 1 : 2) : 0);
+	}
+	break;
+    case 'm':
+	{
+	    char **vals;
+	    Patprog prog;
+
+	    tokenize(args[3]);
+
+	    if ((vals = lookupstyle(args[1], args[2])) &&
+		(prog = patcompile(args[3], PAT_STATIC, NULL))) {
+		while (*vals)
+		    if (pattry(prog, *vals++))
+			return 0;
+	    }
+	    return 1;
+	}
+	break;
+    case 'g':
+	{
+	    LinkList l = newlinklist();
+	    int ret = 1;
+	    Style s;
+	    Stypat p;
+
+	    if (args[2]) {
+		if (args[3]) {
+		    if ((s = getstyle(args[3]))) {
+			for (p = s->pats; p; p = p->next) {
+			    if (!strcmp(args[2], p->pat)) {
+				char **v = p->vals;
+
+				while (*v)
+				    addlinknode(l, *v++);
+
+				ret = 0;
+				break;
+			    }
+			}
+		    }
+		} else {
+		    for (s = zstyles; s; s = s->next)
+			for (p = s->pats; p; p = p->next)
+			    if (!strcmp(args[2], p->pat)) {
+				addlinknode(l, s->name);
+				break;
+			    }
+		    ret = 0;
+		}
+	    } else {
+		LinkNode n;
+
+		for (s = zstyles; s; s = s->next)
+		    for (p = s->pats; p; p = p->next) {
+			for (n = firstnode(l); n; incnode(n))
+			    if (!strcmp(p->pat, (char *) getdata(n)))
+				break;
+			if (!n)
+			    addlinknode(l, p->pat);
+		    }
+		ret = 0;
+	    }
+	    set_list_array(args[1], l);
+
+	    return ret;
+	}
+    }
+    return 0;
+}
+
+/* Format stuff. */
+
+/*
+ * One chunk of text, to allow recursive handling of ternary
+ * expressions in zformat -f output.
+ *   instr	The input string.
+ *   specs	The format specifiers, specs[c] is the string from c:string
+ *   outp	*outp is the start of the output string
+ *   ousedp	(*outp)[*ousedp] is where to write next
+ *   olenp	*olenp is the size allocated for *outp
+ *   endchar    Terminator character in addition to `\0' (may be '\0')
+ *   skip	If 1, don't output, just parse.
+ */
+static char *zformat_substring(char* instr, char **specs, char **outp,
+			       int *ousedp, int *olenp, int endchar, int skip)
+{
+    char *s;
+
+    for (s = instr; *s && *s != endchar; s++) {
+	if (*s == '%') {
+	    int right, min = -1, max = -1, outl, testit;
+	    char *spec, *start = s;
+
+	    if ((right = (*++s == '-')))
+		s++;
+
+	    if (*s >= '0' && *s <= '9') {
+		for (min = 0; *s >= '0' && *s <= '9'; s++)
+		    min = (min * 10) + (int) STOUC(*s) - '0';
+	    }
+
+	    /* Ternary expressions */
+	    testit = (STOUC(*s) == '(');
+	    if (testit && s[1] == '-')
+	    {
+		/* Allow %(-1... etc. */
+		right = 1;
+		s++;
+	    }
+	    if ((*s == '.' || testit) && s[1] >= '0' && s[1] <= '9') {
+		for (max = 0, s++; *s >= '0' && *s <= '9'; s++)
+		    max = (max * 10) + (int) STOUC(*s) - '0';
+	    }
+	    else if (testit)
+		s++;
+
+	    if (testit && STOUC(*s)) {
+		int actval, testval, endcharl;
+
+		/*
+		 * One one number is useful for ternary expressions.
+		 * Remember to put the sign back.
+		 */
+		testval = (min >= 0) ? min : (max >= 0) ? max : 0;
+		if (right)
+		    testval *= -1;
+
+		if (specs[STOUC(*s)])
+		    actval = (int)mathevali(specs[STOUC(*s)]);
+		else
+		    actval = 0;
+		/* zero means values are equal, i.e. true */
+		actval -= testval;
+
+		/* careful about premature end of string */
+		if (!(endcharl = *++s))
+		    return NULL;
+
+		/*
+		 * Either skip true text and output false text, or
+		 * vice versa... unless we are already skipping.
+		 */
+		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
+					    olenp, endcharl, skip || actval)))
+		    return NULL;
+		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
+					    olenp, ')', skip || !actval)))
+		    return NULL;
+	    } else if (skip) {
+		continue;
+	    } else if ((spec = specs[STOUC(*s)])) {
+		int len;
+
+		if ((len = strlen(spec)) > max && max >= 0)
+		    len = max;
+		outl = (min >= 0 ? (min > len ? min : len) : len);
+
+		if (*ousedp + outl >= *olenp) {
+		    int nlen = *olenp + outl + 128;
+		    char *tmp = (char *) zhalloc(nlen);
+
+		    memcpy(tmp, *outp, *olenp);
+		    *olenp = nlen;
+		    *outp = tmp;
+		}
+		if (len >= outl) {
+		    memcpy(*outp + *ousedp, spec, outl);
+		    *ousedp += outl;
+		} else {
+		    int diff = outl - len;
+
+		    if (right) {
+			while (diff--)
+			    (*outp)[(*ousedp)++] = ' ';
+			memcpy(*outp + *ousedp, spec, len);
+			*ousedp += len;
+		    } else {
+			memcpy(*outp + *ousedp, spec, len);
+			*ousedp += len;
+			while (diff--)
+			    (*outp)[(*ousedp)++] = ' ';
+		    }
+		}
+	    } else {
+		int len = s - start + 1;
+
+		if (*ousedp + len >= *olenp) {
+		    int nlen = *olenp + len + 128;
+		    char *tmp = (char *) zhalloc(nlen);
+
+		    memcpy(tmp, *outp, *olenp);
+		    *olenp = nlen;
+		    *outp = tmp;
+		}
+		memcpy(*outp + *ousedp, start, len);
+		*ousedp += len;
+	    }
+	} else {
+	    if (skip)
+		continue;
+	    if (*ousedp + 1 >= *olenp) {
+		char *tmp = (char *) zhalloc((*olenp) << 1);
+
+		memcpy(tmp, *outp, *olenp);
+		*olenp <<= 1;
+		*outp = tmp;
+	    }
+	    (*outp)[(*ousedp)++] = *s;
+	}
+    }
+
+    return s;
+}
+
+static int
+bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
+{
+    char opt;
+
+    if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
+	zwarnnam(nam, "invalid argument: %s", args[0], 0);
+	return 1;
+    }
+    args++;
+
+    switch (opt) {
+    case 'f':
+	{
+	    char **ap, *specs[256], *out;
+	    int olen, oused = 0;
+
+	    memset(specs, 0, 256 * sizeof(char *));
+
+	    specs['%'] = "%";
+	    specs[')'] = ")";
+	    for (ap = args + 2; *ap; ap++) {
+		if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' ||
+		    (ap[0][0] >= '0' && ap[0][0] <= '9') ||
+		    ap[0][1] != ':') {
+		    zwarnnam(nam, "invalid argument: %s", *ap, 0);
+		    return 1;
+		}
+		specs[STOUC(ap[0][0])] = ap[0] + 2;
+	    }
+	    out = (char *) zhalloc(olen = 128);
+
+	    zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0);
+	    out[oused] = '\0';
+
+	    setsparam(args[0], ztrdup(out));
+	    return 0;
+	}
+	break;
+    case 'a':
+	{
+	    char **ap, *cp;
+	    int nbc = 0, colon = 0, pre = 0, suf = 0;
+
+	    for (ap = args + 2; *ap; ap++) {
+		for (nbc = 0, cp = *ap; *cp && *cp != ':'; cp++)
+		    if (*cp == '\\' && cp[1])
+			cp++, nbc++;
+		if (*cp == ':' && cp[1]) {
+		    int d;
+
+		    colon++;
+		    if ((d = cp - *ap - nbc) > pre)
+			pre = d;
+		    if ((d = strlen(cp + 1)) > suf)
+			suf = d;
+		}
+	    }
+	    {
+		int sl = strlen(args[1]);
+		VARARR(char, buf, pre + suf + sl + 1);
+		char **ret, **rp, *copy, *cpp, oldc;
+
+		ret = (char **) zalloc((arrlen(args + 2) + 1) *
+				       sizeof(char *));
+
+		memcpy(buf + pre, args[1], sl);
+		suf = pre + sl;
+
+		for (rp = ret, ap = args + 2; *ap; ap++) {
+		    copy = dupstring(*ap);
+		    for (cp = cpp = copy; *cp && *cp != ':'; cp++) {
+			if (*cp == '\\' && cp[1])
+			    cp++;
+			*cpp++ = *cp;
+		    }
+		    oldc = *cpp;
+		    *cpp = '\0';
+		    if (((cpp == cp && oldc == ':') || *cp == ':') && cp[1]) {
+			memset(buf, ' ', pre);
+			memcpy(buf, copy, (cpp - copy));
+			strcpy(buf + suf, cp + 1);
+			*rp++ = ztrdup(buf);
+		    } else
+			*rp++ = ztrdup(copy);
+		}
+		*rp = NULL;
+
+		setaparam(args[0], ret);
+		return 0;
+	    }
+	}
+	break;
+    }
+    zwarnnam(nam, "invalid option: -%c", 0, opt);
+    return 1;
+}
+
+/* Zregexparse stuff. */
+
+typedef struct {
+    char **match;
+    char **mbegin;
+    char **mend;
+} MatchData;
+
+static void
+savematch(MatchData *m)
+{
+    char **a;
+
+    queue_signals();
+    a = getaparam("match");
+    m->match = a ? zarrdup(a) : NULL;
+    a = getaparam("mbegin");
+    m->mbegin = a ? zarrdup(a) : NULL;
+    a = getaparam("mend");
+    m->mend = a ? zarrdup(a) : NULL;
+    unqueue_signals();
+}
+
+static void
+restorematch(MatchData *m)
+{
+    if (m->match)
+	setaparam("match", m->match);
+    else
+	unsetparam("match");
+    if (m->mbegin)
+	setaparam("mbegin", m->mbegin);
+    else
+	unsetparam("mbegin");
+    if (m->mend)
+	setaparam("mend", m->mend);
+    else
+	unsetparam("mend");
+}
+
+static void
+freematch(MatchData *m)
+{
+    if (m->match)
+	freearray(m->match);
+    if (m->mbegin)
+	freearray(m->mbegin);
+    if (m->mend)
+	freearray(m->mend);
+}
+
+typedef struct {
+    int cutoff;
+    char *pattern;
+    Patprog patprog;
+    char *guard;
+    char *action;
+    LinkList branches;
+} RParseState;
+
+typedef struct {
+    RParseState *state;
+    LinkList actions;
+} RParseBranch;
+
+typedef struct {
+    LinkList nullacts;
+    LinkList in;
+    LinkList out;
+} RParseResult;
+
+static char **rparseargs;
+static LinkList rparsestates;
+
+static int rparsealt(RParseResult *result, jmp_buf *perr);
+
+static void
+connectstates(LinkList out, LinkList in)
+{
+    LinkNode outnode, innode, ln;
+
+    for (outnode = firstnode(out); outnode; outnode = nextnode(outnode)) {
+	RParseBranch *outbranch = getdata(outnode);
+
+	for (innode = firstnode(in); innode; innode = nextnode(innode)) {
+	    RParseBranch *inbranch = getdata(innode);
+	    RParseBranch *br = hcalloc(sizeof(*br));
+
+	    br->state = inbranch->state;
+	    br->actions = newlinklist();
+	    for (ln = firstnode(outbranch->actions); ln; ln = nextnode(ln))
+		addlinknode(br->actions, getdata(ln));
+	    for (ln = firstnode(inbranch->actions); ln; ln = nextnode(ln))
+		addlinknode(br->actions, getdata(ln));
+	    addlinknode(outbranch->state->branches, br);
+	}
+    }
+}
+
+static int
+rparseelt(RParseResult *result, jmp_buf *perr)
+{
+    int l;
+    char *s = *rparseargs;
+
+    if (!s)
+        return 1;
+
+    switch (s[0]) {
+    case '/': {
+	RParseState *st;
+	RParseBranch *br;
+	char *pattern, *lookahead;
+	int patternlen, lookaheadlen = 0;
+
+	l = strlen(s);
+	if (!((2 <= l && s[l - 1] == '/') ||
+	      (3 <= l && s[l - 2] == '/' && (s[l - 1] == '+' ||
+					     s[l - 1] == '-'))))
+	    return 1;
+	st = hcalloc(sizeof(*st));
+	st->branches = newlinklist();
+	st->cutoff = s[l - 1];
+	if (s[l - 1] == '/') {
+	    pattern = s + 1;
+	    patternlen = l - 2;
+	} else {
+	    pattern = s + 1;
+	    patternlen = l - 3;
+	}
+	rparseargs++;
+	if ((s = *rparseargs) && s[0] == '%' &&
+	   2 <= (l = strlen(s)) && s[l - 1] == '%') {
+	    rparseargs++;
+	    lookahead = s + 1;
+	    lookaheadlen = l - 2;
+	} else {
+	    lookahead = NULL;
+	}
+	if (patternlen == 2 && !strncmp(pattern, "[]", 2))
+	    st->pattern = NULL;
+	else {
+	    char *cp;
+	    int l = patternlen + 12; /* (#b)((#B)...)...* */
+	    if(lookahead)
+	        l += lookaheadlen + 4; /* (#B)... */
+	    cp = st->pattern = hcalloc(l);
+	    strcpy(cp, "(#b)((#B)");
+	    cp += 9;
+	    strcpy(cp, pattern);
+	    cp += patternlen;
+	    strcpy(cp, ")");
+	    cp += 1;
+	    if (lookahead) {
+		strcpy(cp, "(#B)");
+		cp += 4;
+		strcpy(cp, lookahead);
+		cp += lookaheadlen;
+	    }
+	    strcpy(cp, "*");
+	}
+	st->patprog = NULL;
+	if ((s = *rparseargs) && *s == '-') {
+	    rparseargs++;
+	    l = strlen(s);
+	    st->guard = hcalloc(l);
+	    memcpy(st->guard, s + 1, l - 1);
+	    st->guard[l - 1] = '\0';
+	} else
+	    st->guard = NULL;
+	if ((s = *rparseargs) && *s == ':') {
+	    rparseargs++;
+	    l = strlen(s);
+	    st->action = hcalloc(l);
+	    memcpy(st->action, s + 1, l - 1);
+	    st->action[l - 1] = '\0';
+	} else
+	    st->action = NULL;
+	result->nullacts = NULL;
+	result->in = newlinklist();
+	br = hcalloc(sizeof(*br));
+	br->state = st;
+	br->actions = newlinklist();
+	addlinknode(result->in, br);
+	result->out = newlinklist();
+	br = hcalloc(sizeof(*br));
+	br->state = st;
+	br->actions = newlinklist();
+	addlinknode(result->out, br);
+	break;
+    }
+    case '(':
+	if (s[1])
+	    return 1;
+	rparseargs++;
+	if (rparsealt(result, perr))
+	    longjmp(*perr, 2);
+	s = *rparseargs;
+	if (!s || s[0] != ')' || s[1] != '\0')
+	    longjmp(*perr, 2);
+	rparseargs++;
+        break;
+    default:
+        return 1;
+    }
+
+    return 0;
+}
+
+static int
+rparseclo(RParseResult *result, jmp_buf *perr)
+{
+    if (rparseelt(result, perr))
+	return 1;
+
+    if (*rparseargs && !strcmp(*rparseargs, "#")) {
+	rparseargs++;
+	while (*rparseargs && !strcmp(*rparseargs, "#"))
+	    rparseargs++;
+
+	connectstates(result->out, result->in);
+	result->nullacts = newlinklist();
+    }
+    return 0;
+}
+
+static void
+prependactions(LinkList acts, LinkList branches)
+{
+    LinkNode aln, bln;
+
+    for (bln = firstnode(branches); bln; bln = nextnode(bln)) {
+	RParseBranch *br = getdata(bln);
+
+	for (aln = lastnode(acts); aln != (LinkNode)acts; aln = prevnode(aln))
+	    pushnode(br->actions, getdata(aln));
+    }
+}
+
+static void
+appendactions(LinkList acts, LinkList branches)
+{
+    LinkNode aln, bln;
+    for (bln = firstnode(branches); bln; bln = nextnode(bln)) {
+	RParseBranch *br = getdata(bln);
+
+	for (aln = firstnode(acts); aln; aln = nextnode(aln))
+	    addlinknode(br->actions, getdata(aln));
+    }
+}
+
+static int
+rparseseq(RParseResult *result, jmp_buf *perr)
+{
+    int l;
+    char *s;
+    RParseResult sub;
+
+    result->nullacts = newlinklist();
+    result->in = newlinklist();
+    result->out = newlinklist();
+
+    while (1) {
+	if ((s = *rparseargs) && s[0] == '{' && s[(l = strlen(s)) - 1] == '}') {
+	    char *action = hcalloc(l - 1);
+	    LinkNode ln;
+
+	    rparseargs++;
+	    memcpy(action, s + 1, l - 2);
+	    action[l - 2] = '\0';
+	    if (result->nullacts)
+		addlinknode(result->nullacts, action);
+	    for (ln = firstnode(result->out); ln; ln = nextnode(ln)) {
+		RParseBranch *br = getdata(ln);
+		addlinknode(br->actions, action);
+	    }
+	}
+        else if (!rparseclo(&sub, perr)) {
+	    connectstates(result->out, sub.in);
+
+	    if (result->nullacts) {
+		prependactions(result->nullacts, sub.in);
+		insertlinklist(sub.in, lastnode(result->in), result->in);
+	    }
+	    if (sub.nullacts) {
+		appendactions(sub.nullacts, result->out);
+		insertlinklist(sub.out, lastnode(result->out), result->out);
+	    } else
+		result->out = sub.out;
+
+	    if (result->nullacts && sub.nullacts)
+		insertlinklist(sub.nullacts, lastnode(result->nullacts),
+			       result->nullacts);
+	    else
+		result->nullacts = NULL;
+	}
+	else
+	    break;
+    }
+    return 0;
+}
+
+static int
+rparsealt(RParseResult *result, jmp_buf *perr)
+{
+    RParseResult sub;
+
+    if (rparseseq(result, perr))
+	return 1;
+
+    while (*rparseargs && !strcmp(*rparseargs, "|")) {
+	rparseargs++;
+	if (rparseseq(&sub, perr))
+	    longjmp(*perr, 2);
+	if (!result->nullacts && sub.nullacts)
+	    result->nullacts = sub.nullacts;
+
+	insertlinklist(sub.in, lastnode(result->in), result->in);
+	insertlinklist(sub.out, lastnode(result->out), result->out);
+    }
+    return 0;
+}
+
+static int
+rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp)
+{
+    LinkNode ln, lnn;
+    LinkList nexts;
+    LinkList nextslist;
+    RParseBranch *br;
+    RParseState *st = NULL;
+    int point1 = 0, point2 = 0;
+
+    setiparam(var1, point1);
+    setiparam(var2, point2);
+
+    if (!comp && !*subj && sm->nullacts) {
+	for (ln = firstnode(sm->nullacts); ln; ln = nextnode(ln)) {
+	    char *action = getdata(ln);
+
+	    if (action)
+		execstring(action, 1, 0);
+	}
+	return 0;
+    }
+
+    nextslist = newlinklist();
+    nexts = sm->in;
+    addlinknode(nextslist, nexts);
+    do {
+	MatchData match1, match2;
+
+	savematch(&match1);
+
+	for (ln = firstnode(nexts); ln; ln = nextnode(ln)) {
+	    int i;
+	    RParseState *next;
+
+	    br = getdata(ln);
+	    next = br->state;
+	    if (next->pattern && !next->patprog) {
+	        tokenize(next->pattern);
+		if (!(next->patprog = patcompile(next->pattern, 0, NULL)))
+		    return 3;
+	    }
+	    if (next->pattern && pattry(next->patprog, subj) &&
+		(!next->guard || (execstring(next->guard, 1, 0), !lastval))) {
+		LinkNode aln;
+		char **mend;
+		int len;
+
+		queue_signals();
+		mend = getaparam("mend");
+		len = atoi(mend[0]);
+		unqueue_signals();
+
+		for (i = len; i; i--)
+		  if (*subj++ == Meta)
+		    subj++;
+
+		savematch(&match2);
+		restorematch(&match1);
+
+		for (aln = firstnode(br->actions); aln; aln = nextnode(aln)) {
+		    char *action = getdata(aln);
+
+		    if (action)
+			execstring(action, 1, 0);
+		}
+		restorematch(&match2);
+
+		point2 += len;
+		setiparam(var2, point2);
+		st = br->state;
+		nexts = st->branches;
+		if (next->cutoff == '-' || (next->cutoff == '/' && len)) {
+		    nextslist = newlinklist();
+		    point1 = point2;
+		    setiparam(var1, point1);
+		}
+		addlinknode(nextslist, nexts);
+		break;
+	    }
+	}
+	if (!ln)
+	    freematch(&match1);
+    } while (ln);
+
+    if (!comp && !*subj)
+	for (ln = firstnode(sm->out); ln; ln = nextnode(ln)) {
+	    br = getdata(ln);
+	    if (br->state == st) {
+		for (ln = firstnode(br->actions); ln; ln = nextnode(ln)) {
+		    char *action = getdata(ln);
+
+		    if (action)
+			execstring(action, 1, 0);
+		}
+		return 0;
+	    }
+	}
+
+    for (lnn = firstnode(nextslist); lnn; lnn = nextnode(lnn)) {
+	nexts = getdata(lnn);
+	for (ln = firstnode(nexts); ln; ln = nextnode(ln)) {
+	    br = getdata(ln);
+	    if (br->state->action)
+		execstring(br->state->action, 1, 0);
+	}
+    }
+    return empty(nexts) ? 2 : 1;
+}
+
+/*
+  usage: zregexparse [-c] var1 var2 string regex...
+  status:
+    0: matched
+    1: unmatched (all next state candidates are failed)
+    2: unmatched (there is no next state candidates)
+    3: regex parse error
+*/
+
+static int
+bin_zregexparse(char *nam, char **args, Options ops, UNUSED(int func))
+{
+    int oldextendedglob = opts[EXTENDEDGLOB];
+    char *var1 = args[0];
+    char *var2 = args[1];
+    char *subj = args[2];
+    int ret;
+    jmp_buf rparseerr;
+    RParseResult result;
+
+    opts[EXTENDEDGLOB] = 1;
+
+    rparseargs = args + 3;
+
+    pushheap();
+    rparsestates = newlinklist();
+    if (setjmp(rparseerr) || rparsealt(&result, &rparseerr) || *rparseargs) {
+	if (*rparseargs)
+	    zwarnnam(nam, "invalid regex : %s", *rparseargs, 0);
+	else
+	    zwarnnam(nam, "not enough regex arguments", NULL, 0);
+	ret = 3;
+    } else
+	ret = 0;
+
+    if (!ret)
+	ret = rmatch(&result, subj, var1, var2, OPT_ISSET(ops,'c'));
+    popheap();
+
+    opts[EXTENDEDGLOB] = oldextendedglob;
+    return ret;
+}
+
+typedef struct zoptdesc *Zoptdesc;
+typedef struct zoptarr *Zoptarr;
+typedef struct zoptval *Zoptval;
+
+struct zoptdesc {
+    Zoptdesc next;
+    char *name;
+    int flags;
+    Zoptarr arr;
+    Zoptval vals, last;
+};
+
+#define ZOF_ARG  1
+#define ZOF_OPT  2
+#define ZOF_MULT 4
+#define ZOF_SAME 8
+
+struct zoptarr {
+    Zoptarr next;
+    char *name;
+    Zoptval vals, last;
+    int num;
+};
+
+struct zoptval {
+    Zoptval next, onext;
+    char *name;
+    char *arg;
+    char *str;
+};
+
+static Zoptdesc opt_descs;
+static Zoptarr opt_arrs;
+
+static Zoptdesc
+get_opt_desc(char *name)
+{
+    Zoptdesc p;
+
+    for (p = opt_descs; p; p = p->next)
+	if (!strcmp(name, p->name))
+	    return p;
+
+    return NULL;
+}
+
+static Zoptdesc
+lookup_opt(char *str)
+{
+    Zoptdesc p;
+
+    for (p = opt_descs; p; p = p->next) {
+	if ((p->flags & ZOF_ARG) ? strpfx(p->name, str) : !strcmp(p->name, str))
+	    return p;
+    }
+    return NULL;
+}
+
+static Zoptarr
+get_opt_arr(char *name)
+{
+    Zoptarr p;
+
+    for (p = opt_arrs; p; p = p->next)
+	if (!strcmp(name, p->name))
+	    return p;
+
+    return NULL;
+}
+
+static void
+add_opt_val(Zoptdesc d, char *arg)
+{
+    Zoptval v = NULL;
+    char *n = dyncat("-", d->name);
+    int new = 0;
+
+    if (!(d->flags & ZOF_MULT))
+	v = d->vals;
+    if (!v) {
+	v = (Zoptval) zhalloc(sizeof(*v));
+	v->next = v->onext = NULL;
+	v->name = n;
+	new = 1;
+    }
+    v->arg = arg;
+    if ((d->flags & ZOF_ARG) && !(d->flags & (ZOF_OPT | ZOF_SAME))) {
+	v->str = NULL;
+	if (d->arr)
+	    d->arr->num += (arg ? 2 : 1);
+    } else if (arg) {
+	char *s = (char *) zhalloc(strlen(d->name) + strlen(arg) + 2);
+
+	*s = '-';
+	strcpy(s + 1, d->name);
+	strcat(s, arg);
+	v->str = s;
+	if (d->arr)
+	    d->arr->num += 1;
+    } else {
+	v->str = NULL;
+	if (d->arr)
+	    d->arr->num += 1;
+    }
+    if (new) {
+	if (d->arr) {
+	    if (d->arr->last)
+		d->arr->last->next = v;
+	    else
+		d->arr->vals = v;
+	    d->arr->last = v;
+	}
+	if (d->last)
+	    d->last->onext = v;
+	else
+	    d->vals = v;
+	d->last = v;
+    }
+}
+
+static int
+bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
+{
+    char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL, **cp, **np;
+    int del = 0, f, extract = 0, keep = 0;
+    Zoptdesc sopts[256], d;
+    Zoptarr a, defarr = NULL;
+    Zoptval v;
+
+    opt_descs = NULL;
+    opt_arrs = NULL;
+    memset(sopts, 0, 256 * sizeof(Zoptdesc));
+
+    while ((o = *args++)) {
+	if (*o == '-') {
+	    switch (o[1]) {
+	    case '\0':
+		o = NULL;
+		break;
+	    case '-':
+		if (o[2])
+		    args--;
+		o = NULL;
+		break;
+	    case 'D':
+		if (o[2]) {
+		    args--;
+		    o = NULL;
+		    break;
+		}
+		del = 1;
+		break;
+	    case 'E':
+		if (o[2]) {
+		    args--;
+		    o = NULL;
+		    break;
+		}
+		extract = 1;
+		break;
+	    case 'K':
+		if (o[2]) {
+		    args--;
+		    o = NULL;
+		    break;
+		}
+		keep = 1;
+		break;
+	    case 'a':
+		if (defarr) {
+		    zwarnnam(nam, "default array given more than once", NULL, 0);
+		    return 1;
+		}
+		if (o[2])
+		    n = o + 2;
+		else if (*args)
+		    n = *args++;
+		else {
+		    zwarnnam(nam, "missing array name", NULL, 0);
+		    return 1;
+		}
+		defarr = (Zoptarr) zhalloc(sizeof(*defarr));
+		defarr->name = n;
+		defarr->num = 0;
+		defarr->vals = defarr->last = NULL;
+		defarr->next = NULL;
+		opt_arrs = defarr;
+		break;
+	    case 'A':
+		if (o[2]) 
+		    assoc = o + 2;
+		else if (*args)
+		    assoc = *args++;
+		else {
+		    zwarnnam(nam, "missing array name", NULL, 0);
+		    return 1;
+		}
+		break;
+	    }
+	    if (!o) {
+		o = "";
+		break;
+	    }
+	} else {
+	    args--;
+	    break;
+	}
+    }
+    if (!o) {
+	zwarnnam(nam, "missing option descriptions", NULL, 0);
+	return 1;
+    }
+    while ((o = dupstring(*args++))) {
+	if (!*o) {
+	    zwarnnam(nam, "invalid option description: %s", o, 0);
+	    return 1;
+	}
+	f = 0;
+	for (p = o; *p; p++) {
+	    if (*p == '\\' && p[1])
+		p++;
+	    else if (*p == '+') {
+		f |= ZOF_MULT;
+		*p = '\0';
+		p++;
+		break;
+	    } else if (*p == ':' || *p == '=')
+		break;
+	}
+	if (*p == ':') {
+	    f |= ZOF_ARG;
+	    *p = '\0';
+	    if (*++p == ':') {
+		p++;
+		f |= ZOF_OPT;
+	    }
+	    if (*p == '-') {
+		p++;
+		f |= ZOF_SAME;
+	    }
+	}
+	a = NULL;
+	if (*p == '=') {
+	    *p++ = '\0';
+	    if (!(a = get_opt_arr(p))) {
+		a = (Zoptarr) zhalloc(sizeof(*a));
+		a->name = p;
+		a->num = 0;
+		a->vals = a->last = NULL;
+		a->next = opt_arrs;
+		opt_arrs = a;
+	    }
+	} else if (*p) {
+	    zwarnnam(nam, "invalid option description: %s", args[-1], 0);
+	    return 1;
+	} else if (!(a = defarr) && !assoc) {
+	    zwarnnam(nam, "no default array defined: %s", args[-1], 0);
+	    return 1;
+	}
+	for (p = n = o; *p; p++) {
+	    if (*p == '\\' && p[1])
+		p++;
+	    *n++ = *p;
+	}
+	if (get_opt_desc(o)) {
+	    zwarnnam(nam, "option defined more than once: %s", o, 0);
+	    return 1;
+	}
+	d = (Zoptdesc) zhalloc(sizeof(*d));
+	d->name = o;
+	d->flags = f;
+	d->arr = a;
+	d->next = opt_descs;
+	d->vals = d->last = NULL;
+	opt_descs = d;
+	if (!o[1])
+	    sopts[STOUC(*o)] = d;
+    }
+    np = cp = pp = ((extract && del) ? arrdup(pparams) : pparams);
+    for (; (o = *pp); pp++) {
+	if (*o != '-') {
+	    if (extract) {
+		if (del)
+		    *cp++ = o;
+		continue;
+	    } else
+		break;
+	}
+	if (!o[1] || (o[1] == '-' && !o[2])) {
+	    if (del && extract)
+		*cp++ = o;
+	    pp++;
+	    break;
+	}
+	if (!(d = lookup_opt(o + 1))) {
+	    while (*++o) {
+		if (!(d = sopts[STOUC(*o)])) {
+		    o = NULL;
+		    break;
+		}
+		if (d->flags & ZOF_ARG) {
+		    if (o[1]) {
+			add_opt_val(d, o + 1);
+			break;
+		    } else if (!(d->flags & ZOF_OPT)) {
+			if (!pp[1]) {
+			    zwarnnam(nam, "missing argument for option: %s",
+				    d->name, 0);
+			    return 1;
+			}
+			add_opt_val(d, *++pp);
+		    } else
+			add_opt_val(d, NULL);
+		} else
+		    add_opt_val(d, NULL);
+	    }
+	    if (!o) {
+		if (extract) {
+		    if (del)
+			*cp++ = *pp;
+		    continue;
+		} else
+		    break;
+	    }
+	} else {
+	    if (d->flags & ZOF_ARG) {
+		char *e = o + strlen(d->name) + 1;
+
+		if (*e)
+		    add_opt_val(d, e);
+		else if (!(d->flags & ZOF_OPT)) {
+		    if (!pp[1]) {
+			zwarnnam(nam, "missing argument for option: %s",
+				d->name, 0);
+			return 1;
+		    }
+		    add_opt_val(d, *++pp);
+		} else
+		    add_opt_val(d, NULL);
+	    } else
+		add_opt_val(d, NULL);
+	}
+    }
+    if (extract && del)
+	while (*pp)
+	    *cp++ = *pp++;
+
+    for (a = opt_arrs; a; a = a->next) {
+	if (!keep || a->num) {
+	    aval = (char **) zalloc((a->num + 1) * sizeof(char *));
+	    for (ap = aval, v = a->vals; v; ap++, v = v->next) {
+		if (v->str)
+		    *ap = ztrdup(v->str);
+		else {
+		    *ap = ztrdup(v->name);
+		    if (v->arg)
+			*++ap = ztrdup(v->arg);
+		}
+	    }
+	    *ap = NULL;
+	    setaparam(a->name, aval);
+	}
+    }
+    if (assoc) {
+	int num;
+
+	for (num = 0, d = opt_descs; d; d = d->next)
+	    if (d->vals)
+		num++;
+
+	if (!keep || num) {
+	    aval = (char **) zalloc(((num * 2) + 1) * sizeof(char *));
+	    for (ap = aval, d = opt_descs; d; d = d->next) {
+		if (d->vals) {
+		    *ap++ = n = (char *) zalloc(strlen(d->name) + 2);
+		    *n = '-';
+		    strcpy(n + 1, d->name);
+
+		    for (num = 1, v = d->vals; v; v = v->onext) {
+			num += (v->arg ? strlen(v->arg) : 0);
+			if (v->next)
+			    num++;
+		    }
+		    *ap++ = n = (char *) zalloc(num);
+		    for (v = d->vals; v; v = v->onext) {
+			if (v->arg) {
+			    strcpy(n, v->arg);
+			    n += strlen(v->arg);
+			}
+			*n = ' ';
+		    }
+		    *n = '\0';
+		}
+	    }
+	    *ap = NULL;
+	    sethparam(assoc, aval);
+	}
+    }
+    if (del) {
+	if (extract) {
+	    *cp = NULL;
+	    freearray(pparams);
+	    pparams = zarrdup(np);
+	} else {
+	    pp = zarrdup(pp);
+	    freearray(pparams);
+	    pparams = pp;
+	}
+    }
+    return 0;
+}
+
+static struct builtin bintab[] = {
+    BUILTIN("zstyle", 0, bin_zstyle, 0, -1, 0, NULL, NULL),
+    BUILTIN("zformat", 0, bin_zformat, 3, -1, 0, NULL, NULL),
+    BUILTIN("zregexparse", 0, bin_zregexparse, 3, -1, 0, "c", NULL),
+    BUILTIN("zparseopts", 0, bin_zparseopts, 1, -1, 0, NULL, NULL),
+};
+
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+    zstyles = zlstyles = NULL;
+
+    return 0;
+}
+
+/**/
+int
+boot_(Module m)
+{
+    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+    freeallstyles();
+
+    return 0;
+}
+
+#endif //__SYMBIAN32__