diff options
Diffstat (limited to 'src/pulse')
-rw-r--r-- | src/pulse/channelmap.c | 49 | ||||
-rw-r--r-- | src/pulse/ext-stream-restore.c | 48 | ||||
-rw-r--r-- | src/pulse/ext-stream-restore.h | 8 | ||||
-rw-r--r-- | src/pulse/operation.c | 1 | ||||
-rw-r--r-- | src/pulse/proplist.c | 384 | ||||
-rw-r--r-- | src/pulse/proplist.h | 2 | ||||
-rw-r--r-- | src/pulse/sample.c | 38 | ||||
-rw-r--r-- | src/pulse/sample.h | 4 | ||||
-rw-r--r-- | src/pulse/utf8.c | 26 | ||||
-rw-r--r-- | src/pulse/utf8.h | 6 | ||||
-rw-r--r-- | src/pulse/volume.c | 62 | ||||
-rw-r--r-- | src/pulse/volume.h | 8 |
12 files changed, 510 insertions, 126 deletions
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 6ff30c26..983b8977 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -434,11 +434,11 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) { const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) { - pa_init_i18n(); - if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) return NULL; + pa_init_i18n(); + return _(pretty_table[pos]); } @@ -448,6 +448,9 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_channel_map_valid(a), 0); + pa_return_val_if_fail(pa_channel_map_valid(b), 0); + if (a->channels != b->channels) return 0; @@ -502,19 +505,19 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { * "mono" here explicitly, because that can be understood as * listing with one channel called "mono". */ - if (strcmp(s, "stereo") == 0) { + if (pa_streq(s, "stereo")) { map.channels = 2; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; goto finish; - } else if (strcmp(s, "surround-40") == 0) { + } else if (pa_streq(s, "surround-40")) { map.channels = 4; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; goto finish; - } else if (strcmp(s, "surround-41") == 0) { + } else if (pa_streq(s, "surround-41")) { map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -522,7 +525,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_LFE; goto finish; - } else if (strcmp(s, "surround-50") == 0) { + } else if (pa_streq(s, "surround-50")) { map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -530,7 +533,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; goto finish; - } else if (strcmp(s, "surround-51") == 0) { + } else if (pa_streq(s, "surround-51")) { map.channels = 6; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -539,7 +542,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; map.map[5] = PA_CHANNEL_POSITION_LFE; goto finish; - } else if (strcmp(s, "surround-71") == 0) { + } else if (pa_streq(s, "surround-71")) { map.channels = 8; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -563,13 +566,13 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { } /* Some special aliases */ - if (strcmp(p, "left") == 0) + if (pa_streq(p, "left")) map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT; - else if (strcmp(p, "right") == 0) + else if (pa_streq(p, "right")) map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT; - else if (strcmp(p, "center") == 0) + else if (pa_streq(p, "center")) map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER; - else if (strcmp(p, "subwoofer") == 0) + else if (pa_streq(p, "subwoofer")) map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER; else { pa_channel_position_t i; @@ -617,11 +620,8 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s pa_assert(map); pa_assert(ss); - if (!pa_channel_map_valid(map)) - return 0; - - if (!pa_sample_spec_valid(ss)) - return 0; + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); return map->channels == ss->channels; } @@ -633,6 +633,9 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_channel_map_valid(a), 0); + pa_return_val_if_fail(pa_channel_map_valid(b), 0); + memset(in_a, 0, sizeof(in_a)); for (i = 0; i < a->channels; i++) @@ -651,6 +654,8 @@ int pa_channel_map_can_balance(const pa_channel_map *map) { pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + for (c = 0; c < map->channels; c++) { switch (map->map[c]) { @@ -687,6 +692,10 @@ int pa_channel_map_can_fade(const pa_channel_map *map) { unsigned c; pa_bool_t front = FALSE, rear = FALSE; + pa_assert(map); + + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + for (c = 0; c < map->channels; c++) { switch (map->map[c]) { @@ -727,6 +736,8 @@ const char* pa_channel_map_to_name(const pa_channel_map *map) { pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + memset(in_map, 0, sizeof(in_map)); for (c = 0; c < map->channels; c++) @@ -779,11 +790,15 @@ const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) { pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + memset(in_map, 0, sizeof(in_map)); for (c = 0; c < map->channels; c++) pa_bitset_set(in_map, map->map[c], TRUE); + pa_init_i18n(); + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, PA_CHANNEL_POSITION_MONO, -1)) return _("Mono"); diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c index 703179c5..469c822a 100644 --- a/src/pulse/ext-stream-restore.c +++ b/src/pulse/ext-stream-restore.c @@ -30,6 +30,7 @@ #include <pulsecore/pstream-util.h> #include "internal.h" +#include "operation.h" #include "ext-stream-restore.h" @@ -191,8 +192,8 @@ pa_operation *pa_ext_stream_restore_write( void *userdata) { uint32_t tag; - pa_operation *o; - pa_tagstruct *t; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); @@ -213,7 +214,15 @@ pa_operation *pa_ext_stream_restore_write( pa_tagstruct_put_boolean(t, apply_immediately); for (; n > 0; n--, data++) { + if (!data->name || !*data->name) + goto fail; + pa_tagstruct_puts(t, data->name); + + if (data->volume.channels > 0 && + !pa_cvolume_compatible_with_channel_map(&data->volume, &data->channel_map)) + goto fail; + pa_tagstruct_put_channel_map(t, &data->channel_map); pa_tagstruct_put_cvolume(t, &data->volume); pa_tagstruct_puts(t, data->device); @@ -224,6 +233,18 @@ pa_operation *pa_ext_stream_restore_write( pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); return o; + +fail: + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + if (t) + pa_tagstruct_free(t); + + pa_context_set_error(c, PA_ERR_INVALID); + return NULL; } pa_operation *pa_ext_stream_restore_delete( @@ -233,8 +254,8 @@ pa_operation *pa_ext_stream_restore_delete( void *userdata) { uint32_t tag; - pa_operation *o; - pa_tagstruct *t; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; const char *const *k; pa_assert(c); @@ -251,13 +272,29 @@ pa_operation *pa_ext_stream_restore_delete( pa_tagstruct_puts(t, "module-stream-restore"); pa_tagstruct_putu32(t, SUBCOMMAND_DELETE); - for (k = s; *k; k++) + for (k = s; *k; k++) { + if (!*k || !**k) + goto fail; + pa_tagstruct_puts(t, *k); + } pa_pstream_send_tagstruct(c->pstream, t); pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); return o; + +fail: + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + if (t) + pa_tagstruct_free(t); + + pa_context_set_error(c, PA_ERR_INVALID); + return NULL; } pa_operation *pa_ext_stream_restore_subscribe( @@ -322,5 +359,4 @@ void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) if (c->ext_stream_restore.callback) c->ext_stream_restore.callback(c, c->ext_stream_restore.userdata); - } diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h index ac01d235..cf9f4ccc 100644 --- a/src/pulse/ext-stream-restore.h +++ b/src/pulse/ext-stream-restore.h @@ -36,10 +36,10 @@ PA_C_DECL_BEGIN * maintained by module-stream-restore. \since 0.9.12 */ typedef struct pa_ext_stream_restore_info { const char *name; /**< Identifier string of the stream. A string like "sink-input-by-role:" or similar followed by some arbitrary property value. */ - pa_channel_map channel_map; /**< The channel map for the volume field */ - pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable */ - const char *device; /**< The sink/source of the stream when it was last seen */ - int mute; /**< The boolean mute state of the stream when it was last seen, if applicable */ + pa_channel_map channel_map; /**< The channel map for the volume field, if applicable */ + pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable and saved */ + const char *device; /**< The sink/source of the stream when it was last seen, if applicable and saved */ + int mute; /**< The boolean mute state of the stream when it was last seen, if applicable and saved */ } pa_ext_stream_restore_info; /** Callback prototype for pa_ext_stream_restore_test(). \since 0.9.12 */ diff --git a/src/pulse/operation.c b/src/pulse/operation.c index 13b470a8..aa2bbc05 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -62,6 +62,7 @@ pa_operation *pa_operation_ref(pa_operation *o) { PA_REFCNT_INC(o); return o; } + void pa_operation_unref(pa_operation *o) { pa_assert(o); pa_assert(PA_REFCNT_VALUE(o) >= 1); diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 282fe5cc..db4c9344 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -24,6 +24,7 @@ #endif #include <string.h> +#include <ctype.h> #include <pulse/xmalloc.h> #include <pulse/utf8.h> @@ -46,7 +47,7 @@ struct property { static pa_bool_t property_name_valid(const char *key) { - if (!pa_utf8_valid(key)) + if (!pa_ascii_valid(key)) return FALSE; if (strlen(key) <= 0) @@ -64,8 +65,6 @@ static void property_free(struct property *prop) { } pa_proplist* pa_proplist_new(void) { - pa_init_i18n(); - return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func)); } @@ -83,6 +82,7 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { pa_assert(p); pa_assert(key); + pa_assert(value); if (!property_name_valid(key) || !pa_utf8_valid(value)) return -1; @@ -104,25 +104,130 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { } /** Will accept only valid UTF-8 */ +static int proplist_setn(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) { + struct property *prop; + pa_bool_t add = FALSE; + char *k, *v; + + pa_assert(p); + pa_assert(key); + pa_assert(value); + + k = pa_xstrndup(key, key_length); + v = pa_xstrndup(value, value_length); + + if (!property_name_valid(k) || !pa_utf8_valid(v)) { + pa_xfree(k); + pa_xfree(v); + return -1; + } + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) { + prop = pa_xnew(struct property, 1); + prop->key = k; + add = TRUE; + } else { + pa_xfree(prop->value); + pa_xfree(k); + } + + prop->value = v; + prop->nbytes = strlen(v)+1; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; +} + +static int proplist_sethex(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) { + struct property *prop; + pa_bool_t add = FALSE; + char *k, *v; + uint8_t *d; + size_t dn; + + pa_assert(p); + pa_assert(key); + pa_assert(value); + + k = pa_xstrndup(key, key_length); + + if (!property_name_valid(k)) { + pa_xfree(k); + return -1; + } + + v = pa_xstrndup(value, value_length); + d = pa_xmalloc(value_length*2+1); + + if ((dn = pa_parsehex(v, d, value_length*2)) == (size_t) -1) { + pa_xfree(k); + pa_xfree(v); + pa_xfree(d); + return -1; + } + + pa_xfree(v); + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) { + prop = pa_xnew(struct property, 1); + prop->key = k; + add = TRUE; + } else { + pa_xfree(prop->value); + pa_xfree(k); + } + + d[dn] = 0; + prop->value = d; + prop->nbytes = dn; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; +} + +/** Will accept only valid UTF-8 */ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) { + struct property *prop; + pa_bool_t add = FALSE; va_list ap; - int r; - char *t; + char *v; pa_assert(p); pa_assert(key); + pa_assert(format); if (!property_name_valid(key) || !pa_utf8_valid(format)) return -1; va_start(ap, format); - t = pa_vsprintf_malloc(format, ap); + v = pa_vsprintf_malloc(format, ap); va_end(ap); - r = pa_proplist_sets(p, key, t); + if (!pa_utf8_valid(v)) + goto fail; - pa_xfree(t); - return r; + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) { + prop = pa_xnew(struct property, 1); + prop->key = pa_xstrdup(key); + add = TRUE; + } else + pa_xfree(prop->value); + + prop->value = v; + prop->nbytes = strlen(v)+1; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; + +fail: + pa_xfree(v); + return -1; } int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) { @@ -131,6 +236,7 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb pa_assert(p); pa_assert(key); + pa_assert(data); if (!property_name_valid(key)) return -1; @@ -142,7 +248,9 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb } else pa_xfree(prop->value); - prop->value = pa_xmemdup(data, nbytes); + prop->value = pa_xmalloc(nbytes+1); + memcpy(prop->value, data, nbytes); + ((char*) prop->value)[nbytes] = 0; prop->nbytes = nbytes; if (add) @@ -183,6 +291,8 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t * pa_assert(p); pa_assert(key); + pa_assert(data); + pa_assert(nbytes); if (!property_name_valid(key)) return -1; @@ -275,9 +385,32 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) { if (!pa_strbuf_isempty(buf)) pa_strbuf_puts(buf, sep); - if ((v = pa_proplist_gets(p, key))) - pa_strbuf_printf(buf, "%s = \"%s\"", key, v); - else { + if ((v = pa_proplist_gets(p, key))) { + const char *t; + + pa_strbuf_printf(buf, "%s = \"", key); + + for (t = v;;) { + size_t h; + + h = strcspn(t, "\""); + + if (h > 0) + pa_strbuf_putsn(buf, t, h); + + t += h; + + if (*t == 0) + break; + + pa_assert(*t == '"'); + pa_strbuf_puts(buf, "\\\""); + + t++; + } + + pa_strbuf_puts(buf, "\""); + } else { const void *value; size_t nbytes; char *c; @@ -304,88 +437,189 @@ char *pa_proplist_to_string(pa_proplist *p) { return t; } -/* Remove all whitepsapce from the beginning and the end of *s. *s may - * be modified. (from conf-parser.c) */ -#define WHITESPACE " \t\n" -#define in_string(c,s) (strchr(s,c) != NULL) - -static char *strip(char *s) { - char *b = s+strspn(s, WHITESPACE); - char *e, *l = NULL; - - for (e = b; *e; e++) - if (!in_string(*e, WHITESPACE)) - l = e; +pa_proplist *pa_proplist_from_string(const char *s) { + enum { + WHITESPACE, + KEY, + AFTER_KEY, + VALUE_START, + VALUE_SIMPLE, + VALUE_DOUBLE_QUOTES, + VALUE_DOUBLE_QUOTES_ESCAPE, + VALUE_TICKS, + VALUE_TICKS_ESCAPED, + VALUE_HEX + } state; + + pa_proplist *pl; + const char *p, *key = NULL, *value = NULL; + size_t key_len = 0, value_len = 0; + + pa_assert(s); + + pl = pa_proplist_new(); + + state = WHITESPACE; + + for (p = s;; p++) { + switch (state) { + + case WHITESPACE: + if (*p == 0) + goto success; + else if (*p == '=') + goto fail; + else if (!isspace(*p)) { + key = p; + state = KEY; + key_len = 1; + } + break; - if (l) - *(l+1) = 0; + case KEY: + if (*p == 0) + goto fail; + else if (*p == '=') + state = VALUE_START; + else if (isspace(*p)) + state = AFTER_KEY; + else + key_len++; + break; - return b; -} + case AFTER_KEY: + if (*p == 0) + goto fail; + else if (*p == '=') + state = VALUE_START; + else if (!isspace(*p)) + goto fail; + break; -pa_proplist *pa_proplist_from_string(const char *str) { - pa_proplist *p; - char *s, *v, *k, *e; + case VALUE_START: + if (*p == 0) + goto fail; + else if (strncmp(p, "hex:", 4) == 0) { + state = VALUE_HEX; + value = p+4; + value_len = 0; + p += 3; + } else if (*p == '\'') { + state = VALUE_TICKS; + value = p+1; + value_len = 0; + } else if (*p == '"') { + state = VALUE_DOUBLE_QUOTES; + value = p+1; + value_len = 0; + } else if (!isspace(*p)) { + state = VALUE_SIMPLE; + value = p; + value_len = 1; + } + break; - pa_assert(str); - pa_assert_se(p = pa_proplist_new()); - pa_assert_se(s = strdup(str)); + case VALUE_SIMPLE: + if (*p == 0 || isspace(*p)) { + if (proplist_setn(pl, key, key_len, value, value_len) < 0) + goto fail; - for (k = s; *k; k = e) { - k = k+strspn(k, WHITESPACE); + if (*p == 0) + goto success; - if (!*k) - break; + state = WHITESPACE; + } else + value_len++; + break; - if (!(v = strchr(k, '='))) { - pa_log("Missing '='."); - break; - } + case VALUE_DOUBLE_QUOTES: + if (*p == 0) + goto fail; + else if (*p == '"') { + char *v; + + v = pa_unescape(pa_xstrndup(value, value_len)); + + if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) { + pa_xfree(v); + goto fail; + } + + pa_xfree(v); + state = WHITESPACE; + } else if (*p == '\\') { + state = VALUE_DOUBLE_QUOTES_ESCAPE; + value_len++; + } else + value_len++; + break; - *v++ = '\0'; - k = strip(k); + case VALUE_DOUBLE_QUOTES_ESCAPE: + if (*p == 0) + goto fail; + else { + state = VALUE_DOUBLE_QUOTES; + value_len++; + } + break; - v = v+strspn(v, WHITESPACE); - if (*v == '"') { - v++; - if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */ - pa_log("Missing '\"' at end of string value."); + case VALUE_TICKS: + if (*p == 0) + goto fail; + else if (*p == '\'') { + char *v; + + v = pa_unescape(pa_xstrndup(value, value_len)); + + if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) { + pa_xfree(v); + goto fail; + } + + pa_xfree(v); + state = WHITESPACE; + } else if (*p == '\\') { + state = VALUE_TICKS_ESCAPED; + value_len++; + } else + value_len++; break; - } - *e++ = '\0'; - pa_proplist_sets(p, k, v); - } else { - uint8_t *blob; - if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') { - pa_log("Value must be a string or \"hex:\""); + case VALUE_TICKS_ESCAPED: + if (*p == 0) + goto fail; + else { + state = VALUE_TICKS; + value_len++; + } break; - } - e = v; - while (in_string(*e, "0123456789abcdefABCDEF")) - ++e; + case VALUE_HEX: + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'F') || + (*p >= 'a' && *p <= 'f')) { + value_len++; + } else if (*p == 0 || isspace(*p)) { - if ((e - v) % 2) { - pa_log("Invalid \"hex:\" value data"); - break; - } + if (proplist_sethex(pl, key, key_len, value, value_len) < 0) + goto fail; - blob = pa_xmalloc((size_t)(e-v)/2); - if (pa_parsehex(v, blob, (e-v)/2) != (size_t)((e-v)/2)) { - pa_log("Invalid \"hex:\" value data"); - pa_xfree(blob); - break; - } + if (*p == 0) + goto success; - pa_proplist_set(p, k, blob, (e-v)/2); - pa_xfree(blob); + state = WHITESPACE; + } else + goto fail; + break; } } - pa_xfree(s); +success: + return MAKE_PROPLIST(pl); - return p; +fail: + pa_proplist_free(pl); + return NULL; } int pa_proplist_contains(pa_proplist *p, const char *key) { diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 990ffd19..d6114062 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -136,7 +136,7 @@ PA_C_DECL_BEGIN #define PA_PROP_MODULE_USAGE "module.usage" #define PA_PROP_MODULE_VERSION "module.version" -/** A property list object. Basically a dictionary with UTF-8 strings +/** A property list object. Basically a dictionary with ASCII strings * as keys and arbitrary data as values. \since 0.9.11 */ typedef struct pa_proplist pa_proplist; diff --git a/src/pulse/sample.c b/src/pulse/sample.c index a6c77345..4b13a337 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -36,7 +36,7 @@ #include "sample.h" -size_t pa_sample_size(const pa_sample_spec *spec) { +size_t pa_sample_size_of_format(pa_sample_format_t f) { static const size_t table[] = { [PA_SAMPLE_U8] = 1, @@ -54,32 +54,44 @@ size_t pa_sample_size(const pa_sample_spec *spec) { [PA_SAMPLE_S24_32BE] = 4 }; + pa_assert(f >= 0); + pa_assert(f < PA_SAMPLE_MAX); + + return table[f]; +} + +size_t pa_sample_size(const pa_sample_spec *spec) { + pa_assert(spec); - pa_assert(spec->format >= 0); - pa_assert(spec->format < PA_SAMPLE_MAX); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); - return table[spec->format]; + return pa_sample_size_of_format(spec->format); } size_t pa_frame_size(const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); return pa_sample_size(spec) * spec->channels; } size_t pa_bytes_per_second(const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); + return spec->rate*pa_frame_size(spec); } pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate); } size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec); } @@ -112,6 +124,9 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_sample_spec_valid(a), 0); + pa_return_val_if_fail(pa_sample_spec_valid(b), 0); + return (a->format == b->format) && (a->rate == b->rate) && @@ -143,7 +158,7 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) { char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { pa_assert(s); - pa_assert(l); + pa_assert(l > 0); pa_assert(spec); pa_init_i18n(); @@ -151,22 +166,25 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { if (!pa_sample_spec_valid(spec)) pa_snprintf(s, l, _("(invalid)")); else - pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); + pa_snprintf(s, l, _("%s %uch %uHz"), pa_sample_format_to_string(spec->format), spec->channels, spec->rate); return s; } char* pa_bytes_snprint(char *s, size_t l, unsigned v) { pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); if (v >= ((unsigned) 1024)*1024*1024) - pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024); + pa_snprintf(s, l, _("%0.1f GiB"), ((double) v)/1024/1024/1024); else if (v >= ((unsigned) 1024)*1024) - pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024); + pa_snprintf(s, l, _("%0.1f MiB"), ((double) v)/1024/1024); else if (v >= (unsigned) 1024) - pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024); + pa_snprintf(s, l, _("%0.1f KiB"), ((double) v)/1024); else - pa_snprintf(s, l, "%u B", (unsigned) v); + pa_snprintf(s, l, _("%u B"), (unsigned) v); return s; } diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 45a481fe..3c05b54a 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -261,6 +261,10 @@ size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE; /** Return the size of a sample with the specific sample type */ size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE; +/** Similar to pa_sample_size() but take a sample format instead of a + * full sample spec. \since 0.9.15 */ +size_t pa_sample_size_of_format(pa_sample_format_t f) PA_GCC_PURE; + /** Calculate the time the specified bytes take to play with the * specified sample type. The return value will always be rounded * down for non-integral return values. */ diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index 7671be46..6b58bde3 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -263,3 +263,29 @@ char* pa_locale_to_utf8 (const char *str) { } #endif + +char *pa_ascii_valid(const char *str) { + const char *p; + pa_assert(str); + + for (p = str; *p; p++) + if ((unsigned char) *p >= 128) + return NULL; + + return (char*) str; +} + +char *pa_ascii_filter(const char *str) { + char *r, *s, *d; + pa_assert(str); + + r = pa_xstrdup(str); + + for (s = r, d = r; *s; s++) + if ((unsigned char) *s < 128) + *(d++) = *s; + + *d = 0; + + return r; +} diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index 4d751953..b9f74952 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -36,9 +36,15 @@ PA_C_DECL_BEGIN /** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */ char *pa_utf8_valid(const char *str) PA_GCC_PURE; +/** Test if the specified strings qualifies as valid 7-bit ASCII. Return the string if so, otherwise NULL. \since 0.9.15 */ +char *pa_ascii_valid(const char *str) PA_GCC_PURE; + /** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */ char *pa_utf8_filter(const char *str); +/** Filter all invalid ASCII characters from the specified string, returning a new fully ASCII valid string. Don't forget to free the returned string with pa_xfree(). \since 0.9.15 */ +char *pa_ascii_filter(const char *str); + /** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */ char* pa_utf8_to_locale (const char *str); diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 01a28e83..54838e89 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -37,6 +37,9 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + pa_return_val_if_fail(pa_cvolume_valid(b), 0); + if (a->channels != b->channels) return 0; @@ -78,7 +81,9 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { uint64_t sum = 0; int i; + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); for (i = 0; i < a->channels; i++) sum += a->values[i]; @@ -91,7 +96,9 @@ pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { pa_volume_t pa_cvolume_max(const pa_cvolume *a) { pa_volume_t m = 0; int i; + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); for (i = 0; i < a->channels; i++) if (a->values[i] > m) @@ -247,11 +254,12 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { return s; } -/** Return non-zero if the volume of all channels is equal to the specified value */ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { unsigned c; pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + for (c = 0; c < a->channels; c++) if (a->values[c] != v) return 0; @@ -266,6 +274,9 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]); @@ -281,6 +292,9 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]); @@ -373,7 +387,11 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa pa_assert(v); pa_assert(from); pa_assert(to); - pa_assert(v->channels == from->channels); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(pa_channel_map_valid(from), NULL); + pa_return_val_if_fail(pa_channel_map_valid(to), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL); if (pa_channel_map_equal(from, to)) return v; @@ -419,15 +437,22 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { pa_assert(v); pa_assert(ss); - if (!pa_cvolume_valid(v)) - return 0; - - if (!pa_sample_spec_valid(ss)) - return 0; + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); return v->channels == ss->channels; } +int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) { + pa_assert(v); + pa_assert(cm); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_channel_map_valid(cm), 0); + + return v->channels == cm->channels; +} + static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r) { int c; pa_volume_t left = 0, right = 0; @@ -465,7 +490,10 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) { pa_assert(v); pa_assert(map); - pa_assert(map->channels == v->channels); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f); + pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); if (!pa_channel_map_can_balance(map)) return 0.0f; @@ -494,12 +522,15 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo pa_volume_t left, nleft, right, nright, m; unsigned c; - pa_assert(map->channels == v->channels); pa_assert(map); pa_assert(v); pa_assert(new_balance >= -1.0f); pa_assert(new_balance <= 1.0f); + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + if (!pa_channel_map_can_balance(map)) return v; @@ -538,6 +569,9 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { pa_assert(v); + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(max != (pa_volume_t) -1, NULL); + for (c = 0; c < v->channels; c++) if (v->values[c] > t) t = v->values[c]; @@ -588,7 +622,10 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) { pa_assert(v); pa_assert(map); - pa_assert(map->channels == v->channels); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f); + pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); if (!pa_channel_map_can_fade(map)) return 0.0f; @@ -608,12 +645,15 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float pa_volume_t front, nfront, rear, nrear, m; unsigned c; - pa_assert(map->channels == v->channels); pa_assert(map); pa_assert(v); pa_assert(new_fade >= -1.0f); pa_assert(new_fade <= 1.0f); + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + if (!pa_channel_map_can_fade(map)) return v; diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 9ad3e9bf..8bfd0687 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -230,10 +230,14 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; /** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to); -/** Return non-zero if the specified volume is compatible with - * the specified sample spec. \since 0.9.13 */ +/** Return non-zero if the specified volume is compatible with the + * specified sample spec. \since 0.9.13 */ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE; +/** Return non-zero if the specified volume is compatible with the + * specified sample spec. \since 0.9.15 */ +int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) PA_GCC_PURE; + /** Calculate a 'balance' value for the specified volume with the * specified channel map. The return value will range from -1.0f * (left) to +1.0f (right). If no balance value is applicable to this |