colibri_imx6: fix video stdout in default environment
[oweals/u-boot.git] / lib / hashtable.c
index 02b41050a44f6656e9605e288f5f6a97faf3539c..b96dbe19be4dd52e0233e9d452f3201f6c57ad69 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
  * This implementation is based on code from uClibc-0.9.30.3 but was
  * modified and extended for use within U-Boot.
  * Copyright (C) 1993, 1995, 1996, 1997, 2002 Free Software Foundation, Inc.
  * This file is part of the GNU C Library.
  * Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1993.
- *
- * SPDX-License-Identifier:    LGPL-2.1+
  */
 
 #include <errno.h>
+#include <log.h>
 #include <malloc.h>
+#include <sort.h>
 
 #ifdef USE_HOSTCC              /* HOST build */
 # include <string.h>
@@ -41,6 +42,9 @@
 #define        CONFIG_ENV_MAX_ENTRIES 512
 #endif
 
+#define USED_FREE 0
+#define USED_DELETED -1
+
 #include <env_callback.h>
 #include <env_flags.h>
 #include <search.h>
  * which describes the current status.
  */
 
-typedef struct _ENTRY {
+struct env_entry_node {
        int used;
-       ENTRY entry;
-} _ENTRY;
+       struct env_entry entry;
+};
 
 
-static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep,
-       int idx);
+static void _hdelete(const char *key, struct hsearch_data *htab,
+                    struct env_entry *ep, int idx);
 
 /*
  * hcreate()
@@ -118,7 +122,8 @@ int hcreate_r(size_t nel, struct hsearch_data *htab)
        htab->filled = 0;
 
        /* allocate memory and zero out */
-       htab->table = (_ENTRY *) calloc(htab->size + 1, sizeof(_ENTRY));
+       htab->table = (struct env_entry_node *)calloc(htab->size + 1,
+                                               sizeof(struct env_entry_node));
        if (htab->table == NULL)
                return 0;
 
