From 9141828e71dac723102ebd652e8a21e26c4b8e65 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Fri, 10 Dec 1999 14:48:04 +0000 Subject: Initial revision --- Doc/Zsh/mod_zutil.yo | 109 ++++++++ Src/Modules/zutil.c | 761 ++++++++++++++++++++++++++++++++++++++++++++++++++ Src/Modules/zutil.mdd | 3 + 3 files changed, 873 insertions(+) create mode 100644 Doc/Zsh/mod_zutil.yo create mode 100644 Src/Modules/zutil.c create mode 100644 Src/Modules/zutil.mdd 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" -- cgit 1.4.1