Add support for per-variable callbacks to the "hashtable" functions. Signed-off-by: Joe Hershberger <joe.hershberger@ni.com> !!!fix comment in callbackmaster
parent
be11235ab8
commit
170ab11075
@ -0,0 +1,222 @@ |
|||||||
|
/*
|
||||||
|
* (C) Copyright 2012 |
||||||
|
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||||
|
* |
||||||
|
* See file CREDITS for list of people who contributed to this |
||||||
|
* project. |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU General Public License as |
||||||
|
* published by the Free Software Foundation; either version 2 of |
||||||
|
* the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||||
|
* MA 02111-1307 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <env_attr.h> |
||||||
|
#include <errno.h> |
||||||
|
#include <linux/string.h> |
||||||
|
#include <malloc.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate through the whole list calling the callback for each found element. |
||||||
|
* "attr_list" takes the form: |
||||||
|
* attributes = [^,:\s]* |
||||||
|
* entry = name[:attributes] |
||||||
|
* list = entry[,list] |
||||||
|
*/ |
||||||
|
int env_attr_walk(const char *attr_list, |
||||||
|
int (*callback)(const char *name, const char *attributes)) |
||||||
|
{ |
||||||
|
const char *entry, *entry_end; |
||||||
|
char *name, *attributes; |
||||||
|
|
||||||
|
if (!attr_list) |
||||||
|
/* list not found */ |
||||||
|
return 1; |
||||||
|
|
||||||
|
entry = attr_list; |
||||||
|
do { |
||||||
|
char *entry_cpy = NULL; |
||||||
|
|
||||||
|
entry_end = strchr(entry, ENV_ATTR_LIST_DELIM); |
||||||
|
/* check if this is the last entry in the list */ |
||||||
|
if (entry_end == NULL) { |
||||||
|
int entry_len = strlen(entry); |
||||||
|
|
||||||
|
if (entry_len) { |
||||||
|
/*
|
||||||
|
* allocate memory to copy the entry into since |
||||||
|
* we will need to inject '\0' chars and squash |
||||||
|
* white-space before calling the callback |
||||||
|
*/ |
||||||
|
entry_cpy = malloc(entry_len + 1); |
||||||
|
if (entry_cpy) |
||||||
|
/* copy the rest of the list */ |
||||||
|
strcpy(entry_cpy, entry); |
||||||
|
else |
||||||
|
return -ENOMEM; |
||||||
|
} |
||||||
|
} else { |
||||||
|
int entry_len = entry_end - entry; |
||||||
|
|
||||||
|
if (entry_len) { |
||||||
|
/*
|
||||||
|
* allocate memory to copy the entry into since |
||||||
|
* we will need to inject '\0' chars and squash |
||||||
|
* white-space before calling the callback |
||||||
|
*/ |
||||||
|
entry_cpy = malloc(entry_len + 1); |
||||||
|
if (entry_cpy) { |
||||||
|
/* copy just this entry and null term */ |
||||||
|
strncpy(entry_cpy, entry, entry_len); |
||||||
|
entry_cpy[entry_len] = '\0'; |
||||||
|
} else |
||||||
|
return -ENOMEM; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* check if there is anything to process (e.g. not ",,,") */ |
||||||
|
if (entry_cpy != NULL) { |
||||||
|
attributes = strchr(entry_cpy, ENV_ATTR_SEP); |
||||||
|
/* check if there is a ':' */ |
||||||
|
if (attributes != NULL) { |
||||||
|
/* replace the ':' with '\0' to term name */ |
||||||
|
*attributes++ = '\0'; |
||||||
|
/* remove white-space from attributes */ |
||||||
|
attributes = strim(attributes); |
||||||
|
} |
||||||
|
/* remove white-space from name */ |
||||||
|
name = strim(entry_cpy); |
||||||
|
|
||||||
|
/* only call the callback if there is a name */ |
||||||
|
if (strlen(name) != 0) { |
||||||
|
int retval = 0; |
||||||
|
|
||||||
|
retval = callback(name, attributes); |
||||||
|
if (retval) { |
||||||
|
free(entry_cpy); |
||||||
|
return retval; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
free(entry_cpy); |
||||||
|
entry = entry_end + 1; |
||||||
|
} while (entry_end != NULL); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Search for the last matching string in another string with the option to |
||||||
|
* start looking at a certain point (i.e. ignore anything beyond that point). |
||||||
|
*/ |
||||||
|
static char *reverse_strstr(const char *searched, const char *search_for, |
||||||
|
const char *searched_start) |
||||||
|
{ |
||||||
|
char *result = NULL; |
||||||
|
|
||||||
|
if (*search_for == '\0') |
||||||
|
return (char *)searched; |
||||||
|
|
||||||
|
for (;;) { |
||||||
|
char *match = strstr(searched, search_for); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop looking if no new match is found or looking past the |
||||||
|
* searched_start pointer |
||||||
|
*/ |
||||||
|
if (match == NULL || (searched_start != NULL && |
||||||
|
match + strlen(search_for) > searched_start)) |
||||||
|
break; |
||||||
|
|
||||||
|
result = match; |
||||||
|
searched = match + 1; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve the attributes string associated with a single name in the list |
||||||
|
* There is no protection on attributes being too small for the value |
||||||
|
*/ |
||||||
|
int env_attr_lookup(const char *attr_list, const char *name, char *attributes) |
||||||
|
{ |
||||||
|
const char *entry = NULL; |
||||||
|
|
||||||
|
if (!attributes) |
||||||
|
/* bad parameter */ |
||||||
|
return -1; |
||||||
|
if (!attr_list) |
||||||
|
/* list not found */ |
||||||
|
return 1; |
||||||
|
|
||||||
|
entry = reverse_strstr(attr_list, name, NULL); |
||||||
|
while (entry != NULL) { |
||||||
|
const char *prevch = entry - 1; |
||||||
|
const char *nextch = entry + strlen(name); |
||||||
|
|
||||||
|
/* Skip spaces */ |
||||||
|
while (*prevch == ' ') |
||||||
|
prevch--; |
||||||
|
while (*nextch == ' ') |
||||||
|
nextch++; |
||||||
|
|
||||||
|
/* check for an exact match */ |
||||||
|
if ((entry == attr_list || |
||||||
|
*prevch == ENV_ATTR_LIST_DELIM) && |
||||||
|
(*nextch == ENV_ATTR_SEP || |
||||||
|
*nextch == ENV_ATTR_LIST_DELIM || |
||||||
|
*nextch == '\0')) |
||||||
|
break; |
||||||
|
|
||||||
|
entry = reverse_strstr(attr_list, name, entry); |
||||||
|
} |
||||||
|
if (entry != NULL) { |
||||||
|
int len; |
||||||
|
|
||||||
|
/* skip the name */ |
||||||
|
entry += strlen(name); |
||||||
|
/* skip spaces */ |
||||||
|
while (*entry == ' ') |
||||||
|
entry++; |
||||||
|
if (*entry != ENV_ATTR_SEP) |
||||||
|
len = 0; |
||||||
|
else { |
||||||
|
const char *delim; |
||||||
|
static const char delims[] = { |
||||||
|
ENV_ATTR_LIST_DELIM, ' ', '\0'}; |
||||||
|
|
||||||
|
/* skip the attr sep */ |
||||||
|
entry += 1; |
||||||
|
/* skip spaces */ |
||||||
|
while (*entry == ' ') |
||||||
|
entry++; |
||||||
|
|
||||||
|
delim = strpbrk(entry, delims); |
||||||
|
if (delim == NULL) |
||||||
|
len = strlen(entry); |
||||||
|
else |
||||||
|
len = delim - entry; |
||||||
|
memcpy(attributes, entry, len); |
||||||
|
} |
||||||
|
attributes[len] = '\0'; |
||||||
|
|
||||||
|
/* success */ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* not found in list */ |
||||||
|
return 2; |
||||||
|
} |
@ -0,0 +1,144 @@ |
|||||||
|
/*
|
||||||
|
* (C) Copyright 2012 |
||||||
|
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||||
|
* |
||||||
|
* See file CREDITS for list of people who contributed to this |
||||||
|
* project. |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU General Public License as |
||||||
|
* published by the Free Software Foundation; either version 2 of |
||||||
|
* the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||||
|
* MA 02111-1307 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <environment.h> |
||||||
|
|
||||||
|
#if defined(CONFIG_NEEDS_MANUAL_RELOC) |
||||||
|
DECLARE_GLOBAL_DATA_PTR; |
||||||
|
#endif |
||||||
|
|
||||||
|
/*
|
||||||
|
* Look up a callback function pointer by name |
||||||
|
*/ |
||||||
|
struct env_clbk_tbl *find_env_callback(const char *name) |
||||||
|
{ |
||||||
|
struct env_clbk_tbl *clbkp; |
||||||
|
int i; |
||||||
|
int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); |
||||||
|
|
||||||
|
if (name == NULL) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
/* look up the callback in the linker-list */ |
||||||
|
for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); |
||||||
|
i < num_callbacks; |
||||||
|
i++, clbkp++) { |
||||||
|
if (strcmp(name, clbkp->name) == 0) |
||||||
|
return clbkp; |
||||||
|
} |
||||||
|
|
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for a possible callback for a newly added variable |
||||||
|
* This is called specifically when the variable did not exist in the hash |
||||||
|
* previously, so the blanket update did not find this variable. |
||||||
|
*/ |
||||||
|
void env_callback_init(ENTRY *var_entry) |
||||||
|
{ |
||||||
|
const char *var_name = var_entry->key; |
||||||
|
const char *callback_list = getenv(ENV_CALLBACK_VAR); |
||||||
|
char callback_name[256] = ""; |
||||||
|
struct env_clbk_tbl *clbkp; |
||||||
|
int ret = 1; |
||||||
|
|
||||||
|
/* look in the ".callbacks" var for a reference to this variable */ |
||||||
|
if (callback_list != NULL) |
||||||
|
ret = env_attr_lookup(callback_list, var_name, callback_name); |
||||||
|
|
||||||
|
/* only if not found there, look in the static list */ |
||||||
|
if (ret) |
||||||
|
ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name, |
||||||
|
callback_name); |
||||||
|
|
||||||
|
/* if an association was found, set the callback pointer */ |
||||||
|
if (!ret && strlen(callback_name)) { |
||||||
|
clbkp = find_env_callback(callback_name); |
||||||
|
if (clbkp != NULL) |
||||||
|
#if defined(CONFIG_NEEDS_MANUAL_RELOC) |
||||||
|
var_entry->callback = clbkp->callback + gd->reloc_off; |
||||||
|
#else |
||||||
|
var_entry->callback = clbkp->callback; |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Called on each existing env var prior to the blanket update since removing |
||||||
|
* a callback association should remove its callback. |
||||||
|
*/ |
||||||
|
static int clear_callback(ENTRY *entry) |
||||||
|
{ |
||||||
|
entry->callback = NULL; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Call for each element in the list that associates variables to callbacks |
||||||
|
*/ |
||||||
|
static int set_callback(const char *name, const char *value) |
||||||
|
{ |
||||||
|
ENTRY e, *ep; |
||||||
|
struct env_clbk_tbl *clbkp; |
||||||
|
|
||||||
|
e.key = name; |
||||||
|
e.data = NULL; |
||||||
|
hsearch_r(e, FIND, &ep, &env_htab, 0); |
||||||
|
|
||||||
|
/* does the env variable actually exist? */ |
||||||
|
if (ep != NULL) { |
||||||
|
/* the assocaition delares no callback, so remove the pointer */ |
||||||
|
if (value == NULL || strlen(value) == 0) |
||||||
|
ep->callback = NULL; |
||||||
|
else { |
||||||
|
/* assign the requested callback */ |
||||||
|
clbkp = find_env_callback(value); |
||||||
|
if (clbkp != NULL) |
||||||
|
#if defined(CONFIG_NEEDS_MANUAL_RELOC) |
||||||
|
ep->callback = clbkp->callback + gd->reloc_off; |
||||||
|
#else |
||||||
|
ep->callback = clbkp->callback; |
||||||
|
#endif |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int on_callbacks(const char *name, const char *value, enum env_op op, |
||||||
|
int flags) |
||||||
|
{ |
||||||
|
/* remove all callbacks */ |
||||||
|
hwalk_r(&env_htab, clear_callback); |
||||||
|
|
||||||
|
/* configure any static callback bindings */ |
||||||
|
env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback); |
||||||
|
/* configure any dynamic callback bindings */ |
||||||
|
env_attr_walk(value, set_callback); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
U_BOOT_ENV_CALLBACK(callbacks, on_callbacks); |
@ -0,0 +1,55 @@ |
|||||||
|
/*
|
||||||
|
* (C) Copyright 2012 |
||||||
|
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||||
|
* |
||||||
|
* See file CREDITS for list of people who contributed to this |
||||||
|
* project. |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU General Public License as |
||||||
|
* published by the Free Software Foundation; either version 2 of |
||||||
|
* the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||||
|
* MA 02111-1307 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __ENV_ATTR_H__ |
||||||
|
#define __ENV_ATTR_H__ |
||||||
|
|
||||||
|
#define ENV_ATTR_LIST_DELIM ',' |
||||||
|
#define ENV_ATTR_SEP ':' |
||||||
|
|
||||||
|
/*
|
||||||
|
* env_attr_walk takes as input an "attr_list" that takes the form: |
||||||
|
* attributes = [^,:\s]* |
||||||
|
* entry = name[:attributes] |
||||||
|
* list = entry[,list] |
||||||
|
* It will call the "callback" function with the "name" and attribute as "value" |
||||||
|
* The callback may return a non-0 to abort the list walk. |
||||||
|
* This return value will be passed through to the caller. |
||||||
|
* 0 is returned on success. |
||||||
|
*/ |
||||||
|
extern int env_attr_walk(const char *attr_list, |
||||||
|
int (*callback)(const char *name, const char *value)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* env_attr_lookup takes as input an "attr_list" with the same form as above. |
||||||
|
* It also takes as input a "name" to look for. |
||||||
|
* If the name is found in the list, it's value is copied into "attributes". |
||||||
|
* There is no protection on attributes being too small for the value. |
||||||
|
* It returns -1 if attributes is NULL, 1 if "name" is not found, 2 if |
||||||
|
* "attr_list" is NULL. |
||||||
|
* Returns 0 on success. |
||||||
|
*/ |
||||||
|
extern int env_attr_lookup(const char *attr_list, const char *name, |
||||||
|
char *attributes); |
||||||
|
|
||||||
|
#endif /* __ENV_ATTR_H__ */ |
@ -0,0 +1,62 @@ |
|||||||
|
/*
|
||||||
|
* (C) Copyright 2012 |
||||||
|
* Joe Hershberger, National Instruments, joe.hershberger@ni.com |
||||||
|
* |
||||||
|
* See file CREDITS for list of people who contributed to this |
||||||
|
* project. |
||||||
|
* |
||||||
|
* This program is free software; you can redistribute it and/or |
||||||
|
* modify it under the terms of the GNU General Public License as |
||||||
|
* published by the Free Software Foundation; either version 2 of |
||||||
|
* the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program; if not, write to the Free Software |
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||||
|
* MA 02111-1307 USA |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __ENV_CALLBACK_H__ |
||||||
|
#define __ENV_CALLBACK_H__ |
||||||
|
|
||||||
|
#include <linker_lists.h> |
||||||
|
#include <search.h> |
||||||
|
|
||||||
|
#define ENV_CALLBACK_VAR ".callbacks" |
||||||
|
|
||||||
|
/* Board configs can define additional static callback bindings */ |
||||||
|
#ifndef CONFIG_ENV_CALLBACK_LIST_STATIC |
||||||
|
#define CONFIG_ENV_CALLBACK_LIST_STATIC |
||||||
|
#endif |
||||||
|
|
||||||
|
/*
|
||||||
|
* This list of callback bindings is static, but may be overridden by defining |
||||||
|
* a new association in the ".callbacks" environment variable. |
||||||
|
*/ |
||||||
|
#define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \ |
||||||
|
CONFIG_ENV_CALLBACK_LIST_STATIC |
||||||
|
|
||||||
|
struct env_clbk_tbl { |
||||||
|
const char *name; /* Callback name */ |
||||||
|
int (*callback)(const char *name, const char *value, enum env_op op, |
||||||
|
int flags); |
||||||
|
}; |
||||||
|
|
||||||
|
struct env_clbk_tbl *find_env_callback(const char *); |
||||||
|
void env_callback_init(ENTRY *var_entry); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Define a callback that can be associated with variables. |
||||||
|
* when associated through the ".callbacks" environment variable, the callback |
||||||
|
* will be executed any time the variable is inserted, overwritten, or deleted. |
||||||
|
*/ |
||||||
|
#define U_BOOT_ENV_CALLBACK(name, callback) \ |
||||||
|
ll_entry_declare(struct env_clbk_tbl, name, env_clbk, env_clbk) = \
|
||||||
|
{#name, callback} |
||||||
|
|
||||||
|
#endif /* __ENV_CALLBACK_H__ */ |
Loading…
Reference in new issue