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