X-Git-Url: https://git.librecmc.org/?p=oweals%2Fu-boot.git;a=blobdiff_plain;f=lib%2Fhashtable.c;h=b96dbe19be4dd52e0233e9d452f3201f6c57ad69;hp=02b41050a44f6656e9605e288f5f6a97faf3539c;hb=1099b2abef35c3c887f6afac1a8ef18c7924d5d2;hpb=fa43ce842c3026c2abf19d4234d02cd4c62eeec0 diff --git a/lib/hashtable.c b/lib/hashtable.c index 02b41050a4..b96dbe19be 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -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. @@ -9,12 +10,12 @@ * Copyright (C) 1993, 1995, 1996, 1997, 2002 Free Software Foundation, Inc. * This file is part of the GNU C Library. * Contributed by Ulrich Drepper , 1993. - * - * SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include +#include #ifdef USE_HOSTCC /* HOST build */ # include @@ -41,6 +42,9 @@ #define CONFIG_ENV_MAX_ENTRIES 512 #endif +#define USED_FREE 0 +#define USED_DELETED -1 + #include #include #include @@ -57,14 +61,14 @@ * 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;