mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-11-20 05:53:52 +01:00
762 lines
14 KiB
C
762 lines
14 KiB
C
/*
|
|
* 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;
|
|
}
|