1 /***********************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 #ifdef HAVE_CONFIG_H
14 #include <fc_config.h>
15 #endif
16 
17 #include <stdlib.h>             /* qsort() */
18 #include <string.h>
19 
20 /* utility */
21 #include "astring.h"
22 #include "mem.h"
23 #include "shared.h"
24 #include "support.h"
25 
26 #include "string_vector.h"
27 
28 /* The string vector structure. */
29 struct strvec {
30   char **vec;
31   size_t size;
32 };
33 
34 /**************************************************************************
35   Free a string.
36 **************************************************************************/
string_free(char * string)37 static void string_free(char *string)
38 {
39   if (string) {
40     free(string);
41   }
42 }
43 
44 /**************************************************************************
45   Duplicate a string.
46 **************************************************************************/
string_duplicate(const char * string)47 static char *string_duplicate(const char *string)
48 {
49   if (string) {
50     return fc_strdup(string);
51   }
52   return NULL;
53 }
54 
55 /**************************************************************************
56   Create a new string vector.
57 **************************************************************************/
strvec_new(void)58 struct strvec *strvec_new(void)
59 {
60   struct strvec *psv = fc_malloc(sizeof(struct strvec));
61 
62   psv->vec = NULL;
63   psv->size = 0;
64 
65   return psv;
66 }
67 
68 /**************************************************************************
69   Destroy a string vector.
70 **************************************************************************/
strvec_destroy(struct strvec * psv)71 void strvec_destroy(struct strvec *psv)
72 {
73   strvec_clear(psv);
74   free(psv);
75 }
76 
77 /**************************************************************************
78   Set the size of the vector.
79 **************************************************************************/
strvec_reserve(struct strvec * psv,size_t reserve)80 void strvec_reserve(struct strvec *psv, size_t reserve)
81 {
82   if (reserve == psv->size) {
83     return;
84   } else if (reserve == 0) {
85     strvec_clear(psv);
86     return;
87   } else if (!psv->vec) {
88     /* Initial reserve */
89     psv->vec = fc_calloc(reserve, sizeof(char *));
90   } else if (reserve > psv->size) {
91     /* Expand the vector. */
92     psv->vec = fc_realloc(psv->vec, reserve * sizeof(char *));
93     memset(psv->vec + psv->size, 0, (reserve - psv->size) * sizeof(char *));
94   } else {
95     /* Shrink the vector: free the extra strings. */
96     size_t i;
97 
98     for (i = psv->size - 1; i >= reserve; i--) {
99       string_free(psv->vec[i]);
100     }
101     psv->vec = fc_realloc(psv->vec, reserve * sizeof(char *));
102   }
103   psv->size = reserve;
104 }
105 
106 /**************************************************************************
107   Stores the string vector from a normal vector. If size == -1, it will
108   assume it is a NULL terminated vector.
109 **************************************************************************/
strvec_store(struct strvec * psv,const char * const * vec,size_t size)110 void strvec_store(struct strvec *psv, const char *const *vec, size_t size)
111 {
112   if (size == (size_t) -1) {
113     strvec_clear(psv);
114     for (; *vec; vec++) {
115       strvec_append(psv, *vec);
116     }
117   } else {
118     size_t i;
119 
120     strvec_reserve(psv, size);
121     for (i = 0; i < size; i++, vec++) {
122       strvec_set(psv, i, *vec);
123     }
124   }
125 }
126 
127 /****************************************************************************
128   Build the string vector from a string until 'str_size' bytes are read.
129   Passing -1 for 'str_size' will assume 'str' as the expected format. Note
130   it's a bit dangerous.
131 
132   This string format is a list of strings separated by 'separator'.
133 
134   See also strvec_to_str().
135 ****************************************************************************/
strvec_from_str(struct strvec * psv,char separator,const char * str)136 void strvec_from_str(struct strvec *psv, char separator, const char *str)
137 {
138   const char *p;
139   char *new_str;
140 
141   strvec_clear(psv);
142   while ((p = strchr(str, separator))) {
143     new_str = fc_malloc(p - str + 1);
144     memcpy(new_str, str, p - str);
145     new_str[p - str] = '\0';
146     psv->size++;
147     psv->vec = fc_realloc(psv->vec, psv->size * sizeof(char *));
148     psv->vec[psv->size - 1] = new_str;
149     str = p + 1;
150   }
151   if ('\0' != *str) {
152     strvec_append(psv, str);
153   }
154 }
155 
156 /**************************************************************************
157   Remove all strings from the vector.
158 **************************************************************************/
strvec_clear(struct strvec * psv)159 void strvec_clear(struct strvec *psv)
160 {
161   size_t i;
162   char **p;
163 
164   if (!psv->vec) {
165     return;
166   }
167 
168   for (i = 0, p = psv->vec; i < psv->size; i++, p++) {
169     string_free(*p);
170   }
171   free(psv->vec);
172   psv->vec = NULL;
173   psv->size = 0;
174 }
175 
176 /**************************************************************************
177   Remove strings which are duplicated inside the vector.
178 **************************************************************************/
strvec_remove_duplicate(struct strvec * psv,int (* cmp_func)(const char *,const char *))179 void strvec_remove_duplicate(struct strvec *psv,
180                              int (*cmp_func) (const char *, const char *))
181 {
182   size_t i, j;
183   const char *str1, *str2;
184 
185   if (!psv->vec || 1 == psv->size) {
186     return;
187   }
188 
189   for (i = 1; i < psv->size; i++) {
190     if ((str1 = psv->vec[i])) {
191       for (j = 0; j < i; j++) {
192         if ((str2 = psv->vec[j])
193             && 0 == cmp_func(str2, str1)) {
194           strvec_remove(psv, i);
195           i--;
196           break;
197         }
198       }
199     }
200   }
201 }
202 
203 /**************************************************************************
204   Remove all empty strings from the vector and removes all leading and
205   trailing spaces.
206 **************************************************************************/
strvec_remove_empty(struct strvec * psv)207 void strvec_remove_empty(struct strvec *psv)
208 {
209   size_t i;
210   char *str;
211 
212   if (!psv->vec) {
213     return;
214   }
215 
216   for (i = 0; i < psv->size;) {
217     str = psv->vec[i];
218 
219     if (!str) {
220       strvec_remove(psv, i);
221       continue;
222     }
223 
224     remove_leading_trailing_spaces(str);
225     if (str[0] == '\0') {
226       strvec_remove(psv, i);
227       continue;
228     }
229 
230     i++;
231   }
232 }
233 
234 /**************************************************************************
235   Copy a string vector.
236 **************************************************************************/
strvec_copy(struct strvec * dest,const struct strvec * src)237 void strvec_copy(struct strvec *dest, const struct strvec *src)
238 {
239   size_t i;
240   char **p;
241   char *const *l;
242 
243   if (!src->vec) {
244     strvec_clear(dest);
245     return;
246   }
247 
248   strvec_reserve(dest, src->size);
249   for (i = 0, p = dest->vec, l = src->vec; i < dest->size; i++, p++, l++) {
250     string_free(*p);
251     *p = string_duplicate(*l);
252   }
253 }
254 
255 /**************************************************************************
256   Sort the string vector, using qsort().
257 **************************************************************************/
strvec_sort(struct strvec * psv,int (* sort_func)(const char * const *,const char * const *))258 void strvec_sort(struct strvec *psv, int (*sort_func) (const char *const *,
259                                                        const char *const *))
260 {
261   qsort(psv->vec, psv->size, sizeof(const char *),
262         (int (*) (const void *, const void *)) sort_func);
263 }
264 
265 /**************************************************************************
266   Insert a string at the start of the vector.
267 **************************************************************************/
strvec_prepend(struct strvec * psv,const char * string)268 void strvec_prepend(struct strvec *psv, const char *string)
269 {
270   strvec_reserve(psv, psv->size + 1);
271   memmove(psv->vec + 1, psv->vec, (psv->size - 1) * sizeof(char *));
272   psv->vec[0] = string_duplicate(string);
273 }
274 
275 /**************************************************************************
276   Insert a string at the end of the vector.
277 **************************************************************************/
strvec_append(struct strvec * psv,const char * string)278 void strvec_append(struct strvec *psv, const char *string)
279 {
280   strvec_reserve(psv, psv->size + 1);
281   psv->vec[psv->size - 1] = string_duplicate(string);
282 }
283 
284 /**************************************************************************
285   Insert a string at the index of the vector.
286 **************************************************************************/
strvec_insert(struct strvec * psv,size_t svindex,const char * string)287 void strvec_insert(struct strvec *psv, size_t svindex, const char *string)
288 {
289   if (svindex <= 0) {
290     strvec_prepend(psv, string);
291   } else if (svindex >= psv->size) {
292     strvec_append(psv, string);
293   } else {
294     strvec_reserve(psv, psv->size + 1);
295     memmove(psv->vec + svindex + 1, psv->vec + svindex,
296             (psv->size - svindex - 1) * sizeof(char *));
297     psv->vec[svindex] = string_duplicate(string);
298   }
299 }
300 
301 /**************************************************************************
302   Replace a string at the index of the vector.
303   Returns TRUE if the element has been really set.
304 **************************************************************************/
strvec_set(struct strvec * psv,size_t svindex,const char * string)305 bool strvec_set(struct strvec *psv, size_t svindex, const char *string)
306 {
307   if (strvec_index_valid(psv, svindex)) {
308     string_free(psv->vec[svindex]);
309     psv->vec[svindex] = string_duplicate(string);
310     return TRUE;
311   }
312   return FALSE;
313 }
314 
315 /**************************************************************************
316   Remove the string at the index from the vector.
317   Returns TRUE if the element has been really removed.
318 **************************************************************************/
strvec_remove(struct strvec * psv,size_t svindex)319 bool strvec_remove(struct strvec *psv, size_t svindex)
320 {
321   if (!strvec_index_valid(psv, svindex)) {
322     return FALSE;
323   }
324 
325   if (psv->size == 1) {
326     /* It is the last. */
327     strvec_clear(psv);
328     return TRUE;
329   }
330 
331   string_free(psv->vec[svindex]);
332   memmove(psv->vec + svindex, psv->vec + svindex + 1,
333           (psv->size - svindex - 1) * sizeof(char *));
334   psv->vec[psv->size - 1] = NULL; /* Do not attempt to free this data. */
335   strvec_reserve(psv, psv->size - 1);
336 
337   return TRUE;
338 }
339 
340 /**************************************************************************
341   Returns the size of the vector.
342 **************************************************************************/
strvec_size(const struct strvec * psv)343 size_t strvec_size(const struct strvec *psv)
344 {
345   return psv->size;
346 }
347 
348 /**************************************************************************
349   Returns the datas of the vector.
350 **************************************************************************/
strvec_data(const struct strvec * psv)351 const char *const *strvec_data(const struct strvec *psv)
352 {
353   return (const char **) psv->vec;
354 }
355 
356 /**************************************************************************
357   Returns TRUE if the index is valid.
358 **************************************************************************/
strvec_index_valid(const struct strvec * psv,size_t svindex)359 bool strvec_index_valid(const struct strvec *psv, size_t svindex)
360 {
361   return svindex >= 0 && svindex < psv->size;
362 }
363 
364 /**************************************************************************
365   Returns the string at the index of the vector.
366 **************************************************************************/
strvec_get(const struct strvec * psv,size_t svindex)367 const char *strvec_get(const struct strvec *psv, size_t svindex)
368 {
369   return strvec_index_valid(psv, svindex) ? psv->vec[svindex] : NULL;
370 }
371 
372 /****************************************************************************
373   Build the string from a string vector.
374 
375   This string format is a list of strings separated by 'separator'.
376 
377   See also strvec_from_str().
378 ****************************************************************************/
strvec_to_str(const struct strvec * psv,char separator,char * buf,size_t buf_len)379 void strvec_to_str(const struct strvec *psv, char separator,
380                    char *buf, size_t buf_len)
381 {
382   int len;
383 
384   strvec_iterate(psv, str) {
385     len = fc_snprintf(buf, buf_len, "%s", str) + 1;
386     if (1 >= len) {
387       /* Truncated. */
388       return;
389     }
390 
391     buf += len;
392     buf_len -= len;
393     if (0 < buf_len) {
394       *(buf - 1) = separator;
395     }
396   } strvec_iterate_end;
397 
398   buf[0] = '\0';
399 }
400 
401 /****************************************************************************
402   Build a localized string with the elements of the string vector. Elements
403   will be "or"-separated.
404 
405   See also astr_build_or_list(), strvec_to_and_list().
406 ****************************************************************************/
strvec_to_or_list(const struct strvec * psv,struct astring * astr)407 const char *strvec_to_or_list(const struct strvec *psv,
408                               struct astring *astr)
409 {
410   fc_assert_ret_val(NULL != psv, NULL);
411   return astr_build_or_list(astr, (const char **) psv->vec, psv->size);
412 }
413 
414 /****************************************************************************
415   Build a localized string with the elements of the string vector. Elements
416   will be "and"-separated.
417 
418   See also astr_build_and_list(), strvec_to_or_list().
419 ****************************************************************************/
strvec_to_and_list(const struct strvec * psv,struct astring * astr)420 const char *strvec_to_and_list(const struct strvec *psv,
421                                struct astring *astr)
422 {
423   fc_assert_ret_val(NULL != psv, NULL);
424   return astr_build_and_list(astr, (const char **) psv->vec, psv->size);
425 }
426