added str_list_unique() and str_list_show()
[jra/samba/.git] / lib / util / util_strlist.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Jelmer Vernooij 2005
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "system/locale.h"
23
24 #undef strcasecmp
25
26 /**
27  * @file
28  * @brief String list manipulation
29  */
30
31 /**
32   build a null terminated list of strings from a input string and a
33   separator list. The separator list must contain characters less than
34   or equal to 0x2f for this to work correctly on multi-byte strings
35 */
36 _PUBLIC_ char **str_list_make(TALLOC_CTX *mem_ctx, const char *string, const char *sep)
37 {
38         int num_elements = 0;
39         char **ret = NULL;
40
41         if (sep == NULL) {
42                 sep = LIST_SEP;
43         }
44
45         ret = talloc_array(mem_ctx, char *, 1);
46         if (ret == NULL) {
47                 return NULL;
48         }
49
50         while (string && *string) {
51                 size_t len = strcspn(string, sep);
52                 char **ret2;
53                 
54                 if (len == 0) {
55                         string += strspn(string, sep);
56                         continue;
57                 }
58
59                 ret2 = talloc_realloc(mem_ctx, ret, char *, num_elements+2);
60                 if (ret2 == NULL) {
61                         talloc_free(ret);
62                         return NULL;
63                 }
64                 ret = ret2;
65
66                 ret[num_elements] = talloc_strndup(ret, string, len);
67                 if (ret[num_elements] == NULL) {
68                         talloc_free(ret);
69                         return NULL;
70                 }
71
72                 num_elements++;
73                 string += len;
74         }
75
76         ret[num_elements] = NULL;
77
78         return ret;
79 }
80
81 /**
82  * build a null terminated list of strings from an argv-like input string 
83  * Entries are seperated by spaces and can be enclosed by quotes. 
84  * Does NOT support escaping
85  */
86 _PUBLIC_ const char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *string, const char *sep)
87 {
88         int num_elements = 0;
89         const char **ret = NULL;
90
91         ret = talloc_array(mem_ctx, const char *, 1);
92         if (ret == NULL) {
93                 return NULL;
94         }
95
96         if (sep == NULL)
97                 sep = " \t\n\r";
98
99         while (string && *string) {
100                 size_t len = strcspn(string, sep);
101                 char *element;
102                 const char **ret2;
103                 
104                 if (len == 0) {
105                         string += strspn(string, sep);
106                         continue;
107                 }
108
109                 if (*string == '\"') {
110                         string++;
111                         len = strcspn(string, "\"");
112                         element = talloc_strndup(ret, string, len);
113                         string += len + 1;
114                 } else {
115                         element = talloc_strndup(ret, string, len);
116                         string += len;
117                 }
118
119                 if (element == NULL) {
120                         talloc_free(ret);
121                         return NULL;
122                 }
123
124                 ret2 = talloc_realloc(mem_ctx, ret, const char *, num_elements+2);
125                 if (ret2 == NULL) {
126                         talloc_free(ret);
127                         return NULL;
128                 }
129                 ret = ret2;
130
131                 ret[num_elements] = element;    
132
133                 num_elements++;
134         }
135
136         ret[num_elements] = NULL;
137
138         return ret;
139
140 }
141
142 /**
143  * join a list back to one string 
144  */
145 _PUBLIC_ char *str_list_join(TALLOC_CTX *mem_ctx, const char **list, char seperator)
146 {
147         char *ret = NULL;
148         int i;
149         
150         if (list[0] == NULL)
151                 return talloc_strdup(mem_ctx, "");
152
153         ret = talloc_strdup(mem_ctx, list[0]);
154
155         for (i = 1; list[i]; i++) {
156                 ret = talloc_asprintf_append_buffer(ret, "%c%s", seperator, list[i]);
157         }
158
159         return ret;
160 }
161
162 /** join a list back to one (shell-like) string; entries 
163  * seperated by spaces, using quotes where necessary */
164 _PUBLIC_ char *str_list_join_shell(TALLOC_CTX *mem_ctx, const char **list, char sep)
165 {
166         char *ret = NULL;
167         int i;
168         
169         if (list[0] == NULL)
170                 return talloc_strdup(mem_ctx, "");
171
172         if (strchr(list[0], ' ') || strlen(list[0]) == 0) 
173                 ret = talloc_asprintf(mem_ctx, "\"%s\"", list[0]);
174         else 
175                 ret = talloc_strdup(mem_ctx, list[0]);
176
177         for (i = 1; list[i]; i++) {
178                 if (strchr(list[i], ' ') || strlen(list[i]) == 0) 
179                         ret = talloc_asprintf_append_buffer(ret, "%c\"%s\"", sep, list[i]);
180                 else 
181                         ret = talloc_asprintf_append_buffer(ret, "%c%s", sep, list[i]);
182         }
183
184         return ret;
185 }
186
187 /**
188   return the number of elements in a string list
189 */
190 _PUBLIC_ size_t str_list_length(const char **list)
191 {
192         size_t ret;
193         for (ret=0;list && list[ret];ret++) /* noop */ ;
194         return ret;
195 }
196
197
198 /**
199   copy a string list
200 */
201 _PUBLIC_ char **str_list_copy(TALLOC_CTX *mem_ctx, const char **list)
202 {
203         int i;
204         char **ret;
205
206         if (list == NULL)
207                 return NULL;
208         
209         ret = talloc_array(mem_ctx, char *, str_list_length(list)+1);
210         if (ret == NULL) 
211                 return NULL;
212
213         for (i=0;list && list[i];i++) {
214                 ret[i] = talloc_strdup(ret, list[i]);
215                 if (ret[i] == NULL) {
216                         talloc_free(ret);
217                         return NULL;
218                 }
219         }
220         ret[i] = NULL;
221         return ret;
222 }
223
224 /**
225    Return true if all the elements of the list match exactly.
226  */
227 _PUBLIC_ bool str_list_equal(const char **list1, const char **list2)
228 {
229         int i;
230         
231         if (list1 == NULL || list2 == NULL) {
232                 return (list1 == list2); 
233         }
234         
235         for (i=0;list1[i] && list2[i];i++) {
236                 if (strcmp(list1[i], list2[i]) != 0) {
237                         return false;
238                 }
239         }
240         if (list1[i] || list2[i]) {
241                 return false;
242         }
243         return true;
244 }
245
246
247 /**
248   add an entry to a string list
249 */
250 _PUBLIC_ char **str_list_add(char **list, const char *s)
251 {
252         size_t len = str_list_length(list);
253         char **ret;
254
255         ret = talloc_realloc(NULL, list, char *, len+2);
256         if (ret == NULL) return NULL;
257
258         ret[len] = talloc_strdup(ret, s);
259         if (ret[len] == NULL) return NULL;
260
261         ret[len+1] = NULL;
262
263         return ret;
264 }
265
266 /**
267   remove an entry from a string list
268 */
269 _PUBLIC_ void str_list_remove(const char **list, const char *s)
270 {
271         int i;
272
273         for (i=0;list[i];i++) {
274                 if (strcmp(list[i], s) == 0) break;
275         }
276         if (!list[i]) return;
277
278         for (;list[i];i++) {
279                 list[i] = list[i+1];
280         }
281 }
282
283
284 /**
285   return true if a string is in a list
286 */
287 _PUBLIC_ bool str_list_check(const char **list, const char *s)
288 {
289         int i;
290
291         for (i=0;list[i];i++) {
292                 if (strcmp(list[i], s) == 0) return true;
293         }
294         return false;
295 }
296
297 /**
298   return true if a string is in a list, case insensitively
299 */
300 _PUBLIC_ bool str_list_check_ci(const char **list, const char *s)
301 {
302         int i;
303
304         for (i=0;list[i];i++) {
305                 if (strcasecmp(list[i], s) == 0) return true;
306         }
307         return false;
308 }
309
310
311 /**
312   append one list to another - expanding list1
313 */
314 _PUBLIC_ char **str_list_append(char **list1, const char **list2)
315 {
316         size_t len1 = str_list_length(list1);
317         size_t len2 = str_list_length(list2);
318         char **ret;
319         int i;
320
321         ret = talloc_realloc(NULL, list1, char *, len1+len2+1);
322         if (ret == NULL) return NULL;
323
324         for (i=len1;i<len1+len2;i++) {
325                 ret[i] = talloc_strdup(ret, list2[i-len1]);
326                 if (ret[i] == NULL) {
327                         return NULL;
328                 }
329         }
330         ret[i] = NULL;
331
332         return ret;
333 }
334
335 static int list_cmp(const char **el1, const char **el2)
336 {
337         return strcmp(*el1, *el2);
338 }
339
340 /*
341   return a list that only contains the unique elements of a list,
342   removing any duplicates
343  */
344 _PUBLIC_ char **str_list_unique(char **list)
345 {
346         size_t len = str_list_length(list);
347         char **list2;
348         int i, j;
349         if (len < 2) {
350                 return list;
351         }
352         list2 = (char **)talloc_memdup(list, list, sizeof(list[0])*(len+1));
353         qsort(list2, len, sizeof(list2[0]), QSORT_CAST list_cmp);
354         list[0] = list2[0];
355         for (i=j=1;i<len;i++) {
356                 if (strcmp(list2[i], list[j-1]) != 0) {
357                         list[j] = list2[i];
358                         j++;
359                 }
360         }
361         list[j] = NULL;
362         list = talloc_realloc(NULL, list, char *, j);
363         talloc_free(list2);
364         return list;
365 }
366
367 /*
368   very useful when debugging complex list related code
369  */
370 _PUBLIC_ void str_list_show(const char **list)
371 {
372         int i;
373         DEBUG(0,("{ "));
374         for (i=0;list && list[i];i++) {
375                 DEBUG(0,("\"%s\", ", list[i]));
376         }
377         DEBUG(0,("}\n"));
378 }
379