about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Doc/Zsh/mod_zutil.yo109
-rw-r--r--Src/Modules/zutil.c761
-rw-r--r--Src/Modules/zutil.mdd3
3 files changed, 873 insertions, 0 deletions
diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo
new file mode 100644
index 000000000..477f955eb
--- /dev/null
+++ b/Doc/Zsh/mod_zutil.yo
@@ -0,0 +1,109 @@
+texinode(The zutil Module)(The complete Module)(The clone Module)(Zsh Modules)
+sect(The zutil Module)
+cindex(builtins, utility)
+The tt(zutil) module only adds some builtins:
+
+startitem()
+xitem(tt(zstyle) [ tt(-L) ])
+xitem(tt(zstyle) [ tt(-) | tt(-)tt(-) ] var(pattern) var(style) var(strings) ...)
+xitem(tt(zstyle -d) [ var(pattern) [ var(styles) ... ] ])
+xitem(tt(zstyle -g) var(name) [ var(pattern) [ var(style) ] ])
+xitem(tt(zstyle -s) var(context) var(style) var(name) [ var(sep) ])
+xitem(tt(zstyle -b) var(context) var(style) var(name))
+xitem(tt(zstyle -a) var(context) var(style) var(name))
+xitem(tt(zstyle -h) var(context) var(style) var(name))
+xitem(tt(zstyle -t) var(context) var(style) [ var(strings) ...])
+item(tt(zstyle -m) var(context) var(style) var(pattern))(
+This builtin command is used to define and lookup styles. Styles are
+pairs of names and values, where the values consist of any number of
+strings. They are stored together with patterns and lookup is done by
+giving a string, called the `context', which is compared to the
+patterns. The definition stored for the first matching pattern will be 
+returned. For this, the patterns are ordered from most specific to
+less specific and patterns that are equally specific keep the order in 
+which they were defined. A pattern is considered to be more specific
+than another if it contains more components (substrings separated by
+colons) or if the patterns for the components are more specific, where 
+simple strings are considered to be more specific than patterns and
+complex patterns are considered to be more specific than the pattern
+`tt(*)'.
+
+The first form (without arguments) lists the definitions in the order
+tt(zstyle) will test them. If the tt(-L) option is given, listing is
+done in the form of calls to tt(zstyle).
+
+In the second form this defines the given var(style) for the
+var(pattern) with the var(strings) as the value.
+
+The third form can be used to delete such definitions. Without
+arguments all definitions are deleted, with a var(pattern) all
+definitions for that pattern are deleted and if any var(styles) are
+given, then only those styles are deleted for the var(pattern).
+
+The fourth form allows to retrieve definitions. The var(name) will be
+used as the name of an array in which the results are stored. Without
+any further arguments, all var(patterns) defined are returned. With a
+var(pattern) the styles defined for that pattern are returned and with 
+both a var(pattern) and a var(style), the value strings of that
+combination is returned.
+
+The other forms can be used to look up or test patterns. With the
+tt(-s) option, the value of the style is returned as a string in the
+parameter var(name). For this, the strings from the value are
+concatenated with spaces (or the var(sep) string if that is given)
+between them. The tt(-b) option makes the value be returned as a
+boolean, i.e. as the string tt(yes) if the value has only one string
+and that is equal to one of tt(yes), tt(true), tt(on), or tt(1). If
+the value has more than one string or only one but that is different
+from the strings mentioned, the parameter will be set to tt(no). The
+tt(-a) option makes the value be returned as an array and the tt(-h)
+makes it be returned as an associative array (with the first, third,
+etc. string being used as the keys and the other strings being used as 
+the values).
+
+The tt(-t) option can be used to test the value of a style, i.e. it
+only sets the return value. Without any var(strings) arguments it is
+zero if the style is defined for at least one matching pattern, has
+only one string in its value and that is equal to one of tt(true),
+tt(yes), tt(on) or tt(1). If any var(strings) are given the return
+zero if and only if at least one of the var(strings) is equal to at
+least one of the strings in the value.
+
+The tt(-m) option can be used to match a value. It returns zero if the 
+var(pattern) matches at least one of the strings in the value.
+)
+xitem(tt(zformat -f) var(param) var(format) var(specs) ...)
+item(tt(zformat -a) var(array) var(sep) var(specs) ...)(
+This builtin provides to different forms of formatting. The first form 
+is selected with the tt(-f) option. If this is given, the var(format)
+string will be modified by replacing sequences starting with a percent 
+sign in it with strings from the var(specs). Each var(spec) has to be
+of the form `var(char)tt(:)var(string)' and this will make every
+appearence of the sequence `tt(%)var(char)' in var(format) be replaced 
+with the var(string). The `tt(%)' sequence may also contain optional
+minimum and maximum field width specifications between the `tt(%)' and 
+the `var(char)' in the form `tt(%)var(min)tt(.)var(max)tt(c)',
+i.e. the minimum field width is given first and if the maximum field
+width is used, it has to be preceded by a dot. Giving a minimum field
+width makes the result be padded with spaces to the right if the
+var(string) is shorter than the requested width. Padding to the left
+can be achieved by giving a negative minimum field width. If a maximum 
+field width is given, the var(string) will be truncated after that
+many characters. After all `tt(%)' sequences for the given var(specs)
+have been processed, the resulting string is stored in the parameter
+var(param).
+
+The second form, using the tt(-a) option, can be used to get aligned
+strings. Here, the var(specs) are of the form
+`var(left)tt(:)var(right)' where `var(left)' and `var(right)' are
+arbitrary strings. These strings are modified by replacing the colons
+with the var(sep) string and padding the var(left) strings with spaces 
+to the right so that the var(sep) strings in the result (and hence the 
+var(right) strings after them) are all aligned if the strings are
+printed below each other. All strings without a colon are left
+unchanged and all strings with a empty var(right) string have the
+trailing colon removed. In both cases the lengths of the strings
+are not used to determine how the other strings have to be aligned.
+The resulting strings are stored in the var(array).
+)
+enditem()
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
new file mode 100644
index 000000000..590678f98
--- /dev/null
+++ b/Src/Modules/zutil.c
@@ -0,0 +1,761 @@
+/*
+ * zutil.c - misc utilities
+ *
+ * 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"
+
+/* Style stuff. */
+
+typedef struct stypat *Stypat;
+typedef struct style *Style;
+
+/* A pattern and the styles for it. */
+
+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;
+
+/* Memory stuff. */
+
+static void
+freestyle(Style s)
+{
+    Style n;
+
+    while (s) {
+	n = s->next;
+
+	zsfree(s->name);
+	if (s->vals)
+	    freearray(s->vals);
+	zfree(s, sizeof(*s));
+
+	s = n;
+    }
+}
+
+static void
+freestypat(Stypat p)
+{
+    Stypat n;
+
+    while (p) {
+	n = p->next;
+
+	zsfree(p->pat);
+	freepatprog(p->prog);
+	zfree(p, sizeof(*p));
+
+	p = n;
+    }
+}
+
+/* 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. */
+
+static Style
+getstyle(Stypat p, char *name)
+{
+    Style s;
+
+    for (s = p->styles; s; s=  s->next)
+	if (!strcmp(name, s->name))
+	    return s;
+
+    return NULL;
+}
+
+/* Store a value for a style. */
+
+static void
+setstyle(Stypat p, char *name, char **vals)
+{
+    Style s;
+
+    for (s = p->styles; s; s = s->next)
+	if (!strcmp(name, s->name)) {
+
+	    /* Exists -> replace. */
+
+	    if (s->vals)
+		freearray(s->vals);
+	    PERMALLOC {
+		s->vals = arrdup(vals);
+	    } LASTALLOC;
+
+	    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. */
+
+static Stypat
+addstypat(char *pat, Patprog prog)
+{
+    Stypat p, q, qq;
+    int weight, tmp, first;
+    char *s;
+
+    /* Calculate the weight. */
+
+    for (weight = 0, tmp = 2, first = 1, s = pat; *s; s++) {
+	if (first && *s == '*' && (!s[1] || s[1] == ':')) {
+	    /* Only `*' in this component. */
+	    tmp = 0;
+	    continue;
+	}
+	first = 0;
+
+	if (*s == '(' || *s == '|' || *s == '*' || *s == '[' || *s == '<' ||
+	    *s == '?' || *s == '#' || *s == '^')
+	    /* Is pattern. */
+	    tmp = 1;
+
+	if (*s == ':') {
+	    /* Yet another component. */
+
+	    first = 1;
+	    weight += tmp;
+	    tmp = 2;
+	}
+    }
+    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;
+	 qq = q, q = q->next);
+
+    p->next = q;
+    if (qq)
+	qq->next = p;
+    else
+	zstyles = p;
+    if (!q)
+	lzstyles = p;
+
+    return 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;
+
+	    s->next = NULL;
+	    freestyle(s);
+
+	    return;
+	}
+}
+
+/* Delete a whole pattern with all its styles. */
+
+static void
+deletestypat(Stypat pat)
+{
+    Stypat pp, p;
+
+    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;
+
+	    p->next = NULL;
+	    zsfree(p->pat);
+	    freepatprog(p->prog);
+	    freestyle(p->styles);
+	    zfree(p, sizeof(*p));
+
+	    return;
+	}
+}
+
+/* Look up a style for a context pattern. This does the matching. */
+
+static Style
+lookupstyle(char *ctxt, char *style)
+{
+    Stypat p;
+    Style s;
+
+    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;
+
+    return NULL;
+}
+
+static int
+bin_zstyle(char *nam, char **args, char *ops, int func)
+{
+    int min, max, n, add = 0, list = 0;
+
+    if (!args[0])
+	list = 1;
+    else if (args[0][0] == '-') {
+	char oc;
+
+	if ((oc = args[0][1]) && oc != '-') {
+	    if (args[0][2]) {
+		zerrnam(nam, "invalid argument: %s", args[0], 0);
+		return 1;
+	    }
+	    if (oc == 'L')
+		list = 2;
+	} else {
+	    add = 1;
+	    args++;
+	}
+    } else
+	add = 1;
+
+    if (add) {
+	Stypat p;
+
+	if (arrlen(args) < 2) {
+	    zerrnam(nam, "not enough arguments", NULL, 0);
+	    return 1;
+	}
+	if (!(p = getstypat(args[0]))) {
+	    Patprog prog;
+	    char *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);
+	}
+	setstyle(p, args[1], args + 2);
+
+	return 0;
+    }
+    if (list) {
+	Stypat p;
+	Style s;
+	char **v;
+
+	for (p = zstyles; p; p = p->next) {
+	    if (list == 1) {
+		quotedzputs(p->pat, stdout);
+		putchar('\n');
+	    }
+	    for (s = p->styles; s; s = s->next) {
+		if (list == 1)
+		    printf("    %s", s->name);
+		else {
+		    printf("zstyle ");
+		    quotedzputs(p->pat, stdout);
+		    printf(" %s", s->name);
+		}
+		for (v = s->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 'h': min = 3; max =  3; 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);
+	return 1;
+    }
+    n = arrlen(args) - 1;
+    if (n < min) {
+	zerrnam(nam, "not enough arguments", NULL, 0);
+	return 1;
+    } else if (max >= 0 && n > max) {
+	zerrnam(nam, "too many arguments", NULL, 0);
+	return 1;
+    }
+    switch (args[0][1]) {
+    case 'd':
+	{
+	    Stypat p;
+
+	    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);
+		}
+	    } else {
+		freestypat(zstyles);
+		zstyles = lzstyles = NULL;
+	    }
+	}
+	break;
+    case 's':
+	{
+	    Style 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;
+		val = 0;
+	    } else {
+		ret = ztrdup("");
+		val = 1;
+	    }
+	    setsparam(args[3], ret);
+
+	    return val;
+	}
+	break;
+    case 'b':
+	{
+	    Style s;
+	    char *ret;
+	    int val;
+
+	    if ((s = lookupstyle(args[1], args[2])) &&
+		s->vals[0] && !s->vals[1] &&
+		(!strcmp(s->vals[0], "yes") ||
+		 !strcmp(s->vals[0], "true") ||
+		 !strcmp(s->vals[0], "on") ||
+		 !strcmp(s->vals[0], "1"))) {
+		ret = "yes";
+		val = 0;
+	    } else {
+		ret = "no";
+		val = 1;
+	    }
+	    setsparam(args[3], ztrdup(ret));
+
+	    return val;
+	}
+	break;
+    case 'a':
+    case 'h':
+	{
+	    Style s;
+	    char **ret;
+	    int val;
+
+	    if ((s = lookupstyle(args[1], args[2]))) {
+		PERMALLOC {
+		    ret = arrdup(s->vals);
+		} LASTALLOC;
+		val = 0;
+	    } else {
+		char *dummy = NULL;
+
+		PERMALLOC {
+		    ret = arrdup(&dummy);
+		} LASTALLOC;
+		val = 1;
+	    }
+	    if (args[0][1] == 'a')
+		setaparam(args[3], ret);
+	    else
+		sethparam(args[3], ret);
+
+	    return val;
+	}
+	break;
+    case 't':
+	{
+	    Style s;
+
+	    if ((s = lookupstyle(args[1], args[2])) && s->vals[0]) {
+		if (args[3]) {
+		    char **ap = args + 3, **p;
+
+		    while (*ap) {
+			p = s->vals;
+			while (*p)
+			    if (!strcmp(*ap, *p++))
+				return 0;
+			ap++;
+		    }
+		    return 1;
+		} else
+		    return !(!strcmp(s->vals[0], "true") ||
+			     !strcmp(s->vals[0], "yes") ||
+			     !strcmp(s->vals[0], "on") ||
+			     !strcmp(s->vals[0], "1"));
+	    }
+	    return 1;
+	}
+	break;
+    case 'm':
+	{
+	    Style s;
+	    Patprog prog;
+
+	    tokenize(args[3]);
+
+	    if ((s = lookupstyle(args[1], args[2])) &&
+		(prog = patcompile(args[3], PAT_STATIC, NULL))) {
+		char **p = s->vals;
+
+		while (*p)
+		    if (pattry(prog, *p++))
+			return 0;
+	    }
+	    return 1;
+	}
+	break;
+    case 'g':
+	{
+	    LinkList l = newlinklist();
+	    int ret = 1;
+	    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;
+
+			    while (*v)
+				addlinknode(l, *v++);
+
+			    ret = 0;
+			}
+		    } else {
+			for (s = p->styles; s; s = s->next)
+			    addlinknode(l, s->name);
+
+			ret = 0;
+		    }
+		}
+	    } else {
+		for (p = zstyles; p; p = p->next)
+		    addlinknode(l, p->pat);
+
+		ret = 0;
+	    }
+	    set_list_array(args[1], l);
+
+	    return ret;
+	}
+    }
+    return 0;
+}
+
+/* Format stuff. */
+
+static int
+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);
+	return 1;
+    }
+    args++;
+
+    switch (opt) {
+    case 'f':
+	{
+	    char **ap, *specs[256], *out, *s;
+	    int olen, oused = 0;
+
+	    memset(specs, 0, 256 * sizeof(char *));
+
+	    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] != ':') {
+		    zerrnam(nam, "invalid argument: %s", *ap, 0);
+		    return 1;
+		}
+		specs[STOUC(ap[0][0])] = ap[0] + 2;
+	    }
+	    out = (char *) zhalloc(olen = 128);
+
+	    for (s = args[1]; *s; s++) {
+		if (*s == '%') {
+		    int right, min = -1, max = -1, outl;
+		    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';
+		    }
+		    if (*s == '.' && s[1] >= '0' && s[1] <= '9') {
+			for (max = 0, s++; *s >= '0' && *s <= '9'; s++)
+			    max = (max * 10) + (int) STOUC(*s) - '0';
+		    }
+		    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 (oused + outl >= olen) {
+			    int nlen = olen + outl + 128;
+			    char *tmp = (char *) zhalloc(nlen);
+
+			    memcpy(tmp, out, olen);
+			    olen = nlen;
+			    out = tmp;
+			}
+			if (len >= outl) {
+			    memcpy(out + oused, spec, outl);
+			    oused += outl;
+			} else {
+			    int diff = outl - len;
+
+			    if (right) {
+				while (diff--)
+				    out[oused++] = ' ';
+				memcpy(out + oused, spec, len);
+				oused += len;
+			    } else {
+				memcpy(out + oused, spec, len);
+				oused += len;
+				while (diff--)
+				    out[oused++] = ' ';
+			    }				
+			}
+		    } else {
+			int len = s - start + 1;
+
+			if (oused + len >= olen) {
+			    int nlen = olen + len + 128;
+			    char *tmp = (char *) zhalloc(nlen);
+
+			    memcpy(tmp, out, olen);
+			    olen = nlen;
+			    out = tmp;
+			}
+			memcpy(out + oused, start, len);
+			oused += len;
+		    }
+		} else {
+		    if (oused + 1 >= olen) {
+			char *tmp = (char *) zhalloc(olen << 1);
+
+			memcpy(tmp, out, olen);
+			olen <<= 1;
+			out = tmp;
+		    }
+		    out[oused++] = *s;
+		}
+	    }
+	    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;
+    }
+    zerrnam(nam, "invalid option: -%c", 0, opt);
+    return 1;
+}
+
+static struct builtin bintab[] = {
+    BUILTIN("zstyle", 0, bin_zstyle, 0, -1, 0, NULL, NULL),
+    BUILTIN("zformat", 0, bin_zformat, 3, -1, 0, NULL, NULL),
+};
+
+
+/**/
+int
+setup_zutil(Module m)
+{
+    zstyles = NULL;
+
+    return 0;
+}
+
+/**/
+int
+boot_zutil(Module m)
+{
+    return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+}
+
+/**/
+int
+cleanup_zutil(Module m)
+{
+    deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
+    return 0;
+}
+
+/**/
+int
+finish_zutil(Module m)
+{
+    freestypat(zstyles);
+
+    return 0;
+}
diff --git a/Src/Modules/zutil.mdd b/Src/Modules/zutil.mdd
new file mode 100644
index 000000000..edc1f0d32
--- /dev/null
+++ b/Src/Modules/zutil.mdd
@@ -0,0 +1,3 @@
+objects="zutil.o"
+
+autobins="zformat zstyle"