1 /*
2  * MOC - music on console
3  * Copyright (C) 2009 Damian Pietras <daper@daper.net> and John Fitzgerald
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  */
11 
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15 
16 #include <assert.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "common.h"
22 #include "lists.h"
23 
24 struct lists_s_strs {
25 	int size;          /* Number of strings on the list */
26 	int capacity;      /* Number of allocated strings */
27 	char **strs;
28 };
29 
30 /* Allocate a new list of strings and return its address. */
lists_strs_new(int reserve)31 lists_t_strs *lists_strs_new (int reserve)
32 {
33 	lists_t_strs *result;
34 
35 	assert (reserve >= 0);
36 
37 	result = (lists_t_strs *) xmalloc (sizeof (lists_t_strs));
38 	result->size = 0;
39 	result->capacity = (reserve ? reserve : 64);
40 	result->strs = (char **) xcalloc (sizeof (char *), result->capacity);
41 
42 	return result;
43 }
44 
45 /* Clear a list to an empty state. */
lists_strs_clear(lists_t_strs * list)46 void lists_strs_clear (lists_t_strs *list)
47 {
48 	int ix;
49 
50 	assert (list);
51 
52 	for (ix = 0; ix < list->size; ix += 1)
53 		free ((void *) list->strs[ix]);
54 	list->size = 0;
55 }
56 
57 /* Free all storage associated with a list of strings. */
lists_strs_free(lists_t_strs * list)58 void lists_strs_free (lists_t_strs *list)
59 {
60 	assert (list);
61 
62 	lists_strs_clear (list);
63 	free (list->strs);
64 	free (list);
65 }
66 
67 /* Return the number of strings in a list. */
lists_strs_size(const lists_t_strs * list)68 int lists_strs_size (const lists_t_strs *list)
69 {
70 	assert (list);
71 
72 	return list->size;
73 }
74 
75 /* Return the total number of strings which could be held without growing. */
lists_strs_capacity(const lists_t_strs * list)76 int lists_strs_capacity (const lists_t_strs *list)
77 {
78 	assert (list);
79 
80 	return list->capacity;
81 }
82 
83 /* Return true iff the list has no members. */
lists_strs_empty(const lists_t_strs * list)84 bool lists_strs_empty (const lists_t_strs *list)
85 {
86 	assert (list);
87 
88 	return list->size == 0 ? true : false;
89 }
90 
91 /* Given an index, return the string at that position in a list. */
lists_strs_at(const lists_t_strs * list,int index)92 char *lists_strs_at (const lists_t_strs *list, int index)
93 {
94 	assert (list);
95 	assert (LIMIT(index, list->size));
96 
97 	return list->strs[index];
98 }
99 
100 /* Sort string list into an order determined by caller's comparitor. */
lists_strs_sort(lists_t_strs * list,lists_t_compare * compare)101 void lists_strs_sort (lists_t_strs *list, lists_t_compare *compare)
102 {
103 	assert (list);
104 	assert (compare);
105 
106 	qsort (list->strs, list->size, sizeof (char *), compare);
107 }
108 
109 /* Reverse the order of entries in a list. */
lists_strs_reverse(lists_t_strs * list)110 void lists_strs_reverse (lists_t_strs *list)
111 {
112 	int ix, iy;
113 
114 	assert (list);
115 
116 	for (ix = 0, iy = list->size - 1; ix < iy; ix += 1, iy -= 1) {
117 		char *str;
118 
119 		str = list->strs[ix];
120 		list->strs[ix] = list->strs[iy];
121 		list->strs[iy] = str;
122 	}
123 }
124 
125 /* Take a string and push it onto the end of a list
126  * (expanding the list if necessary). */
lists_strs_push(lists_t_strs * list,char * s)127 void lists_strs_push (lists_t_strs *list, char *s)
128 {
129 	assert (list);
130 	assert (s);
131 
132 	if (list->size == list->capacity) {
133 		list->capacity *= 2;
134 		list->strs = (char **) xrealloc (list->strs, list->capacity * sizeof (char *));
135 	}
136 
137 	list->strs[list->size] = s;
138 	list->size += 1;
139 }
140 
141 /* Remove the last string on the list and return it, or NULL if the list
142  * is empty. */
lists_strs_pop(lists_t_strs * list)143 char *lists_strs_pop (lists_t_strs *list)
144 {
145 	char *result;
146 
147 	assert (list);
148 
149 	result = NULL;
150 	if (list->size > 0) {
151 		list->size -= 1;
152 		result = list->strs[list->size];
153 	}
154 
155 	return result;
156 }
157 
158 /* Replace the nominated string with a new one and return the old one. */
lists_strs_swap(lists_t_strs * list,int index,char * s)159 char *lists_strs_swap (lists_t_strs *list, int index, char *s)
160 {
161 	char *result;
162 
163 	assert (list);
164 	assert (LIMIT(index, list->size));
165 	assert (s);
166 
167 	result = list->strs[index];
168 	list->strs[index] = s;
169 
170 	return result;
171 }
172 
173 /* Copy a string and append it to the end of a list. */
lists_strs_append(lists_t_strs * list,const char * s)174 void lists_strs_append (lists_t_strs *list, const char *s)
175 {
176 	char *str;
177 
178 	assert (list);
179 	assert (s);
180 
181 	str = xstrdup (s);
182 	lists_strs_push (list, str);
183 }
184 
185 /* Remove a string from the end of the list and free it. */
lists_strs_remove(lists_t_strs * list)186 void lists_strs_remove (lists_t_strs *list)
187 {
188 	char *str;
189 
190 	assert (list);
191 
192 	str = lists_strs_pop (list);
193 	if (str)
194 		free (str);
195 }
196 
197 /* Replace the nominated string with a copy of the new one
198  * and free the old one. */
lists_strs_replace(lists_t_strs * list,int index,char * s)199 void lists_strs_replace (lists_t_strs *list, int index, char *s)
200 {
201 	char *str;
202 
203 	assert (list);
204 	assert (LIMIT(index, list->size));
205 
206 	str = xstrdup (s);
207 	str = lists_strs_swap (list, index, str);
208 	free (str);
209 }
210 
211 /* Split a string at any delimiter in given string.  The resulting segments
212  * are appended to the given string list.  Returns the number of tokens
213  * appended. */
lists_strs_split(lists_t_strs * list,const char * s,const char * delim)214 int lists_strs_split (lists_t_strs *list, const char *s, const char *delim)
215 {
216 	int result;
217 	char *str, *token, *saveptr;
218 
219 	assert (list);
220 	assert (s);
221 	assert (delim);
222 
223 	result = 0;
224 	str = xstrdup (s);
225 	token = strtok_r (str, delim, &saveptr);
226 	while (token) {
227 		result += 1;
228 		lists_strs_append (list, token);
229 		token = strtok_r (NULL, delim, &saveptr);
230 	}
231 
232 	free (str);
233 	return result;
234 }
235 
236 /* Tokenise a string and append the tokens to the list.
237  * Returns the number of tokens appended. */
lists_strs_tokenise(lists_t_strs * list,const char * s)238 int lists_strs_tokenise (lists_t_strs *list, const char *s)
239 {
240 	int result;
241 
242 	assert (list);
243 	assert (s);
244 
245 	result = lists_strs_split (list, s, " \t");
246 
247 	return result;
248 }
249 
250 /* Return the concatenation of all the strings in a list using the
251  * given format for each, or NULL if the list is empty. */
lists_strs_fmt(const lists_t_strs * list,const char * fmt)252 char *lists_strs_fmt (const lists_t_strs *list, const char *fmt)
253 {
254 	int len, ix, rc;
255 	char *result, *ptr;
256 
257 	assert (list);
258 	assert (strstr (fmt, "%s"));
259 
260 	result = NULL;
261 	if (!lists_strs_empty (list)) {
262 		len = 0;
263 		for (ix = 0; ix < lists_strs_size (list); ix += 1)
264 			len += strlen (lists_strs_at (list, ix));
265 		len += ix * (strlen (fmt) - 2);
266 
267 		ptr = result = xmalloc (len + 1);
268 		for (ix = 0; ix < lists_strs_size (list); ix += 1) {
269 			rc = snprintf (ptr, len + 1, fmt, lists_strs_at (list, ix));
270 			if (rc > len)
271 				fatal ("Allocated string area was too small!");
272 			len -= rc;
273 			ptr += rc;
274 		}
275 	}
276 
277 	return result;
278 }
279 
280 /* Return the concatenation of all the strings in a list, or NULL
281  * if the list is empty. */
lists_strs_cat(const lists_t_strs * list)282 char *lists_strs_cat (const lists_t_strs *list)
283 {
284 	char *result;
285 
286 	assert (list);
287 
288 	result = lists_strs_fmt (list, "%s");
289 
290 	return result;
291 }
292 
293 /* Return a "snapshot" of the given string list.  The returned memory is a
294  * null-terminated list of pointers to the given list's strings copied into
295  * memory allocated after the pointer list.  This list is suitable for passing
296  * to functions which take such a list as an argument (e.g., evecv()).
297  * Invoking free() on the returned pointer also frees the strings. */
lists_strs_save(const lists_t_strs * list)298 char **lists_strs_save (const lists_t_strs *list)
299 {
300 	int ix, size;
301 	char *ptr, **result;
302 
303 	assert (list);
304 
305 	size = 0;
306 	for (ix = 0; ix < lists_strs_size (list); ix += 1)
307 		size += strlen (lists_strs_at (list, ix)) + 1;
308 	size += sizeof (char *) * (lists_strs_size (list) + 1);
309 	result = (char **) xmalloc (size);
310 	ptr = (char *) (result + lists_strs_size (list) + 1);
311 	for (ix = 0; ix < lists_strs_size (list); ix += 1) {
312 		strcpy (ptr, lists_strs_at (list, ix));
313 		result[ix] = ptr;
314 		ptr += strlen (ptr) + 1;
315 	}
316 	result[ix] = NULL;
317 
318 	return result;
319 }
320 
321 /* Reload saved strings into a list.  The reloaded strings are appended
322  * to the list.  The number of items reloaded is returned. */
lists_strs_load(lists_t_strs * list,char ** saved)323 int lists_strs_load (lists_t_strs *list, char **saved)
324 {
325 	int size;
326 
327 	assert (list);
328 	assert (saved);
329 
330 	size = lists_strs_size (list);
331 	while (*saved)
332 		lists_strs_append (list, *saved++);
333 
334 	return lists_strs_size (list) - size;
335 }
336 
337 /* Given a string, return the index of the first list entry which matches
338  * it.  If not found, return the total number of entries.
339  * The comparison is case-insensitive. */
lists_strs_find(lists_t_strs * list,const char * sought)340 int lists_strs_find (lists_t_strs *list, const char *sought)
341 {
342 	int result;
343 
344 	assert (list);
345 	assert (sought);
346 
347 	for (result = 0; result < lists_strs_size (list); result += 1) {
348 		if (!strcasecmp (lists_strs_at (list, result), sought))
349 			break;
350 	}
351 
352 	return result;
353 }
354 
355 /* Given a string, return true iff it exists in the list. */
lists_strs_exists(lists_t_strs * list,const char * sought)356 bool lists_strs_exists (lists_t_strs *list, const char *sought)
357 {
358 	bool result = false;
359 
360 	assert (list);
361 	assert (sought);
362 
363 	if (lists_strs_find (list, sought) < lists_strs_size (list))
364 		result = true;
365 
366 	return result;
367 }
368