Bug Summary

File:plugins/epan/mate/mate_util.c
Warning:line 1524, column 22
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name mate_util.c -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 -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-20/lib/clang/20 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D PLUGIN_VERSION="1.0.1" -D WS_DEBUG -D WS_DEBUG_UTF_8 -D mate_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/plugins/epan/mate -I /builds/wireshark/wireshark/build/plugins/epan/mate -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-20/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-truncation -Wno-format-nonliteral -Wno-pointer-sign -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2025-09-09-100415-3933-1 -x c /builds/wireshark/wireshark/plugins/epan/mate/mate_util.c
1/* mate_util.c
2 * MATE -- Meta Analysis Tracing Engine
3 * Utility Library: Single Copy Strings and Attribute Value Pairs
4 *
5 * Copyright 2004, Luis E. Garcia Ontanon <[email protected]>
6 *
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <[email protected]>
9 * Copyright 1998 Gerald Combs
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
14#include "config.h"
15
16#include "mate.h"
17#include "mate_util.h"
18
19#include <errno(*__errno_location ()).h>
20#include <wsutil/file_util.h>
21
22
23/***************************************************************************
24* dbg_print
25***************************************************************************
26* This is the debug facility of the thing.
27***************************************************************************/
28
29/* dbg_print:
30 * which: a pointer to the current level of debugging for a feature
31 * how: the level over which this message should be printed out
32 * where: the file on which to print (ws_message if null)
33 * fmt, ...: what to print
34 */
35
36void dbg_print(const int* which, int how, FILE* where, const char* fmt, ... ) {
37 static char debug_buffer[DEBUG_BUFFER_SIZE4096];
38 va_list list;
39
40 if ( ! which || *which < how ) return;
41
42 va_start( list, fmt )__builtin_va_start(list, fmt);
43 vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE4096,fmt,list);
44 va_end( list )__builtin_va_end(list);
45
46 if (! where) {
47 ws_message("%s", debug_buffer)do { if (1) { ws_log_full("MATE", LOG_LEVEL_MESSAGE, ((void*)
0), -1, ((void*)0), "%s", debug_buffer); } } while (0)
;
48 } else {
49 fputs(debug_buffer,where);
50 fputs("\n",where);
51 }
52
53}
54
55/***************************************************************************
56 * single copy strings
57 ***************************************************************************
58 * Strings repeat more often than don't. In order to save memory
59 * we'll keep only one copy of each as key to a hash with a count of
60 * subscribers as value. Each string is truncated to be no more than
61 * SCS_HUGE_SIZE.
62 * XXX - GLib 2.58 adds reference counted strings, use that?
63 ***************************************************************************/
64
65/**
66 * scs_init:
67 *
68 * Initializes the scs hash.
69 **/
70
71struct _scs_collection {
72 GHashTable* hash; /* key: a string value: unsigned number of subscribers */
73};
74
75/*
76 * XXX: AFAIKT destroy_scs_collection() might be called only when reading a
77 * mate config file. Since reading a new config file can apparently currently
78 * only be done once after starting Wireshark, in theory this fcn
79 * currently should never be called since there will never be an existing
80 * scs_collection to be destroyed.
81 */
82static void destroy_scs_collection(SCS_collection* c) {
83 if (c->hash) g_hash_table_destroy(c->hash);
84}
85
86/* The same djb2 hash used in g_str_hash, except that it stops after
87 * SCS_HUGE_SIZE bytes. */
88static unsigned scs_hash(const void *v)
89{
90 const signed char *p;
91 uint32_t h = 5381;
92 size_t i = 0;
93
94 for (p = v; *p != '\0' && i++ < SCS_HUGE_SIZE65535; p++) {
95 h = (h << 5) + h + *p;
96 }
97
98 return h;
99}
100
101/* Compare the first SCS_HUGE_SIZE bytes for equality. */
102static gboolean scs_equal(const void *a, const void *b)
103{
104 const char *str1 = a;
105 const char *str2 = b;
106
107 return strncmp(str1, str2, SCS_HUGE_SIZE65535) == 0;
108}
109
110static SCS_collection* scs_init(void) {
111 SCS_collection* c = g_new(SCS_collection, 1)((SCS_collection *) g_malloc_n ((1), sizeof (SCS_collection))
)
;
112
113 c->hash = g_hash_table_new_full(scs_hash, scs_equal, g_free, g_free);
114
115 return c;
116}
117
118
119/**
120 * subscribe:
121 * @param c the scs hash
122 * @param s a string
123 *
124 * Checks if the given string exists already and if so it increases the count of
125 * subsscribers and returns a pointer to the stored string. If not it will copy
126 * the given string, store it in the hash, and return the pointer to the copy.
127 * Remember, containment is handled internally, take care of your own strings.
128 *
129 * Return value: a pointer to the subscribed string.
130 **/
131char* scs_subscribe(SCS_collection* c, const char* s) {
132 char* orig = NULL((void*)0);
133 unsigned* ip = NULL((void*)0);
134 size_t len = 0;
135
136 g_hash_table_lookup_extended(c->hash,(const void *)s,(void * *)&orig,(void * *)&ip);
137
138 if (ip) {
139 (*ip)++;
140 } else {
141 ip = g_new0(unsigned, 1)((unsigned *) g_malloc0_n ((1), sizeof (unsigned)));
142
143 len = strlen(s);
144
145 if(G_LIKELY(len <= SCS_HUGE_SIZE)(len <= 65535)) {
146 orig = g_strdup(s)g_strdup_inline (s);
147 } else {
148 ws_warning("mate SCS: string truncated due to huge size")do { if (1) { ws_log_full("MATE", LOG_LEVEL_WARNING, "plugins/epan/mate/mate_util.c"
, 148, __func__, "mate SCS: string truncated due to huge size"
); } } while (0)
;
149 orig = g_strndup(s, SCS_HUGE_SIZE65535);
150 }
151
152 g_hash_table_insert(c->hash, orig, ip);
153 }
154
155 return orig;
156}
157
158/**
159 * unsubscribe:
160 * @param c the scs hash
161 * @param s a string.
162 *
163 * decreases the count of subscribers, if zero frees the internal copy of
164 * the string.
165 **/
166void scs_unsubscribe(SCS_collection* c, char* s) {
167 char* orig = NULL((void*)0);
168 unsigned* ip = NULL((void*)0);
169
170 if (g_hash_table_lookup_extended(c->hash,(const void *)s,(void **)&orig,(void **)&ip)) {
171 if (*ip == 0) {
172 g_hash_table_remove(c->hash,orig);
173 }
174 else {
175 (*ip)--;
176 }
177 } else {
178 ws_warning("mate SCS: not subscribed")do { if (1) { ws_log_full("MATE", LOG_LEVEL_WARNING, "plugins/epan/mate/mate_util.c"
, 178, __func__, "mate SCS: not subscribed"); } } while (0)
;
179 }
180}
181
182/**
183 * scs_subscribe_printf:
184 * @param fmt a format string ...
185 *
186 * Formats the input and subscribes it.
187 *
188 * Return value: the stored copy of the formatted string.
189 *
190 **/
191char* scs_subscribe_printf(SCS_collection* c, char* fmt, ...) {
192 va_list list;
193 char *buf, *ret;
194
195 va_start( list, fmt )__builtin_va_start(list, fmt);
196 buf = g_strdup_vprintf(fmt, list);
197 va_end( list )__builtin_va_end(list);
198
199 ret = scs_subscribe(c, buf);
200 g_free(buf);
201
202 return ret;
203}
204
205/***************************************************************************
206* AVPs & Co.
207***************************************************************************
208* The Thing operates mainly on avps, avpls and loals
209* - attribute value pairs (two strings: the name and the value and an operator)
210* - avp lists a somehow sorted list of avps
211* - loal (list of avp lists) an arbitrarily sorted list of avpls
212*
213*
214***************************************************************************/
215
216
217typedef union _any_avp_type {
218 AVP avp;
219 AVPN avpn;
220 AVPL avpl;
221 LoAL loal;
222 LoALnode loaln;
223} any_avp_type;
224
225
226static SCS_collection* avp_strings;
227
228#ifdef _AVP_DEBUGGING
229static FILE* dbg_fp;
230
231static int dbg_level;
232static int* dbg = &dbg_level;
233
234static int dbg_avp_level;
235static int* dbg_avp = &dbg_avp_level;
236
237static int dbg_avp_op_level;
238static int* dbg_avp_op = &dbg_avp_op_level;
239
240static int dbg_avpl_level;
241static int* dbg_avpl = &dbg_avpl_level;
242
243static int dbg_avpl_op_level;
244static int* dbg_avpl_op = &dbg_avpl_op_level;
245
246/**
247 * setup_avp_debug:
248 * @param fp the file in which to send debugging output.
249 * @param general a pointer to the level of debugging of facility "general"
250 * @param avp a pointer to the level of debugging of facility "avp"
251 * @param avp_op a pointer to the level of debugging of facility "avp_op"
252 * @param avpl a pointer to the level of debugging of facility "avpl"
253 * @param avpl_op a pointer to the level of debugging of facility "avpl_op"
254 *
255 * If enabled sets up the debug facilities for the avp library.
256 *
257 **/
258extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
259 dbg_fp = fp;
260 dbg = general;
261 dbg_avp = avp;
262 dbg_avp_op = avp_op;
263 dbg_avpl = avpl;
264 dbg_avpl_op = avpl_op;
265}
266
267#endif /* _AVP_DEBUGGING */
268
269/**
270 * avp_init:
271 *
272 * (Re)Initializes the avp library.
273 *
274 **/
275extern void avp_init(void) {
276
277 if (avp_strings) destroy_scs_collection(avp_strings);
278 avp_strings = scs_init();
279
280}
281
282/**
283 * new_avp_from_finfo:
284 * @param name the name the avp will have.
285 * @param finfo the field_info from which to fetch the data.
286 *
287 * Creates an avp from a field_info record.
288 *
289 * Return value: a pointer to the newly created avp.
290 *
291 **/
292extern AVP* new_avp_from_finfo(const char* name, field_info* finfo) {
293 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
294 char* value;
295 char* repr;
296
297 new_avp_val->n = scs_subscribe(avp_strings, name);
298
299 repr = fvalue_to_string_repr(NULL((void*)0), finfo->value, FTREPR_DISPLAY, finfo->hfinfo->display);
300
301 if (repr) {
302 value = scs_subscribe(avp_strings, repr);
303 wmem_free(NULL((void*)0), repr);
304#ifdef _AVP_DEBUGGING
305 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from string: %s",value);
306#endif
307 } else {
308#ifdef _AVP_DEBUGGING
309 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
310#endif
311 value = scs_subscribe(avp_strings, "");
312 }
313
314 new_avp_val->v = value;
315
316 new_avp_val->o = '=';
317
318#ifdef _AVP_DEBUGGING
319 dbg_print (dbg_avp,1,dbg_fp,"new_avp_from_finfo: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
320#endif
321
322 return new_avp_val;
323}
324
325
326/**
327 * new_avp:
328 * @param name the name the avp will have.
329 * @param value the value the avp will have.
330 * @param o the operator of this avp.
331 *
332 * Creates an avp given every parameter.
333 *
334 * Return value: a pointer to the newly created avp.
335 *
336 **/
337extern AVP* new_avp(const char* name, const char* value, char o) {
338 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
339
340 new_avp_val->n = scs_subscribe(avp_strings, name);
341 new_avp_val->v = scs_subscribe(avp_strings, value);
342 new_avp_val->o = o;
343
344#ifdef _AVP_DEBUGGING
345 dbg_print(dbg_avp,1,dbg_fp,"new_avp_val: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
346#endif
347 return new_avp_val;
348}
349
350
351/**
352* delete_avp:
353 * @param avp the avp to delete.
354 *
355 * Destroys an avp and releases the resources it uses.
356 *
357 **/
358extern void delete_avp(AVP* avp) {
359#ifdef _AVP_DEBUGGING
360 dbg_print(dbg_avp,1,dbg_fp,"delete_avp: %p %s%c%s;",avp,avp->n,avp->o,avp->v);
361#endif
362
363 scs_unsubscribe(avp_strings, avp->n);
364 scs_unsubscribe(avp_strings, avp->v);
365 g_slice_free(any_avp_type,(any_avp_type*)avp)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)avp)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)avp
)); } while (0)
;
366}
367
368
369/**
370* avp_copy:
371 * @param from the avp to be copied.
372 *
373 * Creates an avp whose name op and value are copies of the given one.
374 *
375 * Return value: a pointer to the newly created avp.
376 *
377 **/
378extern AVP* avp_copy(AVP* from) {
379 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
380
381 new_avp_val->n = scs_subscribe(avp_strings, from->n);
382 new_avp_val->v = scs_subscribe(avp_strings, from->v);
383 new_avp_val->o = from->o;
384
385#ifdef _AVP_DEBUGGING
386 dbg_print(dbg_avp,1,dbg_fp,"copy_avp: %p %s%c%s;",new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
387#endif
388
389 return new_avp_val;
390}
391
392/**
393 * new_avpl:
394 * @param name the name the avpl will have.
395 *
396 * Creates an empty avpl.
397 *
398 * Return value: a pointer to the newly created avpl.
399 *
400 **/
401extern AVPL* new_avpl(const char* name) {
402 AVPL* new_avpl_p = (AVPL*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
403
404#ifdef _AVP_DEBUGGING
405 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl_p: %p name=%s",new_avpl_p,name);
406#endif
407
408 new_avpl_p->name = name ? scs_subscribe(avp_strings, name) : scs_subscribe(avp_strings, "");
409 new_avpl_p->len = 0;
410 new_avpl_p->null.avp = NULL((void*)0);
411 new_avpl_p->null.next = &new_avpl_p->null;
412 new_avpl_p->null.prev = &new_avpl_p->null;
413
414
415 return new_avpl_p;
416}
417
418extern void rename_avpl(AVPL* avpl, char* name) {
419 scs_unsubscribe(avp_strings,avpl->name);
420 avpl->name = scs_subscribe(avp_strings,name);
421}
422
423/**
424 * insert_avp_before_node:
425 * @param avpl the avpl in which to insert.
426 * @param next_node the next node before which the new avpn has to be inserted.
427 * @param avp the avp to be inserted.
428 * @param copy_avp whether the original AVP or a copy thereof must be inserted.
429 *
430 * Pre-condition: the avp is sorted before before_avp and does not already exist
431 * in the avpl.
432 */
433static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, bool_Bool copy_avp) {
434 AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
435
436 new_avp_val->avp = copy_avp ? avp_copy(avp) : avp;
437
438#ifdef _AVP_DEBUGGING
439 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %p",new_avp_val);
440 dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %p in %p before %p;",avp,avpl,next_node);
441#endif
442
443 new_avp_val->next = next_node;
444 new_avp_val->prev = next_node->prev;
445 next_node->prev->next = new_avp_val;
446 next_node->prev = new_avp_val;
447
448 avpl->len++;
449
450#ifdef _AVP_DEBUGGING
451 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
452#endif
453}
454
455/**
456 * insert_avp:
457 * @param avpl the avpl in which to insert.
458 * @param avp the avp to be inserted.
459 *
460 * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
461 *
462 * Return value: whether it was inserted or not.
463 *
464 * BEWARE: Check the return value, you might need to delete the avp if
465 * it is not inserted.
466 **/
467extern bool_Bool insert_avp(AVPL* avpl, AVP* avp) {
468 AVPN* c;
469
470#ifdef _AVP_DEBUGGING
471 dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %p %p %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
472#endif
473
474 /* get to the insertion point */
475 for (c=avpl->null.next; c->avp; c = c->next) {
476 int name_diff = strcmp(avp->n, c->avp->n);
477
478 if (name_diff == 0) {
479 int value_diff = strcmp(avp->v, c->avp->v);
480
481 if (value_diff < 0) {
482 break;
483 }
484
485 if (value_diff == 0) {
486 // ignore duplicate values, prevents (a=1, a=1)
487 // note that this is also used to insert
488 // conditions AVPs, so really check if the name,
489 // value and operator are all equal.
490 if (c->avp->o == avp->o && avp->o == AVP_OP_EQUAL'=') {
491 return false0;
492 }
493 }
494 }
495
496 if (name_diff < 0) {
497 break;
498 }
499 }
500
501 insert_avp_before_node(avpl, c, avp, false0);
502
503 return true1;
504}
505
506/**
507 * get_avp_by_name:
508 * @param avpl the avpl from which to try to get the avp.
509 * @param name the name of the avp we are looking for.
510 * @param cookie variable in which to store the state between calls.
511 *
512 * Gets pointer to the next avp whose name is given; uses cookie to store its
513 * state between calls.
514 *
515 * Return value: a pointer to the next matching avp if there's one, else NULL.
516 *
517 **/
518extern AVP* get_avp_by_name(AVPL* avpl, char* name, void** cookie) {
519 AVPN* curr;
520 AVPN* start = (AVPN*) *cookie;
521
522#ifdef _AVP_DEBUGGING
523 dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %p %s %p",avpl,name,*cookie);
524#endif
525
526 name = scs_subscribe(avp_strings, name);
527
528 if (!start) start = avpl->null.next;
529
530 for ( curr = start; curr->avp; curr = curr->next ) {
531 if ( curr->avp->n == name ) {
532 break;
533 }
534 }
535
536 *cookie = curr;
537
538#ifdef _AVP_DEBUGGING
539 dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %p",curr);
540#endif
541
542 scs_unsubscribe(avp_strings, name);
543
544 return curr->avp;
545}
546
547/**
548 * extract_avp_by_name:
549 * @param avpl the avpl from which to try to extract the avp.
550 * @param name the name of the avp we are looking for.
551 *
552 * Extracts from the avpl the next avp whose name is given;
553 *
554 * Return value: a pointer to extracted avp if there's one, else NULL.
555 *
556 **/
557extern AVP* extract_avp_by_name(AVPL* avpl, char* name) {
558 AVPN* curr;
559 AVP* avp = NULL((void*)0);
560
561#ifdef _AVP_DEBUGGING
562 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %p %s",avpl,name);
563#endif
564
565 name = scs_subscribe(avp_strings, name);
566
567 for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
568 if ( curr->avp->n == name ) {
569 break;
570 }
571 }
572
573 scs_unsubscribe(avp_strings, name);
574
575 if( ! curr->avp ) return NULL((void*)0);
576
577 curr->next->prev = curr->prev;
578 curr->prev->next = curr->next;
579
580 avp = curr->avp;
581
582 g_slice_free(any_avp_type,(any_avp_type*)curr)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)curr)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)curr
)); } while (0)
;
583
584 (avpl->len)--;
585
586#ifdef _AVP_DEBUGGING
587 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
588#endif
589
590#ifdef _AVP_DEBUGGING
591 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %p",avp);
592#endif
593
594 return avp;
595}
596
597
598/**
599 * extract_first_avp:
600 * @param avpl the avpl from which to try to extract the avp.
601 *
602 * Extracts the first avp from the avpl.
603 *
604 * Return value: a pointer to extracted avp if there's one, else NULL.
605 *
606 **/
607extern AVP* extract_first_avp(AVPL* avpl) {
608 AVP* avp;
609 AVPN* node;
610
611#ifdef _AVP_DEBUGGING
612 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %p",avpl);
613#endif
614
615 node = avpl->null.next;
616
617 avpl->null.next->prev = &avpl->null;
618 avpl->null.next = node->next;
619
620 avp = node->avp;
621
622 if (avp) {
623 g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node
)); } while (0)
;
624 (avpl->len)--;
625#ifdef _AVP_DEBUGGING
626 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
627#endif
628 }
629
630#ifdef _AVP_DEBUGGING
631 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %p",avp);
632#endif
633
634 return avp;
635
636}
637
638
639/**
640 * extract_last_avp:
641 * @param avpl the avpl from which to try to extract the avp.
642 *
643 * Extracts the last avp from the avpl.
644 *
645 * Return value: a pointer to extracted avp if there's one, else NULL.
646 *
647 **/
648extern AVP* extract_last_avp(AVPL* avpl) {
649 AVP* avp;
650 AVPN* node;
651
652 node = avpl->null.prev;
653
654 avpl->null.prev->next = &avpl->null;
655 avpl->null.prev = node->prev;
656
657 avp = node->avp;
658
659 if (avp) {
660 g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node
)); } while (0)
;
661 (avpl->len)--;
662#ifdef _AVP_DEBUGGING
663 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
664#endif
665 }
666
667#ifdef _AVP_DEBUGGING
668 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",avp);
669#endif
670
671 return avp;
672
673}
674
675
676/**
677 * delete_avpl:
678 * @param avpl the avpl from which to try to extract the avp.
679 * @param avps_too whether or not it should delete the avps as well.
680 *
681 * Destroys an avpl and releases the resources it uses. If told to do
682 * so releases the avps as well.
683 *
684 **/
685extern void delete_avpl(AVPL* avpl, bool_Bool avps_too) {
686 AVP* avp;
687#ifdef _AVP_DEBUGGING
688 dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %p",avpl);
689#endif
690
691 while(( avp = extract_last_avp(avpl))) {
692 if (avps_too) {
693 delete_avp(avp);
694 }
695 }
696
697 scs_unsubscribe(avp_strings,avpl->name);
698 g_slice_free(any_avp_type,(any_avp_type*)avpl)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)avpl)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)avpl
)); } while (0)
;
699}
700
701
702
703/**
704 * get_next_avp:
705 * @param avpl the avpl from which to try to get the avps.
706 * @param cookie variable in which to store the state between calls.
707 *
708 * Iterates on an avpl to get its avps.
709 *
710 * Return value: a pointer to the next avp if there's one, else NULL.
711 *
712 **/
713extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
714 AVPN* node;
715
716#ifdef _AVP_DEBUGGING
717 dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %p avpn: %p",avpl,*cookie);
718#endif
719
720 if (*cookie) {
721 node = (AVPN*) *cookie;
722 } else {
723 node = avpl->null.next;
724 }
725
726 *cookie = node->next;
727
728#ifdef _AVP_DEBUGGING
729 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %p",node->avp);
730#endif
731
732 return node->avp;
733}
734
735/**
736 * avpl_to_str:
737 * @param avpl the avpl to represent.
738 *
739 * Creates a newly allocated string containing a representation of an avpl.
740 *
741 * Return value: a pointer to the newly allocated string.
742 *
743 **/
744char* avpl_to_str(AVPL* avpl) {
745 AVPN* c;
746 GString* s = g_string_new("");
747 char* avp_s;
748 char* r;
749
750 for(c=avpl->null.next; c->avp; c = c->next) {
751 avp_s = avp_to_str(c->avp)(wmem_strdup_printf(((void*)0), "%s%c%s",c->avp->n,c->
avp->o,c->avp->v))
;
752 g_string_append_printf(s," %s;",avp_s);
753 g_free(avp_s);
754 }
755
756 r = g_string_free(s,FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((s), (
(0))) : g_string_free_and_steal (s)) : (g_string_free) ((s), (
(0))))
;
757
758 /* g_strchug(r); ? */
759 return r;
760}
761
762extern char* avpl_to_dotstr(AVPL* avpl) {
763 AVPN* c;
764 GString* s = g_string_new("");
765 char* avp_s;
766 char* r;
767
768 for(c=avpl->null.next; c->avp; c = c->next) {
769 avp_s = avp_to_str(c->avp)(wmem_strdup_printf(((void*)0), "%s%c%s",c->avp->n,c->
avp->o,c->avp->v))
;
770 g_string_append_printf(s," .%s;",avp_s);
771 g_free(avp_s);
772 }
773
774 r = g_string_free(s,FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((s), (
(0))) : g_string_free_and_steal (s)) : (g_string_free) ((s), (
(0))))
;
775
776 /* g_strchug(r); ? */
777 return r;
778}
779
780/**
781* merge_avpl:
782 * @param dst the avpl in which to merge the avps.
783 * @param src the avpl from which to get the avps.
784 * @param copy_avps whether avps should be copied instead of referenced.
785 *
786 * Adds the avps of src that are not existent in dst into dst.
787 *
788 **/
789extern void merge_avpl(AVPL* dst, AVPL* src, bool_Bool copy_avps) {
790 AVPN* cd = NULL((void*)0);
791 AVPN* cs = NULL((void*)0);
792
793#ifdef _AVP_DEBUGGING
794 dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %p %p",dst,src);
795#endif
796
797 cs = src->null.next;
798 cd = dst->null.next;
799
800 while (cs->avp && cd->avp) {
801
802 int name_diff = strcmp(cd->avp->n, cs->avp->n);
803
804 if (name_diff < 0) {
805 // dest < source, advance dest to find a better place to insert
806 cd = cd->next;
807 } else if (name_diff > 0) {
808 // dest > source, so it can be definitely inserted here.
809 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
810 cs = cs->next;
811 } else {
812 // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
813 int value_diff = strcmp(cd->avp->v, cs->avp->v);
814
815 if (value_diff < 0) {
816 // dest < source, do not insert it yet
817 cd = cd->next;
818 } else if (value_diff > 0) {
819 // dest > source, insert AVP before the current dest AVP
820 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
821 cs = cs->next;
822 } else {
823 // identical AVPs, do not create a duplicate.
824 cs = cs->next;
825 }
826 }
827 }
828
829 // if there are remaining source AVPs while there are no more destination
830 // AVPs (cd now represents the NULL item, after the last item), append
831 // all remaining source AVPs to the end
832 while (cs->avp) {
833 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
834 cs = cs->next;
835 }
836
837#ifdef _AVP_DEBUGGING
838 dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
839#endif
840
841 return;
842}
843
844
845/**
846 * new_avpl_from_avpl:
847 * @param name the name of the new avpl.
848 * @param avpl the avpl from which to get the avps.
849 * @param copy_avps whether avps should be copied instead of referenced.
850 *
851 * Creates a new avpl containing the same avps as the given avpl
852 * It will either reference or copy the avps.
853 *
854 * Return value: a pointer to the newly allocated string.
855 *
856 **/
857extern AVPL* new_avpl_from_avpl(const char* name, AVPL* avpl, bool_Bool copy_avps) {
858 AVPL* newavpl = new_avpl(name);
859 void* cookie = NULL((void*)0);
860 AVP* avp;
861 AVP* copy;
862
863#ifdef _AVP_DEBUGGING
864 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %p from=%p name='%s'",newavpl,avpl,name);
865#endif
866
867 while(( avp = get_next_avp(avpl,&cookie) )) {
868 if (copy_avps) {
869 copy = avp_copy(avp);
870 if ( ! insert_avp(newavpl,copy) ) {
871 delete_avp(copy);
872 }
873 } else {
874 insert_avp(newavpl,avp);
875 }
876 }
877
878#ifdef _AVP_DEBUGGING
879 dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
880#endif
881
882 return newavpl;
883}
884
885/**
886* match_avp:
887 * @param src an src to be compared against an "op" avp
888 * @param op the "op" avp that will be matched against the src avp
889 *
890 * Checks whether or not two avp's match.
891 *
892 * Return value: a pointer to the src avp if there's a match.
893 *
894 **/
895extern AVP* match_avp(AVP* src, AVP* op) {
896 char** splited;
897 int i;
898 char* p;
899 unsigned ls;
900 unsigned lo;
901 double fs = 0.0;
902 double fo = 0.0;
903 bool_Bool lower = false0;
904
905#ifdef _AVP_DEBUGGING
906 dbg_print(dbg_avpl_op,3,dbg_fp,"match_avp: %s%c%s; vs. %s%c%s;",src->n,src->o,src->v,op->n,op->o,op->v);
907#endif
908
909 if ( src->n != op->n ) {
910 return NULL((void*)0);
911 }
912
913 switch (op->o) {
914 case AVP_OP_EXISTS'?':
915 return src;
916 case AVP_OP_EQUAL'=':
917 return src->v == op->v ? src : NULL((void*)0);
918 case AVP_OP_NOTEQUAL'!':
919 return !( src->v == op->v) ? src : NULL((void*)0);
920 case AVP_OP_STARTS'^':
921 return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL((void*)0);
922 case AVP_OP_ONEOFF'|':
923 splited = g_strsplit(op->v,"|",0);
924 if (splited) {
925 for (i=0;splited[i];i++) {
926 if(g_str_equal(splited[i],src->v)(strcmp ((const char *) (splited[i]), (const char *) (src->
v)) == 0)
) {
927 g_strfreev(splited);
928 return src;
929 }
930 }
931 g_strfreev(splited);
932 }
933 return NULL((void*)0);
934
935 case AVP_OP_LOWER'<':
936 lower = true1;
937 /* FALLTHRU */
938 case AVP_OP_HIGHER'>':
939
940 fs = g_ascii_strtod(src->v, NULL((void*)0));
941 fo = g_ascii_strtod(op->v, NULL((void*)0));
942
943 if (lower) {
944 if (fs<fo) return src;
945 else return NULL((void*)0);
946 } else {
947 if (fs>fo) return src;
948 else return NULL((void*)0);
949 }
950 case AVP_OP_ENDS'$':
951 /* does this work? */
952 ls = (unsigned) strlen(src->v);
953 lo = (unsigned) strlen(op->v);
954
955 if ( ls < lo ) {
956 return NULL((void*)0);
957 } else {
958 p = src->v + ( ls - lo );
959 return g_str_equal(p,op->v)(strcmp ((const char *) (p), (const char *) (op->v)) == 0) ? src : NULL((void*)0);
960 }
961
962 /* case AVP_OP_TRANSF: */
963 /* return do_transform(src,op); */
964 case AVP_OP_CONTAINS'~':
965 return g_strrstr(src->v, op->v) ? src : NULL((void*)0);
966 }
967 /* will never get here */
968 return NULL((void*)0);
969}
970
971
972
973/**
974 * new_avpl_loose_match:
975 * @param name the name of the resulting avpl
976 * @param src the data AVPL to be matched against a condition AVPL
977 * @param op the conditions AVPL that will be matched against the data AVPL
978 * @param copy_avps whether the avps in the resulting avpl should be copied
979 *
980 * Creates a new AVP list containing all data AVPs that matched any of the
981 * conditions AVPs. If there are no matches, an empty list will be returned.
982 *
983 * Note: Loose will always be considered a successful match, it matches zero or
984 * more conditions.
985 */
986extern AVPL* new_avpl_loose_match(const char* name,
987 AVPL* src,
988 AVPL* op,
989 bool_Bool copy_avps) {
990
991 AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
992 AVPN* co = NULL((void*)0);
993 AVPN* cs = NULL((void*)0);
994
995#ifdef _AVP_DEBUGGING
996 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %p src=%p op=%p name='%s'",newavpl,src,op,name);
997#endif
998
999
1000 cs = src->null.next;
1001 co = op->null.next;
1002 while (cs->avp && co->avp) {
1003 int name_diff = strcmp(co->avp->n, cs->avp->n);
1004
1005 if (name_diff < 0) {
1006 // op < source, op is not matching
1007 co = co->next;
1008 } else if (name_diff > 0) {
1009 // op > source, source is not matching
1010 cs = cs->next;
1011 } else {
1012 // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
1013 AVPN *cond = co;
1014 do {
1015 if (match_avp(cs->avp, cond->avp)) {
1016 insert_avp_before_node(newavpl, newavpl->null.prev->next, cs->avp, copy_avps);
1017 break;
1018 }
1019 cond = cond->next;
1020 } while (cond->avp && cond->avp->n == cs->avp->n);
1021 cs = cs->next;
1022 }
1023 }
1024
1025 // return matches (possible none)
1026 return newavpl;
1027}
1028
1029/**
1030* new_avpl_pairs_match:
1031 * @param name the name of the resulting avpl
1032 * @param src the data AVPL to be matched against a condition AVPL
1033 * @param op the conditions AVPL that will be matched against the data AVPL
1034 * @param strict true if every condition must have a matching data AVP, false if
1035 * it is also acceptable that only one of the condition AVPs for the same
1036 * attribute is matching.
1037 * @param copy_avps whether the avps in the resulting avpl should be copied
1038 *
1039 * Creates an AVP list by matching pairs of conditions and data AVPs, returning
1040 * the data AVPs. If strict is true, then each condition must be paired with a
1041 * matching data AVP. If strict is false, then some conditions are allowed to
1042 * fail when other conditions for the same attribute do have a match. Note that
1043 * if the condition AVPL is empty, the result will be a match (an empty list).
1044 *
1045 * Return value: a pointer to the newly created avpl containing the
1046 * matching avps or NULL if there is no match.
1047 */
1048extern AVPL* new_avpl_pairs_match(const char* name, AVPL* src, AVPL* op, bool_Bool strict, bool_Bool copy_avps) {
1049 AVPL* newavpl;
1050 AVPN* co = NULL((void*)0);
1051 AVPN* cs = NULL((void*)0);
1052 const char *last_match = NULL((void*)0);
1053 bool_Bool matched = true1;
1054
1055 newavpl = new_avpl(scs_subscribe(avp_strings, name));
1056
1057#ifdef _AVP_DEBUGGING
1058 dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC((const char*) (__func__)),newavpl,src,op,name);
1059#endif
1060
1061 cs = src->null.next;
1062 co = op->null.next;
1063 while (cs->avp && co->avp) {
1064 int name_diff = g_strcmp0(co->avp->n, cs->avp->n);
1065 const char *failed_match = NULL((void*)0);
1066
1067 if (name_diff < 0) {
1068 // op < source, op has no data avp with same attribute.
1069 failed_match = co->avp->n;
1070 co = co->next;
1071 } else if (name_diff > 0) {
1072 // op > source, the source avp is not matched by any condition
1073 cs = cs->next;
1074 } else {
1075 // Matching attributes found, now try to find a matching data AVP for the condition.
1076 if (match_avp(cs->avp, co->avp)) {
1077 insert_avp_before_node(newavpl, newavpl->null.prev->next, cs->avp, copy_avps);
1078 last_match = co->avp->n;
1079 cs = cs->next;
1080 } else {
1081 failed_match = co->avp->n;
1082 }
1083 co = co->next;
1084 }
1085
1086 // condition did not match, check if we can continue matching.
1087 if (failed_match) {
1088 if (strict) {
1089 matched = false0;
1090 break;
1091 } else if (last_match != failed_match) {
1092 // None of the conditions so far matched the attribute, check for other candidates
1093 if (!co->avp || co->avp->n != last_match) {
1094 matched = false0;
1095 break;
1096 }
1097 }
1098 }
1099 }
1100
1101 // if there are any conditions remaining, then those could not be matched
1102 if (matched && strict && co->avp) {
1103 matched = false0;
1104 }
1105
1106 if (matched) {
1107 // there was a match, accept it
1108 return newavpl;
1109 } else {
1110 // no match, only delete AVPs too if they were copied
1111 delete_avpl(newavpl, copy_avps);
1112 return NULL((void*)0);
1113 }
1114}
1115
1116
1117/**
1118 * new_avpl_from_match:
1119 * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
1120 * @param name the name of the resulting avpl
1121 * @param src the data AVPL to be matched against a condition AVPL
1122 * @param op the conditions AVPL that will be matched against the data AVPL
1123 *
1124 * Matches the conditions AVPL against the original AVPL according to the mode.
1125 * If there is no match, NULL is returned. If there is actually a match, then
1126 * the matching AVPs (a subset of the data) are returned.
1127 */
1128extern AVPL* new_avpl_from_match(avpl_match_mode mode, const char* name,AVPL* src, AVPL* op, bool_Bool copy_avps) {
1129 AVPL* avpl = NULL((void*)0);
1130
1131 switch (mode) {
1132 case AVPL_STRICT:
1133 avpl = new_avpl_pairs_match(name, src, op, true1, copy_avps);
1134 break;
1135 case AVPL_LOOSE:
1136 avpl = new_avpl_loose_match(name,src,op,copy_avps);
1137 break;
1138 case AVPL_EVERY:
1139 avpl = new_avpl_pairs_match(name, src, op, false0, copy_avps);
1140 break;
1141 case AVPL_NO_MATCH:
1142 // XXX this seems unused
1143 avpl = new_avpl_from_avpl(name,src,copy_avps);
1144 merge_avpl(avpl, op, copy_avps);
1145 break;
1146 }
1147
1148 return avpl;
1149}
1150
1151/**
1152 * delete_avpl_transform:
1153 * @param op a pointer to the avpl transformation object
1154 *
1155 * Destroys an avpl transformation object and releases all the resources it
1156 * uses.
1157 *
1158 **/
1159extern void delete_avpl_transform(AVPL_Transf* op) {
1160 AVPL_Transf* next;
1161
1162 for (; op ; op = next) {
1163 next = op->next;
1164
1165 g_free(op->name);
1166
1167 if (op->match) {
1168 delete_avpl(op->match,true1);
1169 }
1170
1171 if (op->replace) {
1172 delete_avpl(op->replace,true1);
1173 }
1174
1175 g_free(op);
1176 }
1177
1178}
1179
1180
1181/**
1182 * avpl_transform:
1183 * @param src the source avpl for the transform operation.
1184 * @param op a pointer to the avpl transformation object to apply.
1185 *
1186 * Applies the "op" transformation to an avpl, matches it and eventually
1187 * replaces or inserts the transformed avps.
1188 *
1189 * Return value: whether the transformation was performed or not.
1190 **/
1191extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
1192 AVPL* avpl = NULL((void*)0);
1193 AVPN* cs;
1194 AVPN* cm;
1195 AVPN* n;
1196
1197#ifdef _AVP_DEBUGGING
1198 dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%p op=%p",src,op);
1199#endif
1200
1201 for ( ; op ; op = op->next) {
1202
1203 avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, true1);
1204
1205 if (avpl) {
1206 switch (op->replace_mode) {
1207 case AVPL_NO_REPLACE:
1208 delete_avpl(avpl,true1);
1209 return;
1210 case AVPL_INSERT:
1211 merge_avpl(src,op->replace,true1);
1212 delete_avpl(avpl,true1);
1213 return;
1214 case AVPL_REPLACE:
1215 cs = src->null.next;
1216 cm = avpl->null.next;
1217 // Removes AVPs from the source which are in the matched data.
1218 // Assume that the matched set is a subset of the source.
1219 while (cs->avp && cm->avp) {
1220 if (cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
1221 n = cs->next;
1222
1223 cs->prev->next = cs->next;
1224 cs->next->prev = cs->prev;
1225
1226 if (cs->avp)
1227 delete_avp(cs->avp);
1228 g_slice_free(any_avp_type,(any_avp_type*)cs)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)cs)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)cs)
); } while (0)
;
1229
1230 cs = n;
1231 cm = cm->next;
1232 } else {
1233 // Current matched AVP is not equal to the current
1234 // source AVP. Since there must be a source AVP for
1235 // each matched AVP, advance current source and not
1236 // the match AVP.
1237 cs = cs->next;
1238 }
1239 }
1240
1241 merge_avpl(src,op->replace,true1);
1242 delete_avpl(avpl,true1);
1243 return;
1244 }
1245 }
1246 }
1247}
1248
1249
1250/**
1251 * new_loal:
1252 * @param name the name the loal will take.
1253 *
1254 * Creates an empty list of avp lists.
1255 *
1256 * Return value: a pointer to the newly created loal.
1257 **/
1258extern LoAL* new_loal(const char* name) {
1259 LoAL* new_loal_p = (LoAL*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
1260
1261 if (! name) {
1262 name = "anonymous";
1263 }
1264
1265#ifdef _AVP_DEBUGGING
1266 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_p: %p name=%s",new_loal_p,name);
1267#endif
1268
1269 new_loal_p->name = scs_subscribe(avp_strings,name);
1270 new_loal_p->null.avpl = NULL((void*)0);
1271 new_loal_p->null.next = &new_loal_p->null;
1272 new_loal_p->null.prev = &new_loal_p->null;
1273 new_loal_p->len = 0;
1274 return new_loal_p;
1275}
1276
1277/**
1278 * loal_append:
1279 * @param loal the loal on which to operate.
1280 * @param avpl the avpl to append.
1281 *
1282 * Appends an avpl to a loal.
1283 *
1284 **/
1285extern void loal_append(LoAL* loal, AVPL* avpl) {
1286 LoALnode* node = (LoALnode*)g_slice_new(any_avp_type)((any_avp_type*) g_slice_alloc (sizeof (any_avp_type)));
1287
1288#ifdef _AVP_DEBUGGING
1289 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %p",node);
1290#endif
1291
1292 node->avpl = avpl;
1293 node->next = &loal->null;
1294 node->prev = loal->null.prev;
1295
1296 loal->null.prev->next = node;
1297 loal->null.prev = node;
1298 loal->len++;
1299}
1300
1301
1302/**
1303 * extract_first_avpl:
1304 * @param loal the loal on which to operate.
1305 *
1306 * Extracts the first avpl contained in a loal.
1307 *
1308 * Return value: a pointer to the extracted avpl.
1309 *
1310 **/
1311extern AVPL* extract_first_avpl(LoAL* loal) {
1312 LoALnode* node;
1313 AVPL* avpl;
1314
1315#ifdef _AVP_DEBUGGING
1316 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
1317#endif
1318
1319 node = loal->null.next;
1320
1321 loal->null.next->next->prev = &loal->null;
1322 loal->null.next = node->next;
1323
1324 loal->len--;
1325
1326 avpl = node->avpl;
1327
1328 if ( avpl ) {
1329 g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node
)); } while (0)
;
1330
1331#ifdef _AVP_DEBUGGING
1332 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
1333 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
1334#endif
1335 }
1336
1337 return avpl;
1338}
1339
1340/**
1341* extract_first_avpl:
1342 * @param loal the loal on which to operate.
1343 *
1344 * Extracts the last avpl contained in a loal.
1345 *
1346 * Return value: a pointer to the extracted avpl.
1347 *
1348 **/
1349extern AVPL* extract_last_avpl(LoAL* loal){
1350 LoALnode* node;
1351 AVPL* avpl;
1352
1353 node = loal->null.prev;
1354
1355 loal->null.prev->prev->next = &loal->null;
1356 loal->null.prev = node->prev;
1357
1358 loal->len--;
1359
1360 avpl = node->avpl;
1361
1362 if ( avpl ) {
1363 g_slice_free(any_avp_type,(any_avp_type*)node)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)node)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)node
)); } while (0)
;
1364#ifdef _AVP_DEBUGGING
1365 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %p",node);
1366#endif
1367 }
1368
1369 return avpl;
1370}
1371
1372/**
1373 * extract_first_avpl:
1374 * @param loal the loal on which to operate.
1375 * @param cookie pointer to the pointer variable to contain the state between calls
1376 *
1377 * At each call will return the following avpl from a loal. The given cookie
1378 * will be used to manatain the state between calls.
1379 *
1380 * Return value: a pointer to the next avpl.
1381 *
1382 **/
1383extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
1384 LoALnode* node;
1385
1386#ifdef _AVP_DEBUGGING
1387 dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%p node=%p",loal,*cookie);
1388#endif
1389
1390 if (*cookie) {
1391 node = (LoALnode*) *cookie;
1392 } else {
1393 node = loal->null.next;
1394 }
1395
1396 *cookie = node->next;
1397
1398 return node->avpl;
1399}
1400
1401/**
1402 * delete_loal:
1403 * @param loal the loal to be deleted.
1404 * @param avpls_too whether avpls contained by the loal should be deleted as well
1405 * @param avps_too whether avps contained by the avpls should be also deleted
1406 *
1407 * Destroys a loal and eventually desstroys avpls and avps.
1408 *
1409 **/
1410extern void delete_loal(LoAL* loal, bool_Bool avpls_too, bool_Bool avps_too) {
1411 AVPL* avpl;
1412
1413#ifdef _AVP_DEBUGGING
1414 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %p",loal);
1415#endif
1416
1417 while(( avpl = extract_last_avpl(loal) )) {
1418 if (avpls_too) {
1419 delete_avpl(avpl,avps_too);
1420 }
1421 }
1422
1423 scs_unsubscribe(avp_strings,loal->name);
1424 g_slice_free(any_avp_type,(any_avp_type*)loal)do { if (1) g_slice_free1 (sizeof (any_avp_type), ((any_avp_type
*)loal)); else (void) ((any_avp_type*) 0 == ((any_avp_type*)loal
)); } while (0)
;
1425}
1426
1427
1428
1429/****************************************************************************
1430 ******************* the following are used in load_loal_from_file
1431 ****************************************************************************/
1432
1433/**
1434 * load_loal_error:
1435 * Used by loal_from_file to handle errors while loading.
1436 **/
1437static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const char* fmt, ...) {
1438 va_list list;
1439 char* desc;
1440 LoAL* ret = NULL((void*)0);
1441 char* err;
1442
1443 va_start( list, fmt )__builtin_va_start(list, fmt);
1444 desc = ws_strdup_vprintf(fmt, list)wmem_strdup_vprintf(((void*)0), fmt, list);
1445 va_end( list )__builtin_va_end(list);
1446
1447 if (loal) {
1448 err = ws_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc)wmem_strdup_printf(((void*)0), "Error Loading LoAL from file: in %s at line: %i, %s"
,loal->name,linenum,desc)
;
1449 } else {
1450 err = ws_strdup_printf("Error Loading LoAL at line: %i, %s",linenum,desc)wmem_strdup_printf(((void*)0), "Error Loading LoAL at line: %i, %s"
,linenum,desc)
;
1451 }
1452 ret = new_loal(err);
1453
1454 g_free(desc);
1455 g_free(err);
1456
1457 if (fp) fclose(fp);
1458 if (loal) delete_loal(loal,true1,true1);
1459 if (curr) delete_avpl(curr,true1);
1460
1461 return ret;
1462}
1463
1464
1465/* the maximum length allowed for a line */
1466#define MAX_ITEM_LEN8192 8192
1467
1468/* this two ugly things are used for tokenizing */
1469#define AVP_OP_CHAR'=': case '^': case '$': case '~': case '<': case '>': case
'?': case '|': case '&' : case '!'
'=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1470
1471#define AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G'
: case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case
'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T'
:case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case
'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g'
: case 'h': case 'i': case 'j': case 'k': case 'l': case 'm':
case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case
't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z'
: case '_': case '0': case '1': case '2': case '3': case '4':
case '5': case '6':case '7': case '8': case '9': case '.'
'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1472case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1473case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1474case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1475case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1476case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1477case '7': case '8': case '9': case '.'
1478
1479
1480/**
1481 * loal_from_file:
1482 * @param filename the file containing a loals text representation.
1483 *
1484 * Given a filename it will attempt to load a loal containing a copy of
1485 * the avpls represented in the file.
1486 *
1487 * Return value: if successful a pointer to the new populated loal, else NULL.
1488 *
1489 **/
1490extern LoAL* loal_from_file(char* filename) {
1491 FILE *fp = NULL((void*)0);
1492 char c;
1493 int i = 0;
1494 uint32_t linenum = 1;
1495 char *linenum_buf;
1496 char *name;
1497 char *value;
1498 char op = '?';
1499 LoAL *loal_error, *loal = new_loal(filename);
1500 AVPL* curr = NULL((void*)0);
1501 AVP* avp;
1502
1503 enum _load_loal_states {
1504 START,
1505 BEFORE_NAME,
1506 IN_NAME,
1507 IN_VALUE,
1508 MY_IGNORE
1509 } state;
1510
1511 linenum_buf = (char*)g_malloc(MAX_ITEM_LEN8192);
1512 name = (char*)g_malloc(MAX_ITEM_LEN8192);
1513 value = (char*)g_malloc(MAX_ITEM_LEN8192);
1514#ifndef _WIN32
1515 if (! getuid()) {
1
Assuming the condition is false
2
Taking false branch
1516 loal_error = load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root");
1517 goto error;
1518 }
1519#endif
1520
1521 state = START;
1522
1523 if (( fp = ws_fopenfopen(filename,"r") )) {
3
Assuming 'fp' is non-null
4
Taking true branch
1524 while(( c = (char) fgetc(fp) )){
5
Loop condition is true. Entering loop body
12
Execution continues on line 1524
13
Assuming this stream operation fails
14
Loop condition is true. Entering loop body
20
Execution continues on line 1524
21
File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior
1525
1526 if ( feof(fp) ) {
6
Taking false branch
15
Taking false branch
1527 if ( ferror(fp) ) {
1528 report_read_failure(filename,errno(*__errno_location ()));
1529 loal_error = load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
1530 goto error;
1531 }
1532 break;
1533 }
1534
1535 if ( c == '\n' ) {
7
Assuming the condition is false
8
Taking false branch
16
Taking false branch
1536 linenum++;
1537 }
1538
1539 if ( i >= MAX_ITEM_LEN8192 - 1 ) {
9
Taking false branch
17
Taking false branch
1540 loal_error = load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded");
1541 goto error;
1542 }
1543
1544 switch(state) {
10
Control jumps to 'case START:' at line 1554
18
Control jumps to 'case MY_IGNORE:' at line 1545
1545 case MY_IGNORE:
1546 switch (c) {
19
Control jumps to the 'default' case at line 1551
1547 case '\n':
1548 state = START;
1549 i = 0;
1550 continue;
1551 default:
1552 continue;
1553 }
1554 case START:
1555 switch (c) {
11
Control jumps to 'case 35:' at line 1571
1556 case ' ': case '\t':
1557 /* ignore whitespace at line start */
1558 continue;
1559 case '\n':
1560 /* ignore empty lines */
1561 i = 0;
1562 continue;
1563 case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G'
: case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case
'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T'
:case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case
'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g'
: case 'h': case 'i': case 'j': case 'k': case 'l': case 'm':
case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case
't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z'
: case '_': case '0': case '1': case '2': case '3': case '4':
case '5': case '6':case '7': case '8': case '9': case '.'
:
1564 state = IN_NAME;
1565 i = 0;
1566 name[i++] = c;
1567 name[i] = '\0';
1568 snprintf(linenum_buf,MAX_ITEM_LEN8192,"%s:%u",filename,linenum);
1569 curr = new_avpl(linenum_buf);
1570 continue;
1571 case '#':
1572 state = MY_IGNORE;
1573 continue;
1574 default:
1575 loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1576 goto error;
1577 }
1578 case BEFORE_NAME:
1579 i = 0;
1580 name[0] = '\0';
1581 switch (c) {
1582 case '\\':
1583 c = (char) fgetc(fp);
1584 if (c != '\n') ungetc(c,fp);
1585 continue;
1586 case ' ':
1587 case '\t':
1588 continue;
1589 case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G'
: case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case
'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T'
:case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case
'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g'
: case 'h': case 'i': case 'j': case 'k': case 'l': case 'm':
case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case
't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z'
: case '_': case '0': case '1': case '2': case '3': case '4':
case '5': case '6':case '7': case '8': case '9': case '.'
:
1590 state = IN_NAME;
1591
1592 name[i++] = c;
1593 name[i] = '\0';
1594 continue;
1595 case '\n':
1596 loal_append(loal,curr);
1597 curr = NULL((void*)0);
1598 state = START;
1599 continue;
1600 default:
1601 loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1602 goto error;
1603 }
1604 case IN_NAME:
1605 switch (c) {
1606 case ';':
1607 state = BEFORE_NAME;
1608
1609 op = '?';
1610 name[i] = '\0';
1611 value[0] = '\0';
1612 i = 0;
1613
1614 avp = new_avp(name,value,op);
1615
1616 if (! insert_avp(curr,avp) ) {
1617 delete_avp(avp);
1618 }
1619
1620 continue;
1621 case AVP_OP_CHAR'=': case '^': case '$': case '~': case '<': case '>': case
'?': case '|': case '&' : case '!'
:
1622 name[i] = '\0';
1623 i = 0;
1624 op = c;
1625 state = IN_VALUE;
1626 continue;
1627 case AVP_NAME_CHAR'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G'
: case 'H': case 'I': case 'J':case 'K': case 'L': case 'M': case
'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T'
:case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case
'a': case 'b': case 'c': case 'd':case 'e': case 'f': case 'g'
: case 'h': case 'i': case 'j': case 'k': case 'l': case 'm':
case 'n':case 'o': case 'p': case 'q': case 'r': case 's': case
't': case 'u': case 'v': case 'w': case 'x':case 'y': case 'z'
: case '_': case '0': case '1': case '2': case '3': case '4':
case '5': case '6':case '7': case '8': case '9': case '.'
:
1628 name[i++] = c;
1629 continue;
1630 case '\n':
1631 loal_error = load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
1632 goto error;
1633 default:
1634 loal_error = load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
1635 goto error;
1636 }
1637 case IN_VALUE:
1638 switch (c) {
1639 case '\\':
1640 value[i++] = (char) fgetc(fp);
1641 continue;
1642 case ';':
1643 state = BEFORE_NAME;
1644
1645 value[i] = '\0';
1646 i = 0;
1647
1648 avp = new_avp(name,value,op);
1649
1650 if (! insert_avp(curr,avp) ) {
1651 delete_avp(avp);
1652 }
1653 continue;
1654 case '\n':
1655 loal_error = load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
1656 goto error;
1657 default:
1658 value[i++] = c;
1659 continue;
1660 }
1661 }
1662 }
1663 fclose (fp);
1664
1665 if (curr) {
1666 // Premature EOF? It could just be a file that doesn't
1667 // end in a newline, but hard to say without checking
1668 // state. Error, discard, add to existing loal?
1669 delete_avpl(curr,true1);
1670 }
1671
1672 g_free(linenum_buf);
1673 g_free(name);
1674 g_free(value);
1675
1676 return loal;
1677
1678 } else {
1679 report_open_failure(filename,errno(*__errno_location ()),false0);
1680 loal_error = load_loal_error(NULL((void*)0),loal,NULL((void*)0),0,"Cannot Open file '%s'",filename);
1681 }
1682
1683error:
1684 g_free(linenum_buf);
1685 g_free(name);
1686 g_free(value);
1687
1688 return loal_error;
1689}