1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2006 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <sys/types.h> 25#include <stdlib.h> 26#include <string.h> 27#include <stdarg.h> 28#include <stdio.h> 29 30#include <pulse/xmalloc.h> 31#include <pulsecore/macro.h> 32 33#include "strbuf.h" 34 35/* A chunk of the linked list that makes up the string */ 36struct chunk { 37 struct chunk *next; 38 size_t length; 39}; 40 41#define CHUNK_TO_TEXT(c) ((char*) (c) + PA_ALIGN(sizeof(struct chunk))) 42 43struct pa_strbuf { 44 size_t length; 45 struct chunk *head, *tail; 46}; 47 48pa_strbuf *pa_strbuf_new(void) { 49 pa_strbuf *sb; 50 51 sb = pa_xnew(pa_strbuf, 1); 52 sb->length = 0; 53 sb->head = sb->tail = NULL; 54 55 return sb; 56} 57 58void pa_strbuf_free(pa_strbuf *sb) { 59 pa_assert(sb); 60 61 while (sb->head) { 62 struct chunk *c = sb->head; 63 sb->head = sb->head->next; 64 pa_xfree(c); 65 } 66 67 pa_xfree(sb); 68} 69 70/* Make a C string from the string buffer. The caller has to free 71 * string with pa_xfree(). */ 72char *pa_strbuf_to_string(pa_strbuf *sb) { 73 char *t, *e; 74 struct chunk *c; 75 76 pa_assert(sb); 77 78 e = t = pa_xmalloc(sb->length+1); 79 80 for (c = sb->head; c; c = c->next) { 81 pa_assert((size_t) (e-t) <= sb->length); 82 memcpy(e, CHUNK_TO_TEXT(c), c->length); 83 e += c->length; 84 } 85 86 /* Trailing NUL */ 87 *e = 0; 88 89 pa_assert(e == t+sb->length); 90 91 return t; 92} 93 94/* Combination of pa_strbuf_free() and pa_strbuf_to_string() */ 95char *pa_strbuf_to_string_free(pa_strbuf *sb) { 96 char *t; 97 98 pa_assert(sb); 99 t = pa_strbuf_to_string(sb); 100 pa_strbuf_free(sb); 101 102 return t; 103} 104 105/* Append a string to the string buffer */ 106void pa_strbuf_puts(pa_strbuf *sb, const char *t) { 107 108 pa_assert(sb); 109 pa_assert(t); 110 111 pa_strbuf_putsn(sb, t, strlen(t)); 112} 113 114/* Append a character to the string buffer */ 115void pa_strbuf_putc(pa_strbuf *sb, char c) { 116 pa_assert(sb); 117 118 pa_strbuf_putsn(sb, &c, 1); 119} 120 121/* Append a new chunk to the linked list */ 122static void append(pa_strbuf *sb, struct chunk *c) { 123 pa_assert(sb); 124 pa_assert(c); 125 126 if (sb->tail) { 127 pa_assert(sb->head); 128 sb->tail->next = c; 129 } else { 130 pa_assert(!sb->head); 131 sb->head = c; 132 } 133 134 sb->tail = c; 135 sb->length += c->length; 136 c->next = NULL; 137} 138 139/* Append up to l bytes of a string to the string buffer */ 140void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) { 141 struct chunk *c; 142 143 pa_assert(sb); 144 pa_assert(t); 145 146 if (!l) 147 return; 148 149 c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l); 150 c->length = l; 151 memcpy(CHUNK_TO_TEXT(c), t, l); 152 153 append(sb, c); 154} 155 156/* Append a printf() style formatted string to the string buffer. */ 157/* The following is based on an example from the GNU libc documentation */ 158size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { 159 size_t size = 100; 160 struct chunk *c = NULL; 161 162 pa_assert(sb); 163 pa_assert(format); 164 165 for(;;) { 166 va_list ap; 167 int r; 168 169 c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size); 170 171 va_start(ap, format); 172 r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap); 173 CHUNK_TO_TEXT(c)[size-1] = 0; 174 va_end(ap); 175 176 if (r > -1 && (size_t) r < size) { 177 c->length = (size_t) r; 178 append(sb, c); 179 return (size_t) r; 180 } 181 182 if (r > -1) /* glibc 2.1 */ 183 size = (size_t) r+1; 184 else /* glibc 2.0 */ 185 size *= 2; 186 } 187} 188 189bool pa_strbuf_isempty(pa_strbuf *sb) { 190 pa_assert(sb); 191 192 return sb->length <= 0; 193} 194