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