diff options
author | Axel Beckert <abe@deuxchevaux.org> | 2017-08-10 17:16:37 +0200 |
---|---|---|
committer | Axel Beckert <abe@deuxchevaux.org> | 2017-08-10 17:16:37 +0200 |
commit | e3b67a8198c852bf6c9db3a0a1a20e87a4e1da74 (patch) | |
tree | 8486633f6232f49ea330ab8e036decb5dc3bbf74 /Src/hashtable.c | |
parent | f8edeff2494bf23e2ee29d4c761361b1c878e09d (diff) | |
parent | dc475bfa0ec6cd03789dde3bf28f71e0ea9d5003 (diff) | |
download | zsh-e3b67a8198c852bf6c9db3a0a1a20e87a4e1da74.tar.gz zsh-e3b67a8198c852bf6c9db3a0a1a20e87a4e1da74.zip |
Merge tag '5.4.1' into debian
Release 5.4.1.
Diffstat (limited to 'Src/hashtable.c')
-rw-r--r-- | Src/hashtable.c | 185 |
1 files changed, 180 insertions, 5 deletions
diff --git a/Src/hashtable.c b/Src/hashtable.c index 7c3367568..6ec2ed220 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -889,7 +889,7 @@ freeshfuncnode(HashNode hn) freeeprog(shf->funcdef); if (shf->redir) freeeprog(shf->redir); - zsfree(shf->filename); + dircache_set(&shf->filename, NULL); if (shf->sticky) { if (shf->sticky->n_on_opts) zfree(shf->sticky->on_opts, @@ -926,10 +926,13 @@ printshfuncnode(HashNode hn, int printflags) (f->node.flags & PM_UNDEFINED) ? " is an autoload shell function" : " is a shell function"); - if (f->filename && (printflags & PRINT_WHENCE_VERBOSE) && - strcmp(f->filename, f->node.nam) != 0) { + if ((printflags & PRINT_WHENCE_VERBOSE) && f->filename) { printf(" from "); quotedzputs(f->filename, stdout); + if (f->node.flags & PM_LOADDIR) { + printf("/"); + quotedzputs(f->node.nam, stdout); + } } putchar('\n'); return; @@ -949,16 +952,20 @@ printshfuncnode(HashNode hn, int printflags) zoutputtab(stdout); } if (!t) { - char *fopt = "UtTkz"; + char *fopt = "UtTkzc"; int flgs[] = { PM_UNALIASED, PM_TAGGED, PM_TAGGED_LOCAL, - PM_KSHSTORED, PM_ZSHSTORED, 0 + PM_KSHSTORED, PM_ZSHSTORED, PM_CUR_FPATH, 0 }; int fl;; zputs("builtin autoload -X", stdout); for (fl=0;fopt[fl];fl++) if (f->node.flags & flgs[fl]) putchar(fopt[fl]); + if (f->filename && (f->node.flags & PM_LOADDIR)) { + putchar(' '); + zputs(f->filename, stdout); + } } else { zputs(t, stdout); zsfree(t); @@ -1037,6 +1044,24 @@ printshfuncexpand(HashNode hn, int printflags, int expand) text_expand_tabs = save_expand; } +/* + * Get a heap-duplicated name of the shell function, for + * use in tracing. + */ + +/**/ +mod_export char * +getshfuncfile(Shfunc shf) +{ + if (shf->node.flags & PM_LOADDIR) { + return zhtricat(shf->filename, "/", shf->node.nam); + } else if (shf->filename) { + return dupstring(shf->filename); + } else { + return NULL; + } +} + /**************************************/ /* Reserved Word Hash Table Functions */ /**************************************/ @@ -1420,6 +1445,9 @@ freehistdata(Histent he, int unlink) if (!he) return; + if (he == &curline) + return; + if (!(he->node.flags & (HIST_DUP | HIST_TMPSTORE))) removehashnode(histtab, he->node.nam); @@ -1438,3 +1466,150 @@ freehistdata(Histent he, int unlink) } } } + + +/*********************************************************************** + * Directory name cache mechanism + * + * The idea of this is that there are various shell structures, + * notably functions, that record the directories with which they + * are associated. Rather than store the full string each time, + * we store a pointer to the same location and count the references. + * This is optimised so that retrieval is quick at the expense of + * searching the list when setting up the structure, which is a much + * rarer operation. + * + * There is nothing special about the fact that the strings are + * directories, except for the assumptions for efficiency that many + * structures will point to the same one, and that there are not too + * many different directories associated with the shell. + **********************************************************************/ + +struct dircache_entry +{ + /* Name of directory in cache */ + char *name; + /* Number of references to it */ + int refs; +}; + +/* + * dircache is the cache, of length dircache_size. + * dircache_lastentry is the last entry used, an optimisation + * for multiple references to the same directory, e.g + * "autoload /blah/blah/\*". + */ +static struct dircache_entry *dircache, *dircache_lastentry; +static int dircache_size; + +/* + * Set *name to point to a cached version of value. + * value is copied so may come from any source. + * + * If value is NULL, look for the existing value of *name (safe if this + * too is NULL) and remove a reference to it from the cache. If it's + * not found in the cache, it's assumed to be an allocated string and + * freed --- this currently occurs for a shell function that's been + * loaded as the filename is now a full path, not just a directory, + * though we may one day optimise this to a cached directory plus a + * name, too. Note --- the function does *not* otherwise check + * if *name points to something already cached, so this is + * necessary any time *name may already be in the cache. + */ + +/**/ +mod_export void +dircache_set(char **name, char *value) +{ + struct dircache_entry *dcptr, *dcnew; + + if (!value) { + if (!*name) + return; + if (!dircache_size) { + zsfree(*name); + *name = NULL; + return; + } + + for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++) + { + /* Must be a pointer much, not a string match */ + if (*name == dcptr->name) + { + --dcptr->refs; + if (!dcptr->refs) { + ptrdiff_t ind = dcptr - dircache; + zsfree(dcptr->name); + --dircache_size; + + if (!dircache_size) { + zfree(dircache, sizeof(*dircache)); + dircache = NULL; + dircache_lastentry = NULL; + *name = NULL; + return; + } + dcnew = (struct dircache_entry *) + zalloc(dircache_size * sizeof(*dcnew)); + if (ind) + memcpy(dcnew, dircache, ind * sizeof(*dcnew)); + if (ind < dircache_size) + memcpy(dcnew + ind, dcptr + 1, + (dircache_size - ind) * sizeof(*dcnew)); + zfree(dircache, (dircache_size+1)*sizeof(*dcnew)); + dircache = dcnew; + dircache_lastentry = NULL; + } + *name = NULL; + return; + } + } + zsfree(*name); + *name = NULL; + } else { + /* + * As the function path has been resolved to a particular + * location, we'll store it as an absolute path. + */ + if (*value != '/') { + value = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), + "/", value); + value = xsymlink(value, 1); + } + /* + * We'll maintain the cache at exactly the right size rather + * than overallocating. The rationale here is that typically + * we'll get a lot of functions in a small number of directories + * so the complexity overhead of maintaining a separate count + * isn't really matched by the efficiency gain. + */ + if (dircache_lastentry && + !strcmp(value, dircache_lastentry->name)) { + *name = dircache_lastentry->name; + ++dircache_lastentry->refs; + return; + } else if (!dircache_size) { + dircache_size = 1; + dcptr = dircache = + (struct dircache_entry *)zalloc(sizeof(*dircache)); + } else { + for (dcptr = dircache; dcptr < dircache + dircache_size; dcptr++) + { + if (!strcmp(value, dcptr->name)) { + *name = dcptr->name; + ++dcptr->refs; + return; + } + } + ++dircache_size; + dircache = (struct dircache_entry *) + zrealloc(dircache, sizeof(*dircache) * dircache_size); + dcptr = dircache + dircache_size - 1; + } + dcptr->name = ztrdup(value); + *name = dcptr->name; + dcptr->refs = 1; + dircache_lastentry = dcptr; + } +} |