diff options
Diffstat (limited to 'lib/hashtable.c')
| -rw-r--r-- | lib/hashtable.c | 244 | 
1 files changed, 166 insertions, 78 deletions
| diff --git a/lib/hashtable.c b/lib/hashtable.c index 94a7b6171..07ebfb218 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -54,7 +54,9 @@  #define	CONFIG_ENV_MAX_ENTRIES 512  #endif -#include "search.h" +#include <env_callback.h> +#include <env_flags.h> +#include <search.h>  /*   * [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 @@ -66,12 +68,16 @@   * Instead the interface of all functions is extended to take an argument   * which describes the current status.   */ +  typedef struct _ENTRY {  	int used;  	ENTRY entry;  } _ENTRY; +static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep, +	int idx); +  /*   * hcreate()   */ @@ -142,7 +148,7 @@ int hcreate_r(size_t nel, struct hsearch_data *htab)   * be freed and the local static variable can be marked as not used.   */ -void hdestroy_r(struct hsearch_data *htab, int do_apply) +void hdestroy_r(struct hsearch_data *htab)  {  	int i; @@ -156,10 +162,7 @@ void hdestroy_r(struct hsearch_data *htab, int do_apply)  	for (i = 1; i <= htab->size; ++i) {  		if (htab->table[i].used > 0) {  			ENTRY *ep = &htab->table[i].entry; -			if (do_apply && htab->apply != NULL) { -				/* deletion is always forced */ -				htab->apply(ep->key, ep->data, NULL, H_FORCE); -			} +  			free((void *)ep->key);  			free(ep->data);  		} @@ -250,14 +253,65 @@ int hmatch_r(const char *match, int last_idx, ENTRY ** retval,  	return 0;  } +/* + * Compare an existing entry with the desired key, and overwrite if the action + * is ENTER.  This is simply a helper function for hsearch_r(). + */ +static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action, +	ENTRY **retval, struct hsearch_data *htab, int flag, +	unsigned int hval, unsigned int idx) +{ +	if (htab->table[idx].used == hval +	    && strcmp(item.key, htab->table[idx].entry.key) == 0) { +		/* Overwrite existing value? */ +		if ((action == ENTER) && (item.data != NULL)) { +			/* check for permission */ +			if (htab->change_ok != NULL && htab->change_ok( +			    &htab->table[idx].entry, item.data, +			    env_op_overwrite, flag)) { +				debug("change_ok() rejected setting variable " +					"%s, skipping it!\n", item.key); +				__set_errno(EPERM); +				*retval = NULL; +				return 0; +			} + +			/* If there is a callback, call it */ +			if (htab->table[idx].entry.callback && +			    htab->table[idx].entry.callback(item.key, +			    item.data, env_op_overwrite, flag)) { +				debug("callback() rejected setting variable " +					"%s, skipping it!\n", item.key); +				__set_errno(EINVAL); +				*retval = NULL; +				return 0; +			} + +			free(htab->table[idx].entry.data); +			htab->table[idx].entry.data = strdup(item.data); +			if (!htab->table[idx].entry.data) { +				__set_errno(ENOMEM); +				*retval = NULL; +				return 0; +			} +		} +		/* return found entry */ +		*retval = &htab->table[idx].entry; +		return idx; +	} +	/* keep searching */ +	return -1; +} +  int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, -	      struct hsearch_data *htab) +	      struct hsearch_data *htab, int flag)  {  	unsigned int hval;  	unsigned int count;  	unsigned int len = strlen(item.key);  	unsigned int idx;  	unsigned int first_deleted = 0; +	int ret;  	/* Compute an value for the given string. Perhaps use a better method. */  	hval = len; @@ -289,23 +343,10 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,  		    && !first_deleted)  			first_deleted = idx; -		if (htab->table[idx].used == hval -		    && strcmp(item.key, htab->table[idx].entry.key) == 0) { -			/* Overwrite existing value? */ -			if ((action == ENTER) && (item.data != NULL)) { -				free(htab->table[idx].entry.data); -				htab->table[idx].entry.data = -					strdup(item.data); -				if (!htab->table[idx].entry.data) { -					__set_errno(ENOMEM); -					*retval = NULL; -					return 0; -				} -			} -			/* return found entry */ -			*retval = &htab->table[idx].entry; -			return idx; -		} +		ret = _compare_and_overwrite_entry(item, action, retval, htab, +			flag, hval, idx); +		if (ret != -1) +			return ret;  		/*  		 * Second hash function: @@ -331,23 +372,10 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,  				break;  			/* If entry is found use it. */ -			if ((htab->table[idx].used == hval) -			    && strcmp(item.key, htab->table[idx].entry.key) == 0) { -				/* Overwrite existing value? */ -				if ((action == ENTER) && (item.data != NULL)) { -					free(htab->table[idx].entry.data); -					htab->table[idx].entry.data = -						strdup(item.data); -					if (!htab->table[idx].entry.data) { -						__set_errno(ENOMEM); -						*retval = NULL; -						return 0; -					} -				} -				/* return found entry */ -				*retval = &htab->table[idx].entry; -				return idx; -			} +			ret = _compare_and_overwrite_entry(item, action, retval, +				htab, flag, hval, idx); +			if (ret != -1) +				return ret;  		}  		while (htab->table[idx].used);  	} @@ -383,6 +411,34 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,  		++htab->filled; +		/* This is a new entry, so look up a possible callback */ +		env_callback_init(&htab->table[idx].entry); +		/* Also look for flags */ +		env_flags_init(&htab->table[idx].entry); + +		/* check for permission */ +		if (htab->change_ok != NULL && htab->change_ok( +		    &htab->table[idx].entry, item.data, env_op_create, flag)) { +			debug("change_ok() rejected setting variable " +				"%s, skipping it!\n", item.key); +			_hdelete(item.key, htab, &htab->table[idx].entry, idx); +			__set_errno(EPERM); +			*retval = NULL; +			return 0; +		} + +		/* If there is a callback, call it */ +		if (htab->table[idx].entry.callback && +		    htab->table[idx].entry.callback(item.key, item.data, +		    env_op_create, flag)) { +			debug("callback() rejected setting variable " +				"%s, skipping it!\n", item.key); +			_hdelete(item.key, htab, &htab->table[idx].entry, idx); +			__set_errno(EINVAL); +			*retval = NULL; +			return 0; +		} +  		/* return new entry */  		*retval = &htab->table[idx].entry;  		return 1; @@ -404,7 +460,21 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,   * do that.   */ -int hdelete_r(const char *key, struct hsearch_data *htab, int do_apply) +static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep, +	int idx) +{ +	/* free used ENTRY */ +	debug("hdelete: DELETING key \"%s\"\n", key); +	free((void *)ep->key); +	free(ep->data); +	ep->callback = NULL; +	ep->flags = 0; +	htab->table[idx].used = -1; + +	--htab->filled; +} + +int hdelete_r(const char *key, struct hsearch_data *htab, int flag)  {  	ENTRY e, *ep;  	int idx; @@ -413,20 +483,31 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int do_apply)  	e.key = (char *)key; -	if ((idx = hsearch_r(e, FIND, &ep, htab)) == 0) { +	idx = hsearch_r(e, FIND, &ep, htab, 0); +	if (idx == 0) {  		__set_errno(ESRCH);  		return 0;	/* not found */  	} -	/* free used ENTRY */ -	debug("hdelete: DELETING key \"%s\"\n", key); -	if (do_apply && htab->apply != NULL) -		htab->apply(ep->key, ep->data, NULL, H_FORCE); -	free((void *)ep->key); -	free(ep->data); -	htab->table[idx].used = -1; +	/* Check for permission */ +	if (htab->change_ok != NULL && +	    htab->change_ok(ep, NULL, env_op_delete, flag)) { +		debug("change_ok() rejected deleting variable " +			"%s, skipping it!\n", key); +		__set_errno(EPERM); +		return 0; +	} -	--htab->filled; +	/* If there is a callback, call it */ +	if (htab->table[idx].entry.callback && +	    htab->table[idx].entry.callback(key, NULL, env_op_delete, flag)) { +		debug("callback() rejected deleting variable " +			"%s, skipping it!\n", key); +		__set_errno(EINVAL); +		return 0; +	} + +	_hdelete(key, htab, ep, idx);  	return 1;  } @@ -482,7 +563,7 @@ static int cmpkey(const void *p1, const void *p2)  	return (strcmp(e1->key, e2->key));  } -ssize_t hexport_r(struct hsearch_data *htab, const char sep, +ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag,  		 char **resp, size_t size,  		 int argc, char * const argv[])  { @@ -519,6 +600,9 @@ ssize_t hexport_r(struct hsearch_data *htab, const char sep,  			if ((argc > 0) && (found == 0))  				continue; +			if ((flag & H_HIDE_DOT) && ep->key[0] == '.') +				continue; +  			list[n++] = ep;  			totlen += strlen(ep->key) + 2; @@ -674,7 +758,7 @@ static int drop_var_from_set(const char *name, int nvars, char * vars[])  int himport_r(struct hsearch_data *htab,  		const char *env, size_t size, const char sep, int flag, -		int nvars, char * const vars[], int do_apply) +		int nvars, char * const vars[])  {  	char *data, *sp, *dp, *name, *value;  	char *localvars[nvars]; @@ -704,7 +788,7 @@ int himport_r(struct hsearch_data *htab,  		debug("Destroy Hash Table: %p table = %p\n", htab,  		       htab->table);  		if (htab->table) -			hdestroy_r(htab, do_apply); +			hdestroy_r(htab);  	}  	/* @@ -770,7 +854,7 @@ int himport_r(struct hsearch_data *htab,  			if (!drop_var_from_set(name, nvars, localvars))  				continue; -			if (hdelete_r(name, htab, do_apply) == 0) +			if (hdelete_r(name, htab, flag) == 0)  				debug("DELETE ERROR ##############################\n");  			continue; @@ -794,30 +878,10 @@ int himport_r(struct hsearch_data *htab,  		e.key = name;  		e.data = value; -		/* if there is an apply function, check what it has to say */ -		if (do_apply && htab->apply != NULL) { -			debug("searching before calling cb function" -				" for  %s\n", name); -			/* -			 * Search for variable in existing env, so to pass -			 * its previous value to the apply callback -			 */ -			hsearch_r(e, FIND, &rv, htab); -			debug("previous value was %s\n", rv ? rv->data : ""); -			if (htab->apply(name, rv ? rv->data : NULL, -				value, flag)) { -				debug("callback function refused to set" -					" variable %s, skipping it!\n", name); -				continue; -			} -		} - -		hsearch_r(e, ENTER, &rv, htab); -		if (rv == NULL) { +		hsearch_r(e, ENTER, &rv, htab, flag); +		if (rv == NULL)  			printf("himport_r: can't insert \"%s=%s\" into hash table\n",  				name, value); -			return 0; -		}  		debug("INSERT: table %p, filled %d/%d rv %p ==> name=\"%s\" value=\"%s\"\n",  			htab, htab->filled, htab->size, @@ -839,7 +903,7 @@ int himport_r(struct hsearch_data *htab,  		 * b) if the variable was not present in current env, we notify  		 *    it might be a typo  		 */ -		if (hdelete_r(localvars[i], htab, do_apply) == 0) +		if (hdelete_r(localvars[i], htab, flag) == 0)  			printf("WARNING: '%s' neither in running nor in imported env!\n", localvars[i]);  		else  			printf("WARNING: '%s' not in imported env, deleting it!\n", localvars[i]); @@ -848,3 +912,27 @@ int himport_r(struct hsearch_data *htab,  	debug("INSERT: done\n");  	return 1;		/* everything OK */  } + +/* + * hwalk_r() + */ + +/* + * Walk all of the entries in the hash, calling the callback for each one. + * this allows some generic operation to be performed on each element. + */ +int hwalk_r(struct hsearch_data *htab, int (*callback)(ENTRY *)) +{ +	int i; +	int retval; + +	for (i = 1; i <= htab->size; ++i) { +		if (htab->table[i].used > 0) { +			retval = callback(&htab->table[i].entry); +			if (retval) +				return retval; +		} +	} + +	return 0; +} |