Merge commit 'origin/master-tx'
[platform/upstream/pulseaudio.git] / src / pulsecore / tagstruct.c
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
8   published by the Free Software Foundation; either version 2.1 of the
9   License, 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   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/time.h>
30 #include <stdarg.h>
31
32 #ifdef HAVE_NETINET_IN_H
33 #include <netinet/in.h>
34 #endif
35
36 #include <pulse/xmalloc.h>
37
38 #include <pulsecore/winsock.h>
39 #include <pulsecore/macro.h>
40
41 #include "tagstruct.h"
42
43 #define MAX_TAG_SIZE (64*1024)
44
45 struct pa_tagstruct {
46     uint8_t *data;
47     size_t length, allocated;
48     size_t rindex;
49
50     pa_bool_t dynamic;
51 };
52
53 pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) {
54     pa_tagstruct*t;
55
56     pa_assert(!data || (data && length));
57
58     t = pa_xnew(pa_tagstruct, 1);
59     t->data = (uint8_t*) data;
60     t->allocated = t->length = data ? length : 0;
61     t->rindex = 0;
62     t->dynamic = !data;
63
64     return t;
65 }
66
67 void pa_tagstruct_free(pa_tagstruct*t) {
68     pa_assert(t);
69
70     if (t->dynamic)
71         pa_xfree(t->data);
72     pa_xfree(t);
73 }
74
75 uint8_t* pa_tagstruct_free_data(pa_tagstruct*t, size_t *l) {
76     uint8_t *p;
77
78     pa_assert(t);
79     pa_assert(t->dynamic);
80     pa_assert(l);
81
82     p = t->data;
83     *l = t->length;
84     pa_xfree(t);
85     return p;
86 }
87
88 static void extend(pa_tagstruct*t, size_t l) {
89     pa_assert(t);
90     pa_assert(t->dynamic);
91
92     if (t->length+l <= t->allocated)
93         return;
94
95     t->data = pa_xrealloc(t->data, t->allocated = t->length+l+100);
96 }
97
98 void pa_tagstruct_puts(pa_tagstruct*t, const char *s) {
99     size_t l;
100     pa_assert(t);
101
102     if (s) {
103         l = strlen(s)+2;
104         extend(t, l);
105         t->data[t->length] = PA_TAG_STRING;
106         strcpy((char*) (t->data+t->length+1), s);
107         t->length += l;
108     } else {
109         extend(t, 1);
110         t->data[t->length] = PA_TAG_STRING_NULL;
111         t->length += 1;
112     }
113 }
114
115 void pa_tagstruct_putu32(pa_tagstruct*t, uint32_t i) {
116     pa_assert(t);
117
118     extend(t, 5);
119     t->data[t->length] = PA_TAG_U32;
120     i = htonl(i);
121     memcpy(t->data+t->length+1, &i, 4);
122     t->length += 5;
123 }
124
125 void pa_tagstruct_putu8(pa_tagstruct*t, uint8_t c) {
126     pa_assert(t);
127
128     extend(t, 2);
129     t->data[t->length] = PA_TAG_U8;
130     *(t->data+t->length+1) = c;
131     t->length += 2;
132 }
133
134 void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss) {
135     uint32_t rate;
136
137     pa_assert(t);
138     pa_assert(ss);
139
140     extend(t, 7);
141     t->data[t->length] = PA_TAG_SAMPLE_SPEC;
142     t->data[t->length+1] = (uint8_t) ss->format;
143     t->data[t->length+2] = ss->channels;
144     rate = htonl(ss->rate);
145     memcpy(t->data+t->length+3, &rate, 4);
146     t->length += 7;
147 }
148
149 void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) {
150     uint32_t tmp;
151
152     pa_assert(t);
153     pa_assert(p);
154
155     extend(t, 5+length);
156     t->data[t->length] = PA_TAG_ARBITRARY;
157     tmp = htonl((uint32_t) length);
158     memcpy(t->data+t->length+1, &tmp, 4);
159     if (length)
160         memcpy(t->data+t->length+5, p, length);
161     t->length += 5+length;
162 }
163
164 void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) {
165     pa_assert(t);
166
167     extend(t, 1);
168     t->data[t->length] = (uint8_t) (b ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);
169     t->length += 1;
170 }
171
172 void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv) {
173     uint32_t tmp;
174     pa_assert(t);
175
176     extend(t, 9);
177     t->data[t->length] = PA_TAG_TIMEVAL;
178     tmp = htonl((uint32_t) tv->tv_sec);
179     memcpy(t->data+t->length+1, &tmp, 4);
180     tmp = htonl((uint32_t) tv->tv_usec);
181     memcpy(t->data+t->length+5, &tmp, 4);
182     t->length += 9;
183 }
184
185 void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u) {
186     uint32_t tmp;
187
188     pa_assert(t);
189
190     extend(t, 9);
191     t->data[t->length] = PA_TAG_USEC;
192     tmp = htonl((uint32_t) (u >> 32));
193     memcpy(t->data+t->length+1, &tmp, 4);
194     tmp = htonl((uint32_t) u);
195     memcpy(t->data+t->length+5, &tmp, 4);
196     t->length += 9;
197 }
198
199 void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t u) {
200     uint32_t tmp;
201
202     pa_assert(t);
203
204     extend(t, 9);
205     t->data[t->length] = PA_TAG_U64;
206     tmp = htonl((uint32_t) (u >> 32));
207     memcpy(t->data+t->length+1, &tmp, 4);
208     tmp = htonl((uint32_t) u);
209     memcpy(t->data+t->length+5, &tmp, 4);
210     t->length += 9;
211 }
212
213 void pa_tagstruct_puts64(pa_tagstruct*t, int64_t u) {
214     uint32_t tmp;
215
216     pa_assert(t);
217
218     extend(t, 9);
219     t->data[t->length] = PA_TAG_S64;
220     tmp = htonl((uint32_t) ((uint64_t) u >> 32));
221     memcpy(t->data+t->length+1, &tmp, 4);
222     tmp = htonl((uint32_t) ((uint64_t) u));
223     memcpy(t->data+t->length+5, &tmp, 4);
224     t->length += 9;
225 }
226
227 void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map) {
228     unsigned i;
229
230     pa_assert(t);
231     extend(t, 2 + (size_t) map->channels);
232
233     t->data[t->length++] = PA_TAG_CHANNEL_MAP;
234     t->data[t->length++] = map->channels;
235
236     for (i = 0; i < map->channels; i ++)
237         t->data[t->length++] = (uint8_t) map->map[i];
238 }
239
240 void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) {
241     unsigned i;
242     pa_volume_t vol;
243
244     pa_assert(t);
245     extend(t, 2 + cvolume->channels * sizeof(pa_volume_t));
246
247     t->data[t->length++] = PA_TAG_CVOLUME;
248     t->data[t->length++] = cvolume->channels;
249
250     for (i = 0; i < cvolume->channels; i ++) {
251         vol = htonl(cvolume->values[i]);
252         memcpy(t->data + t->length, &vol, sizeof(pa_volume_t));
253         t->length += sizeof(pa_volume_t);
254     }
255 }
256
257 void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t vol) {
258     uint32_t u;
259     pa_assert(t);
260
261     extend(t, 5);
262     t->data[t->length] = PA_TAG_VOLUME;
263     u = htonl((uint32_t) vol);
264     memcpy(t->data+t->length+1, &u, 4);
265     t->length += 5;
266 }
267
268 void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
269     void *state = NULL;
270     pa_assert(t);
271     pa_assert(p);
272
273     extend(t, 1);
274
275     t->data[t->length++] = PA_TAG_PROPLIST;
276
277     for (;;) {
278         const char *k;
279         const void *d;
280         size_t l;
281
282         if (!(k = pa_proplist_iterate(p, &state)))
283             break;
284
285         pa_tagstruct_puts(t, k);
286         pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0);
287         pa_tagstruct_putu32(t, (uint32_t) l);
288         pa_tagstruct_put_arbitrary(t, d, l);
289     }
290
291     pa_tagstruct_puts(t, NULL);
292 }
293
294 int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
295     int error = 0;
296     size_t n;
297     char *c;
298
299     pa_assert(t);
300     pa_assert(s);
301
302     if (t->rindex+1 > t->length)
303         return -1;
304
305     if (t->data[t->rindex] == PA_TAG_STRING_NULL) {
306         t->rindex++;
307         *s = NULL;
308         return 0;
309     }
310
311     if (t->rindex+2 > t->length)
312         return -1;
313
314     if (t->data[t->rindex] != PA_TAG_STRING)
315         return -1;
316
317     error = 1;
318     for (n = 0, c = (char*) (t->data+t->rindex+1); t->rindex+1+n < t->length; n++, c++)
319         if (!*c) {
320             error = 0;
321             break;
322         }
323
324     if (error)
325         return -1;
326
327     *s = (char*) (t->data+t->rindex+1);
328
329     t->rindex += n+2;
330     return 0;
331 }
332
333 int pa_tagstruct_getu32(pa_tagstruct*t, uint32_t *i) {
334     pa_assert(t);
335     pa_assert(i);
336
337     if (t->rindex+5 > t->length)
338         return -1;
339
340     if (t->data[t->rindex] != PA_TAG_U32)
341         return -1;
342
343     memcpy(i, t->data+t->rindex+1, 4);
344     *i = ntohl(*i);
345     t->rindex += 5;
346     return 0;
347 }
348
349 int pa_tagstruct_getu8(pa_tagstruct*t, uint8_t *c) {
350     pa_assert(t);
351     pa_assert(c);
352
353     if (t->rindex+2 > t->length)
354         return -1;
355
356     if (t->data[t->rindex] != PA_TAG_U8)
357         return -1;
358
359     *c = t->data[t->rindex+1];
360     t->rindex +=2;
361     return 0;
362 }
363
364 int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss) {
365     pa_assert(t);
366     pa_assert(ss);
367
368     if (t->rindex+7 > t->length)
369         return -1;
370
371     if (t->data[t->rindex] != PA_TAG_SAMPLE_SPEC)
372         return -1;
373
374     ss->format = t->data[t->rindex+1];
375     ss->channels = t->data[t->rindex+2];
376     memcpy(&ss->rate, t->data+t->rindex+3, 4);
377     ss->rate = ntohl(ss->rate);
378
379     t->rindex += 7;
380     return 0;
381 }
382
383 int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length) {
384     uint32_t len;
385
386     pa_assert(t);
387     pa_assert(p);
388
389     if (t->rindex+5+length > t->length)
390         return -1;
391
392     if (t->data[t->rindex] != PA_TAG_ARBITRARY)
393         return -1;
394
395     memcpy(&len, t->data+t->rindex+1, 4);
396     if (ntohl(len) != length)
397         return -1;
398
399     *p = t->data+t->rindex+5;
400     t->rindex += 5+length;
401     return 0;
402 }
403
404 int pa_tagstruct_eof(pa_tagstruct*t) {
405     pa_assert(t);
406
407     return t->rindex >= t->length;
408 }
409
410 const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) {
411     pa_assert(t);
412     pa_assert(t->dynamic);
413     pa_assert(l);
414
415     *l = t->length;
416     return t->data;
417 }
418
419 int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) {
420     pa_assert(t);
421     pa_assert(b);
422
423     if (t->rindex+1 > t->length)
424         return -1;
425
426     if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE)
427         *b = TRUE;
428     else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE)
429         *b = FALSE;
430     else
431         return -1;
432
433     t->rindex +=1;
434     return 0;
435 }
436
437 int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv) {
438
439     pa_assert(t);
440     pa_assert(tv);
441
442     if (t->rindex+9 > t->length)
443         return -1;
444
445     if (t->data[t->rindex] != PA_TAG_TIMEVAL)
446         return -1;
447
448     memcpy(&tv->tv_sec, t->data+t->rindex+1, 4);
449     tv->tv_sec = (time_t) ntohl((uint32_t) tv->tv_sec);
450     memcpy(&tv->tv_usec, t->data+t->rindex+5, 4);
451     tv->tv_usec = (suseconds_t) ntohl((uint32_t) tv->tv_usec);
452     t->rindex += 9;
453     return 0;
454 }
455
456 int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u) {
457     uint32_t tmp;
458
459     pa_assert(t);
460     pa_assert(u);
461
462     if (t->rindex+9 > t->length)
463         return -1;
464
465     if (t->data[t->rindex] != PA_TAG_USEC)
466         return -1;
467
468     memcpy(&tmp, t->data+t->rindex+1, 4);
469     *u = (pa_usec_t) ntohl(tmp) << 32;
470     memcpy(&tmp, t->data+t->rindex+5, 4);
471     *u |= (pa_usec_t) ntohl(tmp);
472     t->rindex +=9;
473     return 0;
474 }
475
476 int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *u) {
477     uint32_t tmp;
478
479     pa_assert(t);
480     pa_assert(u);
481
482     if (t->rindex+9 > t->length)
483         return -1;
484
485     if (t->data[t->rindex] != PA_TAG_U64)
486         return -1;
487
488     memcpy(&tmp, t->data+t->rindex+1, 4);
489     *u = (uint64_t) ntohl(tmp) << 32;
490     memcpy(&tmp, t->data+t->rindex+5, 4);
491     *u |= (uint64_t) ntohl(tmp);
492     t->rindex +=9;
493     return 0;
494 }
495
496 int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *u) {
497     uint32_t tmp;
498
499     pa_assert(t);
500     pa_assert(u);
501
502     if (t->rindex+9 > t->length)
503         return -1;
504
505     if (t->data[t->rindex] != PA_TAG_S64)
506         return -1;
507
508     memcpy(&tmp, t->data+t->rindex+1, 4);
509     *u = (int64_t) ((uint64_t) ntohl(tmp) << 32);
510     memcpy(&tmp, t->data+t->rindex+5, 4);
511     *u |= (int64_t) ntohl(tmp);
512     t->rindex +=9;
513     return 0;
514 }
515
516 int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map) {
517     unsigned i;
518
519     pa_assert(t);
520     pa_assert(map);
521
522     if (t->rindex+2 > t->length)
523         return -1;
524
525     if (t->data[t->rindex] != PA_TAG_CHANNEL_MAP)
526         return -1;
527
528     if ((map->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
529         return -1;
530
531     if (t->rindex+2+map->channels > t->length)
532         return -1;
533
534     for (i = 0; i < map->channels; i ++)
535         map->map[i] = (int8_t) t->data[t->rindex + 2 + i];
536
537     t->rindex += 2 + (size_t) map->channels;
538     return 0;
539 }
540
541 int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) {
542     unsigned i;
543     pa_volume_t vol;
544
545     pa_assert(t);
546     pa_assert(cvolume);
547
548     if (t->rindex+2 > t->length)
549         return -1;
550
551     if (t->data[t->rindex] != PA_TAG_CVOLUME)
552         return -1;
553
554     if ((cvolume->channels = t->data[t->rindex+1]) > PA_CHANNELS_MAX)
555         return -1;
556
557     if (t->rindex+2+cvolume->channels*sizeof(pa_volume_t) > t->length)
558         return -1;
559
560     for (i = 0; i < cvolume->channels; i ++) {
561         memcpy(&vol, t->data + t->rindex + 2 + i * sizeof(pa_volume_t), sizeof(pa_volume_t));
562         cvolume->values[i] = (pa_volume_t) ntohl(vol);
563     }
564
565     t->rindex += 2 + cvolume->channels * sizeof(pa_volume_t);
566     return 0;
567 }
568
569 int pa_tagstruct_get_volume(pa_tagstruct*t, pa_volume_t *vol) {
570     uint32_t u;
571
572     pa_assert(t);
573     pa_assert(vol);
574
575     if (t->rindex+5 > t->length)
576         return -1;
577
578     if (t->data[t->rindex] != PA_TAG_VOLUME)
579         return -1;
580
581     memcpy(&u, t->data+t->rindex+1, 4);
582     *vol = (pa_volume_t) ntohl(u);
583
584     t->rindex += 5;
585     return 0;
586 }
587
588 int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) {
589     size_t saved_rindex;
590
591     pa_assert(t);
592     pa_assert(p);
593
594     if (t->rindex+1 > t->length)
595         return -1;
596
597     if (t->data[t->rindex] != PA_TAG_PROPLIST)
598         return -1;
599
600     saved_rindex = t->rindex;
601     t->rindex++;
602
603     for (;;) {
604         const char *k;
605         const void *d;
606         uint32_t length;
607
608         if (pa_tagstruct_gets(t, &k) < 0)
609             goto fail;
610
611         if (!k)
612             break;
613
614         if (pa_tagstruct_getu32(t, &length) < 0)
615             goto fail;
616
617         if (length > MAX_TAG_SIZE)
618             goto fail;
619
620         if (pa_tagstruct_get_arbitrary(t, &d, length) < 0)
621             goto fail;
622
623         if (pa_proplist_set(p, k, d, length) < 0)
624             goto fail;
625     }
626
627     return 0;
628
629 fail:
630     t->rindex = saved_rindex;
631     return -1;
632 }
633
634 void pa_tagstruct_put(pa_tagstruct *t, ...) {
635     va_list va;
636     pa_assert(t);
637
638     va_start(va, t);
639
640     for (;;) {
641         int tag = va_arg(va, int);
642
643         if (tag == PA_TAG_INVALID)
644             break;
645
646         switch (tag) {
647             case PA_TAG_STRING:
648             case PA_TAG_STRING_NULL:
649                 pa_tagstruct_puts(t, va_arg(va, char*));
650                 break;
651
652             case PA_TAG_U32:
653                 pa_tagstruct_putu32(t, va_arg(va, uint32_t));
654                 break;
655
656             case PA_TAG_U8:
657                 pa_tagstruct_putu8(t, (uint8_t) va_arg(va, int));
658                 break;
659
660             case PA_TAG_U64:
661                 pa_tagstruct_putu64(t, va_arg(va, uint64_t));
662                 break;
663
664             case PA_TAG_SAMPLE_SPEC:
665                 pa_tagstruct_put_sample_spec(t, va_arg(va, pa_sample_spec*));
666                 break;
667
668             case PA_TAG_ARBITRARY: {
669                 void *p = va_arg(va, void*);
670                 size_t size = va_arg(va, size_t);
671                 pa_tagstruct_put_arbitrary(t, p, size);
672                 break;
673             }
674
675             case PA_TAG_BOOLEAN_TRUE:
676             case PA_TAG_BOOLEAN_FALSE:
677                 pa_tagstruct_put_boolean(t, va_arg(va, int));
678                 break;
679
680             case PA_TAG_TIMEVAL:
681                 pa_tagstruct_put_timeval(t, va_arg(va, struct timeval*));
682                 break;
683
684             case PA_TAG_USEC:
685                 pa_tagstruct_put_usec(t, va_arg(va, pa_usec_t));
686                 break;
687
688             case PA_TAG_CHANNEL_MAP:
689                 pa_tagstruct_put_channel_map(t, va_arg(va, pa_channel_map *));
690                 break;
691
692             case PA_TAG_CVOLUME:
693                 pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *));
694                 break;
695
696             case PA_TAG_VOLUME:
697                 pa_tagstruct_put_volume(t, va_arg(va, pa_volume_t));
698                 break;
699
700             case PA_TAG_PROPLIST:
701                 pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *));
702                 break;
703
704             default:
705                 pa_assert_not_reached();
706         }
707     }
708
709     va_end(va);
710 }
711
712 int pa_tagstruct_get(pa_tagstruct *t, ...) {
713     va_list va;
714     int ret = 0;
715
716     pa_assert(t);
717
718     va_start(va, t);
719     while (ret == 0) {
720         int tag = va_arg(va, int);
721
722         if (tag == PA_TAG_INVALID)
723             break;
724
725         switch (tag) {
726             case PA_TAG_STRING:
727             case PA_TAG_STRING_NULL:
728                 ret = pa_tagstruct_gets(t, va_arg(va, const char**));
729                 break;
730
731             case PA_TAG_U32:
732                 ret = pa_tagstruct_getu32(t, va_arg(va, uint32_t*));
733                 break;
734
735             case PA_TAG_U8:
736                 ret = pa_tagstruct_getu8(t, va_arg(va, uint8_t*));
737                 break;
738
739             case PA_TAG_U64:
740                 ret = pa_tagstruct_getu64(t, va_arg(va, uint64_t*));
741                 break;
742
743             case PA_TAG_SAMPLE_SPEC:
744                 ret = pa_tagstruct_get_sample_spec(t, va_arg(va, pa_sample_spec*));
745                 break;
746
747             case PA_TAG_ARBITRARY: {
748                 const void **p = va_arg(va, const void**);
749                 size_t size = va_arg(va, size_t);
750                 ret = pa_tagstruct_get_arbitrary(t, p, size);
751                 break;
752             }
753
754             case PA_TAG_BOOLEAN_TRUE:
755             case PA_TAG_BOOLEAN_FALSE:
756                 ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*));
757                 break;
758
759             case PA_TAG_TIMEVAL:
760                 ret = pa_tagstruct_get_timeval(t, va_arg(va, struct timeval*));
761                 break;
762
763             case PA_TAG_USEC:
764                 ret = pa_tagstruct_get_usec(t, va_arg(va, pa_usec_t*));
765                 break;
766
767             case PA_TAG_CHANNEL_MAP:
768                 ret = pa_tagstruct_get_channel_map(t, va_arg(va, pa_channel_map *));
769                 break;
770
771             case PA_TAG_CVOLUME:
772                 ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *));
773                 break;
774
775             case PA_TAG_VOLUME:
776                 ret = pa_tagstruct_get_volume(t, va_arg(va, pa_volume_t *));
777                 break;
778
779             case PA_TAG_PROPLIST:
780                 ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *));
781                 break;
782
783             default:
784                 pa_assert_not_reached();
785         }
786
787     }
788
789     va_end(va);
790     return ret;
791 }