1 /* GNU gettext - internationalization aids
2 Copyright (C) 1995, 1998, 2000-2004, 2006, 2009, 2020 Free Software
3 Foundation, Inc.
4
5 This file was written by Peter Miller <millerp@canb.auug.org.au>
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 <https://www.gnu.org/licenses/>. */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 /* Specification. */
25 #include "str-list.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "xalloc.h"
32
33
34 /* Initialize an empty list of strings. */
35 void
36 string_list_init (string_list_ty *slp)
37 {
38 slp->item = NULL;
39 slp->nitems = 0;
40 slp->nitems_max = 0;
41 }
42
43
44 /* Return a fresh, empty list of strings. */
45 string_list_ty *
46 string_list_alloc ()
47 {
48 string_list_ty *slp;
49
50 slp = XMALLOC (string_list_ty);
51 slp->item = NULL;
52 slp->nitems = 0;
53 slp->nitems_max = 0;
54
55 return slp;
56 }
57
58
59 /* Append a single string to the end of a list of strings. */
60 void
61 string_list_append (string_list_ty *slp, const char *s)
62 {
63 /* Grow the list. */
64 if (slp->nitems >= slp->nitems_max)
65 {
66 size_t nbytes;
67
68 slp->nitems_max = slp->nitems_max * 2 + 4;
69 nbytes = slp->nitems_max * sizeof (slp->item[0]);
70 slp->item = (const char **) xrealloc (slp->item, nbytes);
71 }
72
73 /* Add a copy of the string to the end of the list. */
74 slp->item[slp->nitems++] = xstrdup (s);
75 }
76
77
78 /* Append a single string to the end of a list of strings, unless it is
79 already contained in the list. */
80 void
81 string_list_append_unique (string_list_ty *slp, const char *s)
82 {
83 size_t j;
84
85 /* Do nothing if the string is already in the list. */
86 for (j = 0; j < slp->nitems; ++j)
87 if (strcmp (slp->item[j], s) == 0)
88 return;
89
90 /* Grow the list. */
91 if (slp->nitems >= slp->nitems_max)
92 {
93 slp->nitems_max = slp->nitems_max * 2 + 4;
94 slp->item = (const char **) xrealloc (slp->item,
95 slp->nitems_max
96 * sizeof (slp->item[0]));
97 }
98
99 /* Add a copy of the string to the end of the list. */
100 slp->item[slp->nitems++] = xstrdup (s);
101 }
102
103 /* Likewise with a string descriptor as argument. */
104 void
105 string_list_append_unique_desc (string_list_ty *slp,
106 const char *s, size_t s_len)
107 {
108 size_t j;
109
110 /* Do nothing if the string is already in the list. */
111 for (j = 0; j < slp->nitems; ++j)
112 if (strlen (slp->item[j]) == s_len && memcmp (slp->item[j], s, s_len) == 0)
113 return;
114
115 /* Grow the list. */
116 if (slp->nitems >= slp->nitems_max)
117 {
118 slp->nitems_max = slp->nitems_max * 2 + 4;
119 slp->item = (const char **) xrealloc (slp->item,
120 slp->nitems_max
121 * sizeof (slp->item[0]));
122 }
123
124 /* Add a copy of the string to the end of the list. */
125 {
126 char *copy = XNMALLOC (s_len + 1, char);
127 memcpy (copy, s, s_len);
128 copy[s_len] = '\0';
129
130 slp->item[slp->nitems++] = copy;
131 }
132 }
133
134
135 /* Destroy a list of strings. */
136 void
137 string_list_destroy (string_list_ty *slp)
138 {
139 size_t j;
140
141 for (j = 0; j < slp->nitems; ++j)
142 free ((char *) slp->item[j]);
143 if (slp->item != NULL)
144 free (slp->item);
145 }
146
147
148 /* Free a list of strings. */
149 void
150 string_list_free (string_list_ty *slp)
151 {
152 size_t j;
153
154 for (j = 0; j < slp->nitems; ++j)
155 free ((char *) slp->item[j]);
156 if (slp->item != NULL)
157 free (slp->item);
158 free (slp);
159 }
160
161
162 /* Return a freshly allocated string obtained by concatenating all the
163 strings in the list. */
164 char *
165 string_list_concat (const string_list_ty *slp)
166 {
167 size_t len;
168 size_t j;
169 char *result;
170 size_t pos;
171
172 len = 1;
173 for (j = 0; j < slp->nitems; ++j)
174 len += strlen (slp->item[j]);
175 result = XNMALLOC (len, char);
176 pos = 0;
177 for (j = 0; j < slp->nitems; ++j)
178 {
179 len = strlen (slp->item[j]);
180 memcpy (result + pos, slp->item[j], len);
181 pos += len;
182 }
183 result[pos] = '\0';
184 return result;
185 }
186
187
188 /* Return a freshly allocated string obtained by concatenating all the
189 strings in the list, and destroy the list. */
190 char *
191 string_list_concat_destroy (string_list_ty *slp)
192 {
193 char *result;
194
195 /* Optimize the most frequent case. */
196 if (slp->nitems == 1)
197 {
198 result = (char *) slp->item[0];
199 free (slp->item);
200 }
201 else
202 {
203 result = string_list_concat (slp);
204 string_list_destroy (slp);
205 }
206 return result;
207 }
208
209
210 /* Return a freshly allocated string obtained by concatenating all the
211 strings in the list, separated by the separator string, terminated
212 by the terminator character. The terminator character is not added if
213 drop_redundant_terminator is true and the last string already ends with
214 the terminator. */
215 char *
216 string_list_join (const string_list_ty *slp, const char *separator,
217 char terminator, bool drop_redundant_terminator)
218 {
219 size_t separator_len = strlen (separator);
220 size_t len;
221 size_t j;
222 char *result;
223 size_t pos;
224
225 len = 1;
226 for (j = 0; j < slp->nitems; ++j)
227 {
228 if (j > 0)
229 len += separator_len;
230 len += strlen (slp->item[j]);
231 }
232 if (terminator)
233 ++len;
234 result = XNMALLOC (len, char);
235 pos = 0;
236 for (j = 0; j < slp->nitems; ++j)
237 {
238 if (j > 0)
239 {
240 memcpy (result + pos, separator, separator_len);
241 pos += separator_len;
242 }
243 len = strlen (slp->item[j]);
244 memcpy (result + pos, slp->item[j], len);
245 pos += len;
246 }
247 if (terminator
248 && !(drop_redundant_terminator
249 && slp->nitems > 0
250 && (len = strlen (slp->item[slp->nitems - 1])) > 0
251 && slp->item[slp->nitems - 1][len - 1] == terminator))
252 result[pos++] = terminator;
253 result[pos] = '\0';
254 return result;
255 }
256
257
258 /* Return 1 if s is contained in the list of strings, 0 otherwise. */
259 bool
260 string_list_member (const string_list_ty *slp, const char *s)
261 {
262 size_t j;
263
264 for (j = 0; j < slp->nitems; ++j)
265 if (strcmp (slp->item[j], s) == 0)
266 return true;
267 return false;
268 }
269
270 /* Likewise with a string descriptor as argument. */
271 bool
272 string_list_member_desc (const string_list_ty *slp, const char *s, size_t s_len)
273 {
274 size_t j;
275
276 for (j = 0; j < slp->nitems; ++j)
277 if (strlen (slp->item[j]) == s_len && memcmp (slp->item[j], s, s_len) == 0)
278 return true;
279 return false;
280 }
281
282
283 /* Remove s from the list of strings. Return the removed string or NULL. */
284 const char *
285 string_list_remove (string_list_ty *slp, const char *s)
286 {
287 size_t j;
288
289 for (j = 0; j < slp->nitems; ++j)
290 if (strcmp (slp->item[j], s) == 0)
291 {
292 const char *found = slp->item[j];
293 slp->nitems--;
294 if (slp->nitems > j)
295 memmove (&slp->item[j + 1], &slp->item[j],
296 (slp->nitems - j) * sizeof (const char *));
297 return found;
298 }
299 return NULL;
300 }