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
string_list_init(string_list_ty * slp)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 *
string_list_alloc()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
string_list_append(string_list_ty * slp,const char * s)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
string_list_append_unique(string_list_ty * slp,const char * s)81 string_list_append_unique (string_list_ty *slp, const char *s)
82 {
83 size_t j;
84
85 /* Do not 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
104 /* Destroy a list of strings. */
105 void
string_list_destroy(string_list_ty * slp)106 string_list_destroy (string_list_ty *slp)
107 {
108 size_t j;
109
110 for (j = 0; j < slp->nitems; ++j)
111 free ((char *) slp->item[j]);
112 if (slp->item != NULL)
113 free (slp->item);
114 }
115
116
117 /* Free a list of strings. */
118 void
string_list_free(string_list_ty * slp)119 string_list_free (string_list_ty *slp)
120 {
121 size_t j;
122
123 for (j = 0; j < slp->nitems; ++j)
124 free ((char *) slp->item[j]);
125 if (slp->item != NULL)
126 free (slp->item);
127 free (slp);
128 }
129
130
131 /* Return a freshly allocated string obtained by concatenating all the
132 strings in the list. */
133 char *
string_list_concat(const string_list_ty * slp)134 string_list_concat (const string_list_ty *slp)
135 {
136 size_t len;
137 size_t j;
138 char *result;
139 size_t pos;
140
141 len = 1;
142 for (j = 0; j < slp->nitems; ++j)
143 len += strlen (slp->item[j]);
144 result = XNMALLOC (len, char);
145 pos = 0;
146 for (j = 0; j < slp->nitems; ++j)
147 {
148 len = strlen (slp->item[j]);
149 memcpy (result + pos, slp->item[j], len);
150 pos += len;
151 }
152 result[pos] = '\0';
153 return result;
154 }
155
156
157 /* Return a freshly allocated string obtained by concatenating all the
158 strings in the list, and destroy the list. */
159 char *
string_list_concat_destroy(string_list_ty * slp)160 string_list_concat_destroy (string_list_ty *slp)
161 {
162 char *result;
163
164 /* Optimize the most frequent case. */
165 if (slp->nitems == 1)
166 {
167 result = (char *) slp->item[0];
168 free (slp->item);
169 }
170 else
171 {
172 result = string_list_concat (slp);
173 string_list_destroy (slp);
174 }
175 return result;
176 }
177
178
179 /* Return a freshly allocated string obtained by concatenating all the
180 strings in the list, separated by the separator string, terminated
181 by the terminator character. The terminator character is not added if
182 drop_redundant_terminator is true and the last string already ends with
183 the terminator. */
184 char *
string_list_join(const string_list_ty * slp,const char * separator,char terminator,bool drop_redundant_terminator)185 string_list_join (const string_list_ty *slp, const char *separator,
186 char terminator, bool drop_redundant_terminator)
187 {
188 size_t separator_len = strlen (separator);
189 size_t len;
190 size_t j;
191 char *result;
192 size_t pos;
193
194 len = 1;
195 for (j = 0; j < slp->nitems; ++j)
196 {
197 if (j > 0)
198 len += separator_len;
199 len += strlen (slp->item[j]);
200 }
201 if (terminator)
202 ++len;
203 result = XNMALLOC (len, char);
204 pos = 0;
205 for (j = 0; j < slp->nitems; ++j)
206 {
207 if (j > 0)
208 {
209 memcpy (result + pos, separator, separator_len);
210 pos += separator_len;
211 }
212 len = strlen (slp->item[j]);
213 memcpy (result + pos, slp->item[j], len);
214 pos += len;
215 }
216 if (terminator
217 && !(drop_redundant_terminator
218 && slp->nitems > 0
219 && (len = strlen (slp->item[slp->nitems - 1])) > 0
220 && slp->item[slp->nitems - 1][len - 1] == terminator))
221 result[pos++] = terminator;
222 result[pos] = '\0';
223 return result;
224 }
225
226
227 /* Return 1 if s is contained in the list of strings, 0 otherwise. */
228 bool
string_list_member(const string_list_ty * slp,const char * s)229 string_list_member (const string_list_ty *slp, const char *s)
230 {
231 size_t j;
232
233 for (j = 0; j < slp->nitems; ++j)
234 if (strcmp (slp->item[j], s) == 0)
235 return true;
236 return false;
237 }
238
239
240 /* Remove s from the list of strings. Return the removed string or NULL. */
241 const char *
string_list_remove(string_list_ty * slp,const char * s)242 string_list_remove (string_list_ty *slp, const char *s)
243 {
244 size_t j;
245
246 for (j = 0; j < slp->nitems; ++j)
247 if (strcmp (slp->item[j], s) == 0)
248 {
249 const char *found = slp->item[j];
250 slp->nitems--;
251 if (slp->nitems > j)
252 memmove (&slp->item[j + 1], &slp->item[j],
253 (slp->nitems - j) * sizeof (const char *));
254 return found;
255 }
256 return NULL;
257 }
258