]> git.proxmox.com Git - mirror_ovs.git/blob - lib/svec.c
svec: New function svec_split().
[mirror_ovs.git] / lib / svec.c
1 /*
2 * Copyright (c) 2008, 2009 Nicira Networks.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <config.h>
18 #include "svec.h"
19 #include <assert.h>
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include "dynamic-string.h"
24 #include "util.h"
25
26 #define THIS_MODULE VLM_svec
27 #include "vlog.h"
28
29 void
30 svec_init(struct svec *svec)
31 {
32 svec->names = NULL;
33 svec->n = 0;
34 svec->allocated = 0;
35 }
36
37 void
38 svec_clone(struct svec *svec, const struct svec *other)
39 {
40 svec_init(svec);
41 svec_append(svec, other);
42 }
43
44 void
45 svec_destroy(struct svec *svec)
46 {
47 svec_clear(svec);
48 free(svec->names);
49 }
50
51 void
52 svec_clear(struct svec *svec)
53 {
54 size_t i;
55
56 for (i = 0; i < svec->n; i++) {
57 free(svec->names[i]);
58 }
59 svec->n = 0;
60 }
61
62 void
63 svec_add(struct svec *svec, const char *name)
64 {
65 svec_add_nocopy(svec, xstrdup(name));
66 }
67
68 void
69 svec_del(struct svec *svec, const char *name)
70 {
71 size_t offset;
72
73 offset = svec_find(svec, name);
74 if (offset != SIZE_MAX) {
75 free(svec->names[offset]);
76 memmove(&svec->names[offset], &svec->names[offset + 1],
77 sizeof *svec->names * (svec->n - offset - 1));
78 svec->n--;
79 }
80 }
81
82 static void
83 svec_expand(struct svec *svec)
84 {
85 if (svec->n >= svec->allocated) {
86 svec->names = x2nrealloc(svec->names, &svec->allocated,
87 sizeof *svec->names);
88 }
89 }
90
91 void
92 svec_add_nocopy(struct svec *svec, char *name)
93 {
94 svec_expand(svec);
95 svec->names[svec->n++] = name;
96 }
97
98 void
99 svec_append(struct svec *svec, const struct svec *other)
100 {
101 size_t i;
102 for (i = 0; i < other->n; i++) {
103 svec_add(svec, other->names[i]);
104 }
105 }
106
107 void
108 svec_terminate(struct svec *svec)
109 {
110 svec_expand(svec);
111 svec->names[svec->n] = NULL;
112 }
113
114 static int
115 compare_strings(const void *a_, const void *b_)
116 {
117 char *const *a = a_;
118 char *const *b = b_;
119 return strcmp(*a, *b);
120 }
121
122 void
123 svec_sort(struct svec *svec)
124 {
125 qsort(svec->names, svec->n, sizeof *svec->names, compare_strings);
126 }
127
128 void
129 svec_sort_unique(struct svec *svec)
130 {
131 svec_sort(svec);
132 svec_unique(svec);
133 }
134
135 void
136 svec_unique(struct svec *svec)
137 {
138 assert(svec_is_sorted(svec));
139 if (svec->n > 1) {
140 /* This algorithm is lazy and sub-optimal, but it's "obviously correct"
141 * and asymptotically optimal . */
142 struct svec tmp;
143 size_t i;
144
145 svec_init(&tmp);
146 svec_add(&tmp, svec->names[0]);
147 for (i = 1; i < svec->n; i++) {
148 if (strcmp(svec->names[i - 1], svec->names[i])) {
149 svec_add(&tmp, svec->names[i]);
150 }
151 }
152 svec_swap(&tmp, svec);
153 svec_destroy(&tmp);
154 }
155 }
156
157 void
158 svec_compact(struct svec *svec)
159 {
160 size_t i, j;
161
162 for (i = j = 0; i < svec->n; i++) {
163 if (svec->names[i] != NULL) {
164 svec->names[j++] = svec->names[i];
165 }
166 }
167 svec->n = j;
168 }
169
170 void
171 svec_diff(const struct svec *a, const struct svec *b,
172 struct svec *a_only, struct svec *both, struct svec *b_only)
173 {
174 size_t i, j;
175
176 assert(svec_is_sorted(a));
177 assert(svec_is_sorted(b));
178 if (a_only) {
179 svec_init(a_only);
180 }
181 if (both) {
182 svec_init(both);
183 }
184 if (b_only) {
185 svec_init(b_only);
186 }
187 for (i = j = 0; i < a->n && j < b->n; ) {
188 int cmp = strcmp(a->names[i], b->names[j]);
189 if (cmp < 0) {
190 if (a_only) {
191 svec_add(a_only, a->names[i]);
192 }
193 i++;
194 } else if (cmp > 0) {
195 if (b_only) {
196 svec_add(b_only, b->names[j]);
197 }
198 j++;
199 } else {
200 if (both) {
201 svec_add(both, a->names[i]);
202 }
203 i++;
204 j++;
205 }
206 }
207 if (a_only) {
208 for (; i < a->n; i++) {
209 svec_add(a_only, a->names[i]);
210 }
211 }
212 if (b_only) {
213 for (; j < b->n; j++) {
214 svec_add(b_only, b->names[j]);
215 }
216 }
217 }
218
219 bool
220 svec_contains(const struct svec *svec, const char *name)
221 {
222 return svec_find(svec, name) != SIZE_MAX;
223 }
224
225 size_t
226 svec_find(const struct svec *svec, const char *name)
227 {
228 char **p;
229
230 assert(svec_is_sorted(svec));
231 p = bsearch(&name, svec->names, svec->n, sizeof *svec->names,
232 compare_strings);
233 return p ? p - svec->names : SIZE_MAX;
234 }
235
236 bool
237 svec_is_sorted(const struct svec *svec)
238 {
239 size_t i;
240
241 for (i = 1; i < svec->n; i++) {
242 if (strcmp(svec->names[i - 1], svec->names[i]) > 0) {
243 return false;
244 }
245 }
246 return true;
247 }
248
249 bool
250 svec_is_unique(const struct svec *svec)
251 {
252 return svec_get_duplicate(svec) == NULL;
253 }
254
255 const char *
256 svec_get_duplicate(const struct svec *svec)
257 {
258 assert(svec_is_sorted(svec));
259 if (svec->n > 1) {
260 size_t i;
261 for (i = 1; i < svec->n; i++) {
262 if (!strcmp(svec->names[i - 1], svec->names[i])) {
263 return svec->names[i];
264 }
265 }
266 }
267 return NULL;
268 }
269
270 void
271 svec_swap(struct svec *a, struct svec *b)
272 {
273 struct svec tmp = *a;
274 *a = *b;
275 *b = tmp;
276 }
277
278 void
279 svec_print(const struct svec *svec, const char *title)
280 {
281 size_t i;
282
283 printf("%s:\n", title);
284 for (i = 0; i < svec->n; i++) {
285 printf("\"%s\"\n", svec->names[i]);
286 }
287 }
288
289 /* Breaks 'words' into words at white space, respecting shell-like quoting
290 * conventions, and appends the words to 'svec'. */
291 void
292 svec_parse_words(struct svec *svec, const char *words)
293 {
294 struct ds word = DS_EMPTY_INITIALIZER;
295 const char *p, *q;
296
297 for (p = words; *p != '\0'; p = q) {
298 int quote = 0;
299
300 while (isspace((unsigned char) *p)) {
301 p++;
302 }
303 if (*p == '\0') {
304 break;
305 }
306
307 ds_clear(&word);
308 for (q = p; *q != '\0'; q++) {
309 if (*q == quote) {
310 quote = 0;
311 } else if (*q == '\'' || *q == '"') {
312 quote = *q;
313 } else if (*q == '\\' && (!quote || quote == '"')) {
314 q++;
315 if (*q == '\0') {
316 VLOG_WARN("%s: ends in trailing backslash", words);
317 break;
318 }
319 ds_put_char(&word, *q);
320 } else if (isspace((unsigned char) *q) && !quote) {
321 q++;
322 break;
323 } else {
324 ds_put_char(&word, *q);
325 }
326 }
327 svec_add(svec, ds_cstr(&word));
328 if (quote) {
329 VLOG_WARN("%s: word ends inside quoted string", words);
330 }
331 }
332 ds_destroy(&word);
333 }
334
335 bool
336 svec_equal(const struct svec *a, const struct svec *b)
337 {
338 size_t i;
339
340 if (a->n != b->n) {
341 return false;
342 }
343 for (i = 0; i < a->n; i++) {
344 if (strcmp(a->names[i], b->names[i])) {
345 return false;
346 }
347 }
348 return true;
349 }
350
351 char *
352 svec_join(const struct svec *svec,
353 const char *delimiter, const char *terminator)
354 {
355 struct ds ds;
356 size_t i;
357
358 ds_init(&ds);
359 for (i = 0; i < svec->n; i++) {
360 if (i) {
361 ds_put_cstr(&ds, delimiter);
362 }
363 ds_put_cstr(&ds, svec->names[i]);
364 }
365 ds_put_cstr(&ds, terminator);
366 return ds_cstr(&ds);
367 }
368
369 /* Breaks 's' into tokens at any character in 'delimiters', and appends each
370 * token to 'svec'. Empty tokens are not added. */
371 void
372 svec_split(struct svec *svec, const char *s_, const char *delimiters)
373 {
374 char *s = xstrdup(s_);
375 char *save_ptr = NULL;
376 char *token;
377
378 for (token = strtok_r(s, delimiters, &save_ptr); token != NULL;
379 token = strtok_r(NULL, delimiters, &save_ptr)) {
380 svec_add(svec, token);
381 }
382 free(s);
383 }
384
385 const char *
386 svec_back(const struct svec *svec)
387 {
388 assert(svec->n);
389 return svec->names[svec->n - 1];
390 }
391
392 void
393 svec_pop_back(struct svec *svec)
394 {
395 assert(svec->n);
396 free(svec->names[--svec->n]);
397 }