summaryrefslogtreecommitdiff
path: root/lib/hashtable.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hashtable.c')
-rw-r--r--lib/hashtable.c244
1 files changed, 166 insertions, 78 deletions
diff --git a/lib/hashtable.c b/lib/hashtable.c
index 94a7b61717..07ebfb218f 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;
+}