env: Add support for callbacks to environment vars
[kernel/u-boot.git] / common / env_attr.c
1 /*
2  * (C) Copyright 2012
3  * Joe Hershberger, National Instruments, joe.hershberger@ni.com
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <env_attr.h>
26 #include <errno.h>
27 #include <linux/string.h>
28 #include <malloc.h>
29
30 /*
31  * Iterate through the whole list calling the callback for each found element.
32  * "attr_list" takes the form:
33  *      attributes = [^,:\s]*
34  *      entry = name[:attributes]
35  *      list = entry[,list]
36  */
37 int env_attr_walk(const char *attr_list,
38         int (*callback)(const char *name, const char *attributes))
39 {
40         const char *entry, *entry_end;
41         char *name, *attributes;
42
43         if (!attr_list)
44                 /* list not found */
45                 return 1;
46
47         entry = attr_list;
48         do {
49                 char *entry_cpy = NULL;
50
51                 entry_end = strchr(entry, ENV_ATTR_LIST_DELIM);
52                 /* check if this is the last entry in the list */
53                 if (entry_end == NULL) {
54                         int entry_len = strlen(entry);
55
56                         if (entry_len) {
57                                 /*
58                                  * allocate memory to copy the entry into since
59                                  * we will need to inject '\0' chars and squash
60                                  * white-space before calling the callback
61                                  */
62                                 entry_cpy = malloc(entry_len + 1);
63                                 if (entry_cpy)
64                                         /* copy the rest of the list */
65                                         strcpy(entry_cpy, entry);
66                                 else
67                                         return -ENOMEM;
68                         }
69                 } else {
70                         int entry_len = entry_end - entry;
71
72                         if (entry_len) {
73                                 /*
74                                  * allocate memory to copy the entry into since
75                                  * we will need to inject '\0' chars and squash
76                                  * white-space before calling the callback
77                                  */
78                                 entry_cpy = malloc(entry_len + 1);
79                                 if (entry_cpy) {
80                                         /* copy just this entry and null term */
81                                         strncpy(entry_cpy, entry, entry_len);
82                                         entry_cpy[entry_len] = '\0';
83                                 } else
84                                         return -ENOMEM;
85                         }
86                 }
87
88                 /* check if there is anything to process (e.g. not ",,,") */
89                 if (entry_cpy != NULL) {
90                         attributes = strchr(entry_cpy, ENV_ATTR_SEP);
91                         /* check if there is a ':' */
92                         if (attributes != NULL) {
93                                 /* replace the ':' with '\0' to term name */
94                                 *attributes++ = '\0';
95                                 /* remove white-space from attributes */
96                                 attributes = strim(attributes);
97                         }
98                         /* remove white-space from name */
99                         name = strim(entry_cpy);
100
101                         /* only call the callback if there is a name */
102                         if (strlen(name) != 0) {
103                                 int retval = 0;
104
105                                 retval = callback(name, attributes);
106                                 if (retval) {
107                                         free(entry_cpy);
108                                         return retval;
109                                 }
110                         }
111                 }
112
113                 free(entry_cpy);
114                 entry = entry_end + 1;
115         } while (entry_end != NULL);
116
117         return 0;
118 }
119
120 /*
121  * Search for the last matching string in another string with the option to
122  * start looking at a certain point (i.e. ignore anything beyond that point).
123  */
124 static char *reverse_strstr(const char *searched, const char *search_for,
125         const char *searched_start)
126 {
127         char *result = NULL;
128
129         if (*search_for == '\0')
130                 return (char *)searched;
131
132         for (;;) {
133                 char *match = strstr(searched, search_for);
134
135                 /*
136                  * Stop looking if no new match is found or looking past the
137                  * searched_start pointer
138                  */
139                 if (match == NULL || (searched_start != NULL &&
140                     match + strlen(search_for) > searched_start))
141                         break;
142
143                 result = match;
144                 searched = match + 1;
145         }
146
147         return result;
148 }
149
150 /*
151  * Retrieve the attributes string associated with a single name in the list
152  * There is no protection on attributes being too small for the value
153  */
154 int env_attr_lookup(const char *attr_list, const char *name, char *attributes)
155 {
156         const char *entry = NULL;
157
158         if (!attributes)
159                 /* bad parameter */
160                 return -1;
161         if (!attr_list)
162                 /* list not found */
163                 return 1;
164
165         entry = reverse_strstr(attr_list, name, NULL);
166         while (entry != NULL) {
167                 const char *prevch = entry - 1;
168                 const char *nextch = entry + strlen(name);
169
170                 /* Skip spaces */
171                 while (*prevch == ' ')
172                         prevch--;
173                 while (*nextch == ' ')
174                         nextch++;
175
176                 /* check for an exact match */
177                 if ((entry == attr_list ||
178                      *prevch == ENV_ATTR_LIST_DELIM) &&
179                     (*nextch == ENV_ATTR_SEP ||
180                      *nextch == ENV_ATTR_LIST_DELIM ||
181                      *nextch == '\0'))
182                         break;
183
184                 entry = reverse_strstr(attr_list, name, entry);
185         }
186         if (entry != NULL) {
187                 int len;
188
189                 /* skip the name */
190                 entry += strlen(name);
191                 /* skip spaces */
192                 while (*entry == ' ')
193                         entry++;
194                 if (*entry != ENV_ATTR_SEP)
195                         len = 0;
196                 else {
197                         const char *delim;
198                         static const char delims[] = {
199                                 ENV_ATTR_LIST_DELIM, ' ', '\0'};
200
201                         /* skip the attr sep */
202                         entry += 1;
203                         /* skip spaces */
204                         while (*entry == ' ')
205                                 entry++;
206
207                         delim = strpbrk(entry, delims);
208                         if (delim == NULL)
209                                 len = strlen(entry);
210                         else
211                                 len = delim - entry;
212                         memcpy(attributes, entry, len);
213                 }
214                 attributes[len] = '\0';
215
216                 /* success */
217                 return 0;
218         }
219
220         /* not found in list */
221         return 2;
222 }