Bug Summary

File:su/./sofia-sip/su_tag_inline.h
Warning:line 72, column 7
Access to field 'tc_next' results in a dereference of a null pointer (loaded from field 'tt_class')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name su_taglist.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-eagerly-assume -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/lib/llvm-7/lib/clang/7.0.1 -D HAVE_CONFIG_H -I . -I ../.. -I ../../libsofia-sip-ua/su/sofia-sip -D SU_DEBUG=0 -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-7/lib/clang/7.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir /drone/src/libsofia-sip-ua/su -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -o /drone/src/scan-build/2021-08-26-202507-364-1 -x c su_taglist.c -faddrsig

su_taglist.c

1/*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25/**@SU_TAG
26 *
27 * @CFILE su_taglist.c
28 *
29 * Implementation of tag items and lists.
30 *
31 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
32 *
33 * @date Created: Tue Feb 20 20:03:38 2001 ppessi
34 */
35
36#include "config.h"
37
38#include <stdlib.h>
39#include <string.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <limits.h>
43
44#if defined(va_copy)
45/* Xyzzy */
46#elif defined(__va_copy)
47#define va_copy(dst, src)__builtin_va_copy(dst, src) __va_copy((dst), (src))__builtin_va_copy((dst),(src))
48#else
49#define va_copy(dst, src)__builtin_va_copy(dst, src) (memcpy(&(dst), &(src), sizeof (va_list)))
50#endif
51
52#include <assert.h>
53
54#include <sofia-sip/su_config.h>
55
56#include <sofia-sip/su_tag.h>
57#include <sofia-sip/su_tag_class.h>
58#include <sofia-sip/su_tag_inline.h>
59#include <sofia-sip/su_tagarg.h>
60#include <sofia-sip/su_string.h>
61
62#ifndef HAVE_STRTOULL1
63#if !((defined(WIN32) || defined(_WIN32)) && (_MSC_VER >= 1800))
64unsigned longlonglong long strtoull(const char *, char **, int);
65#endif
66#endif
67
68/**@defgroup su_tag Tag Item Lists
69 *
70 * Object-oriented tag routines for Sofia utility library.
71 *
72 * The <sofia-sip/su_tag.h> defines a interface to object-oriented tag list routines.
73 * A tag list is a linear list (array) of tag items, tagi_t structures,
74 * terminated by a TAG_END() item. Each tag item has a label, tag, (@c
75 * t_tag) and a value (@c t_value). The tag is a pointer (tag_type_t) to a
76 * structure defining how the value should be interpreted, in other words,
77 * the name and the type of the value. The value or pointer to the actual
78 * value is stored as opaque data (tag_value_t). The tag item structure is
79 * defined as follows:
80 *
81 * @code
82 * typedef struct {
83 * tag_type_t t_tag;
84 * tag_value_t t_value;
85 * } tagi_t;
86 * @endcode
87 *
88 * The tag lists are central concept in the Sofia APIs. The tags lists can
89 * be used to a list of named arguments to a @ref tagarg "@em tagarg"
90 * function, to store variable amount of data in a memory area, and pass
91 * data between processes and threads.
92 *
93 * The tagged argument lists can be used like named arguments in
94 * higher-level languages. The argument list consists of tag-value pairs;
95 * tags specify the name and type of the value. All the tag items are not
96 * necessarily interpreted by the called function, but it can pass the list
97 * to other functions. This feature is used also by the Sofia APIs, the
98 * lower-layer settings and options are frequently passed through the
99 * upper-layer API in the tag lists.
100 *
101 * The tagged argument lists are constructed using special macros that
102 * expand to two function arguments, tag and value. Each tag item macro
103 * checks its arguments type so the tagged argument lists are typesafe if
104 * the list is correctly constructed.
105 *
106 * Each function documents the tags it accepts and also the tags it may pass
107 * to the lower layers (at least in theory).
108 *
109 * @par Special Tags
110 *
111 * There are a new special tags that are used to control and modify the tag
112 * list processing itself. These special tags are as follows:
113 * - TAG_NULL() or TAG_END() - indicates the end of tag list
114 * - TAG_SKIP() - indicates an empty (overwritten) tag item
115 * - TAG_NEXT() - contains a pointer to the next tag list.
116 *
117 * The tag type structures are declared as tag_typedef_t. They can be
118 * defined by the macros found in <sofia-sip/su_tag_class.h>. See nta_tag.c or
119 * su_tag_test.c for an example.
120 *
121 */
122
123/**@class tag_class_s sofia-sip/su_tag_class.h <sofia-sip/su_tag_class.h>
124 *
125 * @brief Virtual function table for @ref su_tag "tags".
126 *
127 * The struct tag_class_s contains virtual function table for tags,
128 * specifying non-default behaviour of different tags. It provides functions
129 * for copying, matching, printing and converting the tagged values.
130 */
131
132#ifdef longlonglong long
133typedef longlonglong long unsigned llu;
134#else
135typedef long unsigned llu;
136#endif
137
138/** Print a tag. */
139int t_snprintf(tagi_t const *t, char b[], size_t size)
140{
141 tag_type_t tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null);
142 int n, m;
143
144 n = snprintf(b, size, "%s::%s: ",
145 tt->tt_ns ? tt->tt_ns : "",
146 tt->tt_name ? tt->tt_name : "null");
147 if (n < 0)
148 return n;
149
150 if ((size_t)n > size)
151 size = n;
152
153 if (tt->tt_snprintftt_class->tc_snprintf)
154 m = tt->tt_snprintftt_class->tc_snprintf(t, b + n, size - n);
155 else
156 m = snprintf(b + n, size - n, "%llx", (llu)t->t_value);
157
158 if (m < 0)
159 return m;
160
161 if (m == 0 && 0 < n && (size_t)n < size)
162 b[--n] = '\0';
163
164 return n + m;
165}
166
167/** Get next tag item from list.
168 */
169tagi_t *tl_next(tagi_t const *t)
170{
171 tag_type_t tt;
172
173 t = t_next(t);
174
175 for (tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null); t && tt->tt_nexttt_class->tc_next; tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null)) {
176 t = tt->tt_nexttt_class->tc_next(t);
177 }
178
179 return (tagi_t *)t;
180}
181
182/**Move a tag list.
183 *
184 * The function tl_tmove() moves the tag list arguments to @a dst. The @a
185 * dst must have big enough for all arguments.
186 *
187 * @param dst pointer to the destination buffer
188 * @param size sizeof @a dst
189 * @param t_tag,t_value,... tag list
190 *
191 * @return
192 * The function tl_tmove() returns number of tag list items initialized.
193 */
194size_t tl_tmove(tagi_t *dst, size_t size,
195 tag_type_t t_tag, tag_value_t t_value, ...)
196{
197 size_t n = 0, N = size / sizeof(tagi_t);
198 tagi_t tagi[1];
199 va_list ap;
200
201 va_start(ap, t_value)__builtin_va_start(ap, t_value);
202
203 tagi->t_tag = t_tag, tagi->t_value = t_value;
204
205 for (;;) {
206 assert((size_t)((char *)&dst[n] - (char *)dst) < size)((void) sizeof (((size_t)((char *)&dst[n] - (char *)dst) <
size) ? 1 : 0), __extension__ ({ if ((size_t)((char *)&dst
[n] - (char *)dst) < size) ; else __assert_fail ("(size_t)((char *)&dst[n] - (char *)dst) < size"
, "su_taglist.c", 206, __extension__ __PRETTY_FUNCTION__); })
)
;
207 if (n < N)
208 dst[n] = *tagi;
209 n++;
210 if (t_end(tagi))
211 break;
212
213 tagi->t_tag = va_arg(ap, tag_type_t)__builtin_va_arg(ap, tag_type_t);
214 tagi->t_value = va_arg(ap, tag_value_t)__builtin_va_arg(ap, tag_value_t);
215 }
216
217 va_end(ap)__builtin_va_end(ap);
218
219 return n;
220}
221
222/**Move a tag list.
223 *
224 * The function tl_move() copies the tag list @a src to the buffer @a
225 * dst. The size of the @a dst list must be at least @c tl_len(src) bytes.
226 *
227 * @param dst pointer to the destination buffer
228 * @param src tag list to be moved
229 *
230 * @return
231 * The function tl_move() returns a pointer to the @a dst list after last
232 * moved element.
233 */
234tagi_t *tl_move(tagi_t *dst, tagi_t const src[])
235{
236 do {
237 dst = t_move(dst, src);
238 }
239 while ((src = t_next(src)));
240
241 return dst;
242}
243
244/** Calculate effective length of a tag list as bytes. */
245size_t tl_len(tagi_t const lst[])
246{
247 size_t len = 0;
248
249 do {
250 len += t_len(lst);
251 }
252 while ((lst = t_next(lst)));
253
254 return len;
255}
256
257/** Calculate the size of extra memory areas associated with tag list. */
258size_t tl_xtra(tagi_t const lst[], size_t offset)
259{
260 size_t xtra = offset;
261
262 for (; lst; lst = t_next(lst))
263 xtra += t_xtra(lst, xtra);
264
265 return xtra - offset;
266}
267
268/** Duplicate a tag list.
269 *
270 * Deep copy the tag list @a src to the buffer @a dst. Memory areas
271 * associated with @a src are copied to buffer at @a **bb.
272 *
273 * This is a rather low-level function. See tl_adup() for a more convenient
274 * functionality.
275 *
276 * The size of the @a dst buffer must be at least @c tl_len(src) bytes. The
277 * size of buffer @a **bb must be at least @c tl_dup_xtra(src) bytes.
278 *
279 * @param[out] dst pointer to the destination buffer
280 * @param[in] src tag list to be duplicated
281 * @param[in,out] bb pointer to pointer to buffer
282 *
283 * @return
284 * A pointer to the @a dst list after last
285 * duplicated taglist element.
286 *
287 * The pointer at @a *bb is updated to the byte after last duplicated memory
288 * area.
289 */
290tagi_t *tl_dup(tagi_t dst[], tagi_t const src[], void **bb)
291{
292 do {
293 dst = t_dup(dst, src, bb);
294 } while ((src = t_next(src)));
295
296 return dst;
297}
298
299
300/** Free a tag list.
301 *
302 * The function tl_free() frees resources associated with a tag list.
303 * In other words, it calls t_free on each tag item on the list.
304 *
305 */
306void tl_free(tagi_t list[])
307{
308 while (list)
309 list = t_free(list);
310}
311
312/** Allocate and duplicate a tag list using memory home. */
313tagi_t *tl_adup(su_home_t *home, tagi_t const lst[])
314{
315 size_t len = tl_len(lst);
316 size_t xtra = tl_xtra(lst, 0);
317 void *b = su_alloc(home, len + xtra);
318 tagi_t *d, *newlst = b;
319
320 void *end = (char *)b + len + xtra;
321 tagi_t *tend = (tagi_t*)((char *)b + len);
322
323 b = (char *)b + len;
324
325 d = tl_dup(newlst, lst, &b);
326
327 assert(b == end)((void) sizeof ((b == end) ? 1 : 0), __extension__ ({ if (b ==
end) ; else __assert_fail ("b == end", "su_taglist.c", 327, __extension__
__PRETTY_FUNCTION__); }))
; assert(tend == d)((void) sizeof ((tend == d) ? 1 : 0), __extension__ ({ if (tend
== d) ; else __assert_fail ("tend == d", "su_taglist.c", 327
, __extension__ __PRETTY_FUNCTION__); }))
; (void)end; (void)tend;
328
329 return newlst;
330}
331
332/** Allocate and duplicate tagged arguments as a tag list using memory home. */
333tagi_t *tl_tlist(su_home_t *home, tag_type_t tag, tag_value_t value, ...)
334{
335 tagi_t *tl;
336 ta_list ta;
337
338 ta_start(ta, tag, value)do { tag_type_t ta_start__tag = (tag); tag_value_t ta_start__value
= (value); __builtin_va_start((ta).ap, (value)); while ((ta_start__tag
) == tag_next && (ta_start__value) != 0) { ta_start__tag
= ((tagi_t *)ta_start__value)->t_tag; if (ta_start__tag ==
tag_null || ta_start__tag == ((void*)0)) break; if (ta_start__tag
== tag_next) { ta_start__value = ((tagi_t *)ta_start__value)
->t_value; } else { ta_start__tag = tag_next; break; } } (
ta).tl->t_tag = ta_start__tag; (ta).tl->t_value = ta_start__value
; if (ta_start__tag != ((void*)0) && ta_start__tag !=
tag_null && ta_start__tag != tag_next) { va_list ta_start__ap
; __builtin_va_copy((ta_start__ap), ((ta).ap)); (ta).tl[1].t_tag
= tag_next; (ta).tl[1].t_value = (tag_value_t)tl_vlist(ta_start__ap
); __builtin_va_end(ta_start__ap); } else { (ta).tl[1].t_value
= 0; (ta).tl[1].t_value = (tag_value_t)0; } } while(0)
;
339 tl = tl_adup(home, ta_args(ta)(ta).tl);
340 ta_end(ta)((((ta).tl[1].t_value) ? (tl_vfree((tagi_t *)((ta).tl[1].t_value
))) : (void)0), (ta).tl[1].t_value = 0, __builtin_va_end((ta)
.ap))
;
341
342 return tl;
343}
344
345/** Find first tag item with type @a tt from list. */
346tagi_t *tl_find(tagi_t const lst[], tag_type_t tt)
347{
348 return (tagi_t *)t_find(tt, lst);
349}
350
351/** Find last tag item with type @a tt from list. */
352tagi_t *tl_find_last(tagi_t const lst[], tag_type_t tt)
353{
354 tagi_t const *last, *next;
355
356 for (next = last = t_find(tt, lst); next; next = t_find(tt, t_next(last)))
357 last = next;
358
359 return (tagi_t *)last;
360}
361
362su_inlinestatic inline
363int t_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
364{
365 if (value == NULL((void*)0))
366 return 0;
367
368 if (tt->tt_class->tc_ref_set)
369 return tt->tt_class->tc_ref_set(tt, ref, value);
370
371 *(tag_value_t *)ref = value->t_value;
372
373 return 1;
374}
375
376static int tl_get(tag_type_t tt, void *p, tagi_t const lst[])
377{
378 tagi_t const *t, *latest = NULL((void*)0);
379
380 assert(tt)((void) sizeof ((tt) ? 1 : 0), __extension__ ({ if (tt) ; else
__assert_fail ("tt", "su_taglist.c", 380, __extension__ __PRETTY_FUNCTION__
); }))
;
381
382 if (tt == NULL((void*)0) || p == NULL((void*)0))
383 return 0;
384
385 if (tt->tt_class == ref_tag_class)
386 tt = (tag_type_t)tt->tt_magic;
387
388 for (t = t_find(tt, lst); t; t = t_find(tt, t_next(t)))
389 latest = t;
390
391 return t_ref_set(tt, p, latest);
392}
393
394/** Find tags from given list. */
395int tl_gets(tagi_t const lst[], tag_type_t tag, tag_value_t value, ...)
396{
397 int n = 0;
398 tagi_t *t;
399 ta_list ta;
400
401 ta_start(ta, tag, value)do { tag_type_t ta_start__tag = (tag); tag_value_t ta_start__value
= (value); __builtin_va_start((ta).ap, (value)); while ((ta_start__tag
) == tag_next && (ta_start__value) != 0) { ta_start__tag
= ((tagi_t *)ta_start__value)->t_tag; if (ta_start__tag ==
tag_null || ta_start__tag == ((void*)0)) break; if (ta_start__tag
== tag_next) { ta_start__value = ((tagi_t *)ta_start__value)
->t_value; } else { ta_start__tag = tag_next; break; } } (
ta).tl->t_tag = ta_start__tag; (ta).tl->t_value = ta_start__value
; if (ta_start__tag != ((void*)0) && ta_start__tag !=
tag_null && ta_start__tag != tag_next) { va_list ta_start__ap
; __builtin_va_copy((ta_start__ap), ((ta).ap)); (ta).tl[1].t_tag
= tag_next; (ta).tl[1].t_value = (tag_value_t)tl_vlist(ta_start__ap
); __builtin_va_end(ta_start__ap); } else { (ta).tl[1].t_value
= 0; (ta).tl[1].t_value = (tag_value_t)0; } } while(0)
;
1
Within the expansion of the macro 'ta_start':
a
Assuming 'ta_start__tag' is not equal to ta_start
402
403 for (t = ta_args(ta)(ta).tl; t; t = (tagi_t *)t_next(t)) {
2
Loop condition is true. Entering loop body
6
Calling 't_next'
404 tag_type_t tt = t->t_tag;
405
406 if (!tt)
3
Taking false branch
407 continue;
408
409 if (tt->tt_class == ref_tag_class) {
4
Taking false branch
410 assert(((tag_type_t)tt->tt_magic)->tt_class->tc_ref_set)((void) sizeof ((((tag_type_t)tt->tt_magic)->tt_class->
tc_ref_set) ? 1 : 0), __extension__ ({ if (((tag_type_t)tt->
tt_magic)->tt_class->tc_ref_set) ; else __assert_fail (
"((tag_type_t)tt->tt_magic)->tt_class->tc_ref_set", "su_taglist.c"
, 410, __extension__ __PRETTY_FUNCTION__); }))
;
411 n += tl_get(tt, (void *)t->t_value, lst);
412 }
413#if !defined(NDEBUG)
414 else if (tt->tt_class && tt->tt_class->tc_ref_set) {
5
Assuming pointer value is null
415 fprintf(stderrstderr, "WARNING: tag %s::%s directly used by tl_gets()\n",
416 tt->tt_ns ? tt->tt_ns : "", tt->tt_name ? tt->tt_name : "");
417 assert(tt->tt_class == ref_tag_class)((void) sizeof ((tt->tt_class == ref_tag_class) ? 1 : 0), __extension__
({ if (tt->tt_class == ref_tag_class) ; else __assert_fail
("tt->tt_class == ref_tag_class", "su_taglist.c", 417, __extension__
__PRETTY_FUNCTION__); }))
;
418 }
419#endif
420 }
421
422 ta_end(ta)((((ta).tl[1].t_value) ? (tl_vfree((tagi_t *)((ta).tl[1].t_value
))) : (void)0), (ta).tl[1].t_value = 0, __builtin_va_end((ta)
.ap))
;
423
424 return n;
425}
426
427/** Find tags from given list.
428 *
429 * Copies values of argument tag list into the reference tags in the tag
430 * list @a lst.
431 *
432 * @sa tl_gets()
433 */
434int tl_tgets(tagi_t lst[], tag_type_t tag, tag_value_t value, ...)
435{
436 int n = 0;
437 tagi_t *t;
438
439 ta_list ta;
440 ta_start(ta, tag, value)do { tag_type_t ta_start__tag = (tag); tag_value_t ta_start__value
= (value); __builtin_va_start((ta).ap, (value)); while ((ta_start__tag
) == tag_next && (ta_start__value) != 0) { ta_start__tag
= ((tagi_t *)ta_start__value)->t_tag; if (ta_start__tag ==
tag_null || ta_start__tag == ((void*)0)) break; if (ta_start__tag
== tag_next) { ta_start__value = ((tagi_t *)ta_start__value)
->t_value; } else { ta_start__tag = tag_next; break; } } (
ta).tl->t_tag = ta_start__tag; (ta).tl->t_value = ta_start__value
; if (ta_start__tag != ((void*)0) && ta_start__tag !=
tag_null && ta_start__tag != tag_next) { va_list ta_start__ap
; __builtin_va_copy((ta_start__ap), ((ta).ap)); (ta).tl[1].t_tag
= tag_next; (ta).tl[1].t_value = (tag_value_t)tl_vlist(ta_start__ap
); __builtin_va_end(ta_start__ap); } else { (ta).tl[1].t_value
= 0; (ta).tl[1].t_value = (tag_value_t)0; } } while(0)
;
441
442 for (t = lst; t; t = (tagi_t *)t_next(t)) {
443 tag_type_t tt = t->t_tag;
444
445 if (!tt)
446 continue;
447
448 if (tt->tt_class == ref_tag_class) {
449 assert(((tag_type_t)tt->tt_magic)->tt_class->tc_ref_set)((void) sizeof ((((tag_type_t)tt->tt_magic)->tt_class->
tc_ref_set) ? 1 : 0), __extension__ ({ if (((tag_type_t)tt->
tt_magic)->tt_class->tc_ref_set) ; else __assert_fail (
"((tag_type_t)tt->tt_magic)->tt_class->tc_ref_set", "su_taglist.c"
, 449, __extension__ __PRETTY_FUNCTION__); }))
;
450 n += tl_get(tt, (void *)t->t_value, ta_args(ta)(ta).tl);
451 }
452#if !defined(NDEBUG)
453 else if (tt->tt_class->tc_ref_set) {
454 fprintf(stderrstderr, "WARNING: tag %s::%s used in tl_tgets(lst)\n",
455 tt->tt_ns, tt->tt_name);
456 assert(tt->tt_class == ref_tag_class)((void) sizeof ((tt->tt_class == ref_tag_class) ? 1 : 0), __extension__
({ if (tt->tt_class == ref_tag_class) ; else __assert_fail
("tt->tt_class == ref_tag_class", "su_taglist.c", 456, __extension__
__PRETTY_FUNCTION__); }))
;
457 }
458#endif
459 }
460
461 ta_end(ta)((((ta).tl[1].t_value) ? (tl_vfree((tagi_t *)((ta).tl[1].t_value
))) : (void)0), (ta).tl[1].t_value = 0, __builtin_va_end((ta)
.ap))
;
462
463 return n;
464}
465
466
467/** Filter an element in tag list */
468tagi_t *t_filter(tagi_t *dst,
469 tagi_t const filter[],
470 tagi_t const *src,
471 void **bb)
472{
473 tag_type_t tt = TAG_TYPE_OF(src)((src) && (src)->t_tag ? (src)->t_tag : tag_null
)
;
474 tagi_t const *f;
475
476 if (dst) {
477 for (f = filter; f; f = t_next(f)) {
478 if (TAG_TYPE_OF(f)((f) && (f)->t_tag ? (f)->t_tag : tag_null)->tt_filtertt_class->tc_filter)
479 dst = TAG_TYPE_OF(f)((f) && (f)->t_tag ? (f)->t_tag : tag_null)->tt_filtertt_class->tc_filter(dst, f, src, bb);
480 else if (f->t_tag == tt)
481 dst = t_dup(dst, src, bb);
482 }
483 }
484 else {
485 size_t d = 0;
486
487 for (f = filter; f; f = t_next(f)) {
488 tag_type_t tt_f = TAG_TYPE_OF(f)((f) && (f)->t_tag ? (f)->t_tag : tag_null);
489 if (tt_f->tt_filtertt_class->tc_filter)
490 d += (size_t)tt_f->tt_filtertt_class->tc_filter(NULL((void*)0), f, src, bb);
491 else if (tt == f->t_tag) {
492 d += t_len(src);
493 *bb = (char *)*bb + t_xtra(src, (size_t)*bb);
494 }
495 }
496
497 dst = (tagi_t *)d;
498 }
499
500 return dst;
501}
502
503/** Make filtered copy of a tag list @a src with @a filter to @a dst.
504 *
505 * Each tag in @a src is checked against tags in list @a filter. If the tag
506 * is in the @a filter list, or there is a special filter tag in the list
507 * which matches with the tag in @a src, the tag is duplicated to @a dst using
508 * memory buffer in @a b.
509 *
510 * When @a dst is NULL, this function calculates the size of the filtered list.
511 *
512 * @sa tl_afilter(), tl_tfilter(), tl_filtered_tlist(),
513 * TAG_FILTER(), TAG_ANY(), #ns_tag_class
514 */
515tagi_t *tl_filter(tagi_t dst[],
516 tagi_t const filter[],
517 tagi_t const src[],
518 void **b)
519{
520 tagi_t const *s;
521 tagi_t *d;
522
523 if (dst) {
524 for (s = src, d = dst; s; s = t_next(s))
525 d = t_filter(d, filter, s, b);
526 }
527 else {
528 size_t rv = 0;
529
530 for (s = src, d = dst; s; s = t_next(s)) {
531 d = t_filter(NULL((void*)0), filter, s, b);
532 rv += (char *)d - (char *)NULL((void*)0);
533 }
534
535 d = (tagi_t *)rv;
536 }
537
538 return d;
539}
540
541
542
543/**Filter a tag list.
544 *
545 * The function tl_afilter() will build a tag list containing tags specified
546 * in @a filter and extracted from @a src. It will allocate the memory used by
547 * tag list via the specified memory @a home, which may be also @c NULL.
548 *
549 * @sa tl_afilter(), tl_tfilter(), tl_filtered_tlist(),
550 * TAG_FILTER(), TAG_ANY(), #ns_tag_class
551 */
552tagi_t *tl_afilter(su_home_t *home, tagi_t const filter[], tagi_t const src[])
553{
554 tagi_t *dst, *d, *t_end = NULL((void*)0);
555 void *b, *end = NULL((void*)0);
556 size_t len;
557
558 /* Calculate length of the result */
559 t_end = tl_filter(NULL((void*)0), filter, src, &end);
560 len = ((char *)t_end - (char *)NULL((void*)0)) + ((char *)end - (char*)NULL((void*)0));
561
562 if (len == 0)
563 return NULL((void*)0);
564
565 /* Allocate the result */
566 if (!(dst = su_alloc(home, len)))
567 return NULL((void*)0);
568
569 /* Build the result */
570 b = (dst + (t_end - (tagi_t *)NULL((void*)0)));
571 d = tl_filter(dst, filter, src, (void **)&b);
572
573 /* Ensure that everything is consistent */
574 assert(d == dst + (t_end - (tagi_t *)NULL))((void) sizeof ((d == dst + (t_end - (tagi_t *)((void*)0))) ?
1 : 0), __extension__ ({ if (d == dst + (t_end - (tagi_t *)(
(void*)0))) ; else __assert_fail ("d == dst + (t_end - (tagi_t *)NULL)"
, "su_taglist.c", 574, __extension__ __PRETTY_FUNCTION__); })
)
;
575 assert(b == (char *)dst + len)((void) sizeof ((b == (char *)dst + len) ? 1 : 0), __extension__
({ if (b == (char *)dst + len) ; else __assert_fail ("b == (char *)dst + len"
, "su_taglist.c", 575, __extension__ __PRETTY_FUNCTION__); })
)
;
576
577 return dst;
578}
579
580/** Filter tag list @a src with given tags.
581 *
582 * @sa tl_afilter(), tl_filtered_tlist(), TAG_FILTER(), TAG_ANY(), #ns_tag_class
583 */
584tagi_t *tl_tfilter(su_home_t *home, tagi_t const src[],
585 tag_type_t tag, tag_value_t value, ...)
586{
587 tagi_t *tl;
588 ta_list ta;
589 ta_start(ta, tag, value)do { tag_type_t ta_start__tag = (tag); tag_value_t ta_start__value
= (value); __builtin_va_start((ta).ap, (value)); while ((ta_start__tag
) == tag_next && (ta_start__value) != 0) { ta_start__tag
= ((tagi_t *)ta_start__value)->t_tag; if (ta_start__tag ==
tag_null || ta_start__tag == ((void*)0)) break; if (ta_start__tag
== tag_next) { ta_start__value = ((tagi_t *)ta_start__value)
->t_value; } else { ta_start__tag = tag_next; break; } } (
ta).tl->t_tag = ta_start__tag; (ta).tl->t_value = ta_start__value
; if (ta_start__tag != ((void*)0) && ta_start__tag !=
tag_null && ta_start__tag != tag_next) { va_list ta_start__ap
; __builtin_va_copy((ta_start__ap), ((ta).ap)); (ta).tl[1].t_tag
= tag_next; (ta).tl[1].t_value = (tag_value_t)tl_vlist(ta_start__ap
); __builtin_va_end(ta_start__ap); } else { (ta).tl[1].t_value
= 0; (ta).tl[1].t_value = (tag_value_t)0; } } while(0)
;
590 tl = tl_afilter(home, ta_args(ta)(ta).tl, src);
591 ta_end(ta)((((ta).tl[1].t_value) ? (tl_vfree((tagi_t *)((ta).tl[1].t_value
))) : (void)0), (ta).tl[1].t_value = 0, __builtin_va_end((ta)
.ap))
;
592 return tl;
593}
594
595/** Create a filtered tag list.
596 *
597 * @sa tl_afilter(), tl_tfilter(), TAG_FILTER(), TAG_ANY(), #ns_tag_class
598 */
599tagi_t *tl_filtered_tlist(su_home_t *home, tagi_t const filter[],
600 tag_type_t tag, tag_value_t value, ...)
601{
602 tagi_t *tl;
603 ta_list ta;
604
605 ta_start(ta, tag, value)do { tag_type_t ta_start__tag = (tag); tag_value_t ta_start__value
= (value); __builtin_va_start((ta).ap, (value)); while ((ta_start__tag
) == tag_next && (ta_start__value) != 0) { ta_start__tag
= ((tagi_t *)ta_start__value)->t_tag; if (ta_start__tag ==
tag_null || ta_start__tag == ((void*)0)) break; if (ta_start__tag
== tag_next) { ta_start__value = ((tagi_t *)ta_start__value)
->t_value; } else { ta_start__tag = tag_next; break; } } (
ta).tl->t_tag = ta_start__tag; (ta).tl->t_value = ta_start__value
; if (ta_start__tag != ((void*)0) && ta_start__tag !=
tag_null && ta_start__tag != tag_next) { va_list ta_start__ap
; __builtin_va_copy((ta_start__ap), ((ta).ap)); (ta).tl[1].t_tag
= tag_next; (ta).tl[1].t_value = (tag_value_t)tl_vlist(ta_start__ap
); __builtin_va_end(ta_start__ap); } else { (ta).tl[1].t_value
= 0; (ta).tl[1].t_value = (tag_value_t)0; } } while(0)
;
606 tl = tl_afilter(home, filter, ta_args(ta)(ta).tl);
607 ta_end(ta)((((ta).tl[1].t_value) ? (tl_vfree((tagi_t *)((ta).tl[1].t_value
))) : (void)0), (ta).tl[1].t_value = 0, __builtin_va_end((ta)
.ap))
;
608
609 return tl;
610}
611
612
613/** Remove listed tags from the list @a lst. */
614int tl_tremove(tagi_t lst[], tag_type_t tag, tag_value_t value, ...)
615{
616 tagi_t *l, *l_next;
617 int retval = 0;
618 ta_list ta;
619
620 ta_start(ta, tag, value)do { tag_type_t ta_start__tag = (tag); tag_value_t ta_start__value
= (value); __builtin_va_start((ta).ap, (value)); while ((ta_start__tag
) == tag_next && (ta_start__value) != 0) { ta_start__tag
= ((tagi_t *)ta_start__value)->t_tag; if (ta_start__tag ==
tag_null || ta_start__tag == ((void*)0)) break; if (ta_start__tag
== tag_next) { ta_start__value = ((tagi_t *)ta_start__value)
->t_value; } else { ta_start__tag = tag_next; break; } } (
ta).tl->t_tag = ta_start__tag; (ta).tl->t_value = ta_start__value
; if (ta_start__tag != ((void*)0) && ta_start__tag !=
tag_null && ta_start__tag != tag_next) { va_list ta_start__ap
; __builtin_va_copy((ta_start__ap), ((ta).ap)); (ta).tl[1].t_tag
= tag_next; (ta).tl[1].t_value = (tag_value_t)tl_vlist(ta_start__ap
); __builtin_va_end(ta_start__ap); } else { (ta).tl[1].t_value
= 0; (ta).tl[1].t_value = (tag_value_t)0; } } while(0)
;
621
622 for (l = lst; l; l = l_next) {
623 if ((l_next = (tagi_t *)t_next(l))) {
624 if (tl_find(ta_args(ta)(ta).tl, l->t_tag))
625 l->t_tag = tag_skip;
626 else
627 retval++;
628 }
629 }
630
631 ta_end(ta)((((ta).tl[1].t_value) ? (tl_vfree((tagi_t *)((ta).tl[1].t_value
))) : (void)0), (ta).tl[1].t_value = 0, __builtin_va_end((ta)
.ap))
;
632
633 return retval;
634}
635
636/** Calculate length of a tag list with a @c va_list. */
637size_t tl_vlen(va_list ap)
638{
639 size_t len = 0;
640 tagi_t tagi[2] = {{ NULL((void*)0) }};
641
642 do {
643 tagi->t_tag = va_arg(ap, tag_type_t )__builtin_va_arg(ap, tag_type_t);
644 tagi->t_value = va_arg(ap, tag_value_t)__builtin_va_arg(ap, tag_value_t);
645 len += sizeof(tagi_t);
646 } while (!t_end(tagi));
647
648 return len;
649}
650
651/** Convert va_list to tag list */
652tagi_t *tl_vlist(va_list ap)
653{
654 tagi_t *t, *rv;
655 va_list aq;
656
657 va_copy(aq, ap)__builtin_va_copy(aq, ap);
658 rv = malloc(tl_vlen(aq));
659 va_end(aq)__builtin_va_end(aq);
660
661 for (t = rv; t; t++) {
662 t->t_tag = va_arg(ap, tag_type_t)__builtin_va_arg(ap, tag_type_t);
663 t->t_value = va_arg(ap, tag_value_t)__builtin_va_arg(ap, tag_value_t);
664
665 if (t_end(t))
666 break;
667 }
668
669 return rv;
670}
671
672tagi_t *tl_vlist2(tag_type_t tag, tag_value_t value, va_list ap)
673{
674 tagi_t *t, *rv;
675 tagi_t tagi[1];
676 size_t size;
677
678 tagi->t_tag = tag, tagi->t_value = value;
679
680 if (!t_end(tagi)) {
681 va_list aq;
682 va_copy(aq, ap)__builtin_va_copy(aq, ap);
683 size = sizeof(tagi) + tl_vlen(aq);
684 va_end(aq)__builtin_va_end(aq);
685 }
686 else
687 size = sizeof(tagi);
688
689 t = rv = malloc(size);
690
691 for (;t;) {
692 *t++ = *tagi;
693
694 if (t_end(tagi))
695 break;
696
697 tagi->t_tag = va_arg(ap, tag_type_t)__builtin_va_arg(ap, tag_type_t);
698 tagi->t_value = va_arg(ap, tag_value_t)__builtin_va_arg(ap, tag_value_t);
699 }
700
701 assert((char *)rv + size == (char *)t)((void) sizeof (((char *)rv + size == (char *)t) ? 1 : 0), __extension__
({ if ((char *)rv + size == (char *)t) ; else __assert_fail (
"(char *)rv + size == (char *)t", "su_taglist.c", 701, __extension__
__PRETTY_FUNCTION__); }))
;
702
703 return rv;
704}
705
706/** Make a tag list until TAG_NEXT() or TAG_END() */
707tagi_t *tl_list(tag_type_t tag, tag_value_t value, ...)
708{
709 va_list ap;
710 tagi_t *t;
711
712 va_start(ap, value)__builtin_va_start(ap, value);
713 t = tl_vlist2(tag, value, ap);
714 va_end(ap)__builtin_va_end(ap);
715
716 return t;
717}
718
719/** Calculate length of a linear tag list. */
720size_t tl_vllen(tag_type_t tag, tag_value_t value, va_list ap)
721{
722 size_t len = sizeof(tagi_t);
723 tagi_t const *next;
724 tagi_t tagi[3];
725
726 tagi[0].t_tag = tag;
727 tagi[0].t_value = value;
728 tagi[1].t_tag = tag_any;
729 tagi[1].t_value = 0;
730
731 for (;;) {
732 next = tl_next(tagi);
733 if (next != tagi + 1)
734 break;
735
736 if (tagi->t_tag != tag_skip)
737 len += sizeof(tagi_t);
738 tagi->t_tag = va_arg(ap, tag_type_t)__builtin_va_arg(ap, tag_type_t);
739 tagi->t_value = va_arg(ap, tag_value_t)__builtin_va_arg(ap, tag_value_t);
740 }
741
742 for (; next; next = tl_next(next))
743 len += sizeof(tagi_t);
744
745 return len;
746}
747
748/** Make a linear tag list. */
749tagi_t *tl_vllist(tag_type_t tag, tag_value_t value, va_list ap)
750{
751 va_list aq;
752 tagi_t *t, *rv;
753 tagi_t const *next;
754 tagi_t tagi[2];
755
756 size_t size;
757
758 va_copy(aq, ap)__builtin_va_copy(aq, ap);
759 size = tl_vllen(tag, value, aq);
760 va_end(aq)__builtin_va_end(aq);
761
762 t = rv = malloc(size);
763 if (rv == NULL((void*)0))
764 return rv;
765
766 tagi[0].t_tag = tag;
767 tagi[0].t_value = value;
768 tagi[1].t_tag = tag_any;
769 tagi[1].t_value = 0;
770
771 for (;;) {
772 next = tl_next(tagi);
773 if (next != tagi + 1)
774 break;
775
776 if (tagi->t_tag != tag_skip)
777 *t++ = *tagi;
778
779 tagi->t_tag = va_arg(ap, tag_type_t)__builtin_va_arg(ap, tag_type_t);
780 tagi->t_value = va_arg(ap, tag_value_t)__builtin_va_arg(ap, tag_value_t);
781 }
782
783 for (; next; next = tl_next(next))
784 *t++ = *next;
785
786 t->t_tag = NULL((void*)0); t->t_value = 0; t++;
787
788 assert((char *)rv + size == (char *)t)((void) sizeof (((char *)rv + size == (char *)t) ? 1 : 0), __extension__
({ if ((char *)rv + size == (char *)t) ; else __assert_fail (
"(char *)rv + size == (char *)t", "su_taglist.c", 788, __extension__
__PRETTY_FUNCTION__); }))
;
789
790 return rv;
791}
792
793/** Make a linear tag list until TAG_END().
794 *
795 */
796tagi_t *tl_llist(tag_type_t tag, tag_value_t value, ...)
797{
798 va_list ap;
799 tagi_t *t;
800
801 va_start(ap, value)__builtin_va_start(ap, value);
802 t = tl_vllist(tag, value, ap);
803 va_end(ap)__builtin_va_end(ap);
804
805 return t;
806}
807
808/** Free a tag list allocated by tl_list(), tl_llist() or tl_vlist(). */
809void tl_vfree(tagi_t *t)
810{
811 if (t)
812 free(t);
813}
814
815/** Convert a string to the a value of a tag. */
816int t_scan(tag_type_t tt, su_home_t *home, char const *s,
817 tag_value_t *return_value)
818{
819 if (tt == NULL((void*)0) || s == NULL((void*)0) || return_value == NULL((void*)0))
820 return -1;
821
822 if (tt->tt_class->tc_scan) {
823 return tt->tt_class->tc_scan(tt, home, s, return_value);
824 }
825 else { /* Not implemented */
826 *return_value = (tag_value_t)0;
827 return -2;
828 }
829}
830
831
832/* ====================================================================== */
833/* null tag */
834
835static
836tagi_t const *t_null_next(tagi_t const *t)
837{
838 return NULL((void*)0);
839}
840
841static
842tagi_t *t_null_move(tagi_t *dst, tagi_t const *src)
843{
844 memset(dst, 0, sizeof(*dst));
845 return dst + 1;
846}
847
848static
849tagi_t *t_null_dup(tagi_t *dst, tagi_t const *src, void **bb)
850{
851 memset(dst, 0, sizeof(*dst));
852 return dst + 1;
853}
854
855static
856tagi_t const * t_null_find(tag_type_t tt, tagi_t const lst[])
857{
858 return NULL((void*)0);
859}
860
861tagi_t *t_null_filter(tagi_t *dst,
862 tagi_t const filter[],
863 tagi_t const *src,
864 void **bb)
865{
866 if (TAG_TYPE_OF(src)((src) && (src)->t_tag ? (src)->t_tag : tag_null
)
== tag_null) {
867 if (dst) {
868 dst->t_tag = NULL((void*)0);
869 dst->t_value = 0;
870 }
871 return dst + 1;
872 }
873 return dst;
874}
875
876tag_class_t null_tag_class[1] =
877 {{
878 sizeof(null_tag_class),
879 /* tc_next */ t_null_next,
880 /* tc_len */ NULL((void*)0),
881 /* tc_move */ t_null_move,
882 /* tc_xtra */ NULL((void*)0),
883 /* tc_dup */ t_null_dup,
884 /* tc_free */ NULL((void*)0),
885 /* tc_find */ t_null_find,
886 /* tc_snprintf */ NULL((void*)0),
887 /* tc_filter */ t_null_filter,
888 /* tc_ref_set */ NULL((void*)0),
889 /* tc_scan */ NULL((void*)0),
890 }};
891
892tag_typedef_t tag_null = TAG_TYPEDEF(tag_null, null){{ "", "tag_null", null_tag_class, 0 }};
893
894/* ====================================================================== */
895/* end tag */
896
897tagi_t *t_end_filter(tagi_t *dst,
898 tagi_t const filter[],
899 tagi_t const *src,
900 void **bb)
901{
902 return dst;
903}
904
905tag_class_t end_tag_class[1] =
906 {{
907 sizeof(end_tag_class),
908 /* tc_next */ NULL((void*)0),
909 /* tc_len */ NULL((void*)0),
910 /* tc_move */ NULL((void*)0),
911 /* tc_xtra */ NULL((void*)0),
912 /* tc_dup */ NULL((void*)0),
913 /* tc_free */ NULL((void*)0),
914 /* tc_find */ NULL((void*)0),
915 /* tc_snprintf */ NULL((void*)0),
916 /* tc_filter */ t_end_filter,
917 /* tc_ref_set */ NULL((void*)0),
918 /* tc_scan */ NULL((void*)0),
919 }};
920
921/* ====================================================================== */
922/* skip tag - placeholder in tag list */
923
924static
925tagi_t const *t_skip_next(tagi_t const *t)
926{
927 return t + 1;
928}
929
930static
931tagi_t *t_skip_move(tagi_t *dst, tagi_t const *src)
932{
933 return dst;
934}
935
936static
937size_t t_skip_len(tagi_t const *t)
938{
939 return 0;
940}
941
942static
943tagi_t *t_skip_dup(tagi_t *dst, tagi_t const *src, void **bb)
944{
945 return dst;
946}
947
948static
949tagi_t *t_skip_filter(tagi_t *dst,
950 tagi_t const filter[],
951 tagi_t const *src,
952 void **bb)
953{
954 return dst;
955}
956
957tag_class_t skip_tag_class[1] =
958 {{
959 sizeof(skip_tag_class),
960 /* tc_next */ t_skip_next,
961 /* tc_len */ t_skip_len,
962 /* tc_move */ t_skip_move,
963 /* tc_xtra */ NULL((void*)0),
964 /* tc_dup */ t_skip_dup,
965 /* tc_free */ NULL((void*)0),
966 /* tc_find */ t_null_find,
967 /* tc_snprintf */ NULL((void*)0),
968 /* tc_filter */ t_skip_filter,
969 /* tc_ref_set */ NULL((void*)0),
970 /* tc_scan */ NULL((void*)0),
971 }};
972
973tag_typedef_t tag_skip = TAG_TYPEDEF(tag_skip, skip){{ "", "tag_skip", skip_tag_class, 0 }};
974
975/* ====================================================================== */
976/* next tag - jump to next tag list */
977
978static
979tagi_t const *t_next_next(tagi_t const *t)
980{
981 return (tagi_t *)(t->t_value);
982}
983
984static
985tagi_t *t_next_move(tagi_t *dst, tagi_t const *src)
986{
987 if (!src->t_value)
988 return t_null_move(dst, src);
989 return dst;
990}
991
992static
993size_t t_next_len(tagi_t const *t)
994{
995 if (!t->t_value)
996 return sizeof(*t);
997 return 0;
998}
999
1000static
1001tagi_t *t_next_dup(tagi_t *dst, tagi_t const *src, void **bb)
1002{
1003 if (!src->t_value)
1004 return t_null_dup(dst, src, bb);
1005 return dst;
1006}
1007
1008static
1009tagi_t *t_next_filter(tagi_t *dst,
1010 tagi_t const filter[],
1011 tagi_t const *src,
1012 void **bb)
1013{
1014 return dst;
1015}
1016
1017tag_class_t next_tag_class[1] =
1018 {{
1019 sizeof(next_tag_class),
1020 /* tc_next */ t_next_next,
1021 /* tc_len */ t_next_len,
1022 /* tc_move */ t_next_move,
1023 /* tc_xtra */ NULL((void*)0),
1024 /* tc_dup */ t_next_dup,
1025 /* tc_free */ NULL((void*)0),
1026 /* tc_find */ t_null_find,
1027 /* tc_snprintf */ NULL((void*)0),
1028 /* tc_filter */ t_next_filter,
1029 /* tc_ref_set */ NULL((void*)0),
1030 /* tc_scan */ NULL((void*)0),
1031 }};
1032
1033tag_typedef_t tag_next = TAG_TYPEDEF(tag_next, next){{ "", "tag_next", next_tag_class, 0 }};
1034
1035/* ====================================================================== */
1036/* filter tag - use function to filter tag */
1037
1038static
1039tagi_t *t_filter_with(tagi_t *dst,
1040 tagi_t const *t,
1041 tagi_t const *src,
1042 void **bb)
1043{
1044 tag_filter_f *function;
1045
1046 if (!src || !t)
1047 return dst;
1048
1049 function = (tag_filter_f *)t->t_value;
1050
1051 if (!function || !function(t, src))
1052 return dst;
1053
1054 if (dst) {
1055 return t_dup(dst, src, bb);
1056 }
1057 else {
1058 dst = (tagi_t *)((char *)dst + t_len(src));
1059 *bb = (char *)*bb + t_xtra(src, (size_t)*bb);
1060 return dst;
1061 }
1062}
1063
1064tag_class_t filter_tag_class[1] =
1065 {{
1066 sizeof(filter_tag_class),
1067 /* tc_next */ NULL((void*)0),
1068 /* tc_len */ NULL((void*)0),
1069 /* tc_move */ NULL((void*)0),
1070 /* tc_xtra */ NULL((void*)0),
1071 /* tc_dup */ NULL((void*)0),
1072 /* tc_free */ NULL((void*)0),
1073 /* tc_find */ NULL((void*)0),
1074 /* tc_snprintf */ NULL((void*)0),
1075 /* tc_filter */ t_filter_with,
1076 /* tc_ref_set */ NULL((void*)0),
1077 /* tc_scan */ NULL((void*)0),
1078 }};
1079
1080/** Filter tag - apply function in order to filter tag. */
1081tag_typedef_t tag_filter = TAG_TYPEDEF(tag_filter, filter){{ "", "tag_filter", filter_tag_class, 0 }};
1082
1083/* ====================================================================== */
1084/* any tag - match to any tag when filtering */
1085
1086static
1087tagi_t *t_any_filter(tagi_t *dst,
1088 tagi_t const filter[],
1089 tagi_t const *src,
1090 void **bb)
1091{
1092 if (!src)
1093 return dst;
1094 else if (dst) {
1095 return t_dup(dst, src, bb);
1096 }
1097 else {
1098 dst = (tagi_t *)((char *)dst + t_len(src));
1099 *bb = (char *)*bb + t_xtra(src, (size_t)*bb);
1100 return dst;
1101 }
1102}
1103
1104tag_class_t any_tag_class[1] =
1105 {{
1106 sizeof(any_tag_class),
1107 /* tc_next */ NULL((void*)0),
1108 /* tc_len */ NULL((void*)0),
1109 /* tc_move */ NULL((void*)0),
1110 /* tc_xtra */ NULL((void*)0),
1111 /* tc_dup */ NULL((void*)0),
1112 /* tc_free */ NULL((void*)0),
1113 /* tc_find */ NULL((void*)0),
1114 /* tc_snprintf */ NULL((void*)0),
1115 /* tc_filter */ t_any_filter,
1116 /* tc_ref_set */ NULL((void*)0),
1117 /* tc_scan */ NULL((void*)0),
1118 }};
1119
1120/** Any tag - match any tag when filtering. */
1121tag_typedef_t tag_any = TAG_TYPEDEF(tag_any, any){{ "", "tag_any", any_tag_class, 0 }};
1122
1123/* ====================================================================== */
1124/* ns tag - match to any tag with same namespace when filtering */
1125
1126static
1127tagi_t *t_ns_filter(tagi_t *dst,
1128 tagi_t const filter[],
1129 tagi_t const *src,
1130 void **bb)
1131{
1132 char const *match, *ns;
1133
1134 if (!src)
1135 return dst;
1136
1137 assert(filter)((void) sizeof ((filter) ? 1 : 0), __extension__ ({ if (filter
) ; else __assert_fail ("filter", "su_taglist.c", 1137, __extension__
__PRETTY_FUNCTION__); }))
;
1138
1139 match = TAG_TYPE_OF(filter)((filter) && (filter)->t_tag ? (filter)->t_tag :
tag_null)
->tt_ns;
1140 ns = TAG_TYPE_OF(src)((src) && (src)->t_tag ? (src)->t_tag : tag_null
)
->tt_ns;
1141
1142 if (match == NULL((void*)0))
1143 /* everything matches with this */;
1144 else if (match == ns)
1145 /* namespaces matche */;
1146 else if (ns == NULL((void*)0))
1147 /* no match */
1148 return dst;
1149 else if (strcmp(match, ns))
1150 /* no match */
1151 return dst;
1152
1153 if (dst) {
1154 return t_dup(dst, src, bb);
1155 }
1156 else {
1157 dst = (tagi_t *)((char *)dst + t_len(src));
1158 *bb = (char *)*bb + t_xtra(src, (size_t)*bb);
1159 return dst;
1160 }
1161}
1162
1163/** Namespace filtering class */
1164tag_class_t ns_tag_class[1] =
1165 {{
1166 sizeof(ns_tag_class),
1167 /* tc_next */ NULL((void*)0),
1168 /* tc_len */ NULL((void*)0),
1169 /* tc_move */ NULL((void*)0),
1170 /* tc_xtra */ NULL((void*)0),
1171 /* tc_dup */ NULL((void*)0),
1172 /* tc_free */ NULL((void*)0),
1173 /* tc_find */ NULL((void*)0),
1174 /* tc_snprintf */ NULL((void*)0),
1175 /* tc_filter */ t_ns_filter,
1176 /* tc_ref_set */ NULL((void*)0),
1177 /* tc_scan */ NULL((void*)0),
1178 }};
1179
1180/* ====================================================================== */
1181/* int tag - pass integer value */
1182
1183int t_int_snprintf(tagi_t const *t, char b[], size_t size)
1184{
1185 return snprintf(b, size, "%i", (int)t->t_value);
1186}
1187
1188int t_int_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
1189{
1190 *(int *)ref = (int)value->t_value;
1191
1192 return 1;
1193}
1194
1195int t_int_scan(tag_type_t tt, su_home_t *home,
1196 char const *s,
1197 tag_value_t *return_value)
1198{
1199 int value;
1200 char *rest;
1201
1202 value = strtol(s, &rest, 0);
1203
1204 if (s != rest) {
1205 *return_value = (tag_value_t)value;
1206 return 1;
1207 }
1208 else {
1209 *return_value = (tag_value_t)0;
1210 return -1;
1211 }
1212}
1213
1214tag_class_t int_tag_class[1] =
1215 {{
1216 sizeof(int_tag_class),
1217 /* tc_next */ NULL((void*)0),
1218 /* tc_len */ NULL((void*)0),
1219 /* tc_move */ NULL((void*)0),
1220 /* tc_xtra */ NULL((void*)0),
1221 /* tc_dup */ NULL((void*)0),
1222 /* tc_free */ NULL((void*)0),
1223 /* tc_find */ NULL((void*)0),
1224 /* tc_snprintf */ t_int_snprintf,
1225 /* tc_filter */ NULL((void*)0),
1226 /* tc_ref_set */ t_int_ref_set,
1227 /* tc_scan */ t_int_scan,
1228 }};
1229
1230/* ====================================================================== */
1231/* uint tag - pass unsigned integer value */
1232
1233int t_uint_snprintf(tagi_t const *t, char b[], size_t size)
1234{
1235 return snprintf(b, size, "%u", (unsigned)t->t_value);
1236}
1237
1238int t_uint_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
1239{
1240 *(unsigned *)ref = (unsigned)value->t_value;
1241
1242 return 1;
1243}
1244
1245int t_uint_scan(tag_type_t tt, su_home_t *home,
1246 char const *s,
1247 tag_value_t *return_value)
1248{
1249 unsigned value;
1250 char *rest;
1251
1252 value = strtoul(s, &rest, 0);
1253
1254 if (s != rest) {
1255 *return_value = (tag_value_t)value;
1256 return 1;
1257 }
1258 else {
1259 *return_value = (tag_value_t)0;
1260 return -1;
1261 }
1262}
1263
1264tag_class_t uint_tag_class[1] =
1265 {{
1266 sizeof(int_tag_class),
1267 /* tc_next */ NULL((void*)0),
1268 /* tc_len */ NULL((void*)0),
1269 /* tc_move */ NULL((void*)0),
1270 /* tc_xtra */ NULL((void*)0),
1271 /* tc_dup */ NULL((void*)0),
1272 /* tc_free */ NULL((void*)0),
1273 /* tc_find */ NULL((void*)0),
1274 /* tc_snprintf */ t_uint_snprintf,
1275 /* tc_filter */ NULL((void*)0),
1276 /* tc_ref_set */ t_uint_ref_set,
1277 /* tc_scan */ t_uint_scan,
1278 }};
1279
1280
1281/* ====================================================================== */
1282/* size tag - pass size_t value @NEW_1_12_5 */
1283
1284static
1285int t_size_snprintf(tagi_t const *t, char b[], size_t size)
1286{
1287 return snprintf(b, size, MOD_ZU"%zu", (size_t)t->t_value);
1288}
1289
1290static
1291int t_size_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
1292{
1293 *(size_t *)ref = (size_t)value->t_value;
1294
1295 return 1;
1296}
1297
1298static
1299int t_size_scan(tag_type_t tt, su_home_t *home,
1300 char const *s,
1301 tag_value_t *return_value)
1302{
1303 unsigned longlonglong long value;
1304 char *rest;
1305
1306 value = strtoull(s, &rest, 0);
1307
1308 if (s != rest && value <= SIZE_MAX(18446744073709551615UL)) {
1309 *return_value = (tag_value_t)value;
1310 return 1;
1311 }
1312 else {
1313 *return_value = (tag_value_t)0;
1314 return -1;
1315 }
1316}
1317
1318/** Tag class for tags with size_t value. @NEW_1_12_5. */
1319tag_class_t size_tag_class[1] =
1320 {{
1321 sizeof(int_tag_class),
1322 /* tc_next */ NULL((void*)0),
1323 /* tc_len */ NULL((void*)0),
1324 /* tc_move */ NULL((void*)0),
1325 /* tc_xtra */ NULL((void*)0),
1326 /* tc_dup */ NULL((void*)0),
1327 /* tc_free */ NULL((void*)0),
1328 /* tc_find */ NULL((void*)0),
1329 /* tc_snprintf */ t_size_snprintf,
1330 /* tc_filter */ NULL((void*)0),
1331 /* tc_ref_set */ t_size_ref_set,
1332 /* tc_scan */ t_size_scan,
1333 }};
1334
1335/* ====================================================================== */
1336/* usize tag - pass usize_t value */
1337
1338static
1339int t_usize_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
1340{
1341 *(usize_t *)ref = (usize_t)value->t_value;
1342
1343 return 1;
1344}
1345
1346static
1347int t_usize_scan(tag_type_t tt, su_home_t *home,
1348 char const *s,
1349 tag_value_t *return_value)
1350{
1351 unsigned longlonglong long value;
1352 char *rest;
1353
1354 value = strtoull(s, &rest, 0);
1355
1356 if (s != rest && value <= USIZE_MAX(2147483647 *2U +1U)) {
1357 *return_value = (tag_value_t)value;
1358 return 1;
1359 }
1360 else {
1361 *return_value = (tag_value_t)0;
1362 return -1;
1363 }
1364}
1365
1366/** Tag class for tags with usize_t value. @NEW_1_12_5. */
1367tag_class_t usize_tag_class[1] =
1368 {{
1369 sizeof(int_tag_class),
1370 /* tc_next */ NULL((void*)0),
1371 /* tc_len */ NULL((void*)0),
1372 /* tc_move */ NULL((void*)0),
1373 /* tc_xtra */ NULL((void*)0),
1374 /* tc_dup */ NULL((void*)0),
1375 /* tc_free */ NULL((void*)0),
1376 /* tc_find */ NULL((void*)0),
1377 /* tc_snprintf */ t_size_snprintf,
1378 /* tc_filter */ NULL((void*)0),
1379 /* tc_ref_set */ t_usize_ref_set,
1380 /* tc_scan */ t_usize_scan,
1381 }};
1382
1383
1384/* ====================================================================== */
1385/* bool tag - pass boolean value */
1386
1387int t_bool_snprintf(tagi_t const *t, char b[], size_t size)
1388{
1389 return snprintf(b, size, "%s", t->t_value ? "true" : "false");
1390}
1391
1392int t_bool_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
1393{
1394 *(int *)ref = (value->t_value != 0);
1395
1396 return 1;
1397}
1398
1399int t_bool_scan(tag_type_t tt, su_home_t *home,
1400 char const *s,
1401 tag_value_t *return_value)
1402{
1403 int retval;
1404 int value = 0;
1405
1406 if (su_casenmatch(s, "true", 4)
1407 && strlen(s + 4) == strspn(s + 4, " \t\r\n")) {
1408 value = 1, retval = 1;
1409 } else if (su_casenmatch(s, "false", 5)
1410 && strlen(s + 5) == strspn(s + 5, " \t\r\n")) {
1411 value = 0, retval = 1;
1412 } else {
1413 retval = t_int_scan(tt, home, s, return_value);
1414 value = *return_value != 0;
1415 }
1416
1417 if (retval == 1)
1418 *return_value = (tag_value_t)value;
1419 else
1420 *return_value = (tag_value_t)0;
1421
1422 return retval;
1423}
1424
1425tag_class_t bool_tag_class[1] =
1426 {{
1427 sizeof(bool_tag_class),
1428 /* tc_next */ NULL((void*)0),
1429 /* tc_len */ NULL((void*)0),
1430 /* tc_move */ NULL((void*)0),
1431 /* tc_xtra */ NULL((void*)0),
1432 /* tc_dup */ NULL((void*)0),
1433 /* tc_free */ NULL((void*)0),
1434 /* tc_find */ NULL((void*)0),
1435 /* tc_snprintf */ t_bool_snprintf,
1436 /* tc_filter */ NULL((void*)0),
1437 /* tc_ref_set */ t_bool_ref_set,
1438 /* tc_scan */ t_bool_scan,
1439 }};
1440
1441/* ====================================================================== */
1442/* ptr tag - pass pointer value */
1443
1444int t_ptr_snprintf(tagi_t const *t, char b[], size_t size)
1445{
1446 return snprintf(b, size, "%p", (void *)t->t_value);
1447}
1448
1449int t_ptr_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
1450{
1451 *(void **)ref = (void *)value->t_value;
1452
1453 return 1;
1454}
1455
1456/* This is not usually very safe, so it is not used */
1457int t_ptr_scan(tag_type_t tt, su_home_t *home,
1458 char const *s,
1459 tag_value_t *return_value)
1460{
1461 int retval;
1462 void *ptr;
1463
1464 retval = sscanf(s, "%p", &ptr);
1465
1466 if (retval == 1)
1467 *return_value = (tag_value_t)ptr;
1468 else
1469 *return_value = (tag_value_t)NULL((void*)0);
1470
1471 return retval;
1472}
1473
1474tag_class_t ptr_tag_class[1] =
1475 {{
1476 sizeof(ptr_tag_class),
1477 /* tc_next */ NULL((void*)0),
1478 /* tc_len */ NULL((void*)0),
1479 /* tc_move */ NULL((void*)0),
1480 /* tc_xtra */ NULL((void*)0),
1481 /* tc_dup */ NULL((void*)0),
1482 /* tc_free */ NULL((void*)0),
1483 /* tc_find */ NULL((void*)0),
1484 /* tc_snprintf */ t_ptr_snprintf,
1485 /* tc_filter */ NULL((void*)0),
1486 /* tc_ref_set */ t_ptr_ref_set,
1487 /* tc_scan */ NULL((void*)0),
1488 }};
1489
1490/* ====================================================================== */
1491/* socket tag - pass socket */
1492
1493#include <sofia-sip/su.h>
1494
1495int t_socket_snprintf(tagi_t const *t, char b[], size_t size)
1496{
1497 /* socket can be int or DWORD (or QWORD on win64?) */
1498 return snprintf(b, size, LLI"%lli", (longlonglong long)t->t_value);
1499}
1500
1501int t_socket_ref_set(tag_type_t tt, void *ref, tagi_t const value[])
1502{
1503 *(su_socket_t *)ref = (su_socket_t)value->t_value;
1504
1505 return 1;
1506}
1507
1508tag_class_t socket_tag_class[1] =
1509 {{
1510 sizeof(socket_tag_class),
1511 /* tc_next */ NULL((void*)0),
1512 /* tc_len */ NULL((void*)0),
1513 /* tc_move */ NULL((void*)0),
1514 /* tc_xtra */ NULL((void*)0),
1515 /* tc_dup */ NULL((void*)0),
1516 /* tc_free */ NULL((void*)0),
1517 /* tc_find */ NULL((void*)0),
1518 /* tc_snprintf */ t_socket_snprintf,
1519 /* tc_filter */ NULL((void*)0),
1520 /* tc_ref_set */ t_socket_ref_set,
1521 /* tc_scan */ NULL((void*)0),
1522 }};
1523
1524/* ====================================================================== */
1525/* str tag - pass string value */
1526
1527int t_str_snprintf(tagi_t const *t, char b[], size_t size)
1528{
1529 if (t->t_value)
1530 return snprintf(b, size, "\"%s\"", (char const *)t->t_value);
1531 else
1532 return snprintf(b, size, "<null>");
1533}
1534
1535int t_str_scan(tag_type_t tt, su_home_t *home,
1536 char const *s,
1537 tag_value_t *return_value)
1538{
1539 int retval;
1540
1541 s = su_strdup(home, s);
1542
1543 if (s)
1544 *return_value = (tag_value_t)s, retval = 1;
1545 else
1546 *return_value = (tag_value_t)NULL((void*)0), retval = -1;
1547
1548 return retval;
1549}
1550
1551tagi_t *t_str_dup(tagi_t *dst, tagi_t const *src, void **bb)
1552{
1553 dst->t_tag = src->t_tag;
1554 if (src->t_value) {
1555 char const *s = (char const *)src->t_value;
1556 size_t len = strlen(s) + 1;
1557 dst->t_value = (tag_value_t)strcpy(*bb, s);
1558 *bb = (char *)*bb + len;
1559 }
1560 else
1561 dst->t_value = (tag_value_t)0;
1562
1563 return dst + 1;
1564}
1565
1566size_t t_str_xtra(tagi_t const *t, size_t offset)
1567{
1568 return t->t_value ? strlen((char *)t->t_value) + 1 : 0;
1569}
1570
1571tag_class_t str_tag_class[1] =
1572 {{
1573 sizeof(str_tag_class),
1574 /* tc_next */ NULL((void*)0),
1575 /* tc_len */ NULL((void*)0),
1576 /* tc_move */ NULL((void*)0),
1577 /* tc_xtra */ t_str_xtra,
1578 /* tc_dup */ t_str_dup,
1579 /* tc_free */ NULL((void*)0),
1580 /* tc_find */ NULL((void*)0),
1581 /* tc_snprintf */ t_str_snprintf,
1582 /* tc_filter */ NULL((void*)0),
1583 /* tc_ref_set */ t_ptr_ref_set,
1584 /* tc_scan */ t_str_scan,
1585 }};
1586
1587/* ====================================================================== */
1588/* cstr tag - pass constant string value (no need to dup) */
1589
1590/** Tag class for constant strings */
1591tag_class_t cstr_tag_class[1] =
1592 {{
1593 sizeof(cstr_tag_class),
1594 /* tc_next */ NULL((void*)0),
1595 /* tc_len */ NULL((void*)0),
1596 /* tc_move */ NULL((void*)0),
1597 /* tc_xtra */ NULL((void*)0),
1598 /* tc_dup */ NULL((void*)0),
1599 /* tc_free */ NULL((void*)0),
1600 /* tc_find */ NULL((void*)0),
1601 /* tc_snprintf */ t_str_snprintf,
1602 /* tc_filter */ NULL((void*)0),
1603 /* tc_ref_set */ t_ptr_ref_set,
1604 /* tc_scan */ t_str_scan,
1605 }};
1606
1607/* ====================================================================== */
1608/* ref tag - pass reference */
1609
1610tag_class_t ref_tag_class[1] =
1611 {{
1612 sizeof(ref_tag_class),
1613 /* tc_next */ NULL((void*)0),
1614 /* tc_len */ NULL((void*)0),
1615 /* tc_move */ NULL((void*)0),
1616 /* tc_xtra */ NULL((void*)0),
1617 /* tc_dup */ NULL((void*)0),
1618 /* tc_free */ NULL((void*)0),
1619 /* tc_find */ NULL((void*)0),
1620 /* tc_snprintf */ t_ptr_snprintf,
1621 /* tc_filter */ NULL((void*)0),
1622 /* tc_ref_set */ t_ptr_ref_set,
1623 /* tc_scan */ NULL((void*)0),
1624 }};
1625
1626

