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