1*6c72b531Sjca /* $OpenBSD: table.c,v 1.25 2018/01/16 22:52:32 jca Exp $ */
27cb960a2Sdownsj
37cb960a2Sdownsj /*
47cb960a2Sdownsj * dynamic hashed associative table for commands and variables
57cb960a2Sdownsj */
67cb960a2Sdownsj
7dbfe7957Smmcc #include <limits.h>
864fca78aSmmcc #include <stddef.h>
956018212Smmcc #include <string.h>
10dbfe7957Smmcc
117cb960a2Sdownsj #include "sh.h"
127cb960a2Sdownsj
137cb960a2Sdownsj #define INIT_TBLS 8 /* initial table size (power of 2) */
147cb960a2Sdownsj
157df1fdf4Snicm struct table taliases; /* tracked aliases */
167df1fdf4Snicm struct table builtins; /* built-in commands */
177df1fdf4Snicm struct table aliases; /* aliases */
187df1fdf4Snicm struct table keywords; /* keywords */
197df1fdf4Snicm struct table homedirs; /* homedir() cache */
207df1fdf4Snicm
21201e0776Smillert char *search_path; /* copy of either PATH or def_path */
227df1fdf4Snicm const char *def_path; /* path to use if PATH not set */
237df1fdf4Snicm char *tmpdir; /* TMPDIR value */
247df1fdf4Snicm const char *prompt;
257df1fdf4Snicm int cur_prompt; /* PS1 or PS2 */
267df1fdf4Snicm int current_lineno; /* LINENO value */
277df1fdf4Snicm
28c5d5393cSotto static void texpand(struct table *, int);
29019e3566Smillert static int tnamecmp(const void *, const void *);
307cb960a2Sdownsj
317cb960a2Sdownsj
327cb960a2Sdownsj unsigned int
hash(const char * n)33c5d5393cSotto hash(const char *n)
347cb960a2Sdownsj {
357894b443Smillert unsigned int h = 0;
367cb960a2Sdownsj
377cb960a2Sdownsj while (*n != '\0')
380d4df784Sotto h = 33*h + (unsigned char)(*n++);
390d4df784Sotto return h;
407cb960a2Sdownsj }
417cb960a2Sdownsj
427cb960a2Sdownsj void
ktinit(struct table * tp,Area * ap,int tsize)436df3ee40Sotto ktinit(struct table *tp, Area *ap, int tsize)
447cb960a2Sdownsj {
457cb960a2Sdownsj tp->areap = ap;
467cb960a2Sdownsj tp->tbls = NULL;
477cb960a2Sdownsj tp->size = tp->nfree = 0;
487cb960a2Sdownsj if (tsize)
497cb960a2Sdownsj texpand(tp, tsize);
507cb960a2Sdownsj }
517cb960a2Sdownsj
527cb960a2Sdownsj static void
texpand(struct table * tp,int nsize)53c5d5393cSotto texpand(struct table *tp, int nsize)
547cb960a2Sdownsj {
557894b443Smillert int i;
567894b443Smillert struct tbl *tblp, **p;
577894b443Smillert struct tbl **ntblp, **otblp = tp->tbls;
587cb960a2Sdownsj int osize = tp->size;
597cb960a2Sdownsj
60d67c3782Smmcc ntblp = areallocarray(NULL, nsize, sizeof(struct tbl *), tp->areap);
617cb960a2Sdownsj for (i = 0; i < nsize; i++)
627cb960a2Sdownsj ntblp[i] = NULL;
637cb960a2Sdownsj tp->size = nsize;
640d4df784Sotto tp->nfree = 7*nsize/10; /* table can get 70% full */
657cb960a2Sdownsj tp->tbls = ntblp;
667cb960a2Sdownsj if (otblp == NULL)
677cb960a2Sdownsj return;
687cb960a2Sdownsj for (i = 0; i < osize; i++)
6918bbba6bSmillert if ((tblp = otblp[i]) != NULL) {
707cb960a2Sdownsj if ((tblp->flag&DEFINED)) {
717a8124d8Sderaadt for (p = &ntblp[hash(tblp->name) &
727a8124d8Sderaadt (tp->size-1)]; *p != NULL; p--)
737cb960a2Sdownsj if (p == ntblp) /* wrap */
747cb960a2Sdownsj p += tp->size;
757cb960a2Sdownsj *p = tblp;
767cb960a2Sdownsj tp->nfree--;
77dcacb757Sdownsj } else if (!(tblp->flag & FINUSE)) {
78bfd561bcStedu afree(tblp, tp->areap);
797cb960a2Sdownsj }
8018bbba6bSmillert }
81bfd561bcStedu afree(otblp, tp->areap);
827cb960a2Sdownsj }
837cb960a2Sdownsj
84c5d5393cSotto /* table */
85c5d5393cSotto /* name to enter */
86c5d5393cSotto /* hash(n) */
87b3b39737Sderaadt struct tbl *
ktsearch(struct table * tp,const char * n,unsigned int h)886df3ee40Sotto ktsearch(struct table *tp, const char *n, unsigned int h)
897cb960a2Sdownsj {
907894b443Smillert struct tbl **pp, *p;
917cb960a2Sdownsj
927cb960a2Sdownsj if (tp->size == 0)
937cb960a2Sdownsj return NULL;
947cb960a2Sdownsj
957cb960a2Sdownsj /* search for name in hashed table */
967cb960a2Sdownsj for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
977a8124d8Sderaadt if (*p->name == *n && strcmp(p->name, n) == 0 &&
987a8124d8Sderaadt (p->flag&DEFINED))
997cb960a2Sdownsj return p;
1007cb960a2Sdownsj if (pp == tp->tbls) /* wrap */
1017cb960a2Sdownsj pp += tp->size;
1027cb960a2Sdownsj }
1037cb960a2Sdownsj
1047cb960a2Sdownsj return NULL;
1057cb960a2Sdownsj }
1067cb960a2Sdownsj
107c5d5393cSotto /* table */
108c5d5393cSotto /* name to enter */
109c5d5393cSotto /* hash(n) */
110b3b39737Sderaadt struct tbl *
ktenter(struct table * tp,const char * n,unsigned int h)1116df3ee40Sotto ktenter(struct table *tp, const char *n, unsigned int h)
1127cb960a2Sdownsj {
1137894b443Smillert struct tbl **pp, *p;
1147894b443Smillert int len;
1157cb960a2Sdownsj
1167cb960a2Sdownsj if (tp->size == 0)
1177cb960a2Sdownsj texpand(tp, INIT_TBLS);
1187cb960a2Sdownsj Search:
1197cb960a2Sdownsj /* search for name in hashed table */
1207cb960a2Sdownsj for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp) != NULL; pp--) {
1217cb960a2Sdownsj if (*p->name == *n && strcmp(p->name, n) == 0)
1227cb960a2Sdownsj return p; /* found */
1237cb960a2Sdownsj if (pp == tp->tbls) /* wrap */
1247cb960a2Sdownsj pp += tp->size;
1257cb960a2Sdownsj }
1267cb960a2Sdownsj
1277cb960a2Sdownsj if (tp->nfree <= 0) { /* too full */
1280d4df784Sotto if (tp->size <= INT_MAX/2)
1297cb960a2Sdownsj texpand(tp, 2*tp->size);
13051c6246aSotto else
131*6c72b531Sjca internal_errorf("too many vars");
1327cb960a2Sdownsj goto Search;
1337cb960a2Sdownsj }
1347cb960a2Sdownsj
1357cb960a2Sdownsj /* create new tbl entry */
1367cb960a2Sdownsj len = strlen(n) + 1;
1378c046d24Snicm p = alloc(offsetof(struct tbl, name[0]) + len,
1387cb960a2Sdownsj tp->areap);
1397cb960a2Sdownsj p->flag = 0;
1407cb960a2Sdownsj p->type = 0;
1417cb960a2Sdownsj p->areap = tp->areap;
142dcacb757Sdownsj p->u2.field = 0;
143f7654a50Snicm p->u.array = NULL;
1447cb960a2Sdownsj memcpy(p->name, n, len);
1457cb960a2Sdownsj
1467cb960a2Sdownsj /* enter in tp->tbls */
1477cb960a2Sdownsj tp->nfree--;
1487cb960a2Sdownsj *pp = p;
1497cb960a2Sdownsj return p;
1507cb960a2Sdownsj }
1517cb960a2Sdownsj
1527cb960a2Sdownsj void
ktdelete(struct tbl * p)1536df3ee40Sotto ktdelete(struct tbl *p)
1547cb960a2Sdownsj {
1557cb960a2Sdownsj p->flag = 0;
1567cb960a2Sdownsj }
1577cb960a2Sdownsj
1587cb960a2Sdownsj void
ktwalk(struct tstate * ts,struct table * tp)1596df3ee40Sotto ktwalk(struct tstate *ts, struct table *tp)
1607cb960a2Sdownsj {
1617cb960a2Sdownsj ts->left = tp->size;
1627cb960a2Sdownsj ts->next = tp->tbls;
1637cb960a2Sdownsj }
1647cb960a2Sdownsj
1657cb960a2Sdownsj struct tbl *
ktnext(struct tstate * ts)1666df3ee40Sotto ktnext(struct tstate *ts)
1677cb960a2Sdownsj {
1687cb960a2Sdownsj while (--ts->left >= 0) {
1697cb960a2Sdownsj struct tbl *p = *ts->next++;
1707cb960a2Sdownsj if (p != NULL && (p->flag&DEFINED))
1717cb960a2Sdownsj return p;
1727cb960a2Sdownsj }
1737cb960a2Sdownsj return NULL;
1747cb960a2Sdownsj }
1757cb960a2Sdownsj
1767cb960a2Sdownsj static int
tnamecmp(const void * p1,const void * p2)177019e3566Smillert tnamecmp(const void *p1, const void *p2)
1787cb960a2Sdownsj {
179019e3566Smillert char *name1 = (*(struct tbl **)p1)->name;
180019e3566Smillert char *name2 = (*(struct tbl **)p2)->name;
181019e3566Smillert return strcmp(name1, name2);
1827cb960a2Sdownsj }
1837cb960a2Sdownsj
1847cb960a2Sdownsj struct tbl **
ktsort(struct table * tp)1856df3ee40Sotto ktsort(struct table *tp)
1867cb960a2Sdownsj {
1877894b443Smillert int i;
1887894b443Smillert struct tbl **p, **sp, **dp;
1897cb960a2Sdownsj
190d67c3782Smmcc p = areallocarray(NULL, tp->size + 1,
191d67c3782Smmcc sizeof(struct tbl *), ATEMP);
1927cb960a2Sdownsj sp = tp->tbls; /* source */
1937cb960a2Sdownsj dp = p; /* dest */
1947cb960a2Sdownsj for (i = 0; i < tp->size; i++)
1957cb960a2Sdownsj if ((*dp = *sp++) != NULL && (((*dp)->flag&DEFINED) ||
1967cb960a2Sdownsj ((*dp)->flag&ARRAY)))
1977cb960a2Sdownsj dp++;
1987cb960a2Sdownsj i = dp - p;
1997cb960a2Sdownsj qsortp((void**)p, (size_t)i, tnamecmp);
2007cb960a2Sdownsj p[i] = NULL;
2017cb960a2Sdownsj return p;
2027cb960a2Sdownsj }
2037cb960a2Sdownsj
2047cb960a2Sdownsj #ifdef PERF_DEBUG /* performance debugging */
2057cb960a2Sdownsj
20669b9f96bSmillert void tprintinfo(struct table *tp);
2077cb960a2Sdownsj
2087cb960a2Sdownsj void
tprintinfo(struct table * tp)209c5d5393cSotto tprintinfo(struct table *tp)
2107cb960a2Sdownsj {
2117cb960a2Sdownsj struct tbl *te;
2127cb960a2Sdownsj char *n;
2137cb960a2Sdownsj unsigned int h;
2147cb960a2Sdownsj int ncmp;
2157cb960a2Sdownsj int totncmp = 0, maxncmp = 0;
2167cb960a2Sdownsj int nentries = 0;
2177cb960a2Sdownsj struct tstate ts;
2187cb960a2Sdownsj
2197cb960a2Sdownsj shellf("table size %d, nfree %d\n", tp->size, tp->nfree);
2207cb960a2Sdownsj shellf(" Ncmp name\n");
2216df3ee40Sotto ktwalk(&ts, tp);
2226df3ee40Sotto while ((te = ktnext(&ts))) {
2237894b443Smillert struct tbl **pp, *p;
2247cb960a2Sdownsj
2257cb960a2Sdownsj h = hash(n = te->name);
2267cb960a2Sdownsj ncmp = 0;
2277cb960a2Sdownsj
2286df3ee40Sotto /* taken from ktsearch() and added counter */
2297cb960a2Sdownsj for (pp = &tp->tbls[h & (tp->size-1)]; (p = *pp); pp--) {
2307cb960a2Sdownsj ncmp++;
2317a8124d8Sderaadt if (*p->name == *n && strcmp(p->name, n) == 0 &&
2327a8124d8Sderaadt (p->flag&DEFINED))
2337cb960a2Sdownsj break; /* return p; */
2347cb960a2Sdownsj if (pp == tp->tbls) /* wrap */
2357cb960a2Sdownsj pp += tp->size;
2367cb960a2Sdownsj }
2377cb960a2Sdownsj shellf(" %4d %s\n", ncmp, n);
2387cb960a2Sdownsj totncmp += ncmp;
2397cb960a2Sdownsj nentries++;
2407cb960a2Sdownsj if (ncmp > maxncmp)
2417cb960a2Sdownsj maxncmp = ncmp;
2427cb960a2Sdownsj }
2437cb960a2Sdownsj if (nentries)
2447cb960a2Sdownsj shellf(" %d entries, worst ncmp %d, avg ncmp %d.%02d\n",
2457cb960a2Sdownsj nentries, maxncmp,
2467cb960a2Sdownsj totncmp / nentries,
2477cb960a2Sdownsj (totncmp % nentries) * 100 / nentries);
2487cb960a2Sdownsj }
2497cb960a2Sdownsj #endif /* PERF_DEBUG */
250