1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2010 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "libpspp/string-array.h"
20
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "libpspp/array.h"
26 #include "libpspp/str.h"
27
28 #include "gl/xalloc.h"
29
30 /* Initializes SA as an initially empty array of strings. */
31 void
string_array_init(struct string_array * sa)32 string_array_init (struct string_array *sa)
33 {
34 sa->strings = NULL;
35 sa->n = 0;
36 sa->allocated = 0;
37 }
38
39 /* Initializes DST as an array of strings whose contents are initially copies
40 of the strings in SRC. */
41 void
string_array_clone(struct string_array * dst,const struct string_array * src)42 string_array_clone (struct string_array *dst, const struct string_array *src)
43 {
44 size_t i;
45
46 dst->strings = xmalloc (sizeof *dst->strings * src->n);
47 for (i = 0; i < src->n; i++)
48 dst->strings[i] = xstrdup (src->strings[i]);
49 dst->n = src->n;
50 dst->allocated = src->n;
51 }
52
53 /* Exchanges the contents of A and B. */
54 void
string_array_swap(struct string_array * a,struct string_array * b)55 string_array_swap (struct string_array *a, struct string_array *b)
56 {
57 struct string_array tmp = *a;
58 *a = *b;
59 *b = tmp;
60 }
61
62 /* Frees the strings in SA. SA must be reinitialized (with
63 string_array_init()) before it is used again. */
64 void
string_array_destroy(struct string_array * sa)65 string_array_destroy (struct string_array *sa)
66 {
67 if (sa != NULL)
68 {
69 string_array_clear (sa);
70 free (sa->strings);
71 }
72 }
73
74 /* Returns true if SA contains at least one copy of STRING, otherwise false.
75
76 This function runs in O(n) time in the number of strings in SA. */
77 bool
string_array_contains(const struct string_array * sa,const char * string)78 string_array_contains (const struct string_array *sa, const char *string)
79 {
80 return string_array_find (sa, string) != SIZE_MAX;
81 }
82
83 /* If SA contains at least one copy of STRING, returns the smallest index of
84 any of those copies. If SA does not contain STRING, returns SIZE_MAX.
85
86 This function runs in O(n) time in the number of strings in SA. */
87 size_t
string_array_find(const struct string_array * sa,const char * string)88 string_array_find (const struct string_array *sa, const char *string)
89 {
90 size_t i;
91
92 for (i = 0; i < sa->n; i++)
93 if (!strcmp (sa->strings[i], string))
94 return i;
95 return SIZE_MAX;
96 }
97
98 /* Appends a copy of STRING to SA. The caller retains ownership of STRING. */
99 void
string_array_append(struct string_array * sa,const char * string)100 string_array_append (struct string_array *sa, const char *string)
101 {
102 string_array_insert (sa, string, sa->n);
103 }
104
105 /* Appends STRING to SA. Ownership of STRING transfers to SA. */
106 void
string_array_append_nocopy(struct string_array * sa,char * string)107 string_array_append_nocopy (struct string_array *sa, char *string)
108 {
109 string_array_insert_nocopy (sa, string, sa->n);
110 }
111
112 /* Inserts a copy of STRING in SA just before the string with index BEFORE,
113 which must be less than or equal to the number of strings in SA. The caller
114 retains ownership of STRING.
115
116 In general, this function runs in O(n) time in the number of strings that
117 must be shifted to higher indexes; if BEFORE is the number of strings in SA,
118 it runs in amortized constant time. */
119 void
string_array_insert(struct string_array * sa,const char * string,size_t before)120 string_array_insert (struct string_array *sa,
121 const char *string, size_t before)
122 {
123 string_array_insert_nocopy (sa, xstrdup (string), before);
124 }
125
126 static void
string_array_expand__(struct string_array * sa)127 string_array_expand__ (struct string_array *sa)
128 {
129 if (sa->n >= sa->allocated)
130 sa->strings = x2nrealloc (sa->strings, &sa->allocated,
131 sizeof *sa->strings);
132 }
133
134 /* Inserts STRING in SA just before the string with index BEFORE, which must be
135 less than or equal to the number of strings in SA. Ownership of STRING
136 transfers to SA.
137
138 In general, this function runs in O(n) time in the number of strings that
139 must be shifted to higher indexes; if BEFORE is the number of strings in SA,
140 it runs in amortized constant time. */
141 void
string_array_insert_nocopy(struct string_array * sa,char * string,size_t before)142 string_array_insert_nocopy (struct string_array *sa, char *string,
143 size_t before)
144 {
145 string_array_expand__ (sa);
146 if (before < sa->n)
147 insert_element (sa->strings, sa->n, sizeof *sa->strings, before);
148
149 sa->strings[before] = string;
150 sa->n++;
151 }
152
153 /* Deletes from SA the string with index IDX, which must be less than the
154 number of strings in SA, and shifts down the strings with higher indexes.
155 Frees the string.
156
157 In general, this function runs in O(n) time in the number of strings that
158 must be shifted to lower indexes. If IDX is the last string in SA, it runs
159 in amortized constant time. */
160 void
string_array_delete(struct string_array * sa,size_t idx)161 string_array_delete (struct string_array *sa, size_t idx)
162 {
163 free (string_array_delete_nofree (sa, idx));
164 }
165
166 /* Deletes from SA the string with index IDX, which must be less than the
167 number of strings in SA. Returns the string, which the caller is
168 responsible for freeing with free().
169
170 In general, this function runs in O(n) time in the number of strings that
171 must be shifted to lower indexes. If IDX is the last string in SA, it runs
172 in amortized constant time. */
173 char *
string_array_delete_nofree(struct string_array * sa,size_t idx)174 string_array_delete_nofree (struct string_array *sa, size_t idx)
175 {
176 char *s = sa->strings[idx];
177 if (idx != sa->n - 1)
178 remove_element (sa->strings, sa->n, sizeof *sa->strings, idx);
179 sa->n--;
180 return s;
181 }
182
183 /* Deletes all of the strings from SA and frees them. */
184 void
string_array_clear(struct string_array * sa)185 string_array_clear (struct string_array *sa)
186 {
187 size_t i;
188
189 for (i = 0; i < sa->n; i++)
190 free (sa->strings[i]);
191 sa->n = 0;
192 }
193
194 /* Ensures that 'sa->strings[sa->n]' is a null pointer (until SA is modified
195 further). */
196 void
string_array_terminate_null(struct string_array * sa)197 string_array_terminate_null (struct string_array *sa)
198 {
199 string_array_expand__ (sa);
200 sa->strings[sa->n] = NULL;
201 }
202
203 /* Reduces the amount of memory allocated for SA's strings to the minimum
204 necessary. */
205 void
string_array_shrink(struct string_array * sa)206 string_array_shrink (struct string_array *sa)
207 {
208 if (sa->allocated > sa->n)
209 {
210 if (sa->n > 0)
211 sa->strings = xrealloc (sa->strings, sa->n * sizeof *sa->strings);
212 else
213 {
214 free (sa->strings);
215 sa->strings = NULL;
216 }
217 sa->allocated = sa->n;
218 }
219 }
220
221 static int
compare_strings(const void * a_,const void * b_)222 compare_strings (const void *a_, const void *b_)
223 {
224 const void *const *a = a_;
225 const void *const *b = b_;
226
227 return strcmp (*a, *b);
228 }
229
230 /* Sorts the strings in SA into order according to strcmp(). */
231 void
string_array_sort(struct string_array * sa)232 string_array_sort (struct string_array *sa)
233 {
234 qsort (sa->strings, sa->n, sizeof *sa->strings, compare_strings);
235 }
236
237 /* Divides STRING into tokens at DELIMITERS and adds each token to SA. */
238 void
string_array_parse(struct string_array * sa,struct substring string,struct substring delimiters)239 string_array_parse (struct string_array *sa, struct substring string,
240 struct substring delimiters)
241 {
242 size_t save_idx = 0;
243 struct substring token;
244 while (ss_tokenize (string, delimiters, &save_idx, &token))
245 string_array_append_nocopy (sa, ss_xstrdup (token));
246 }
247
248 /* Returns a single string that consists of each of the strings in SA
249 concatenated, separated from each other with SEPARATOR.
250
251 The caller is responsible for freeing the returned string with free(). */
252 char *
string_array_join(const struct string_array * sa,const char * separator)253 string_array_join (const struct string_array *sa, const char *separator)
254 {
255 struct string dst;
256 const char *s;
257 size_t i;
258
259 ds_init_empty (&dst);
260 STRING_ARRAY_FOR_EACH (s, i, sa)
261 {
262 if (i > 0)
263 ds_put_cstr (&dst, separator);
264 ds_put_cstr (&dst, s);
265 }
266 return ds_steal_cstr (&dst);
267 }
268