1 #include "cache.h"
2 #include "string-list.h"
3 
string_list_init(struct string_list * list,int strdup_strings)4 void string_list_init(struct string_list *list, int strdup_strings)
5 {
6 	memset(list, 0, sizeof(*list));
7 	list->strdup_strings = strdup_strings;
8 }
9 
10 /* if there is no exact match, point to the index where the entry could be
11  * inserted */
get_entry_index(const struct string_list * list,const char * string,int * exact_match)12 static int get_entry_index(const struct string_list *list, const char *string,
13 		int *exact_match)
14 {
15 	int left = -1, right = list->nr;
16 	compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
17 
18 	while (left + 1 < right) {
19 		int middle = left + (right - left) / 2;
20 		int compare = cmp(string, list->items[middle].string);
21 		if (compare < 0)
22 			right = middle;
23 		else if (compare > 0)
24 			left = middle;
25 		else {
26 			*exact_match = 1;
27 			return middle;
28 		}
29 	}
30 
31 	*exact_match = 0;
32 	return right;
33 }
34 
35 /* returns -1-index if already exists */
add_entry(int insert_at,struct string_list * list,const char * string)36 static int add_entry(int insert_at, struct string_list *list, const char *string)
37 {
38 	int exact_match = 0;
39 	int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match);
40 
41 	if (exact_match)
42 		return -1 - index;
43 
44 	ALLOC_GROW(list->items, list->nr+1, list->alloc);
45 	if (index < list->nr)
46 		MOVE_ARRAY(list->items + index + 1, list->items + index,
47 			   list->nr - index);
48 	list->items[index].string = list->strdup_strings ?
49 		xstrdup(string) : (char *)string;
50 	list->items[index].util = NULL;
51 	list->nr++;
52 
53 	return index;
54 }
55 
string_list_insert(struct string_list * list,const char * string)56 struct string_list_item *string_list_insert(struct string_list *list, const char *string)
57 {
58 	int index = add_entry(-1, list, string);
59 
60 	if (index < 0)
61 		index = -1 - index;
62 
63 	return list->items + index;
64 }
65 
string_list_remove(struct string_list * list,const char * string,int free_util)66 void string_list_remove(struct string_list *list, const char *string,
67 			int free_util)
68 {
69 	int exact_match;
70 	int i = get_entry_index(list, string, &exact_match);
71 
72 	if (exact_match) {
73 		if (list->strdup_strings)
74 			free(list->items[i].string);
75 		if (free_util)
76 			free(list->items[i].util);
77 
78 		list->nr--;
79 		MOVE_ARRAY(list->items + i, list->items + i + 1, list->nr - i);
80 	}
81 }
82 
string_list_has_string(const struct string_list * list,const char * string)83 int string_list_has_string(const struct string_list *list, const char *string)
84 {
85 	int exact_match;
86 	get_entry_index(list, string, &exact_match);
87 	return exact_match;
88 }
89 
string_list_find_insert_index(const struct string_list * list,const char * string,int negative_existing_index)90 int string_list_find_insert_index(const struct string_list *list, const char *string,
91 				  int negative_existing_index)
92 {
93 	int exact_match;
94 	int index = get_entry_index(list, string, &exact_match);
95 	if (exact_match)
96 		index = -1 - (negative_existing_index ? index : 0);
97 	return index;
98 }
99 
string_list_lookup(struct string_list * list,const char * string)100 struct string_list_item *string_list_lookup(struct string_list *list, const char *string)
101 {
102 	int exact_match, i = get_entry_index(list, string, &exact_match);
103 	if (!exact_match)
104 		return NULL;
105 	return list->items + i;
106 }
107 
string_list_remove_duplicates(struct string_list * list,int free_util)108 void string_list_remove_duplicates(struct string_list *list, int free_util)
109 {
110 	if (list->nr > 1) {
111 		int src, dst;
112 		compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
113 		for (src = dst = 1; src < list->nr; src++) {
114 			if (!cmp(list->items[dst - 1].string, list->items[src].string)) {
115 				if (list->strdup_strings)
116 					free(list->items[src].string);
117 				if (free_util)
118 					free(list->items[src].util);
119 			} else
120 				list->items[dst++] = list->items[src];
121 		}
122 		list->nr = dst;
123 	}
124 }
125 
for_each_string_list(struct string_list * list,string_list_each_func_t fn,void * cb_data)126 int for_each_string_list(struct string_list *list,
127 			 string_list_each_func_t fn, void *cb_data)
128 {
129 	int i, ret = 0;
130 	for (i = 0; i < list->nr; i++)
131 		if ((ret = fn(&list->items[i], cb_data)))
132 			break;
133 	return ret;
134 }
135 
filter_string_list(struct string_list * list,int free_util,string_list_each_func_t want,void * cb_data)136 void filter_string_list(struct string_list *list, int free_util,
137 			string_list_each_func_t want, void *cb_data)
138 {
139 	int src, dst = 0;
140 	for (src = 0; src < list->nr; src++) {
141 		if (want(&list->items[src], cb_data)) {
142 			list->items[dst++] = list->items[src];
143 		} else {
144 			if (list->strdup_strings)
145 				free(list->items[src].string);
146 			if (free_util)
147 				free(list->items[src].util);
148 		}
149 	}
150 	list->nr = dst;
151 }
152 
item_is_not_empty(struct string_list_item * item,void * unused)153 static int item_is_not_empty(struct string_list_item *item, void *unused)
154 {
155 	return *item->string != '\0';
156 }
157 
string_list_remove_empty_items(struct string_list * list,int free_util)158 void string_list_remove_empty_items(struct string_list *list, int free_util)
159 {
160 	filter_string_list(list, free_util, item_is_not_empty, NULL);
161 }
162 
string_list_clear(struct string_list * list,int free_util)163 void string_list_clear(struct string_list *list, int free_util)
164 {
165 	if (list->items) {
166 		int i;
167 		if (list->strdup_strings) {
168 			for (i = 0; i < list->nr; i++)
169 				free(list->items[i].string);
170 		}
171 		if (free_util) {
172 			for (i = 0; i < list->nr; i++)
173 				free(list->items[i].util);
174 		}
175 		free(list->items);
176 	}
177 	list->items = NULL;
178 	list->nr = list->alloc = 0;
179 }
180 
string_list_clear_func(struct string_list * list,string_list_clear_func_t clearfunc)181 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc)
182 {
183 	if (list->items) {
184 		int i;
185 		if (clearfunc) {
186 			for (i = 0; i < list->nr; i++)
187 				clearfunc(list->items[i].util, list->items[i].string);
188 		}
189 		if (list->strdup_strings) {
190 			for (i = 0; i < list->nr; i++)
191 				free(list->items[i].string);
192 		}
193 		free(list->items);
194 	}
195 	list->items = NULL;
196 	list->nr = list->alloc = 0;
197 }
198 
string_list_append_nodup(struct string_list * list,char * string)199 struct string_list_item *string_list_append_nodup(struct string_list *list,
200 						  char *string)
201 {
202 	struct string_list_item *retval;
203 	ALLOC_GROW(list->items, list->nr + 1, list->alloc);
204 	retval = &list->items[list->nr++];
205 	retval->string = string;
206 	retval->util = NULL;
207 	return retval;
208 }
209 
string_list_append(struct string_list * list,const char * string)210 struct string_list_item *string_list_append(struct string_list *list,
211 					    const char *string)
212 {
213 	return string_list_append_nodup(
214 			list,
215 			list->strdup_strings ? xstrdup(string) : (char *)string);
216 }
217 
218 /*
219  * Encapsulate the compare function pointer because ISO C99 forbids
220  * casting from void * to a function pointer and vice versa.
221  */
222 struct string_list_sort_ctx
223 {
224 	compare_strings_fn cmp;
225 };
226 
cmp_items(const void * a,const void * b,void * ctx)227 static int cmp_items(const void *a, const void *b, void *ctx)
228 {
229 	struct string_list_sort_ctx *sort_ctx = ctx;
230 	const struct string_list_item *one = a;
231 	const struct string_list_item *two = b;
232 	return sort_ctx->cmp(one->string, two->string);
233 }
234 
string_list_sort(struct string_list * list)235 void string_list_sort(struct string_list *list)
236 {
237 	struct string_list_sort_ctx sort_ctx = {list->cmp ? list->cmp : strcmp};
238 
239 	QSORT_S(list->items, list->nr, cmp_items, &sort_ctx);
240 }
241 
unsorted_string_list_lookup(struct string_list * list,const char * string)242 struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
243 						     const char *string)
244 {
245 	struct string_list_item *item;
246 	compare_strings_fn cmp = list->cmp ? list->cmp : strcmp;
247 
248 	for_each_string_list_item(item, list)
249 		if (!cmp(string, item->string))
250 			return item;
251 	return NULL;
252 }
253 
unsorted_string_list_has_string(struct string_list * list,const char * string)254 int unsorted_string_list_has_string(struct string_list *list,
255 				    const char *string)
256 {
257 	return unsorted_string_list_lookup(list, string) != NULL;
258 }
259 
unsorted_string_list_delete_item(struct string_list * list,int i,int free_util)260 void unsorted_string_list_delete_item(struct string_list *list, int i, int free_util)
261 {
262 	if (list->strdup_strings)
263 		free(list->items[i].string);
264 	if (free_util)
265 		free(list->items[i].util);
266 	list->items[i] = list->items[list->nr-1];
267 	list->nr--;
268 }
269 
string_list_split(struct string_list * list,const char * string,int delim,int maxsplit)270 int string_list_split(struct string_list *list, const char *string,
271 		      int delim, int maxsplit)
272 {
273 	int count = 0;
274 	const char *p = string, *end;
275 
276 	if (!list->strdup_strings)
277 		die("internal error in string_list_split(): "
278 		    "list->strdup_strings must be set");
279 	for (;;) {
280 		count++;
281 		if (maxsplit >= 0 && count > maxsplit) {
282 			string_list_append(list, p);
283 			return count;
284 		}
285 		end = strchr(p, delim);
286 		if (end) {
287 			string_list_append_nodup(list, xmemdupz(p, end - p));
288 			p = end + 1;
289 		} else {
290 			string_list_append(list, p);
291 			return count;
292 		}
293 	}
294 }
295 
string_list_split_in_place(struct string_list * list,char * string,int delim,int maxsplit)296 int string_list_split_in_place(struct string_list *list, char *string,
297 			       int delim, int maxsplit)
298 {
299 	int count = 0;
300 	char *p = string, *end;
301 
302 	if (list->strdup_strings)
303 		die("internal error in string_list_split_in_place(): "
304 		    "list->strdup_strings must not be set");
305 	for (;;) {
306 		count++;
307 		if (maxsplit >= 0 && count > maxsplit) {
308 			string_list_append(list, p);
309 			return count;
310 		}
311 		end = strchr(p, delim);
312 		if (end) {
313 			*end = '\0';
314 			string_list_append(list, p);
315 			p = end + 1;
316 		} else {
317 			string_list_append(list, p);
318 			return count;
319 		}
320 	}
321 }
322