@@ -149,7 +154,7 @@ void hdestroy_r(struct hsearch_data *htab)
        /* free used memory */
        for (i = 1; i <= htab->size; ++i) {
                if (htab->table[i].used > 0) {
-                       ENTRY *ep = &htab->table[i].entry;
+                       struct env_entry *ep = &htab->table[i].entry;
 
                        free((void *)ep->key);
                        free(ep->data);
@@ -191,14 +196,14 @@ void hdestroy_r(struct hsearch_data *htab)
  *   data any more.
  * - The standard implementation does not provide a way to update an
  *   existing entry.  This version will create a new entry or update an
- *   existing one when both "action == ENTER" and "item.data != NULL".
+ *   existing one when both "action == ENV_ENTER" and "item.data != NULL".
  * - Instead of returning 1 on success, we return the index into the
  *   internal hash table, which is also guaranteed to be positive.
  *   This allows us direct access to the found hash table slot for
  *   example for functions like hdelete().
  */
 
-int hmatch_r(const char *match, int last_idx, ENTRY ** retval,
+int hmatch_r(const char *match, int last_idx, struct env_entry **retval,
             struct hsearch_data *htab)
 {
        unsigned int idx;
@@ -218,18 +223,30 @@ int hmatch_r(const char *match, int last_idx, ENTRY ** retval,
        return 0;
 }
 
+static int
+do_callback(const struct env_entry *e, const char *name, const char *value,
+           enum env_op op, int flags)
+{
+#ifndef CONFIG_SPL_BUILD
+       if (e->callback)
+               return e->callback(name, value, op, flags);
+#endif
+       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().
+ * is ENV_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)
+static inline int _compare_and_overwrite_entry(struct env_entry item,
+               enum env_action action, struct env_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)) {
+               if (action == ENV_ENTER && item.data) {
                        /* check for permission */
                        if (htab->change_ok != NULL && htab->change_ok(
                            &htab->table[idx].entry, item.data,
@@ -242,9 +259,8 @@ static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action,
                        }
 
                        /* 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)) {
+                       if (do_callback(&htab->table[idx].entry, item.key,
+                                       item.data, env_op_overwrite, flag)) {
                                debug("callback() rejected setting variable "
                                        "%s, skipping it!\n", item.key);
                                __set_errno(EINVAL);
@@ -268,8 +284,8 @@ static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action,
        return -1;
 }
 
-int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
-             struct hsearch_data *htab, int flag)
+int hsearch_r(struct env_entry item, enum env_action action,
+             struct env_entry **retval, struct hsearch_data *htab, int flag)
 {
        unsigned int hval;
        unsigned int count;
@@ -304,7 +320,7 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
                 */
                unsigned hval2;
 
-               if (htab->table[idx].used == -1
+               if (htab->table[idx].used == USED_DELETED
                    && !first_deleted)
                        first_deleted = idx;
 
@@ -336,17 +352,21 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
                        if (idx == hval)
                                break;
 
+                       if (htab->table[idx].used == USED_DELETED
+                           && !first_deleted)
+                               first_deleted = idx;
+
                        /* If entry is found use it. */
                        ret = _compare_and_overwrite_entry(item, action, retval,
                                htab, flag, hval, idx);
                        if (ret != -1)
                                return ret;
                }
-               while (htab->table[idx].used);
+               while (htab->table[idx].used != USED_FREE);
        }
 
        /* An empty bucket has been found. */
-       if (action == ENTER) {
+       if (action == ENV_ENTER) {
                /*
                 * If table is full and another entry should be
                 * entered return with error.
@@ -393,9 +413,8 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
                }
 
                /* 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)) {
+               if (do_callback(&htab->table[idx].entry, 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);
@@ -425,30 +444,29 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
  * do that.
  */
 
-static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep,
-       int idx)
+static void _hdelete(const char *key, struct hsearch_data *htab,
+                    struct env_entry *ep, int idx)
 {
-       /* free used ENTRY */
+       /* 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->table[idx].used = USED_DELETED;
 
        --htab->filled;
 }
 
 int hdelete_r(const char *key, struct hsearch_data *htab, int flag)
 {
-       ENTRY e, *ep;
+       struct env_entry e, *ep;
        int idx;
 
        debug("hdelete: DELETE key \"%s\"\n", key);
 
        e.key = (char *)key;
 
-       idx = hsearch_r(e, FIND, &ep, htab, 0);
+       idx = hsearch_r(e, ENV_FIND, &ep, htab, 0);
        if (idx == 0) {
                __set_errno(ESRCH);
                return 0;       /* not found */
@@ -464,8 +482,8 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag)
        }
 
        /* If there is a callback, call it */
-       if (htab->table[idx].entry.callback &&
-           htab->table[idx].entry.callback(key, NULL, env_op_delete, flag)) {
+       if (do_callback(&htab->table[idx].entry, key, NULL,
+                       env_op_delete, flag)) {
                debug("callback() rejected deleting variable "
                        "%s, skipping it!\n", key);
                __set_errno(EINVAL);
@@ -477,11 +495,11 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag)
        return 1;
 }
 
+#if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV))
 /*
  * hexport()
  */
 
-#ifndef CONFIG_SPL_BUILD
 /*
  * Export the data stored in the hash table in linearized form.
  *
@@ -499,7 +517,7 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag)
  *
  * If the separator character is different from NUL, then any
  * separator characters and backslash characters in the values will
- * be escaped by a preceeding backslash in output. This is needed for
+ * be escaped by a preceding backslash in output. This is needed for
  * example to enable multi-line values, especially when the output
  * shall later be parsed (for example, for re-import).
  *
@@ -522,8 +540,8 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag)
 
 static int cmpkey(const void *p1, const void *p2)
 {
-       ENTRY *e1 = *(ENTRY **) p1;
-       ENTRY *e2 = *(ENTRY **) p2;
+       struct env_entry *e1 = *(struct env_entry **)p1;
+       struct env_entry *e2 = *(struct env_entry **)p2;
 
        return (strcmp(e1->key, e2->key));
 }
@@ -543,9 +561,8 @@ static int match_string(int flag, const char *str, const char *pat, void *priv)
        case H_MATCH_REGEX:
                {
                        struct slre *slrep = (struct slre *)priv;
-                       struct cap caps[slrep->num_caps + 2];
 
-                       if (slre_match(slrep, str, strlen(str), caps))
+                       if (slre_match(slrep, str, strlen(str), NULL))
                                return 1;
                }
                break;
@@ -558,8 +575,8 @@ static int match_string(int flag, const char *str, const char *pat, void *priv)
        return 0;
 }
 
-static int match_entry(ENTRY *ep, int flag,
-                int argc, char * const argv[])
+static int match_entry(struct env_entry *ep, int flag, int argc,
+                      char *const argv[])
 {
        int arg;
        void *priv = NULL;
@@ -589,9 +606,9 @@ static int match_entry(ENTRY *ep, int flag,
 
 ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag,
                 char **resp, size_t size,
-                int argc, char * const argv[])
+                int argc, char *const argv[])
 {
-       ENTRY *list[htab->size];
+       struct env_entry *list[htab->size];
        char *res, *p;
        size_t totlen;
        int i, n;
@@ -602,8 +619,8 @@ ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag,
                return (-1);
        }
 
-       debug("EXPORT  table = %p, htab.size = %d, htab.filled = %d, "
-               "size = %zu\n", htab, htab->size, htab->filled, size);
+       debug("EXPORT  table = %p, htab.size = %d, htab.filled = %d, size = %lu\n",
+             htab, htab->size, htab->filled, (ulong)size);
        /*
         * Pass 1:
         * search used entries,
@@ -612,7 +629,7 @@ ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag,
        for (i = 1, n = 0, totlen = 0; i <= htab->size; ++i) {
 
                if (htab->table[i].used > 0) {
-                       ENTRY *ep = &htab->table[i].entry;
+                       struct env_entry *ep = &htab->table[i].entry;
                        int found = match_entry(ep, flag, argc, argv);
 
                        if ((argc > 0) && (found == 0))
@@ -623,7 +640,7 @@ ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag,
 
                        list[n++] = ep;
 
-                       totlen += strlen(ep->key) + 2;
+                       totlen += strlen(ep->key);
 
                        if (sep == '\0') {
                                totlen += strlen(ep->data);
@@ -652,13 +669,13 @@ ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag,
 #endif
 
        /* Sort list by keys */
-       qsort(list, n, sizeof(ENTRY *), cmpkey);
+       qsort(list, n, sizeof(struct env_entry *), cmpkey);
 
        /* Check if the user supplied buffer size is sufficient */
        if (size) {
                if (size < totlen + 1) {        /* provided buffer too small */
-                       printf("Env export buffer too small: %zu, "
-                               "but need %zu\n", size, totlen + 1);
+                       printf("Env export buffer too small: %lu, but need %lu\n",
+                              (ulong)size, (ulong)totlen + 1);
                        __set_errno(ENOMEM);
                        return (-1);
                }
@@ -750,8 +767,11 @@ static int drop_var_from_set(const char *name, int nvars, char * vars[])
  *
  * The "flag" argument can be used to control the behaviour: when the
  * H_NOCLEAR bit is set, then an existing hash table will kept, i. e.
- * new data will be added to an existing hash table; otherwise, old
- * data will be discarded and a new hash table will be created.
+ * new data will be added to an existing hash table; otherwise, if no
+ * vars are passed, old data will be discarded and a new hash table
+ * will be created. If vars are passed, passed vars that are not in
+ * the linear list of "name=value" pairs will be removed from the
+ * current hash table.
  *
  * The separator character for the "name=value" pairs can be selected,
  * so we both support importing from externally stored environment
@@ -790,7 +810,7 @@ int himport_r(struct hsearch_data *htab,
 
        /* we allocate new space to make sure we can write to the array */
        if ((data = malloc(size + 1)) == NULL) {
-               debug("himport_r: can't malloc %zu bytes\n", size + 1);
+               debug("himport_r: can't malloc %lu bytes\n", (ulong)size + 1);
                __set_errno(ENOMEM);
                return 0;
        }
@@ -802,7 +822,7 @@ int himport_r(struct hsearch_data *htab,
        if (nvars)
                memcpy(localvars, vars, sizeof(vars[0]) * nvars);
 
-       if ((flag & H_NOCLEAR) == 0) {
+       if ((flag & H_NOCLEAR) == 0 && !nvars) {
                /* Destroy old hash table if one exists */
                debug("Destroy Hash Table: %p table = %p\n", htab,
                       htab->table);
@@ -822,7 +842,7 @@ int himport_r(struct hsearch_data *htab,
         * (CONFIG_ENV_SIZE).  This heuristics will result in
         * unreasonably large numbers (and thus memory footprint) for
         * big flash environments (>8,000 entries for 64 KB
-        * envrionment size), so we clip it to a reasonable value.
+        * environment size), so we clip it to a reasonable value.
         * On the other hand we need to add some more entries for free
         * space when importing very small buffers. Both boundaries can
         * be overwritten in the board config file if needed.
@@ -861,7 +881,7 @@ int himport_r(struct hsearch_data *htab,
        }
        /* Parse environment; allow for '\0' and 'sep' as separators */
        do {
-               ENTRY e, *rv;
+               struct env_entry e, *rv;
 
                /* skip leading white space */
                while (isblank(*dp))
@@ -921,7 +941,7 @@ int himport_r(struct hsearch_data *htab,
                e.key = name;
                e.data = value;
 
-               hsearch_r(e, ENTER, &rv, htab, flag);
+               hsearch_r(e, ENV_ENTER, &rv, htab, flag);
                if (rv == NULL)
                        printf("himport_r: can't insert \"%s=%s\" into hash table\n",
                                name, value);
@@ -934,6 +954,9 @@ int himport_r(struct hsearch_data *htab,
        debug("INSERT: free(data = %p)\n", data);
        free(data);
 
+       if (flag & H_NOCLEAR)
+               goto end;
+
        /* process variables which were not considered */
        for (i = 0; i < nvars; i++) {
                if (localvars[i] == NULL)
@@ -952,6 +975,7 @@ int himport_r(struct hsearch_data *htab,
                        printf("WARNING: '%s' not in imported env, deleting it!\n", localvars[i]);
        }
 
+end:
        debug("INSERT: done\n");
        return 1;               /* everything OK */
 }
@@ -964,7 +988,7 @@ int himport_r(struct hsearch_data *htab,
  * 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 hwalk_r(struct hsearch_data *htab, int (*callback)(struct env_entry *entry))
 {
        int i;
        int retval;