From f1d2fddeca5388c2d6009cc5488cb431e11387b2 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 8 Jun 2007 13:58:40 +0000 Subject: 23537: make style list a hash table --- ChangeLog | 8 ++ Doc/Zsh/mod_zutil.yo | 3 +- Src/Modules/zutil.c | 311 +++++++++++++++++++++++++++++++-------------------- Test/V05styles.ztst | 145 ++++++++++++++++++++++++ 4 files changed, 344 insertions(+), 123 deletions(-) create mode 100644 Test/V05styles.ztst diff --git a/ChangeLog b/ChangeLog index 3974f7e87..dfc783343 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2007-06-08 Peter Stephenson + + * 23537: Doc/Zsh/mod_zutil.yo, Src/Module/zutil.c, + Test/V05styles.ztst: use hash table for styles and add + tests. Apart from ordering of styles by name (patterns + are still ordered by weight) there should be no effect on + shell syntax and output. + 2007-06-07 Peter Stephenson * 23535: Src/Module/parameter.mdd: autoload saliases and diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index 659b4aff7..d3699b158 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -30,7 +30,8 @@ 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 +The first form (without arguments) lists the definitions. Styles +are shown in alphabetic order and patterns are shown in the order tt(zstyle) will test them. If the tt(-L) option is given, listing is done in the form of calls to diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index a4172c581..127509b01 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -38,9 +38,8 @@ typedef struct style *Style; /* A pattern and the styles for it. */ struct style { - Style next; /* next in stypat list */ + struct hashnode node; Stypat pats; /* patterns */ - char *name; }; struct stypat { @@ -51,13 +50,42 @@ struct stypat { Eprog eval; /* eval-on-retrieve? */ char **vals; }; - -/* List of styles. */ -static Style zstyles; +/* Hash table of styles and associated functions. */ + +static HashTable zstyletab; /* Memory stuff. */ +static void +freestylepatnode(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 +freestylenode(HashNode hn) +{ + Style s = (Style) hn; + Stypat p, pn; + + p = s->pats; + while (p) { + pn = p->next; + freestylepatnode(p); + p = pn; + } + + zsfree(s->node.nam); + zfree(s, sizeof(struct style)); +} + /* * Free the information for one of the patterns associated with * a style. @@ -79,60 +107,135 @@ freestypat(Stypat p, Style s, Stypat prev) s->pats = p->next; } - zsfree(p->pat); - freepatprog(p->prog); - if (p->vals) - freearray(p->vals); - if (p->eval) - freeeprog(p->eval); - zfree(p, sizeof(*p)); + freestylepatnode(p); if (s && !s->pats) { /* No patterns left, free style */ - if (s == zstyles) { - zstyles = s->next; - } else { - Style s2; - for (s2 = zstyles; s2->next != s; s2 = s2->next) - ; - s2->next = s->next; - } - zsfree(s->name); + zstyletab->removenode(zstyletab, s->node.nam); + zsfree(s->node.nam); zfree(s, sizeof(*s)); } } +/* Pattern to match context when printing nodes */ + +static Patprog zstyle_contprog; + +/* + * Print a node. Print flags as shown. + */ +enum { + ZSLIST_NONE, + ZSLIST_BASIC, + ZSLIST_SYNTAX, +}; + static void -freeallstyles(void) +printstylenode(HashNode hn, int printflags) { - Style s, sn; - Stypat p, pn; + Style s = (Style)hn; + Stypat p; + char **v; + + if (printflags == ZSLIST_BASIC) { + quotedzputs(s->node.nam, stdout); + putchar('\n'); + } - for (s = zstyles; s; s = sn) { - sn = s->next; - for (p = s->pats; p; p = pn) { - pn = p->next; - freestypat(p, NULL, NULL); + for (p = s->pats; p; p = p->next) { + if (zstyle_contprog && !pattry(zstyle_contprog, p->pat)) + continue; + if (printflags == ZSLIST_BASIC) + printf("%s %s", (p->eval ? "(eval)" : " "), p->pat); + else { + printf("zstyle %s", (p->eval ? "-e " : "")); + quotedzputs(p->pat, stdout); + printf(" %s", s->node.nam); } - zsfree(s->name); - zfree(s, sizeof(*s)); + for (v = p->vals; *v; v++) { + putchar(' '); + quotedzputs(*v, stdout); + } + putchar('\n'); } - zstyles = NULL; } -/* Get the style struct for a name. */ +/* + * Scan the list for a particular pattern, maybe adding matches to + * the link list (heap memory). Value to be added as + * shown in enum + */ +static LinkList zstyle_list; +static char *zstyle_patname; -static Style -getstyle(char *name) +enum { + ZSPAT_NAME, /* Add style names for matched pattern to list */ + ZSPAT_PAT, /* Add all patterns to list, doesn't use patname */ + ZSPAT_REMOVE, /* Remove matched pattern, doesn't use list */ +}; + +static void +scanpatstyles(HashNode hn, int spatflags) { - Style s; + Style s = (Style)hn; + Stypat p, q; + LinkNode n; + + for (q = NULL, p = s->pats; p; q = p, p = p->next) { + switch (spatflags) { + case ZSPAT_NAME: + if (!strcmp(p->pat, zstyle_patname)) { + addlinknode(zstyle_list, s->node.nam); + return; + } + break; - for (s = zstyles; s; s = s->next) - if (!strcmp(name, s->name)) { - return s; + case ZSPAT_PAT: + /* Check pattern isn't already there */ + for (n = firstnode(zstyle_list); n; incnode(n)) + if (!strcmp(p->pat, (char *) getdata(n))) + break; + if (!n) + addlinknode(zstyle_list, p->pat); + break; + + case ZSPAT_REMOVE: + if (!strcmp(p->pat, zstyle_patname)) { + freestypat(p, s, q); + /* + * May remove link node itself; that's OK + * when scanning but we need to make sure + * we don't look at it any more. + */ + return; + } + break; } + } +} - return NULL; + +static HashTable +newzstyletable(int size, char const *name) +{ + HashTable ht; + ht = newhashtable(size, name, NULL); + + ht->hash = hasher; + ht->emptytable = emptyhashtable; + ht->filltable = NULL; + ht->cmpnodes = strcmp; + ht->addnode = addhashnode; + /* DISABLED is not supported */ + ht->getnode = gethashnode2; + ht->getnode2 = gethashnode2; + ht->removenode = removehashnode; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = freestylenode; + ht->printnode = printstylenode; + + return ht; } /* Store a value for a style. */ @@ -226,15 +329,9 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) static Style addstyle(char *name) { - Style s; - - s = (Style) zalloc(sizeof(*s)); - s->next = NULL; - s->pats = NULL; - s->name = ztrdup(name); + Style s = (Style) zshcalloc(sizeof(*s)); - s->next = zstyles; - zstyles = s; + zstyletab->addnode(zstyletab, ztrdup(name), s); return s; } @@ -274,11 +371,12 @@ 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); + s = (Style)zstyletab->getnode2(zstyletab, style); + if (!s) + return NULL; + for (p = s->pats; p; p = p->next) + if (pattry(p->prog, ctxt)) + return (p->eval ? evalstyle(p) : p->vals); return NULL; } @@ -286,10 +384,10 @@ lookupstyle(char *ctxt, char *style) static int bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { - int min, max, n, add = 0, list = 0, eval = 0; + int min, max, n, add = 0, list = ZSLIST_NONE, eval = 0; if (!args[0]) - list = 1; + list = ZSLIST_BASIC; else if (args[0][0] == '-') { char oc; @@ -299,7 +397,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) return 1; } if (oc == 'L') { - list = 2; + list = ZSLIST_SYNTAX; args++; } else if (oc == 'e') { eval = add = 1; @@ -328,16 +426,13 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zwarnnam(nam, "invalid pattern: %s", args[0]); return 1; } - if (!(s = getstyle(args[1]))) + if (!(s = (Style)zstyletab->getnode2(zstyletab, args[1]))) s = addstyle(args[1]); return setstypat(s, args[0], prog, args + 2, eval); } if (list) { Style s; - Stypat p; - char **v; char *context, *stylename; - Patprog contprog; switch (arrlen(args)) { case 2: @@ -360,34 +455,23 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } if (context) { tokenize(context); - contprog = patcompile(context, PAT_STATIC, NULL); + zstyle_contprog = patcompile(context, PAT_STATIC, NULL); + + if (!zstyle_contprog) + return 1; } else - contprog = NULL; + zstyle_contprog = NULL; - for (s = zstyles; s; s = s->next) { - if (list == 1) { - quotedzputs(s->name, stdout); - putchar('\n'); - } - if (stylename && strcmp(s->name, stylename) != 0) - continue; - for (p = s->pats; p; p = p->next) { - if (contprog && !pattry(contprog, p->pat)) - continue; - 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'); - } + if (stylename) { + s = (Style)zstyletab->getnode2(zstyletab, stylename); + if (!s) + return 1; + zstyletab->printnode(&s->node, list); + } else { + scanhashtable(zstyletab, 1, 0, 0, + zstyletab->printnode, list); } + return 0; } switch (args[0][1]) { @@ -421,7 +505,8 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) char *pat = args[1]; for (args += 2; *args; args++) { - if ((s = getstyle(*args))) { + if ((s = (Style)zstyletab->getnode2(zstyletab, + *args))) { Stypat p, q; for (q = NULL, p = s->pats; p; @@ -434,22 +519,14 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } } } else { - Style next; - Stypat p, q; - - /* careful! style itself may be deleted */ - for (s = zstyles; s; s = next) { - next = s->next; - for (q = NULL, p = s->pats; p; q = p, p = p->next) { - if (!strcmp(p->pat, args[1])) { - freestypat(p, s, q); - break; - } - } - } + zstyle_patname = args[1]; + + /* sorting not needed for deletion */ + scanhashtable(zstyletab, 0, 0, 0, scanpatstyles, + ZSPAT_REMOVE); } } else - freeallstyles(); + zstyletab->emptytable(zstyletab); } break; case 's': @@ -554,20 +631,21 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) break; case 'g': { - LinkList l = newlinklist(); int ret = 1; Style s; Stypat p; + zstyle_list = newlinklist(); + if (args[2]) { if (args[3]) { - if ((s = getstyle(args[3]))) { + if ((s = (Style)zstyletab->getnode2(zstyletab, 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++); + addlinknode(zstyle_list, *v++); ret = 0; break; @@ -575,28 +653,17 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } } } 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; - } + zstyle_patname = args[2]; + scanhashtable(zstyletab, 1, 0, 0, scanpatstyles, + ZSPAT_NAME); 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); - } + scanhashtable(zstyletab, 1, 0, 0, scanpatstyles, + ZSPAT_PAT); ret = 0; } - set_list_array(args[1], l); + set_list_array(args[1], zstyle_list); return ret; } @@ -1752,7 +1819,7 @@ static struct features module_features = { int setup_(UNUSED(Module m)) { - zstyles = NULL; + zstyletab = newzstyletable(17, "zstyletab"); return 0; } @@ -1790,7 +1857,7 @@ cleanup_(Module m) int finish_(UNUSED(Module m)) { - freeallstyles(); + deletehashtable(zstyletab); return 0; } diff --git a/Test/V05styles.ztst b/Test/V05styles.ztst new file mode 100644 index 000000000..e932b5474 --- /dev/null +++ b/Test/V05styles.ztst @@ -0,0 +1,145 @@ +%prep + +# Test the use of styles, if the zsh/zutil module is available. + + if (zmodload zsh/zutil >/dev/null 2>/dev/null); then + zmodload zsh/zutil + else + ZTST_unimplemented="can't load the zsh/zutil module for testing" + fi + +%test + zstyle :random:stuff any-old-style with any old value + zstyle :randomly:chosen some-other-style I can go on and on + zstyle -d + zstyle +0:zstyle -d restores a pristine state + +# patterns should be ordered by weight, so add in reverse order to check + zstyle ':ztst:context*' scalar-style other-scalar-value + zstyle ':ztst:context:*' scalar-style second-scalar-value + zstyle ':ztst:context:sub1' scalar-style scalar-value + zstyle ':ztst:context:sub1' array-style array value elements 'with spaces' + zstyle ':ztst:context*' boolean-style false + zstyle ':ztst:context:sub1' boolean-style true +0:defining styles + +# styles are now sorted, but patterns are in order of definition + zstyle +0:listing styles in default format +>array-style +> :ztst:context:sub1 array value elements 'with spaces' +>boolean-style +> :ztst:context:sub1 true +> :ztst:context* false +>scalar-style +> :ztst:context:sub1 scalar-value +> :ztst:context:* second-scalar-value +> :ztst:context* other-scalar-value + + zstyle -L +0:listing styles in zstyle format +>zstyle :ztst:context:sub1 array-style array value elements 'with spaces' +>zstyle :ztst:context:sub1 boolean-style true +>zstyle ':ztst:context*' boolean-style false +>zstyle :ztst:context:sub1 scalar-style scalar-value +>zstyle ':ztst:context:*' scalar-style second-scalar-value +>zstyle ':ztst:context*' scalar-style other-scalar-value + + zstyle -b :ztst:context:sub1 boolean-style bool; print $bool + zstyle -t :ztst:context:sub1 boolean-style +0:boolean test -b/-t + true +>yes + + zstyle -b :ztst:context:sub2 boolean-style bool; print $bool + zstyle -t :ztst:context:sub2 boolean-style +1:boolean test -b/-t + false +>no + + zstyle -b :ztst:context:sub1 boolean-unset-style bool; print $bool + zstyle -t :ztst:context:sub1 boolean-unset-style +2:boolean test -b/-t + unset +>no + + zstyle -T :ztst:context:sub1 boolean-style +0:boolean test -T + true + + zstyle -T :ztst:context:sub2 boolean-style +1:boolean test -T + false + + zstyle -T :ztst:context:sub1 boolean-unset-style +0:boolean test -T + unset + + zstyle -s :ztst:context:sub1 scalar-style scalar && print $scalar + zstyle -s :ztst:context:sub2 scalar-style scalar && print $scalar + zstyle -s :ztst:contextual-psychedelia scalar-style scalar && print $scalar + zstyle -s :ztst:contemplative scalar-style scalar || print no match +0:pattern matching rules +>scalar-value +>second-scalar-value +>other-scalar-value +>no match + + zstyle -s :ztst:context:sub1 array-style scalar + && print $scalar +0:scalar with separator +>array+value+elements+with spaces + + zstyle -e :ztst:\* eval-style 'reply=($something)' + something=(one two three) + zstyle -a :ztst:eval eval-style array && print -l $array +0:zstyle -e evaluations +>one +>two +>three + +# pattern ordering on output is not specified, so although in the +# current implementation it's deterministic we shouldn't +# assume it's always the same. Thus we sort the array. +# (It might be a nice touch to order patterns by weight, which is +# the way they are stored for each separate style.) + zstyle -g array && print -l ${(o)array} +0:retrieving patterns +>:ztst:* +>:ztst:context* +>:ztst:context:* +>:ztst:context:sub1 + + zstyle -m :ztst:context:sub1 array-style 'w* *s' +0:positive pattern match + + zstyle -m :ztst:context:sub1 array-style 'v' +1:negative pattern match + + zstyle -g array ':ztst:context*' && print -l $array +0:retrieving styles by pattern +>boolean-style +>scalar-style + + zstyle -g array ':ztst:context:sub1' array-style && print -l $array +0:retrieving values by pattern and name +>array +>value +>elements +>with spaces + + zstyle -d :ztst:context:sub1 + zstyle +0:deleting styles by pattern only +>boolean-style +> :ztst:context* false +>eval-style +>(eval) :ztst:* 'reply=($something)' +>scalar-style +> :ztst:context:* second-scalar-value +> :ztst:context* other-scalar-value + + zstyle -d :ztst:context\* scalar-style + zstyle +0:deleting styles by pattern and style name +>boolean-style +> :ztst:context* false +>eval-style +>(eval) :ztst:* 'reply=($something)' +>scalar-style +> :ztst:context:* second-scalar-value + -- cgit 1.4.1