standardize return values + errno
[protos/libecoli.git] / lib / ecoli_strvec.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <sys/types.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <errno.h>
9
10 #include <ecoli_malloc.h>
11 #include <ecoli_test.h>
12 #include <ecoli_log.h>
13 #include <ecoli_strvec.h>
14
15 EC_LOG_TYPE_REGISTER(strvec);
16
17 struct ec_strvec_elt {
18         unsigned int refcnt;
19         char *str;
20 };
21
22 struct ec_strvec {
23         size_t len;
24         struct ec_strvec_elt **vec;
25 };
26
27 struct ec_strvec *ec_strvec(void)
28 {
29         struct ec_strvec *strvec;
30
31         strvec = ec_calloc(1, sizeof(*strvec));
32         if (strvec == NULL)
33                 return NULL;
34
35         return strvec;
36 }
37
38 int ec_strvec_add(struct ec_strvec *strvec, const char *s)
39 {
40         struct ec_strvec_elt *elt, **new_vec;
41
42         new_vec = ec_realloc(strvec->vec,
43                 sizeof(*strvec->vec) * (strvec->len + 1));
44         if (new_vec == NULL)
45                 return -1;
46
47         strvec->vec = new_vec;
48
49         elt = ec_malloc(sizeof(*elt));
50         if (elt == NULL)
51                 return -1;
52
53         elt->str = ec_strdup(s);
54         if (elt->str == NULL) {
55                 ec_free(elt);
56                 return -1;
57         }
58         elt->refcnt = 1;
59
60         new_vec[strvec->len] = elt;
61         strvec->len++;
62         return 0;
63 }
64
65 struct ec_strvec *ec_strvec_from_array(const char * const *strarr,
66         size_t n)
67 {
68         struct ec_strvec *strvec = NULL;
69         size_t i;
70
71         strvec = ec_strvec();
72         if (strvec == NULL)
73                 goto fail;
74
75         for (i = 0; i < n; i++) {
76                 if (ec_strvec_add(strvec, strarr[i]) < 0)
77                         goto fail;
78         }
79
80         return strvec;
81
82 fail:
83         ec_strvec_free(strvec);
84         return NULL;
85 }
86
87 int ec_strvec_del_last(struct ec_strvec *strvec)
88 {
89         struct ec_strvec_elt *elt;
90
91         if (strvec->len == 0) {
92                 errno = EINVAL;
93                 return -1;
94         }
95
96         elt = strvec->vec[strvec->len - 1];
97         elt->refcnt--;
98         if (elt->refcnt == 0) {
99                 ec_free(elt->str);
100                 ec_free(elt);
101         }
102         strvec->len--;
103         return 0;
104 }
105
106 struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t off,
107         size_t len)
108 {
109         struct ec_strvec *copy = NULL;
110         size_t i, veclen;
111
112         veclen = ec_strvec_len(strvec);
113         if (off + len > veclen)
114                 return NULL;
115
116         copy = ec_strvec();
117         if (copy == NULL)
118                 goto fail;
119
120         if (len == 0)
121                 return copy;
122
123         copy->vec = ec_calloc(len, sizeof(*copy->vec));
124         if (copy->vec == NULL)
125                 goto fail;
126
127         for (i = 0; i < len; i++) {
128                 copy->vec[i] = strvec->vec[i + off];
129                 copy->vec[i]->refcnt++;
130         }
131         copy->len = len;
132
133         return copy;
134
135 fail:
136         ec_strvec_free(copy);
137         return NULL;
138 }
139
140 struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
141 {
142         return ec_strvec_ndup(strvec, 0, ec_strvec_len(strvec));
143 }
144
145 void ec_strvec_free(struct ec_strvec *strvec)
146 {
147         struct ec_strvec_elt *elt;
148         size_t i;
149
150         if (strvec == NULL)
151                 return;
152
153         for (i = 0; i < ec_strvec_len(strvec); i++) {
154                 elt = strvec->vec[i];
155                 elt->refcnt--;
156                 if (elt->refcnt == 0) {
157                         ec_free(elt->str);
158                         ec_free(elt);
159                 }
160         }
161
162         ec_free(strvec->vec);
163         ec_free(strvec);
164 }
165
166 size_t ec_strvec_len(const struct ec_strvec *strvec)
167 {
168         return strvec->len;
169 }
170
171 const char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
172 {
173         if (strvec == NULL || idx >= strvec->len)
174                 return NULL;
175
176         return strvec->vec[idx]->str;
177 }
178
179 int ec_strvec_cmp(const struct ec_strvec *strvec1,
180                 const struct ec_strvec *strvec2)
181 {
182         size_t i;
183
184         if (ec_strvec_len(strvec1) != ec_strvec_len(strvec2))
185                 return -1;
186
187         for (i = 0; i < ec_strvec_len(strvec1); i++) {
188                 if (strcmp(ec_strvec_val(strvec1, i),
189                                 ec_strvec_val(strvec2, i)))
190                         return -1;
191         }
192
193         return 0;
194 }
195
196 void ec_strvec_dump(FILE *out, const struct ec_strvec *strvec)
197 {
198         size_t i;
199
200         if (strvec == NULL) {
201                 fprintf(out, "none\n");
202                 return;
203         }
204
205         fprintf(out, "strvec (len=%zu) [", strvec->len);
206         for (i = 0; i < ec_strvec_len(strvec); i++) {
207                 if (i == 0)
208                         fprintf(out, "%s", strvec->vec[i]->str);
209                 else
210                         fprintf(out, ", %s", strvec->vec[i]->str);
211         }
212         fprintf(out, "]\n");
213
214 }
215
216 /* LCOV_EXCL_START */
217 static int ec_strvec_testcase(void)
218 {
219         struct ec_strvec *strvec = NULL;
220         struct ec_strvec *strvec2 = NULL;
221         FILE *f = NULL;
222         char *buf = NULL;
223         size_t buflen = 0;
224         int testres = 0;
225
226         strvec = ec_strvec();
227         if (strvec == NULL) {
228                 EC_TEST_ERR("cannot create strvec\n");
229                 goto fail;
230         }
231         if (ec_strvec_len(strvec) != 0) {
232                 EC_TEST_ERR("bad strvec len (0)\n");
233                 goto fail;
234         }
235         if (ec_strvec_add(strvec, "0") < 0) {
236                 EC_TEST_ERR("cannot add (0) in strvec\n");
237                 goto fail;
238         }
239         if (ec_strvec_len(strvec) != 1) {
240                 EC_TEST_ERR("bad strvec len (1)\n");
241                 goto fail;
242         }
243         if (ec_strvec_add(strvec, "1") < 0) {
244                 EC_TEST_ERR("cannot add (1) in strvec\n");
245                 goto fail;
246         }
247         if (ec_strvec_len(strvec) != 2) {
248                 EC_TEST_ERR("bad strvec len (2)\n");
249                 goto fail;
250         }
251         if (strcmp(ec_strvec_val(strvec, 0), "0")) {
252                 EC_TEST_ERR("invalid element in strvec (0)\n");
253                 goto fail;
254         }
255         if (strcmp(ec_strvec_val(strvec, 1), "1")) {
256                 EC_TEST_ERR("invalid element in strvec (1)\n");
257                 goto fail;
258         }
259         if (ec_strvec_val(strvec, 2) != NULL) {
260                 EC_TEST_ERR("strvec val should be NULL\n");
261                 goto fail;
262         }
263
264         strvec2 = ec_strvec_dup(strvec);
265         if (strvec2 == NULL) {
266                 EC_TEST_ERR("cannot create strvec2\n");
267                 goto fail;
268         }
269         if (ec_strvec_len(strvec2) != 2) {
270                 EC_TEST_ERR("bad strvec2 len (2)\n");
271                 goto fail;
272         }
273         if (strcmp(ec_strvec_val(strvec2, 0), "0")) {
274                 EC_TEST_ERR("invalid element in strvec2 (0)\n");
275                 goto fail;
276         }
277         if (strcmp(ec_strvec_val(strvec2, 1), "1")) {
278                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
279                 goto fail;
280         }
281         if (ec_strvec_val(strvec2, 2) != NULL) {
282                 EC_TEST_ERR("strvec2 val should be NULL\n");
283                 goto fail;
284         }
285         ec_strvec_free(strvec2);
286
287         strvec2 = ec_strvec_ndup(strvec, 0, 0);
288         if (strvec2 == NULL) {
289                 EC_TEST_ERR("cannot create strvec2\n");
290                 goto fail;
291         }
292         if (ec_strvec_len(strvec2) != 0) {
293                 EC_TEST_ERR("bad strvec2 len (0)\n");
294                 goto fail;
295         }
296         if (ec_strvec_val(strvec2, 0) != NULL) {
297                 EC_TEST_ERR("strvec2 val should be NULL\n");
298                 goto fail;
299         }
300         ec_strvec_free(strvec2);
301
302         strvec2 = ec_strvec_ndup(strvec, 1, 1);
303         if (strvec2 == NULL) {
304                 EC_TEST_ERR("cannot create strvec2\n");
305                 goto fail;
306         }
307         if (ec_strvec_len(strvec2) != 1) {
308                 EC_TEST_ERR("bad strvec2 len (1)\n");
309                 goto fail;
310         }
311         if (strcmp(ec_strvec_val(strvec2, 0), "1")) {
312                 EC_TEST_ERR("invalid element in strvec2 (1)\n");
313                 goto fail;
314         }
315         if (ec_strvec_val(strvec2, 1) != NULL) {
316                 EC_TEST_ERR("strvec2 val should be NULL\n");
317                 goto fail;
318         }
319         ec_strvec_free(strvec2);
320
321         strvec2 = ec_strvec_ndup(strvec, 3, 1);
322         if (strvec2 != NULL) {
323                 EC_TEST_ERR("strvec2 should be NULL\n");
324                 goto fail;
325         }
326         ec_strvec_free(strvec2);
327
328         strvec2 = EC_STRVEC("0", "1");
329         if (strvec2 == NULL) {
330                 EC_TEST_ERR("cannot create strvec from array\n");
331                 goto fail;
332         }
333         testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
334                 "strvec and strvec2 should be equal\n");
335         ec_strvec_free(strvec2);
336
337         f = open_memstream(&buf, &buflen);
338         if (f == NULL)
339                 goto fail;
340         ec_strvec_dump(f, strvec);
341         fclose(f);
342         f = NULL;
343         testres |= EC_TEST_CHECK(
344                 strstr(buf, "strvec (len=2) [0, 1]"), "bad dump\n");
345         free(buf);
346         buf = NULL;
347
348         ec_strvec_del_last(strvec);
349         strvec2 = EC_STRVEC("0");
350         if (strvec2 == NULL) {
351                 EC_TEST_ERR("cannot create strvec from array\n");
352                 goto fail;
353         }
354         testres |= EC_TEST_CHECK(ec_strvec_cmp(strvec, strvec2) == 0,
355                 "strvec and strvec2 should be equal\n");
356         ec_strvec_free(strvec2);
357
358         f = open_memstream(&buf, &buflen);
359         if (f == NULL)
360                 goto fail;
361         ec_strvec_dump(f, NULL);
362         fclose(f);
363         f = NULL;
364         testres |= EC_TEST_CHECK(
365                 strstr(buf, "none"), "bad dump\n");
366         free(buf);
367         buf = NULL;
368
369         ec_strvec_free(strvec);
370
371         return testres;
372
373 fail:
374         if (f != NULL)
375                 fclose(f);
376         ec_strvec_free(strvec);
377         ec_strvec_free(strvec2);
378         free(buf);
379
380         return -1;
381 }
382 /* LCOV_EXCL_STOP */
383
384 static struct ec_test ec_node_str_test = {
385         .name = "strvec",
386         .test = ec_strvec_testcase,
387 };
388
389 EC_TEST_REGISTER(ec_node_str_test);