summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/hashtable.c
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-05-08 18:49:34 -0500
committerCraig Jennings <c@cjennings.net>2025-05-08 18:51:59 -0500
commit000e00871830cd15de032c80e2b62946cf19445c (patch)
tree794a7922750472bbe0e024042d6ba84f411fc3e0 /dotfiles/system/.zsh/modules/Src/hashtable.c
parentfe302606931e4bad91c4ed6df81a4403523ba780 (diff)
adding missing dotfiles and folders
- profile.d/ - bashrc - authinfo.gpg - .zsh/
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/hashtable.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/hashtable.c1617
1 files changed, 1617 insertions, 0 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/hashtable.c b/dotfiles/system/.zsh/modules/Src/hashtable.c
new file mode 100644
index 0000000..b7baa31
--- /dev/null
+++ b/dotfiles/system/.zsh/modules/Src/hashtable.c
@@ -0,0 +1,1617 @@
+/*
+ * hashtable.c - hash tables
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 1992-1997 Paul Falstad
+ * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Paul Falstad 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 Paul Falstad and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "../config.h"
+
+#ifdef ZSH_HASH_DEBUG
+# define HASHTABLE_DEBUG_MEMBERS \
+ /* Members of struct hashtable used for debugging hash tables */ \
+ HashTable next, last; /* linked list of all hash tables */ \
+ char *tablename; /* string containing name of the hash table */ \
+ PrintTableStats printinfo; /* pointer to function to print table stats */
+#else /* !ZSH_HASH_DEBUG */
+# define HASHTABLE_DEBUG_MEMBERS
+#endif /* !ZSH_HASH_DEBUG */
+
+#define HASHTABLE_INTERNAL_MEMBERS \
+ ScanStatus scan; /* status of a scan over this hashtable */ \
+ HASHTABLE_DEBUG_MEMBERS
+
+typedef struct scanstatus *ScanStatus;
+
+#include "zsh.mdh"
+#include "hashtable.pro"
+
+/* Structure for recording status of a hashtable scan in progress. When a *
+ * scan starts, the .scan member of the hashtable structure points to one *
+ * of these. That member being non-NULL disables resizing of the *
+ * hashtable (when adding elements). When elements are deleted, the *
+ * contents of this structure is used to make sure the scan won't stumble *
+ * into the deleted element. */
+
+struct scanstatus {
+ int sorted;
+ union {
+ struct {
+ HashNode *hashtab;
+ int ct;
+ } s;
+ HashNode u;
+ } u;
+};
+
+/********************************/
+/* Generic Hash Table functions */
+/********************************/
+
+#ifdef ZSH_HASH_DEBUG
+static HashTable firstht, lastht;
+#endif /* ZSH_HASH_DEBUG */
+
+/* Generic hash function */
+
+/**/
+mod_export unsigned
+hasher(const char *str)
+{
+ unsigned hashval = 0, c;
+
+ while ((c = *((unsigned char *) str++)))
+ hashval += (hashval << 5) + c;
+
+ return hashval;
+}
+
+/* Get a new hash table */
+
+/**/
+mod_export HashTable
+newhashtable(int size, UNUSED(char const *name), UNUSED(PrintTableStats printinfo))
+{
+ HashTable ht;
+
+ ht = (HashTable) zshcalloc(sizeof *ht);
+#ifdef ZSH_HASH_DEBUG
+ ht->next = NULL;
+ if(!firstht)
+ firstht = ht;
+ ht->last = lastht;
+ if(lastht)
+ lastht->next = ht;
+ lastht = ht;
+ ht->printinfo = printinfo ? printinfo : printhashtabinfo;
+ ht->tablename = ztrdup(name);
+#endif /* ZSH_HASH_DEBUG */
+ ht->nodes = (HashNode *) zshcalloc(size * sizeof(HashNode));
+ ht->hsize = size;
+ ht->ct = 0;
+ ht->scan = NULL;
+ ht->scantab = NULL;
+ return ht;
+}
+
+/* Delete a hash table. After this function has been used, any *
+ * existing pointers to the hash table are invalid. */
+
+/**/
+mod_export void
+deletehashtable(HashTable ht)
+{
+ ht->emptytable(ht);
+#ifdef ZSH_HASH_DEBUG
+ if(ht->next)
+ ht->next->last = ht->last;
+ else
+ lastht = ht->last;
+ if(ht->last)
+ ht->last->next = ht->next;
+ else
+ firstht = ht->next;
+ zsfree(ht->tablename);
+#endif /* ZSH_HASH_DEBUG */
+ zfree(ht->nodes, ht->hsize * sizeof(HashNode));
+ zfree(ht, sizeof(*ht));
+}
+
+/* Add a node to a hash table. *
+ * nam is the key to use in hashing. nodeptr points *
+ * to the node to add. If there is already a node in *
+ * the table with the same key, it is first freed, and *
+ * then the new node is added. If the number of nodes *
+ * is now greater than twice the number of hash values, *
+ * the table is then expanded. */
+
+/**/
+mod_export void
+addhashnode(HashTable ht, char *nam, void *nodeptr)
+{
+ HashNode oldnode = addhashnode2(ht, nam, nodeptr);
+ if (oldnode)
+ ht->freenode(oldnode);
+}
+
+/* Add a node to a hash table, returning the old node on replacement. */
+
+/**/
+HashNode
+addhashnode2(HashTable ht, char *nam, void *nodeptr)
+{
+ unsigned hashval;
+ HashNode hn, hp, hq;
+
+ hn = (HashNode) nodeptr;
+ hn->nam = nam;
+
+ hashval = ht->hash(hn->nam) % ht->hsize;
+ hp = ht->nodes[hashval];
+
+ /* check if this is the first node for this hash value */
+ if (!hp) {
+ hn->next = NULL;
+ ht->nodes[hashval] = hn;
+ if (++ht->ct >= ht->hsize * 2 && !ht->scan)
+ expandhashtable(ht);
+ return NULL;
+ }
+
+ /* else check if the first node contains the same key */
+ if (ht->cmpnodes(hp->nam, hn->nam) == 0) {
+ ht->nodes[hashval] = hn;
+ replacing:
+ hn->next = hp->next;
+ if(ht->scan) {
+ if(ht->scan->sorted) {
+ HashNode *hashtab = ht->scan->u.s.hashtab;
+ int i;
+ for(i = ht->scan->u.s.ct; i--; )
+ if(hashtab[i] == hp)
+ hashtab[i] = hn;
+ } else if(ht->scan->u.u == hp)
+ ht->scan->u.u = hn;
+ }
+ return hp;
+ }
+
+ /* else run through the list and check all the keys */
+ hq = hp;
+ hp = hp->next;
+ for (; hp; hq = hp, hp = hp->next) {
+ if (ht->cmpnodes(hp->nam, hn->nam) == 0) {
+ hq->next = hn;
+ goto replacing;
+ }
+ }
+
+ /* else just add it at the front of the list */
+ hn->next = ht->nodes[hashval];
+ ht->nodes[hashval] = hn;
+ if (++ht->ct >= ht->hsize * 2 && !ht->scan)
+ expandhashtable(ht);
+ return NULL;
+}
+
+/* Get an enabled entry in a hash table. *
+ * If successful, it returns a pointer to *
+ * the hashnode. If the node is DISABLED *
+ * or isn't found, it returns NULL */
+
+/**/
+mod_export HashNode
+gethashnode(HashTable ht, const char *nam)
+{
+ unsigned hashval;
+ HashNode hp;
+
+ hashval = ht->hash(nam) % ht->hsize;
+ for (hp = ht->nodes[hashval]; hp; hp = hp->next) {
+ if (ht->cmpnodes(hp->nam, nam) == 0) {
+ if (hp->flags & DISABLED)
+ return NULL;
+ else
+ return hp;
+ }
+ }
+ return NULL;
+}
+
+/* Get an entry in a hash table. It will *
+ * ignore the DISABLED flag and return a *
+ * pointer to the hashnode if found, else *
+ * it returns NULL. */
+
+/**/
+mod_export HashNode
+gethashnode2(HashTable ht, const char *nam)
+{
+ unsigned hashval;
+ HashNode hp;
+
+ hashval = ht->hash(nam) % ht->hsize;
+ for (hp = ht->nodes[hashval]; hp; hp = hp->next) {
+ if (ht->cmpnodes(hp->nam, nam) == 0)
+ return hp;
+ }
+ return NULL;
+}
+
+/* Remove an entry from a hash table. *
+ * If successful, it removes the node from the *
+ * table and returns a pointer to it. If there *
+ * is no such node, then it returns NULL */
+
+/**/
+mod_export HashNode
+removehashnode(HashTable ht, const char *nam)
+{
+ unsigned hashval;
+ HashNode hp, hq;
+
+ hashval = ht->hash(nam) % ht->hsize;
+ hp = ht->nodes[hashval];
+
+ /* if no nodes at this hash value, return NULL */
+ if (!hp)
+ return NULL;
+
+ /* else check if the key in the first one matches */
+ if (ht->cmpnodes(hp->nam, nam) == 0) {
+ ht->nodes[hashval] = hp->next;
+ gotit:
+ ht->ct--;
+ if(ht->scan) {
+ if(ht->scan->sorted) {
+ HashNode *hashtab = ht->scan->u.s.hashtab;
+ int i;
+ for(i = ht->scan->u.s.ct; i--; )
+ if(hashtab[i] == hp)
+ hashtab[i] = NULL;
+ } else if(ht->scan->u.u == hp)
+ ht->scan->u.u = hp->next;
+ }
+ return hp;
+ }
+
+ /* else run through the list and check the rest of the keys */
+ hq = hp;
+ hp = hp->next;
+ for (; hp; hq = hp, hp = hp->next) {
+ if (ht->cmpnodes(hp->nam, nam) == 0) {
+ hq->next = hp->next;
+ goto gotit;
+ }
+ }
+
+ /* else it is not in the list, so return NULL */
+ return NULL;
+}
+
+/* Disable a node in a hash table */
+
+/**/
+void
+disablehashnode(HashNode hn, UNUSED(int flags))
+{
+ hn->flags |= DISABLED;
+}
+
+/* Enable a node in a hash table */
+
+/**/
+void
+enablehashnode(HashNode hn, UNUSED(int flags))
+{
+ hn->flags &= ~DISABLED;
+}
+
+/* Compare two hash table entries by name */
+
+/**/
+static int
+hnamcmp(const void *ap, const void *bp)
+{
+ HashNode a = *(HashNode *)ap;
+ HashNode b = *(HashNode *)bp;
+ return ztrcmp(a->nam, b->nam);
+}
+
+/* Scan the nodes in a hash table and execute scanfunc on nodes based on
+ * the flags that are set/unset. scanflags is passed unchanged to
+ * scanfunc (if executed).
+ *
+ * If sorted != 0, then sort entries of hash table before scanning.
+ * If flags1 > 0, then execute scanfunc on a node only if at least one of
+ * these flags is set.
+ * If flags2 > 0, then execute scanfunc on a node only if all of
+ * these flags are NOT set.
+ * The conditions above for flags1/flags2 must both be true.
+ *
+ * It is safe to add, remove or replace hash table elements from within
+ * the scanfunc. Replaced elements will appear in the scan exactly once,
+ * the new version if it was not scanned before the replacement was made.
+ * Added elements might or might not appear in the scan.
+ *
+ * pprog, if non-NULL, is a pattern that must match the name
+ * of the node.
+ *
+ * The function returns the number of matches, as reduced by pprog, flags1
+ * and flags2.
+ */
+
+/**/
+mod_export int
+scanmatchtable(HashTable ht, Patprog pprog, int sorted,
+ int flags1, int flags2, ScanFunc scanfunc, int scanflags)
+{
+ int match = 0;
+ struct scanstatus st;
+
+ /*
+ * scantab is currently only used by modules to scan
+ * tables where the contents are generated on the fly from
+ * other objects. Note the fact that in this case pprog,
+ * sorted, flags1 and flags2 are ignore.
+ */
+ if (!pprog && ht->scantab) {
+ ht->scantab(ht, scanfunc, scanflags);
+ return ht->ct;
+ }
+ if (sorted) {
+ int i, ct = ht->ct;
+ VARARR(HashNode, hnsorttab, ct);
+ HashNode *htp, hn;
+
+ /*
+ * Because the structure might change under our feet,
+ * we can't apply the flags and the pattern before sorting,
+ * tempting though that is.
+ */
+ for (htp = hnsorttab, i = 0; i < ht->hsize; i++)
+ for (hn = ht->nodes[i]; hn; hn = hn->next)
+ *htp++ = hn;
+ qsort((void *)hnsorttab, ct, sizeof(HashNode), hnamcmp);
+
+ st.sorted = 1;
+ st.u.s.hashtab = hnsorttab;
+ st.u.s.ct = ct;
+ ht->scan = &st;
+
+ for (htp = hnsorttab, i = 0; i < ct; i++, htp++) {
+ if ((!flags1 || ((*htp)->flags & flags1)) &&
+ !((*htp)->flags & flags2) &&
+ (!pprog || pattry(pprog, (*htp)->nam))) {
+ match++;
+ scanfunc(*htp, scanflags);
+ }
+ }
+
+ ht->scan = NULL;
+ } else {
+ int i, hsize = ht->hsize;
+ HashNode *nodes = ht->nodes;
+
+ st.sorted = 0;
+ ht->scan = &st;
+
+ for (i = 0; i < hsize; i++)
+ for (st.u.u = nodes[i]; st.u.u; ) {
+ HashNode hn = st.u.u;
+ st.u.u = st.u.u->next;
+ if ((!flags1 || (hn->flags & flags1)) && !(hn->flags & flags2)
+ && (!pprog || pattry(pprog, hn->nam))) {
+ match++;
+ scanfunc(hn, scanflags);
+ }
+ }
+
+ ht->scan = NULL;
+ }
+
+ return match;
+}
+
+
+/**/
+mod_export int
+scanhashtable(HashTable ht, int sorted, int flags1, int flags2,
+ ScanFunc scanfunc, int scanflags)
+{
+ return scanmatchtable(ht, NULL, sorted, flags1, flags2,
+ scanfunc, scanflags);
+}
+
+/* Expand hash tables when they get too many entries. *
+ * The new size is 4 times the previous size. */
+
+/**/
+static void
+expandhashtable(HashTable ht)
+{
+ struct hashnode **onodes, **ha, *hn, *hp;
+ int i, osize;
+
+ osize = ht->hsize;
+ onodes = ht->nodes;
+
+ ht->hsize = osize * 4;
+ ht->nodes = (HashNode *) zshcalloc(ht->hsize * sizeof(HashNode));
+ ht->ct = 0;
+
+ /* scan through the old list of nodes, and *
+ * rehash them into the new list of nodes */
+ for (i = 0, ha = onodes; i < osize; i++, ha++) {
+ for (hn = *ha; hn;) {
+ hp = hn->next;
+ ht->addnode(ht, hn->nam, hn);
+ hn = hp;
+ }
+ }
+ zfree(onodes, osize * sizeof(HashNode));
+}
+
+/* Empty the hash table and resize it if necessary */
+
+/**/
+static void
+resizehashtable(HashTable ht, int newsize)
+{
+ struct hashnode **ha, *hn, *hp;
+ int i;
+
+ /* free all the hash nodes */
+ ha = ht->nodes;
+ for (i = 0; i < ht->hsize; i++, ha++) {
+ for (hn = *ha; hn;) {
+ hp = hn->next;
+ ht->freenode(hn);
+ hn = hp;
+ }
+ }
+
+ /* If new size desired is different from current size, *
+ * we free it and allocate a new nodes array. */
+ if (ht->hsize != newsize) {
+ zfree(ht->nodes, ht->hsize * sizeof(HashNode));
+ ht->nodes = (HashNode *) zshcalloc(newsize * sizeof(HashNode));
+ ht->hsize = newsize;
+ } else {
+ /* else we just re-zero the current nodes array */
+ memset(ht->nodes, 0, newsize * sizeof(HashNode));
+ }
+
+ ht->ct = 0;
+}
+
+/* Generic method to empty a hash table */
+
+/**/
+mod_export void
+emptyhashtable(HashTable ht)
+{
+ resizehashtable(ht, ht->hsize);
+}
+
+/**/
+#ifdef ZSH_HASH_DEBUG
+
+/* Print info about hash table */
+
+#define MAXDEPTH 7
+
+/**/
+static void
+printhashtabinfo(HashTable ht)
+{
+ HashNode hn;
+ int chainlen[MAXDEPTH + 1];
+ int i, tmpcount, total;
+
+ printf("name of table : %s\n", ht->tablename);
+ printf("size of nodes[] : %d\n", ht->hsize);
+ printf("number of nodes : %d\n\n", ht->ct);
+
+ memset(chainlen, 0, sizeof(chainlen));
+
+ /* count the number of nodes just to be sure */
+ total = 0;
+ for (i = 0; i < ht->hsize; i++) {
+ tmpcount = 0;
+ for (hn = ht->nodes[i]; hn; hn = hn->next)
+ tmpcount++;
+ if (tmpcount >= MAXDEPTH)
+ chainlen[MAXDEPTH]++;
+ else
+ chainlen[tmpcount]++;
+ total += tmpcount;
+ }
+
+ for (i = 0; i < MAXDEPTH; i++)
+ printf("number of hash values with chain of length %d : %4d\n", i, chainlen[i]);
+ printf("number of hash values with chain of length %d+ : %4d\n", MAXDEPTH, chainlen[MAXDEPTH]);
+ printf("total number of nodes : %4d\n", total);
+}
+
+/**/
+int
+bin_hashinfo(UNUSED(char *nam), UNUSED(char **args), UNUSED(Options ops), UNUSED(int func))
+{
+ HashTable ht;
+
+ printf("----------------------------------------------------\n");
+ queue_signals();
+ for(ht = firstht; ht; ht = ht->next) {
+ ht->printinfo(ht);
+ printf("----------------------------------------------------\n");
+ }
+ unqueue_signals();
+ return 0;
+}
+
+/**/
+#endif /* ZSH_HASH_DEBUG */
+
+/********************************/
+/* Command Hash Table Functions */
+/********************************/
+
+/* hash table containing external commands */
+
+/**/
+mod_export HashTable cmdnamtab;
+
+/* how far we've hashed the PATH so far */
+
+/**/
+mod_export char **pathchecked;
+
+/* Create a new command hash table */
+
+/**/
+void
+createcmdnamtable(void)
+{
+ cmdnamtab = newhashtable(201, "cmdnamtab", NULL);
+
+ cmdnamtab->hash = hasher;
+ cmdnamtab->emptytable = emptycmdnamtable;
+ cmdnamtab->filltable = fillcmdnamtable;
+ cmdnamtab->cmpnodes = strcmp;
+ cmdnamtab->addnode = addhashnode;
+ cmdnamtab->getnode = gethashnode2;
+ cmdnamtab->getnode2 = gethashnode2;
+ cmdnamtab->removenode = removehashnode;
+ cmdnamtab->disablenode = NULL;
+ cmdnamtab->enablenode = NULL;
+ cmdnamtab->freenode = freecmdnamnode;
+ cmdnamtab->printnode = printcmdnamnode;
+
+ pathchecked = path;
+}
+
+/**/
+static void
+emptycmdnamtable(HashTable ht)
+{
+ emptyhashtable(ht);
+ pathchecked = path;
+}
+
+/* Add all commands in a given directory *
+ * to the command hashtable. */
+
+/**/
+void
+hashdir(char **dirp)
+{
+ Cmdnam cn;
+ DIR *dir;
+ char *fn, *unmetadir, *pathbuf, *pathptr;
+ int dirlen;
+#if defined(_WIN32) || defined(__CYGWIN__)
+ char *exe;
+#endif /* _WIN32 || _CYGWIN__ */
+
+ if (isrelative(*dirp))
+ return;
+ unmetadir = unmeta(*dirp);
+ if (!(dir = opendir(unmetadir)))
+ return;
+
+ dirlen = strlen(unmetadir);
+ pathbuf = (char *)zalloc(dirlen + PATH_MAX + 2);
+ sprintf(pathbuf, "%s/", unmetadir);
+ pathptr = pathbuf + dirlen + 1;
+
+ while ((fn = zreaddir(dir, 1))) {
+ if (!cmdnamtab->getnode(cmdnamtab, fn)) {
+ char *fname = ztrdup(fn);
+ struct stat statbuf;
+ int add = 0, dummylen;
+
+ unmetafy(fn, &dummylen);
+ if (strlen(fn) > PATH_MAX) {
+ /* Too heavy to do all the allocation */
+ add = 1;
+ } else {
+ strcpy(pathptr, fn);
+ /*
+ * This is the same test as for the glob qualifier for
+ * executable plain files.
+ */
+ if (unset(HASHEXECUTABLESONLY) ||
+ (access(pathbuf, X_OK) == 0 &&
+ stat(pathbuf, &statbuf) == 0 &&
+ S_ISREG(statbuf.st_mode) && (statbuf.st_mode & S_IXUGO)))
+ add = 1;
+ }
+ if (add) {
+ cn = (Cmdnam) zshcalloc(sizeof *cn);
+ cn->node.flags = 0;
+ cn->u.name = dirp;
+ cmdnamtab->addnode(cmdnamtab, fname, cn);
+ } else
+ zsfree(fname);
+ }
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /* Hash foo.exe as foo, since when no real foo exists, foo.exe
+ will get executed by DOS automatically. This quiets
+ spurious corrections when CORRECT or CORRECT_ALL is set. */
+ if ((exe = strrchr(fn, '.')) &&
+ (exe[1] == 'E' || exe[1] == 'e') &&
+ (exe[2] == 'X' || exe[2] == 'x') &&
+ (exe[3] == 'E' || exe[3] == 'e') && exe[4] == 0) {
+ *exe = 0;
+ if (!cmdnamtab->getnode(cmdnamtab, fn)) {
+ cn = (Cmdnam) zshcalloc(sizeof *cn);
+ cn->node.flags = 0;
+ cn->u.name = dirp;
+ cmdnamtab->addnode(cmdnamtab, ztrdup(fn), cn);
+ }
+ }
+#endif /* _WIN32 || __CYGWIN__ */
+ }
+ closedir(dir);
+ zfree(pathbuf, dirlen + PATH_MAX + 2);
+}
+
+/* Go through user's PATH and add everything to *
+ * the command hashtable. */
+
+/**/
+static void
+fillcmdnamtable(UNUSED(HashTable ht))
+{
+ char **pq;
+
+ for (pq = pathchecked; *pq; pq++)
+ hashdir(pq);
+
+ pathchecked = pq;
+}
+
+/**/
+static void
+freecmdnamnode(HashNode hn)
+{
+ Cmdnam cn = (Cmdnam) hn;
+
+ zsfree(cn->node.nam);
+ if (cn->node.flags & HASHED)
+ zsfree(cn->u.cmd);
+
+ zfree(cn, sizeof(struct cmdnam));
+}
+
+/* Print an element of the cmdnamtab hash table (external command) */
+
+/**/
+static void
+printcmdnamnode(HashNode hn, int printflags)
+{
+ Cmdnam cn = (Cmdnam) hn;
+
+ if (printflags & PRINT_WHENCE_WORD) {
+ printf("%s: %s\n", cn->node.nam, (cn->node.flags & HASHED) ?
+ "hashed" : "command");
+ return;
+ }
+
+ if ((printflags & PRINT_WHENCE_CSH) || (printflags & PRINT_WHENCE_SIMPLE)) {
+ if (cn->node.flags & HASHED) {
+ zputs(cn->u.cmd, stdout);
+ putchar('\n');
+ } else {
+ zputs(*(cn->u.name), stdout);
+ putchar('/');
+ zputs(cn->node.nam, stdout);
+ putchar('\n');
+ }
+ return;
+ }
+
+ if (printflags & PRINT_WHENCE_VERBOSE) {
+ if (cn->node.flags & HASHED) {
+ nicezputs(cn->node.nam, stdout);
+ printf(" is hashed to ");
+ nicezputs(cn->u.cmd, stdout);
+ putchar('\n');
+ } else {
+ nicezputs(cn->node.nam, stdout);
+ printf(" is ");
+ nicezputs(*(cn->u.name), stdout);
+ putchar('/');
+ nicezputs(cn->node.nam, stdout);
+ putchar('\n');
+ }
+ return;
+ }
+
+ if (printflags & PRINT_LIST) {
+ printf("hash ");
+
+ if(cn->node.nam[0] == '-')
+ printf("-- ");
+ }
+
+ if (cn->node.flags & HASHED) {
+ quotedzputs(cn->node.nam, stdout);
+ putchar('=');
+ quotedzputs(cn->u.cmd, stdout);
+ putchar('\n');
+ } else {
+ quotedzputs(cn->node.nam, stdout);
+ putchar('=');
+ quotedzputs(*(cn->u.name), stdout);
+ putchar('/');
+ quotedzputs(cn->node.nam, stdout);
+ putchar('\n');
+ }
+}
+
+/***************************************/
+/* Shell Function Hash Table Functions */
+/***************************************/
+
+/* hash table containing the shell functions */
+
+/**/
+mod_export HashTable shfunctab;
+
+/**/
+void
+createshfunctable(void)
+{
+ shfunctab = newhashtable(7, "shfunctab", NULL);
+
+ shfunctab->hash = hasher;
+ shfunctab->emptytable = NULL;
+ shfunctab->filltable = NULL;
+ shfunctab->cmpnodes = strcmp;
+ shfunctab->addnode = addhashnode;
+ shfunctab->getnode = gethashnode;
+ shfunctab->getnode2 = gethashnode2;
+ shfunctab->removenode = removeshfuncnode;
+ shfunctab->disablenode = disableshfuncnode;
+ shfunctab->enablenode = enableshfuncnode;
+ shfunctab->freenode = freeshfuncnode;
+ shfunctab->printnode = printshfuncnode;
+}
+
+/* Remove an entry from the shell function hash table. *
+ * It checks if the function is a signal trap and if so, *
+ * it will disable the trapping of that signal. */
+
+/**/
+static HashNode
+removeshfuncnode(UNUSED(HashTable ht), const char *nam)
+{
+ HashNode hn;
+ int signum;
+
+ if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1)
+ hn = removetrap(signum);
+ else
+ hn = removehashnode(shfunctab, nam);
+
+ return hn;
+}
+
+/* Disable an entry in the shell function hash table. *
+ * It checks if the function is a signal trap and if so, *
+ * it will disable the trapping of that signal. */
+
+/**/
+static void
+disableshfuncnode(HashNode hn, UNUSED(int flags))
+{
+ hn->flags |= DISABLED;
+ if (!strncmp(hn->nam, "TRAP", 4)) {
+ int signum = getsignum(hn->nam + 4);
+ if (signum != -1) {
+ sigtrapped[signum] &= ~ZSIG_FUNC;
+ unsettrap(signum);
+ }
+ }
+}
+
+/* Re-enable an entry in the shell function hash table. *
+ * It checks if the function is a signal trap and if so, *
+ * it will re-enable the trapping of that signal. */
+
+/**/
+static void
+enableshfuncnode(HashNode hn, UNUSED(int flags))
+{
+ Shfunc shf = (Shfunc) hn;
+
+ shf->node.flags &= ~DISABLED;
+ if (!strncmp(shf->node.nam, "TRAP", 4)) {
+ int signum = getsignum(shf->node.nam + 4);
+ if (signum != -1) {
+ settrap(signum, NULL, ZSIG_FUNC);
+ }
+ }
+}
+
+/**/
+static void
+freeshfuncnode(HashNode hn)
+{
+ Shfunc shf = (Shfunc) hn;
+
+ zsfree(shf->node.nam);
+ if (shf->funcdef)
+ freeeprog(shf->funcdef);
+ if (shf->redir)
+ freeeprog(shf->redir);
+ dircache_set(&shf->filename, NULL);
+ if (shf->sticky) {
+ if (shf->sticky->n_on_opts)
+ zfree(shf->sticky->on_opts,
+ shf->sticky->n_on_opts * sizeof(*shf->sticky->on_opts));
+ if (shf->sticky->n_off_opts)
+ zfree(shf->sticky->off_opts,
+ shf->sticky->n_off_opts * sizeof(*shf->sticky->off_opts));
+ zfree(shf->sticky, sizeof(*shf->sticky));
+ }
+ zfree(shf, sizeof(struct shfunc));
+}
+
+/* Print a shell function */
+
+/**/
+static void
+printshfuncnode(HashNode hn, int printflags)
+{
+ Shfunc f = (Shfunc) hn;
+ char *t = 0;
+
+ if ((printflags & PRINT_NAMEONLY) ||
+ ((printflags & PRINT_WHENCE_SIMPLE) &&
+ !(printflags & PRINT_WHENCE_FUNCDEF))) {
+ zputs(f->node.nam, stdout);
+ putchar('\n');
+ return;
+ }
+
+ if ((printflags & (PRINT_WHENCE_VERBOSE|PRINT_WHENCE_WORD)) &&
+ !(printflags & PRINT_WHENCE_FUNCDEF)) {
+ nicezputs(f->node.nam, stdout);
+ printf((printflags & PRINT_WHENCE_WORD) ? ": function" :
+ (f->node.flags & PM_UNDEFINED) ?
+ " is an autoload shell function" :
+ " is a shell function");
+ 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;
+ }
+
+ quotedzputs(f->node.nam, stdout);
+ if (f->funcdef || f->node.flags & PM_UNDEFINED) {
+ printf(" () {\n");
+ zoutputtab(stdout);
+ if (f->node.flags & PM_UNDEFINED) {
+ printf("%c undefined\n", hashchar);
+ zoutputtab(stdout);
+ } else
+ t = getpermtext(f->funcdef, NULL, 1);
+ if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL)) {
+ printf("%c traced\n", hashchar);
+ zoutputtab(stdout);
+ }
+ if (!t) {
+ char *fopt = "UtTkzc";
+ int flgs[] = {
+ PM_UNALIASED, PM_TAGGED, PM_TAGGED_LOCAL,
+ 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);
+ if (f->funcdef->flags & EF_RUN) {
+ printf("\n");
+ zoutputtab(stdout);
+ quotedzputs(f->node.nam, stdout);
+ printf(" \"$@\"");
+ }
+ }
+ printf("\n}");
+ } else {
+ printf(" () { }");
+ }
+ if (f->redir) {
+ t = getpermtext(f->redir, NULL, 1);
+ if (t) {
+ zputs(t, stdout);
+ zsfree(t);
+ }
+ }
+
+ putchar('\n');
+}
+
+/*
+ * Wrap scanmatchtable for shell functions with optional
+ * expansion of leading tabs.
+ * expand = 0 is standard: use hard tabs.
+ * expand > 0 uses that many spaces.
+ * expand < 0 uses no identation.
+ *
+ * Note this function and the following two are called with
+ * interrupts queued, so saving and restoring text_expand_tabs
+ * is safe.
+ */
+
+/**/
+mod_export int
+scanmatchshfunc(Patprog pprog, int sorted, int flags1, int flags2,
+ ScanFunc scanfunc, int scanflags, int expand)
+{
+ int ret, save_expand;
+
+ save_expand = text_expand_tabs;
+ text_expand_tabs = expand;
+ ret = scanmatchtable(shfunctab, pprog, sorted, flags1, flags2,
+ scanfunc, scanflags);
+ text_expand_tabs = save_expand;
+
+ return ret;
+}
+
+/* Wrap scanhashtable to expand tabs for shell functions */
+
+/**/
+mod_export int
+scanshfunc(int sorted, int flags1, int flags2,
+ ScanFunc scanfunc, int scanflags, int expand)
+{
+ return scanmatchshfunc(NULL, sorted, flags1, flags2,
+ scanfunc, scanflags, expand);
+}
+
+/* Wrap shfunctab->printnode to expand tabs */
+
+/**/
+mod_export void
+printshfuncexpand(HashNode hn, int printflags, int expand)
+{
+ int save_expand;
+
+ save_expand = text_expand_tabs;
+ text_expand_tabs = expand;
+ shfunctab->printnode(hn, printflags);
+ 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 */
+/**************************************/
+
+/* Nodes for reserved word hash table */
+
+static struct reswd reswds[] = {
+ {{NULL, "!", 0}, BANG},
+ {{NULL, "[[", 0}, DINBRACK},
+ {{NULL, "{", 0}, INBRACE},
+ {{NULL, "}", 0}, OUTBRACE},
+ {{NULL, "case", 0}, CASE},
+ {{NULL, "coproc", 0}, COPROC},
+ {{NULL, "declare", 0}, TYPESET},
+ {{NULL, "do", 0}, DOLOOP},
+ {{NULL, "done", 0}, DONE},
+ {{NULL, "elif", 0}, ELIF},
+ {{NULL, "else", 0}, ELSE},
+ {{NULL, "end", 0}, ZEND},
+ {{NULL, "esac", 0}, ESAC},
+ {{NULL, "export", 0}, TYPESET},
+ {{NULL, "fi", 0}, FI},
+ {{NULL, "float", 0}, TYPESET},
+ {{NULL, "for", 0}, FOR},
+ {{NULL, "foreach", 0}, FOREACH},
+ {{NULL, "function", 0}, FUNC},
+ {{NULL, "if", 0}, IF},
+ {{NULL, "integer", 0}, TYPESET},
+ {{NULL, "local", 0}, TYPESET},
+ {{NULL, "nocorrect", 0}, NOCORRECT},
+ {{NULL, "readonly", 0}, TYPESET},
+ {{NULL, "repeat", 0}, REPEAT},
+ {{NULL, "select", 0}, SELECT},
+ {{NULL, "then", 0}, THEN},
+ {{NULL, "time", 0}, TIME},
+ {{NULL, "typeset", 0}, TYPESET},
+ {{NULL, "until", 0}, UNTIL},
+ {{NULL, "while", 0}, WHILE},
+ {{NULL, NULL, 0}, 0}
+};
+
+/* hash table containing the reserved words */
+
+/**/
+mod_export HashTable reswdtab;
+
+/* Build the hash table containing zsh's reserved words. */
+
+/**/
+void
+createreswdtable(void)
+{
+ Reswd rw;
+
+ reswdtab = newhashtable(23, "reswdtab", NULL);
+
+ reswdtab->hash = hasher;
+ reswdtab->emptytable = NULL;
+ reswdtab->filltable = NULL;
+ reswdtab->cmpnodes = strcmp;
+ reswdtab->addnode = addhashnode;
+ reswdtab->getnode = gethashnode;
+ reswdtab->getnode2 = gethashnode2;
+ reswdtab->removenode = NULL;
+ reswdtab->disablenode = disablehashnode;
+ reswdtab->enablenode = enablehashnode;
+ reswdtab->freenode = NULL;
+ reswdtab->printnode = printreswdnode;
+
+ for (rw = reswds; rw->node.nam; rw++)
+ reswdtab->addnode(reswdtab, rw->node.nam, rw);
+}
+
+/* Print a reserved word */
+
+/**/
+static void
+printreswdnode(HashNode hn, int printflags)
+{
+ Reswd rw = (Reswd) hn;
+
+ if (printflags & PRINT_WHENCE_WORD) {
+ printf("%s: reserved\n", rw->node.nam);
+ return;
+ }
+
+ if (printflags & PRINT_WHENCE_CSH) {
+ printf("%s: shell reserved word\n", rw->node.nam);
+ return;
+ }
+
+ if (printflags & PRINT_WHENCE_VERBOSE) {
+ printf("%s is a reserved word\n", rw->node.nam);
+ return;
+ }
+
+ /* default is name only */
+ printf("%s\n", rw->node.nam);
+}
+
+/********************************/
+/* Aliases Hash Table Functions */
+/********************************/
+
+/* hash table containing the aliases */
+
+/**/
+mod_export HashTable aliastab;
+
+/* has table containing suffix aliases */
+
+/**/
+mod_export HashTable sufaliastab;
+
+/* Create new hash tables for aliases */
+
+/**/
+void
+createaliastable(HashTable ht)
+{
+ ht->hash = hasher;
+ ht->emptytable = NULL;
+ ht->filltable = NULL;
+ ht->cmpnodes = strcmp;
+ ht->addnode = addhashnode;
+ ht->getnode = gethashnode;
+ ht->getnode2 = gethashnode2;
+ ht->removenode = removehashnode;
+ ht->disablenode = disablehashnode;
+ ht->enablenode = enablehashnode;
+ ht->freenode = freealiasnode;
+ ht->printnode = printaliasnode;
+}
+
+/**/
+void
+createaliastables(void)
+{
+ /* Table for regular and global aliases */
+
+ aliastab = newhashtable(23, "aliastab", NULL);
+
+ createaliastable(aliastab);
+
+ /* add the default aliases */
+ aliastab->addnode(aliastab, ztrdup("run-help"), createaliasnode(ztrdup("man"), 0));
+ aliastab->addnode(aliastab, ztrdup("which-command"), createaliasnode(ztrdup("whence"), 0));
+
+
+ /* Table for suffix aliases --- make this smaller */
+
+ sufaliastab = newhashtable(11, "sufaliastab", NULL);
+
+ createaliastable(sufaliastab);
+}
+
+/* Create a new alias node */
+
+/**/
+mod_export Alias
+createaliasnode(char *txt, int flags)
+{
+ Alias al;
+
+ al = (Alias) zshcalloc(sizeof *al);
+ al->node.flags = flags;
+ al->text = txt;
+ al->inuse = 0;
+ return al;
+}
+
+/**/
+static void
+freealiasnode(HashNode hn)
+{
+ Alias al = (Alias) hn;
+
+ zsfree(al->node.nam);
+ zsfree(al->text);
+ zfree(al, sizeof(struct alias));
+}
+
+/* Print an alias */
+
+/**/
+static void
+printaliasnode(HashNode hn, int printflags)
+{
+ Alias a = (Alias) hn;
+
+ if (printflags & PRINT_NAMEONLY) {
+ zputs(a->node.nam, stdout);
+ putchar('\n');
+ return;
+ }
+
+ if (printflags & PRINT_WHENCE_WORD) {
+ if (a->node.flags & ALIAS_SUFFIX)
+ printf("%s: suffix alias\n", a->node.nam);
+ else if (a->node.flags & ALIAS_GLOBAL)
+ printf("%s: global alias\n", a->node.nam);
+ else
+ printf("%s: alias\n", a->node.nam);
+ return;
+ }
+
+ if (printflags & PRINT_WHENCE_SIMPLE) {
+ zputs(a->text, stdout);
+ putchar('\n');
+ return;
+ }
+
+ if (printflags & PRINT_WHENCE_CSH) {
+ nicezputs(a->node.nam, stdout);
+ printf(": ");
+ if (a->node.flags & ALIAS_SUFFIX)
+ printf("suffix ");
+ else if (a->node.flags & ALIAS_GLOBAL)
+ printf("globally ");
+ printf ("aliased to ");
+ nicezputs(a->text, stdout);
+ putchar('\n');
+ return;
+ }
+
+ if (printflags & PRINT_WHENCE_VERBOSE) {
+ nicezputs(a->node.nam, stdout);
+ printf(" is a");
+ if (a->node.flags & ALIAS_SUFFIX)
+ printf(" suffix");
+ else if (a->node.flags & ALIAS_GLOBAL)
+ printf(" global");
+ else
+ printf("n");
+ printf(" alias for ");
+ nicezputs(a->text, stdout);
+ putchar('\n');
+ return;
+ }
+
+ if (printflags & PRINT_LIST) {
+ /* Fast fail on unrepresentable values. */
+ if (strchr(a->node.nam, '=')) {
+ zwarn("invalid alias '%s' encountered while printing aliases",
+ a->node.nam);
+ /* ### TODO: Return an error status to the C caller */
+ return;
+ }
+
+ /* Normal path. */
+ printf("alias ");
+ if (a->node.flags & ALIAS_SUFFIX)
+ printf("-s ");
+ else if (a->node.flags & ALIAS_GLOBAL)
+ printf("-g ");
+
+ /* If an alias begins with `-' or `+', then we must output `-- '
+ * first, so that it is not interpreted as an option. */
+ if(a->node.nam[0] == '-' || a->node.nam[0] == '+')
+ printf("-- ");
+ }
+
+ quotedzputs(a->node.nam, stdout);
+ putchar('=');
+ quotedzputs(a->text, stdout);
+
+ putchar('\n');
+}
+
+/*************************************/
+/* History Line Hash Table Functions */
+/*************************************/
+
+/**/
+void
+createhisttable(void)
+{
+ histtab = newhashtable(599, "histtab", NULL);
+
+ histtab->hash = histhasher;
+ histtab->emptytable = emptyhisttable;
+ histtab->filltable = NULL;
+ histtab->cmpnodes = histstrcmp;
+ histtab->addnode = addhistnode;
+ histtab->getnode = gethashnode2;
+ histtab->getnode2 = gethashnode2;
+ histtab->removenode = removehashnode;
+ histtab->disablenode = NULL;
+ histtab->enablenode = NULL;
+ histtab->freenode = freehistnode;
+ histtab->printnode = NULL;
+}
+
+/**/
+unsigned
+histhasher(const char *str)
+{
+ unsigned hashval = 0;
+
+ while (inblank(*str)) str++;
+
+ while (*str) {
+ if (inblank(*str)) {
+ do str++; while (inblank(*str));
+ if (*str)
+ hashval += (hashval << 5) + ' ';
+ }
+ else
+ hashval += (hashval << 5) + *(unsigned char *)str++;
+ }
+ return hashval;
+}
+
+/**/
+void
+emptyhisttable(HashTable ht)
+{
+ emptyhashtable(ht);
+ if (hist_ring)
+ histremovedups();
+}
+
+/* Compare two strings with normalized white-space */
+
+/**/
+int
+histstrcmp(const char *str1, const char *str2)
+{
+ while (inblank(*str1)) str1++;
+ while (inblank(*str2)) str2++;
+ while (*str1 && *str2) {
+ if (inblank(*str1)) {
+ if (!inblank(*str2))
+ break;
+ do str1++; while (inblank(*str1));
+ do str2++; while (inblank(*str2));
+ }
+ else {
+ if (*str1 != *str2)
+ break;
+ str1++;
+ str2++;
+ }
+ }
+ return *str1 - *str2;
+}
+
+/**/
+void
+addhistnode(HashTable ht, char *nam, void *nodeptr)
+{
+ HashNode oldnode = addhashnode2(ht, nam, nodeptr);
+ Histent he = (Histent)nodeptr;
+ if (oldnode && oldnode != (HashNode)nodeptr) {
+ if (he->node.flags & HIST_MAKEUNIQUE
+ || (he->node.flags & HIST_FOREIGN && (Histent)oldnode == he->up)) {
+ (void) addhashnode2(ht, oldnode->nam, oldnode); /* restore hash */
+ he->node.flags |= HIST_DUP;
+ he->node.flags &= ~HIST_MAKEUNIQUE;
+ }
+ else {
+ oldnode->flags |= HIST_DUP;
+ if (hist_ignore_all_dups)
+ freehistnode(oldnode); /* Remove the old dup */
+ }
+ }
+ else
+ he->node.flags &= ~HIST_MAKEUNIQUE;
+}
+
+/**/
+void
+freehistnode(HashNode nodeptr)
+{
+ freehistdata((Histent)nodeptr, 1);
+ zfree(nodeptr, sizeof (struct histent));
+}
+
+/**/
+void
+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);
+
+ zsfree(he->node.nam);
+ if (he->nwords)
+ zfree(he->words, he->nwords*2*sizeof(short));
+
+ if (unlink) {
+ if (!--histlinect)
+ hist_ring = NULL;
+ else {
+ if (he == hist_ring)
+ hist_ring = hist_ring->up;
+ he->up->down = he->down;
+ he->down->up = he->up;
+ }
+ }
+}
+
+
+/***********************************************************************
+ * 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;
+ }
+}