about summary refs log tree commit diff
path: root/Src/Modules/zutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Modules/zutil.c')
-rw-r--r--Src/Modules/zutil.c1245
1 files changed, 1015 insertions, 230 deletions
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 590678f98..9dd228930 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -37,81 +37,62 @@ 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? */
-    Style styles, lstyles;	/* first/last style */
-};
-    
-struct style {
-    Style next;
-    char *name;
     char **vals;
 };
-
+    
 /* List of styles. */
 
-static Stypat zstyles, lzstyles;
+static Style zstyles, zlstyles;
 
 /* Memory stuff. */
 
 static void
-freestyle(Style s)
+freestypat(Stypat p)
 {
-    Style n;
-
-    while (s) {
-	n = s->next;
-
-	zsfree(s->name);
-	if (s->vals)
-	    freearray(s->vals);
-	zfree(s, sizeof(*s));
-
-	s = n;
-    }
+    zsfree(p->pat);
+    freepatprog(p->prog);
+    if (p->vals)
+	freearray(p->vals);
+    zfree(p, sizeof(*p));
 }
 
 static void
-freestypat(Stypat p)
+freeallstyles(void)
 {
-    Stypat n;
-
-    while (p) {
-	n = p->next;
-
-	zsfree(p->pat);
-	freepatprog(p->prog);
-	zfree(p, sizeof(*p));
-
-	p = n;
+    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 struct for a pattern, if any. */
-
-static Stypat
-getstypat(char *pat)
-{
-    Stypat p;
-
-    for (p = zstyles; p; p = p->next)
-	if (!strcmp(pat, p->pat))
-	    return p;
-
-    return NULL;
-}
-
-/* Get the style stuff for a name. */
+/* Get the style struct for a name. */
 
 static Style
-getstyle(Stypat p, char *name)
+getstyle(char *name)
 {
     Style s;
 
-    for (s = p->styles; s; s=  s->next)
+    for (s = zstyles; s; s = s->next)
 	if (!strcmp(name, s->name))
 	    return s;
 
@@ -121,66 +102,48 @@ getstyle(Stypat p, char *name)
 /* Store a value for a style. */
 
 static void
-setstyle(Stypat p, char *name, char **vals)
+setstypat(Style s, char *pat, Patprog prog, char **vals)
 {
-    Style s;
+    int weight, tmp, first;
+    char *str;
+    Stypat p, q, qq;
 
-    for (s = p->styles; s; s = s->next)
-	if (!strcmp(name, s->name)) {
+    for (p = s->pats; p; p = p->next)
+	if (!strcmp(pat, p->pat)) {
 
 	    /* Exists -> replace. */
 
-	    if (s->vals)
-		freearray(s->vals);
-	    PERMALLOC {
-		s->vals = arrdup(vals);
-	    } LASTALLOC;
+	    if (p->vals)
+		freearray(p->vals);
+	    p->vals = zarrdup(vals);
 
 	    return;
 	}
 
-    /* New style. */
-
-    s = (Style) zalloc(sizeof(*s));
-
-    s->name = ztrdup(name);
-    PERMALLOC {
-	s->vals = arrdup(vals);
-    } LASTALLOC;
-    s->next = NULL;
-
-    if (p->lstyles)
-	p->lstyles->next = s;
-    else
-	p->styles = s;
-    p->lstyles = s;
-}
-
-/* Add a new pattern. */
+    /* New pattern. */
 
-static Stypat
-addstypat(char *pat, Patprog prog)
-{
-    Stypat p, q, qq;
-    int weight, tmp, first;
-    char *s;
+    p = (Stypat) zalloc(sizeof(*p));
+    p->pat = ztrdup(pat);
+    p->prog = prog;
+    p->vals = zarrdup(vals);
+    p->next = NULL;
 
     /* Calculate the weight. */
 
-    for (weight = 0, tmp = 2, first = 1, s = pat; *s; s++) {
-	if (first && *s == '*' && (!s[1] || s[1] == ':')) {
+    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 (*s == '(' || *s == '|' || *s == '*' || *s == '[' || *s == '<' ||
-	    *s == '?' || *s == '#' || *s == '^')
+	if (*str == '(' || *str == '|' || *str == '*' || *str == '[' ||
+	    *str == '<' ||  *str == '?' || *str == '#' || *str == '^')
 	    /* Is pattern. */
 	    tmp = 1;
 
-	if (*s == ':') {
+	if (*str == ':') {
 	    /* Yet another component. */
 
 	    first = 1;
@@ -188,91 +151,52 @@ addstypat(char *pat, Patprog prog)
 	    tmp = 2;
 	}
     }
-    weight += tmp;
+    p->weight = (weight += tmp);
 
-    p = (Stypat) zalloc(sizeof(*p));
-
-    p->pat = ztrdup(pat);
-    p->weight = weight;
-    p->prog = prog;
-    p->styles = p->lstyles = NULL;
-
-    for (qq = NULL, q = zstyles; q && q->weight >= weight;
+    for (qq = NULL, q = s->pats; q && q->weight >= weight;
 	 qq = q, q = q->next);
 
     p->next = q;
     if (qq)
 	qq->next = p;
     else
-	zstyles = p;
-    if (!q)
-	lzstyles = p;
-
-    return p;
+	s->pats = p;
 }
 
-/* Delete a style. */
-
-static void
-deletestyle(Stypat p, char *name)
-{
-    Style ps, s;
-
-    for (ps = NULL, s = p->styles; s; ps = s, s = s->next)
-	if (!strcmp(name, s->name)) {
-	    if (ps)
-		ps->next = s->next;
-	    else
-		p->styles = s->next;
-	    if (s == p->lstyles)
-		p->lstyles = ps;
+/* Add a new style. */
 
-	    s->next = NULL;
-	    freestyle(s);
-
-	    return;
-	}
-}
-
-/* Delete a whole pattern with all its styles. */
-
-static void
-deletestypat(Stypat pat)
+static Style
+addstyle(char *name)
 {
-    Stypat pp, p;
+    Style s;
 
-    for (pp = NULL, p = zstyles; p; pp = p, p = p->next)
-	if (p == pat) {
-	    if (pp)
-		pp->next = p->next;
-	    else
-		zstyles = p->next;
-	    if (p == lzstyles)
-		lzstyles = pp;
+    s = (Style) zalloc(sizeof(*s));
+    s->next = NULL;
+    s->pats = NULL;
+    s->name = ztrdup(name);
 
-	    p->next = NULL;
-	    zsfree(p->pat);
-	    freepatprog(p->prog);
-	    freestyle(p->styles);
-	    zfree(p, sizeof(*p));
+    if (zlstyles)
+	zlstyles->next = s;
+    else
+	zstyles = s;
+    zlstyles = s;
 
-	    return;
-	}
+    return s;
 }
 
 /* Look up a style for a context pattern. This does the matching. */
 
-static Style
+static Stypat
 lookupstyle(char *ctxt, char *style)
 {
-    Stypat p;
     Style s;
+    Stypat p;
 
-    for (p = zstyles; p; p = p->next)
-	if (pattry(p->prog, ctxt))
-	    for (s = p->styles; s; s = s->next)
-		if (!strcmp(style, s->name))
-		    return s;
+    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;
 
     return NULL;
 }
@@ -289,7 +213,7 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 
 	if ((oc = args[0][1]) && oc != '-') {
 	    if (args[0][2]) {
-		zerrnam(nam, "invalid argument: %s", args[0], 0);
+		zwarnnam(nam, "invalid argument: %s", args[0], 0);
 		return 1;
 	    }
 	    if (oc == 'L')
@@ -302,47 +226,46 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	add = 1;
 
     if (add) {
-	Stypat p;
+	Style s;
+	Patprog prog;
+	char *pat;
 
 	if (arrlen(args) < 2) {
-	    zerrnam(nam, "not enough arguments", NULL, 0);
+	    zwarnnam(nam, "not enough arguments", NULL, 0);
 	    return 1;
 	}
-	if (!(p = getstypat(args[0]))) {
-	    Patprog prog;
-	    char *pat = dupstring(args[0]);
-
-	    tokenize(pat);
+	pat = dupstring(args[0]);
+	tokenize(pat);
 
-	    if (!(prog = patcompile(pat, PAT_ZDUP, NULL))) {
-		zerrnam(nam, "invalid pattern: %s", args[0], 0);
-		return 1;
-	    }
-	    p = addstypat(args[0], prog);
+	if (!(prog = patcompile(pat, PAT_ZDUP, NULL))) {
+	    zwarnnam(nam, "invalid pattern: %s", args[0], 0);
+	    return 1;
 	}
-	setstyle(p, args[1], args + 2);
+	if (!(s = getstyle(args[1])))
+	    s = addstyle(args[1]);
+	setstypat(s, args[0], prog, args + 2);
 
 	return 0;
     }
     if (list) {
-	Stypat p;
 	Style s;
+	Stypat p;
 	char **v;
 
-	for (p = zstyles; p; p = p->next) {
+	for (s = zstyles; s; s = s->next) {
 	    if (list == 1) {
-		quotedzputs(p->pat, stdout);
+		quotedzputs(s->name, stdout);
 		putchar('\n');
 	    }
-	    for (s = p->styles; s; s = s->next) {
+	    for (p = s->pats; p; p = p->next) {
 		if (list == 1)
-		    printf("    %s", s->name);
+		    printf("    %s", p->pat);
 		else {
 		    printf("zstyle ");
 		    quotedzputs(p->pat, stdout);
 		    printf(" %s", s->name);
 		}
-		for (v = s->vals; *v; v++) {
+		for (v = p->vals; *v; v++) {
 		    putchar(' ');
 		    quotedzputs(*v, stdout);
 		}
@@ -358,54 +281,75 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
     case 'a': min = 3; max =  3; break;
     case 'h': 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:
-	zerrnam(nam, "invalid option: %s", args[0], 0);
+	zwarnnam(nam, "invalid option: %s", args[0], 0);
 	return 1;
     }
     n = arrlen(args) - 1;
     if (n < min) {
-	zerrnam(nam, "not enough arguments", NULL, 0);
+	zwarnnam(nam, "not enough arguments", NULL, 0);
 	return 1;
     } else if (max >= 0 && n > max) {
-	zerrnam(nam, "too many arguments", NULL, 0);
+	zwarnnam(nam, "too many arguments", NULL, 0);
 	return 1;
     }
     switch (args[0][1]) {
     case 'd':
 	{
-	    Stypat p;
+	    Style s;
 
 	    if (args[1]) {
-		if ((p = getstypat(args[1]))) {
-		    if (args[2]) {
-			char **ap = args + 2;
-
-			while (*ap)
-			    deletestyle(p, *ap++);
-
-			if (!p->styles)
-			    deletestypat(p);
-		    } else
-			deletestypat(p);
+		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 {
-		freestypat(zstyles);
-		zstyles = lzstyles = NULL;
-	    }
+	    } else
+		freeallstyles();
 	}
 	break;
     case 's':
 	{
-	    Style s;
+	    Stypat s;
 	    char *ret;
 	    int val;
 
 	    if ((s = lookupstyle(args[1], args[2])) && s->vals[0]) {
-		PERMALLOC {
-		    ret = sepjoin(s->vals, (args[4] ? args[4] : " "));
-		} LASTALLOC;
+		ret = sepjoin(s->vals, (args[4] ? args[4] : " "), 0);
 		val = 0;
 	    } else {
 		ret = ztrdup("");
@@ -418,7 +362,7 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	break;
     case 'b':
 	{
-	    Style s;
+	    Stypat s;
 	    char *ret;
 	    int val;
 
@@ -442,21 +386,17 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
     case 'a':
     case 'h':
 	{
-	    Style s;
+	    Stypat s;
 	    char **ret;
 	    int val;
 
 	    if ((s = lookupstyle(args[1], args[2]))) {
-		PERMALLOC {
-		    ret = arrdup(s->vals);
-		} LASTALLOC;
+		ret = zarrdup(s->vals);
 		val = 0;
 	    } else {
 		char *dummy = NULL;
 
-		PERMALLOC {
-		    ret = arrdup(&dummy);
-		} LASTALLOC;
+		ret = zarrdup(&dummy);
 		val = 1;
 	    }
 	    if (args[0][1] == 'a')
@@ -468,8 +408,9 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	}
 	break;
     case 't':
+    case 'T':
 	{
-	    Style s;
+	    Stypat s;
 
 	    if ((s = lookupstyle(args[1], args[2])) && s->vals[0]) {
 		if (args[3]) {
@@ -489,12 +430,12 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 			     !strcmp(s->vals[0], "on") ||
 			     !strcmp(s->vals[0], "1"));
 	    }
-	    return 1;
+	    return (args[0][1] == 't' ? (s ? 1 : 2) : 0);
 	}
 	break;
     case 'm':
 	{
-	    Style s;
+	    Stypat s;
 	    Patprog prog;
 
 	    tokenize(args[3]);
@@ -514,32 +455,44 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	{
 	    LinkList l = newlinklist();
 	    int ret = 1;
+	    Style s;
 	    Stypat p;
 
 	    if (args[2]) {
-		if ((p = getstypat(args[2]))) {
-		    Style s;
-
-		    if (args[3]) {
-			if ((s = getstyle(p, args[3]))) {
-			    char **v = s->vals;
+		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++);
+				while (*v)
+				    addlinknode(l, *v++);
 
-			    ret = 0;
+				ret = 0;
+				break;
+			    }
 			}
-		    } else {
-			for (s = p->styles; s; s = s->next)
-			    addlinknode(l, s->name);
-
-			ret = 0;
 		    }
+		} 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 {
-		for (p = zstyles; p; p = p->next)
-		    addlinknode(l, p->pat);
-
+		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);
@@ -558,7 +511,7 @@ bin_zformat(char *nam, char **args, char *ops, int func)
     char opt;
 
     if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
-	zerrnam(nam, "invalid argument: %s", args[0], 0);
+	zwarnnam(nam, "invalid argument: %s", args[0], 0);
 	return 1;
     }
     args++;
@@ -575,7 +528,7 @@ bin_zformat(char *nam, char **args, char *ops, int func)
 		if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' ||
 		    (ap[0][0] >= '0' && ap[0][0] <= '9') ||
 		    ap[0][1] != ':') {
-		    zerrnam(nam, "invalid argument: %s", *ap, 0);
+		    zwarnnam(nam, "invalid argument: %s", *ap, 0);
 		    return 1;
 		}
 		specs[STOUC(ap[0][0])] = ap[0] + 2;
@@ -717,35 +670,867 @@ bin_zformat(char *nam, char **args, char *ops, int func)
 	}
 	break;
     }
-    zerrnam(nam, "invalid option: -%c", 0, opt);
+    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;
+
+    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;
+}
+
+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 = getaparam("mend");
+		int len = atoi(mend[0]);
+
+		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, char *ops, 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, 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, char *ops, int func)
+{
+    char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL;
+    int del = 0, f;
+    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 '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)
+		break;
+	} else {
+	    args--;
+	    break;
+	}
+    }
+    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;
+    }
+    for (pp = pparams; (o = *pp); pp++) {
+	if (*o != '-')
+	    break;
+	if (!o[1] || (o[1] == '-' && !o[2])) {
+	    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)
+		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);
+	}
+    }
+    for (a = opt_arrs; a; a = a->next) {
+	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++;
+
+	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) {
+	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_zutil(Module m)
+setup_(Module m)
 {
-    zstyles = NULL;
+    zstyles = zlstyles = NULL;
 
     return 0;
 }
 
 /**/
 int
-boot_zutil(Module m)
+boot_(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
 }
 
 /**/
 int
-cleanup_zutil(Module m)
+cleanup_(Module m)
 {
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
@@ -753,9 +1538,9 @@ cleanup_zutil(Module m)
 
 /**/
 int
-finish_zutil(Module m)
+finish_(Module m)
 {
-    freestypat(zstyles);
+    freeallstyles();
 
     return 0;
 }