xref: /openbsd-src/bin/ksh/table.c (revision 6c72b53185cf1421eca9c3faf6b8693af66cf9d5)
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