1/*
2 * Automated Testing Framework (atf)
3 *
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <errno.h>
31#include <stdarg.h>
32#include <stdint.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "atf-c/error.h"
38
39#include "dynstr.h"
40#include "sanity.h"
41#include "text.h"
42
43/* ---------------------------------------------------------------------
44 * Auxiliary functions.
45 * --------------------------------------------------------------------- */
46
47static
48atf_error_t
49resize(atf_dynstr_t *ad, size_t newsize)
50{
51    char *newdata;
52    atf_error_t err;
53
54    PRE(newsize > ad->m_datasize);
55
56    newdata = (char *)malloc(newsize);
57    if (newdata == NULL) {
58        err = atf_no_memory_error();
59    } else {
60        strcpy(newdata, ad->m_data);
61        free(ad->m_data);
62        ad->m_data = newdata;
63        ad->m_datasize = newsize;
64        err = atf_no_error();
65    }
66
67    return err;
68}
69
70ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(2, 0)
71static
72atf_error_t
73prepend_or_append(atf_dynstr_t *ad, const char *fmt, va_list ap,
74                  bool prepend)
75{
76    char *aux;
77    atf_error_t err;
78    size_t newlen;
79    va_list ap2;
80
81    va_copy(ap2, ap);
82    err = atf_text_format_ap(&aux, fmt, ap2);
83    va_end(ap2);
84    if (atf_is_error(err))
85        goto out;
86    newlen = ad->m_length + strlen(aux);
87
88    if (newlen + sizeof(char) > ad->m_datasize) {
89        err = resize(ad, newlen + sizeof(char));
90        if (atf_is_error(err))
91            goto out_free;
92    }
93
94    if (prepend) {
95        memmove(ad->m_data + strlen(aux), ad->m_data, ad->m_length + 1);
96        memcpy(ad->m_data, aux, strlen(aux));
97    } else
98        strcpy(ad->m_data + ad->m_length, aux);
99    ad->m_length = newlen;
100    err = atf_no_error();
101
102out_free:
103    free(aux);
104out:
105    return err;
106}
107
108/* ---------------------------------------------------------------------
109 * The "atf_dynstr" type.
110 * --------------------------------------------------------------------- */
111
112/*
113 * Constants.
114 */
115
116const size_t atf_dynstr_npos = SIZE_MAX;
117
118/*
119 * Constructors and destructors.
120 */
121
122atf_error_t
123atf_dynstr_init(atf_dynstr_t *ad)
124{
125    atf_error_t err;
126
127    ad->m_data = (char *)malloc(sizeof(char));
128    if (ad->m_data == NULL) {
129        err = atf_no_memory_error();
130        goto out;
131    }
132
133    ad->m_data[0] = '\0';
134    ad->m_datasize = 1;
135    ad->m_length = 0;
136    err = atf_no_error();
137
138out:
139    return err;
140}
141
142atf_error_t
143atf_dynstr_init_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
144{
145    atf_error_t err;
146
147    ad->m_datasize = strlen(fmt) + 1;
148    ad->m_length = 0;
149
150    do {
151        va_list ap2;
152        int ret;
153
154        ad->m_datasize *= 2;
155        ad->m_data = (char *)malloc(ad->m_datasize);
156        if (ad->m_data == NULL) {
157            err = atf_no_memory_error();
158            goto out;
159        }
160
161        va_copy(ap2, ap);
162        ret = vsnprintf(ad->m_data, ad->m_datasize, fmt, ap2);
163        va_end(ap2);
164        if (ret < 0) {
165            free(ad->m_data);
166            err = atf_libc_error(errno, "Cannot format string");
167            goto out;
168        }
169
170        INV(ret >= 0);
171        if ((size_t)ret >= ad->m_datasize) {
172            free(ad->m_data);
173            ad->m_data = NULL;
174        }
175        ad->m_length = ret;
176    } while (ad->m_length >= ad->m_datasize);
177
178    err = atf_no_error();
179out:
180    POST(atf_is_error(err) || ad->m_data != NULL);
181    return err;
182}
183
184atf_error_t
185atf_dynstr_init_fmt(atf_dynstr_t *ad, const char *fmt, ...)
186{
187    va_list ap;
188    atf_error_t err;
189
190    va_start(ap, fmt);
191    err = atf_dynstr_init_ap(ad, fmt, ap);
192    va_end(ap);
193
194    return err;
195}
196
197atf_error_t
198atf_dynstr_init_raw(atf_dynstr_t *ad, const void *mem, size_t memlen)
199{
200    atf_error_t err;
201
202    if (memlen >= SIZE_MAX - 1) {
203        err = atf_no_memory_error();
204        goto out;
205    }
206
207    ad->m_data = (char *)malloc(memlen + 1);
208    if (ad->m_data == NULL) {
209        err = atf_no_memory_error();
210        goto out;
211    }
212
213    ad->m_datasize = memlen + 1;
214    memcpy(ad->m_data, mem, memlen);
215    ad->m_data[memlen] = '\0';
216    ad->m_length = strlen(ad->m_data);
217    INV(ad->m_length <= memlen);
218    err = atf_no_error();
219
220out:
221    return err;
222}
223
224atf_error_t
225atf_dynstr_init_rep(atf_dynstr_t *ad, size_t len, char ch)
226{
227    atf_error_t err;
228
229    if (len == SIZE_MAX) {
230        err = atf_no_memory_error();
231        goto out;
232    }
233
234    ad->m_datasize = (len + 1) * sizeof(char);
235    ad->m_data = (char *)malloc(ad->m_datasize);
236    if (ad->m_data == NULL) {
237        err = atf_no_memory_error();
238        goto out;
239    }
240
241    memset(ad->m_data, ch, len);
242    ad->m_data[len] = '\0';
243    ad->m_length = len;
244    err = atf_no_error();
245
246out:
247    return err;
248}
249
250atf_error_t
251atf_dynstr_init_substr(atf_dynstr_t *ad, const atf_dynstr_t *src,
252                       size_t beg, size_t end)
253{
254    if (beg > src->m_length)
255        beg = src->m_length;
256
257    if (end == atf_dynstr_npos || end > src->m_length)
258        end = src->m_length;
259
260    return atf_dynstr_init_raw(ad, src->m_data + beg, end - beg);
261}
262
263atf_error_t
264atf_dynstr_copy(atf_dynstr_t *dest, const atf_dynstr_t *src)
265{
266    atf_error_t err;
267
268    dest->m_data = (char *)malloc(src->m_datasize);
269    if (dest->m_data == NULL)
270        err = atf_no_memory_error();
271    else {
272        memcpy(dest->m_data, src->m_data, src->m_datasize);
273        dest->m_datasize = src->m_datasize;
274        dest->m_length = src->m_length;
275        err = atf_no_error();
276    }
277
278    return err;
279}
280
281void
282atf_dynstr_fini(atf_dynstr_t *ad)
283{
284    INV(ad->m_data != NULL);
285    free(ad->m_data);
286}
287
288char *
289atf_dynstr_fini_disown(atf_dynstr_t *ad)
290{
291    INV(ad->m_data != NULL);
292    return ad->m_data;
293}
294
295/*
296 * Getters.
297 */
298
299const char *
300atf_dynstr_cstring(const atf_dynstr_t *ad)
301{
302    return ad->m_data;
303}
304
305size_t
306atf_dynstr_length(const atf_dynstr_t *ad)
307{
308    return ad->m_length;
309}
310
311size_t
312atf_dynstr_rfind_ch(const atf_dynstr_t *ad, char ch)
313{
314    size_t pos;
315
316    for (pos = ad->m_length; pos > 0 && ad->m_data[pos - 1] != ch; pos--)
317        ;
318
319    return pos == 0 ? atf_dynstr_npos : pos - 1;
320}
321
322/*
323 * Modifiers.
324 */
325
326atf_error_t
327atf_dynstr_append_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
328{
329    atf_error_t err;
330    va_list ap2;
331
332    va_copy(ap2, ap);
333    err = prepend_or_append(ad, fmt, ap2, false);
334    va_end(ap2);
335
336    return err;
337}
338
339atf_error_t
340atf_dynstr_append_fmt(atf_dynstr_t *ad, const char *fmt, ...)
341{
342    va_list ap;
343    atf_error_t err;
344
345    va_start(ap, fmt);
346    err = prepend_or_append(ad, fmt, ap, false);
347    va_end(ap);
348
349    return err;
350}
351
352void
353atf_dynstr_clear(atf_dynstr_t *ad)
354{
355    ad->m_data[0] = '\0';
356    ad->m_length = 0;
357}
358
359atf_error_t
360atf_dynstr_prepend_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
361{
362    atf_error_t err;
363    va_list ap2;
364
365    va_copy(ap2, ap);
366    err = prepend_or_append(ad, fmt, ap2, true);
367    va_end(ap2);
368
369    return err;
370}
371
372atf_error_t
373atf_dynstr_prepend_fmt(atf_dynstr_t *ad, const char *fmt, ...)
374{
375    va_list ap;
376    atf_error_t err;
377
378    va_start(ap, fmt);
379    err = prepend_or_append(ad, fmt, ap, true);
380    va_end(ap);
381
382    return err;
383}
384
385/*
386 * Operators.
387 */
388
389bool
390atf_equal_dynstr_cstring(const atf_dynstr_t *ad, const char *str)
391{
392    return strcmp(ad->m_data, str) == 0;
393}
394
395bool
396atf_equal_dynstr_dynstr(const atf_dynstr_t *s1, const atf_dynstr_t *s2)
397{
398    return strcmp(s1->m_data, s2->m_data) == 0;
399}
400