./sofia-sip/su_tag_inline.h

1/*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25#ifndef SU_TAG_INLINE_H
26/** Defined when <sofia-sip/su_tag_inline.h> has been included */
27#define SU_TAG_INLINE_H
28/**@SU_TAG
29 * @file sofia-sip/su_tag_inline.h
30 * Inline functions for object tags and tag lists.
31 *
32 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
33 *
34 * @date Created: Tue Feb 20 19:48:18 2001 ppessi
35 */
36
37#ifndef SU_TAG_H
38#include <sofia-sip/su_tag.h>
39#endif
40#ifndef SU_TAG_CLASS_H
41#include <sofia-sip/su_tag_class.h>
42#endif
43
44SOFIA_BEGIN_DECLS
45
46#define tt_nexttt_class->tc_next tt_class->tc_next
47#define tt_lentt_class->tc_len tt_class->tc_len
48#define tt_movett_class->tc_move tt_class->tc_move
49#define tt_xtratt_class->tc_xtra tt_class->tc_xtra
50#define tt_duptt_class->tc_dup tt_class->tc_dup
51#define tt_freett_class->tc_free tt_class->tc_free
52#define tt_findtt_class->tc_find tt_class->tc_find
53#define tt_snprintftt_class->tc_snprintf tt_class->tc_snprintf
54#define tt_filtertt_class->tc_filter tt_class->tc_filter
55
56#define TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null) ((t) && (t)->t_tag ? (t)->t_tag : tag_null)
57
58/** Check if the tag item is last in current list */
59su_inlinestatic inline int t_end(tagi_t const *t)
60{
61 tag_type_t tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null);
62
63 /* XXX - virtualize this */
64
65 return tt == tag_null || tt == tag_next;
66}
67
68su_inlinestatic inline tagi_t const *t_next(tagi_t const *t)
69{
70 tag_type_t tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null);
71
72 if (tt->tt_nexttt_class->tc_next)
7
Access to field 'tc_next' results in a dereference of a null pointer (loaded from field 'tt_class')
73 return tt->tt_nexttt_class->tc_next(t);
74 else
75 return t + 1;
76}
77
78su_inlinestatic inline tagi_t *t_move(tagi_t *dst, tagi_t const *src)
79{
80 tag_type_t tt = TAG_TYPE_OF(src)((src) && (src)->t_tag ? (src)->t_tag : tag_null
)
;
81
82 if (tt->tt_movett_class->tc_move)
83 return tt->tt_movett_class->tc_move(dst, src);
84
85 *dst = *src;
86 return dst + 1;
87}
88
89su_inlinestatic inline size_t t_xtra(tagi_t const *t, size_t offset)
90{
91 tag_type_t tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null);
92
93 if (tt->tt_xtratt_class->tc_xtra)
94 return tt->tt_xtratt_class->tc_xtra(t, offset);
95
96 return 0;
97}
98
99su_inlinestatic inline tagi_t *t_dup(tagi_t *dst, tagi_t const *src, void **bb)
100{
101 tag_type_t tt = TAG_TYPE_OF(src)((src) && (src)->t_tag ? (src)->t_tag : tag_null
)
;
102
103 if (tt->tt_duptt_class->tc_dup)
104 return tt->tt_duptt_class->tc_dup(dst, src, bb);
105
106 *dst = *src;
107 return dst + 1;
108}
109
110su_inlinestatic inline tagi_t const *t_find(tag_type_t tt, tagi_t const *lst)
111{
112 if (!tt)
113 return NULL((void*)0);
114
115 if (tt->tt_findtt_class->tc_find)
116 return tt->tt_findtt_class->tc_find(tt, lst);
117
118 for (; lst; lst = t_next(lst)) {
119 if (tt == lst->t_tag)
120 return lst;
121 }
122
123 return NULL((void*)0);
124}
125
126su_inlinestatic inline tagi_t *t_free(tagi_t *t)
127{
128 tag_type_t tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null);
129
130 if (tt->tt_freett_class->tc_free)
131 return tt->tt_freett_class->tc_free(t);
132 else if (tt->tt_nexttt_class->tc_next)
133 return (tagi_t *)tt->tt_nexttt_class->tc_next(t);
134 else
135 return t + 1;
136}
137
138su_inlinestatic inline size_t t_len(tagi_t const *t)
139{
140 tag_type_t tt = TAG_TYPE_OF(t)((t) && (t)->t_tag ? (t)->t_tag : tag_null);
141
142 if (tt->tt_lentt_class->tc_len)
143 return tt->tt_lentt_class->tc_len(t);
144
145 return sizeof(*t);
146}
147
148SOFIA_END_DECLS
149
150#endif /* !defined(SU_TAG_INLINE_H) */