summaryrefslogtreecommitdiff
path: root/Src/hashtable.c
diff options
context:
space:
mode:
authorAxel Beckert <abe@deuxchevaux.org>2017-08-10 17:16:37 +0200
committerAxel Beckert <abe@deuxchevaux.org>2017-08-10 17:16:37 +0200
commite3b67a8198c852bf6c9db3a0a1a20e87a4e1da74 (patch)
tree8486633f6232f49ea330ab8e036decb5dc3bbf74 /Src/hashtable.c
parentf8edeff2494bf23e2ee29d4c761361b1c878e09d (diff)
parentdc475bfa0ec6cd03789dde3bf28f71e0ea9d5003 (diff)
downloadzsh-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.c185
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;
+ }
+}