153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <sys/types.h>
2553a5a1b3Sopenharmony_ci#include <stdlib.h>
2653a5a1b3Sopenharmony_ci#include <string.h>
2753a5a1b3Sopenharmony_ci#include <stdarg.h>
2853a5a1b3Sopenharmony_ci#include <stdio.h>
2953a5a1b3Sopenharmony_ci
3053a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3153a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#include "strbuf.h"
3453a5a1b3Sopenharmony_ci
3553a5a1b3Sopenharmony_ci/* A chunk of the linked list that makes up the string */
3653a5a1b3Sopenharmony_cistruct chunk {
3753a5a1b3Sopenharmony_ci    struct chunk *next;
3853a5a1b3Sopenharmony_ci    size_t length;
3953a5a1b3Sopenharmony_ci};
4053a5a1b3Sopenharmony_ci
4153a5a1b3Sopenharmony_ci#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk)))
4253a5a1b3Sopenharmony_ci
4353a5a1b3Sopenharmony_cistruct pa_strbuf {
4453a5a1b3Sopenharmony_ci    size_t length;
4553a5a1b3Sopenharmony_ci    struct chunk *head, *tail;
4653a5a1b3Sopenharmony_ci};
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_cipa_strbuf *pa_strbuf_new(void) {
4953a5a1b3Sopenharmony_ci    pa_strbuf *sb;
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci    sb = pa_xnew(pa_strbuf, 1);
5253a5a1b3Sopenharmony_ci    sb->length = 0;
5353a5a1b3Sopenharmony_ci    sb->head = sb->tail = NULL;
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_ci    return sb;
5653a5a1b3Sopenharmony_ci}
5753a5a1b3Sopenharmony_ci
5853a5a1b3Sopenharmony_civoid pa_strbuf_free(pa_strbuf *sb) {
5953a5a1b3Sopenharmony_ci    pa_assert(sb);
6053a5a1b3Sopenharmony_ci
6153a5a1b3Sopenharmony_ci    while (sb->head) {
6253a5a1b3Sopenharmony_ci        struct chunk *c = sb->head;
6353a5a1b3Sopenharmony_ci        sb->head = sb->head->next;
6453a5a1b3Sopenharmony_ci        pa_xfree(c);
6553a5a1b3Sopenharmony_ci    }
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_ci    pa_xfree(sb);
6853a5a1b3Sopenharmony_ci}
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci/* Make a C string from the string buffer. The caller has to free
7153a5a1b3Sopenharmony_ci * string with pa_xfree(). */
7253a5a1b3Sopenharmony_cichar *pa_strbuf_to_string(pa_strbuf *sb) {
7353a5a1b3Sopenharmony_ci    char *t, *e;
7453a5a1b3Sopenharmony_ci    struct chunk *c;
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    pa_assert(sb);
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci    e = t = pa_xmalloc(sb->length+1);
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    for (c = sb->head; c; c = c->next) {
8153a5a1b3Sopenharmony_ci        pa_assert((size_t) (e-t) <= sb->length);
8253a5a1b3Sopenharmony_ci        memcpy(e, CHUNK_TO_TEXT(c), c->length);
8353a5a1b3Sopenharmony_ci        e += c->length;
8453a5a1b3Sopenharmony_ci    }
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci    /* Trailing NUL */
8753a5a1b3Sopenharmony_ci    *e = 0;
8853a5a1b3Sopenharmony_ci
8953a5a1b3Sopenharmony_ci    pa_assert(e == t+sb->length);
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_ci    return t;
9253a5a1b3Sopenharmony_ci}
9353a5a1b3Sopenharmony_ci
9453a5a1b3Sopenharmony_ci/* Combination of pa_strbuf_free() and pa_strbuf_to_string() */
9553a5a1b3Sopenharmony_cichar *pa_strbuf_to_string_free(pa_strbuf *sb) {
9653a5a1b3Sopenharmony_ci    char *t;
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci    pa_assert(sb);
9953a5a1b3Sopenharmony_ci    t = pa_strbuf_to_string(sb);
10053a5a1b3Sopenharmony_ci    pa_strbuf_free(sb);
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci    return t;
10353a5a1b3Sopenharmony_ci}
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci/* Append a string to the string buffer */
10653a5a1b3Sopenharmony_civoid pa_strbuf_puts(pa_strbuf *sb, const char *t) {
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci    pa_assert(sb);
10953a5a1b3Sopenharmony_ci    pa_assert(t);
11053a5a1b3Sopenharmony_ci
11153a5a1b3Sopenharmony_ci    pa_strbuf_putsn(sb, t, strlen(t));
11253a5a1b3Sopenharmony_ci}
11353a5a1b3Sopenharmony_ci
11453a5a1b3Sopenharmony_ci/* Append a character to the string buffer */
11553a5a1b3Sopenharmony_civoid pa_strbuf_putc(pa_strbuf *sb, char c) {
11653a5a1b3Sopenharmony_ci    pa_assert(sb);
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci    pa_strbuf_putsn(sb, &c, 1);
11953a5a1b3Sopenharmony_ci}
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_ci/* Append a new chunk to the linked list */
12253a5a1b3Sopenharmony_cistatic void append(pa_strbuf *sb, struct chunk *c) {
12353a5a1b3Sopenharmony_ci    pa_assert(sb);
12453a5a1b3Sopenharmony_ci    pa_assert(c);
12553a5a1b3Sopenharmony_ci
12653a5a1b3Sopenharmony_ci    if (sb->tail) {
12753a5a1b3Sopenharmony_ci        pa_assert(sb->head);
12853a5a1b3Sopenharmony_ci        sb->tail->next = c;
12953a5a1b3Sopenharmony_ci    } else {
13053a5a1b3Sopenharmony_ci        pa_assert(!sb->head);
13153a5a1b3Sopenharmony_ci        sb->head = c;
13253a5a1b3Sopenharmony_ci    }
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    sb->tail = c;
13553a5a1b3Sopenharmony_ci    sb->length += c->length;
13653a5a1b3Sopenharmony_ci    c->next = NULL;
13753a5a1b3Sopenharmony_ci}
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci/* Append up to l bytes of a string to the string buffer */
14053a5a1b3Sopenharmony_civoid pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) {
14153a5a1b3Sopenharmony_ci    struct chunk *c;
14253a5a1b3Sopenharmony_ci
14353a5a1b3Sopenharmony_ci    pa_assert(sb);
14453a5a1b3Sopenharmony_ci    pa_assert(t);
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_ci    if (!l)
14753a5a1b3Sopenharmony_ci        return;
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci    c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l);
15053a5a1b3Sopenharmony_ci    c->length = l;
15153a5a1b3Sopenharmony_ci    memcpy(CHUNK_TO_TEXT(c), t, l);
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci    append(sb, c);
15453a5a1b3Sopenharmony_ci}
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci/* Append a printf() style formatted string to the string buffer. */
15753a5a1b3Sopenharmony_ci/* The following is based on an example from the GNU libc documentation */
15853a5a1b3Sopenharmony_cisize_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
15953a5a1b3Sopenharmony_ci    size_t size = 100;
16053a5a1b3Sopenharmony_ci    struct chunk *c = NULL;
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci    pa_assert(sb);
16353a5a1b3Sopenharmony_ci    pa_assert(format);
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_ci    for(;;) {
16653a5a1b3Sopenharmony_ci        va_list ap;
16753a5a1b3Sopenharmony_ci        int r;
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_ci        c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size);
17053a5a1b3Sopenharmony_ci
17153a5a1b3Sopenharmony_ci        va_start(ap, format);
17253a5a1b3Sopenharmony_ci        r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap);
17353a5a1b3Sopenharmony_ci        CHUNK_TO_TEXT(c)[size-1] = 0;
17453a5a1b3Sopenharmony_ci        va_end(ap);
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci        if (r > -1 && (size_t) r < size) {
17753a5a1b3Sopenharmony_ci            c->length = (size_t) r;
17853a5a1b3Sopenharmony_ci            append(sb, c);
17953a5a1b3Sopenharmony_ci            return (size_t) r;
18053a5a1b3Sopenharmony_ci        }
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci        if (r > -1)    /* glibc 2.1 */
18353a5a1b3Sopenharmony_ci            size = (size_t) r+1;
18453a5a1b3Sopenharmony_ci        else           /* glibc 2.0 */
18553a5a1b3Sopenharmony_ci            size *= 2;
18653a5a1b3Sopenharmony_ci    }
18753a5a1b3Sopenharmony_ci}
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_cibool pa_strbuf_isempty(pa_strbuf *sb) {
19053a5a1b3Sopenharmony_ci    pa_assert(sb);
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci    return sb->length <= 0;
19353a5a1b3Sopenharmony_ci}
194