2 * MATE -- Meta Analysis Tracing Engine
3 * Utility Library: Single Copy Strings and Attribute Value Pairs
5 * Copyright 2004, Luis E. Garcia Ontanon <luis@ontanon.org>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include "mate_util.h"
20 #include <wsutil/file_util.h>
23 /***************************************************************************
25 ***************************************************************************
26 * This is the debug facility of the thing.
27 ***************************************************************************/
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
36 void dbg_print(const int* which
, int how
, FILE* where
, const char* fmt
, ... ) {
37 static char debug_buffer
[DEBUG_BUFFER_SIZE
];
40 if ( ! which
|| *which
< how
) return;
42 va_start( list
, fmt
);
43 vsnprintf(debug_buffer
,DEBUG_BUFFER_SIZE
,fmt
,list
);
47 ws_message("%s", debug_buffer
);
49 fputs(debug_buffer
,where
);
55 /***************************************************************************
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.
61 ***************************************************************************/
66 * Initializes the scs hash.
69 struct _scs_collection
{
70 GHashTable
* hash
; /* key: a string value: unsigned number of subscribers */
73 /* ToDo? free any string,ctr entries pointed to by the hash table ??
74 * XXX: AFAIKT destroy_scs_collection() might be called only when reading a
75 * mate config file. Since reading a new config file can apparently currently
76 * only be done once after starting Wireshark, in theory this fcn
77 * currently should never be called since there will never be an existing
78 * scs_collection to be destroyed.
80 static void destroy_scs_collection(SCS_collection
* c
) {
81 if (c
->hash
) g_hash_table_destroy(c
->hash
);
84 static SCS_collection
* scs_init(void) {
85 SCS_collection
* c
= g_new(SCS_collection
, 1);
87 c
->hash
= g_hash_table_new(g_str_hash
,g_str_equal
);
95 * @param c the scs hash
98 * Checks if the given string exists already and if so it increases the count of
99 * subsscribers and returns a pointer to the stored string. If not It will copy
100 * the given string store it in the hash and return the pointer to the copy.
101 * Remember, containment is handled internally, take care of your own strings.
103 * Return value: a pointer to the subscribed string.
105 char* scs_subscribe(SCS_collection
* c
, const char* s
) {
110 g_hash_table_lookup_extended(c
->hash
,(const void *)s
,(void * *)&orig
,(void * *)&ip
);
115 ip
= g_slice_new(unsigned);
120 if (len
<= SCS_SMALL_SIZE
) {
121 len
= SCS_SMALL_SIZE
;
122 } else if (len
<= SCS_MEDIUM_SIZE
) {
123 len
= SCS_MEDIUM_SIZE
;
124 } else if (len
<= SCS_LARGE_SIZE
) {
125 len
= SCS_LARGE_SIZE
;
126 } else if (len
< SCS_HUGE_SIZE
) {
130 ws_warning("mate SCS: string truncated due to huge size");
133 orig
= (char *)g_slice_alloc(len
);
134 (void) g_strlcpy(orig
,s
,len
);
136 g_hash_table_insert(c
->hash
,orig
,ip
);
144 * @param c the scs hash
147 * decreases the count of subscribers, if zero frees the internal copy of
150 void scs_unsubscribe(SCS_collection
* c
, char* s
) {
155 g_hash_table_lookup_extended(c
->hash
,(const void *)s
,(void * *)&orig
,(void * *)&ip
);
159 g_hash_table_remove(c
->hash
,orig
);
163 if (len
< SCS_SMALL_SIZE
) {
164 len
= SCS_SMALL_SIZE
;
165 } else if (len
< SCS_MEDIUM_SIZE
) {
166 len
= SCS_MEDIUM_SIZE
;
167 } else if (len
< SCS_LARGE_SIZE
) {
168 len
= SCS_LARGE_SIZE
;
173 g_slice_free1(len
, orig
);
174 g_slice_free(unsigned,ip
);
180 ws_warning("unsubscribe: not subscribed");
185 * scs_subscribe_printf:
186 * @param fmt a format string ...
188 * Formats the input and subscribes it.
190 * Return value: the stored copy of the formated string.
193 char* scs_subscribe_printf(SCS_collection
* c
, char* fmt
, ...) {
195 static char buf
[SCS_HUGE_SIZE
];
197 va_start( list
, fmt
);
198 vsnprintf(buf
, SCS_HUGE_SIZE
, fmt
, list
);
201 return scs_subscribe(c
,buf
);
204 /***************************************************************************
206 ***************************************************************************
207 * The Thing operates mainly on avps, avpls and loals
208 * - attribute value pairs (two strings: the name and the value and an operator)
209 * - avp lists a somehow sorted list of avps
210 * - loal (list of avp lists) an arbitrarily sorted list of avpls
213 ***************************************************************************/
216 typedef union _any_avp_type
{
225 static SCS_collection
* avp_strings
;
227 #ifdef _AVP_DEBUGGING
230 static int dbg_level
;
231 static int* dbg
= &dbg_level
;
233 static int dbg_avp_level
;
234 static int* dbg_avp
= &dbg_avp_level
;
236 static int dbg_avp_op_level
;
237 static int* dbg_avp_op
= &dbg_avp_op_level
;
239 static int dbg_avpl_level
;
240 static int* dbg_avpl
= &dbg_avpl_level
;
242 static int dbg_avpl_op_level
;
243 static int* dbg_avpl_op
= &dbg_avpl_op_level
;
247 * @param fp the file in which to send debugging output.
248 * @param general a pointer to the level of debugging of facility "general"
249 * @param avp a pointer to the level of debugging of facility "avp"
250 * @param avp_op a pointer to the level of debugging of facility "avp_op"
251 * @param avpl a pointer to the level of debugging of facility "avpl"
252 * @param avpl_op a pointer to the level of debugging of facility "avpl_op"
254 * If enabled sets up the debug facilities for the avp library.
257 extern void setup_avp_debug(FILE* fp
, int* general
, int* avp
, int* avp_op
, int* avpl
, int* avpl_op
) {
263 dbg_avpl_op
= avpl_op
;
266 #endif /* _AVP_DEBUGGING */
271 * (Re)Initializes the avp library.
274 extern void avp_init(void) {
276 if (avp_strings
) destroy_scs_collection(avp_strings
);
277 avp_strings
= scs_init();
282 * new_avp_from_finfo:
283 * @param name the name the avp will have.
284 * @param finfo the field_info from which to fetch the data.
286 * Creates an avp from a field_info record.
288 * Return value: a pointer to the newly created avp.
291 extern AVP
* new_avp_from_finfo(const char* name
, field_info
* finfo
) {
292 AVP
* new_avp_val
= (AVP
*)g_slice_new(any_avp_type
);
296 new_avp_val
->n
= scs_subscribe(avp_strings
, name
);
298 repr
= fvalue_to_string_repr(NULL
, finfo
->value
, FTREPR_DISPLAY
, finfo
->hfinfo
->display
);
301 value
= scs_subscribe(avp_strings
, repr
);
302 wmem_free(NULL
, repr
);
303 #ifdef _AVP_DEBUGGING
304 dbg_print (dbg_avp
,2,dbg_fp
,"new_avp_from_finfo: from string: %s",value
);
307 #ifdef _AVP_DEBUGGING
308 dbg_print (dbg_avp
,2,dbg_fp
,"new_avp_from_finfo: a proto: %s",finfo
->hfinfo
->abbrev
);
310 value
= scs_subscribe(avp_strings
, "");
313 new_avp_val
->v
= value
;
315 new_avp_val
->o
= '=';
317 #ifdef _AVP_DEBUGGING
318 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
);
327 * @param name the name the avp will have.
328 * @param value the value the avp will have.
329 * @param o the operator of this avp.
331 * Creates an avp given every parameter.
333 * Return value: a pointer to the newly created avp.
336 extern AVP
* new_avp(const char* name
, const char* value
, char o
) {
337 AVP
* new_avp_val
= (AVP
*)g_slice_new(any_avp_type
);
339 new_avp_val
->n
= scs_subscribe(avp_strings
, name
);
340 new_avp_val
->v
= scs_subscribe(avp_strings
, value
);
343 #ifdef _AVP_DEBUGGING
344 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
);
352 * @param avp the avp to delete.
354 * Destroys an avp and releases the resources it uses.
357 extern void delete_avp(AVP
* avp
) {
358 #ifdef _AVP_DEBUGGING
359 dbg_print(dbg_avp
,1,dbg_fp
,"delete_avp: %p %s%c%s;",avp
,avp
->n
,avp
->o
,avp
->v
);
362 scs_unsubscribe(avp_strings
, avp
->n
);
363 scs_unsubscribe(avp_strings
, avp
->v
);
364 g_slice_free(any_avp_type
,(any_avp_type
*)avp
);
370 * @param from the avp to be copied.
372 * Creates an avp whose name op and value are copies of the given one.
374 * Return value: a pointer to the newly created avp.
377 extern AVP
* avp_copy(AVP
* from
) {
378 AVP
* new_avp_val
= (AVP
*)g_slice_new(any_avp_type
);
380 new_avp_val
->n
= scs_subscribe(avp_strings
, from
->n
);
381 new_avp_val
->v
= scs_subscribe(avp_strings
, from
->v
);
382 new_avp_val
->o
= from
->o
;
384 #ifdef _AVP_DEBUGGING
385 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
);
393 * @param name the name the avpl will have.
395 * Creates an empty avpl.
397 * Return value: a pointer to the newly created avpl.
400 extern AVPL
* new_avpl(const char* name
) {
401 AVPL
* new_avpl_p
= (AVPL
*)g_slice_new(any_avp_type
);
403 #ifdef _AVP_DEBUGGING
404 dbg_print(dbg_avpl_op
,7,dbg_fp
,"new_avpl_p: %p name=%s",new_avpl_p
,name
);
407 new_avpl_p
->name
= name
? scs_subscribe(avp_strings
, name
) : scs_subscribe(avp_strings
, "");
409 new_avpl_p
->null
.avp
= NULL
;
410 new_avpl_p
->null
.next
= &new_avpl_p
->null
;
411 new_avpl_p
->null
.prev
= &new_avpl_p
->null
;
417 extern void rename_avpl(AVPL
* avpl
, char* name
) {
418 scs_unsubscribe(avp_strings
,avpl
->name
);
419 avpl
->name
= scs_subscribe(avp_strings
,name
);
423 * insert_avp_before_node:
424 * @param avpl the avpl in which to insert.
425 * @param next_node the next node before which the new avpn has to be inserted.
426 * @param avp the avp to be inserted.
427 * @param copy_avp whether the original AVP or a copy thereof must be inserted.
429 * Pre-condition: the avp is sorted before before_avp and does not already exist
432 static void insert_avp_before_node(AVPL
* avpl
, AVPN
* next_node
, AVP
*avp
, bool copy_avp
) {
433 AVPN
* new_avp_val
= (AVPN
*)g_slice_new(any_avp_type
);
435 new_avp_val
->avp
= copy_avp
? avp_copy(avp
) : avp
;
437 #ifdef _AVP_DEBUGGING
438 dbg_print(dbg_avpl_op
,7,dbg_fp
,"new_avpn: %p",new_avp_val
);
439 dbg_print(dbg_avpl
,5,dbg_fp
,"insert_avp: inserting %p in %p before %p;",avp
,avpl
,next_node
);
442 new_avp_val
->next
= next_node
;
443 new_avp_val
->prev
= next_node
->prev
;
444 next_node
->prev
->next
= new_avp_val
;
445 next_node
->prev
= new_avp_val
;
449 #ifdef _AVP_DEBUGGING
450 dbg_print(dbg_avpl
,4,dbg_fp
,"avpl: %p new len: %i",avpl
,avpl
->len
);
456 * @param avpl the avpl in which to insert.
457 * @param avp the avp to be inserted.
459 * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
461 * Return value: whether it was inserted or not.
463 * BEWARE: Check the return value, you might need to delete the avp if
464 * it is not inserted.
466 extern bool insert_avp(AVPL
* avpl
, AVP
* avp
) {
469 #ifdef _AVP_DEBUGGING
470 dbg_print(dbg_avpl_op
,4,dbg_fp
,"insert_avp: %p %p %s%c%s;",avpl
,avp
,avp
->n
,avp
->o
,avp
->v
);
473 /* get to the insertion point */
474 for (c
=avpl
->null
.next
; c
->avp
; c
= c
->next
) {
475 int name_diff
= strcmp(avp
->n
, c
->avp
->n
);
477 if (name_diff
== 0) {
478 int value_diff
= strcmp(avp
->v
, c
->avp
->v
);
480 if (value_diff
< 0) {
484 if (value_diff
== 0) {
485 // ignore duplicate values, prevents (a=1, a=1)
486 // note that this is also used to insert
487 // conditions AVPs, so really check if the name,
488 // value and operator are all equal.
489 if (c
->avp
->o
== avp
->o
&& avp
->o
== AVP_OP_EQUAL
) {
500 insert_avp_before_node(avpl
, c
, avp
, false);
507 * @param avpl the avpl from which to try to get the avp.
508 * @param name the name of the avp we are looking for.
509 * @param cookie variable in which to store the state between calls.
511 * Gets pointer to the next avp whose name is given; uses cookie to store its
512 * state between calls.
514 * Return value: a pointer to the next matching avp if there's one, else NULL.
517 extern AVP
* get_avp_by_name(AVPL
* avpl
, char* name
, void** cookie
) {
519 AVPN
* start
= (AVPN
*) *cookie
;
521 #ifdef _AVP_DEBUGGING
522 dbg_print(dbg_avpl_op
,7,dbg_fp
,"get_avp_by_name: entering: %p %s %p",avpl
,name
,*cookie
);
525 name
= scs_subscribe(avp_strings
, name
);
527 if (!start
) start
= avpl
->null
.next
;
529 for ( curr
= start
; curr
->avp
; curr
= curr
->next
) {
530 if ( curr
->avp
->n
== name
) {
537 #ifdef _AVP_DEBUGGING
538 dbg_print(dbg_avpl_op
,5,dbg_fp
,"get_avp_by_name: got avp: %p",curr
);
541 scs_unsubscribe(avp_strings
, name
);
547 * extract_avp_by_name:
548 * @param avpl the avpl from which to try to extract the avp.
549 * @param name the name of the avp we are looking for.
551 * Extracts from the avpl the next avp whose name is given;
553 * Return value: a pointer to extracted avp if there's one, else NULL.
556 extern AVP
* extract_avp_by_name(AVPL
* avpl
, char* name
) {
560 #ifdef _AVP_DEBUGGING
561 dbg_print(dbg_avpl_op
,7,dbg_fp
,"extract_avp_by_name: entering: %p %s",avpl
,name
);
564 name
= scs_subscribe(avp_strings
, name
);
566 for ( curr
= avpl
->null
.next
; curr
->avp
; curr
= curr
->next
) {
567 if ( curr
->avp
->n
== name
) {
572 scs_unsubscribe(avp_strings
, name
);
574 if( ! curr
->avp
) return NULL
;
576 curr
->next
->prev
= curr
->prev
;
577 curr
->prev
->next
= curr
->next
;
581 g_slice_free(any_avp_type
,(any_avp_type
*)curr
);
585 #ifdef _AVP_DEBUGGING
586 dbg_print(dbg_avpl
,4,dbg_fp
,"avpl: %p new len: %i",avpl
,avpl
->len
);
589 #ifdef _AVP_DEBUGGING
590 dbg_print(dbg_avpl_op
,5,dbg_fp
,"extract_avp_by_name: got avp: %p",avp
);
599 * @param avpl the avpl from which to try to extract the avp.
601 * Extracts the fisrt avp from the avpl.
603 * Return value: a pointer to extracted avp if there's one, else NULL.
606 extern AVP
* extract_first_avp(AVPL
* avpl
) {
610 #ifdef _AVP_DEBUGGING
611 dbg_print(dbg_avpl_op
,7,dbg_fp
,"extract_first_avp: %p",avpl
);
614 node
= avpl
->null
.next
;
616 avpl
->null
.next
->prev
= &avpl
->null
;
617 avpl
->null
.next
= node
->next
;
622 g_slice_free(any_avp_type
,(any_avp_type
*)node
);
624 #ifdef _AVP_DEBUGGING
625 dbg_print(dbg_avpl
,4,dbg_fp
,"avpl: %p new len: %i",avpl
,avpl
->len
);
629 #ifdef _AVP_DEBUGGING
630 dbg_print(dbg_avpl_op
,5,dbg_fp
,"extract_first_avp: got avp: %p",avp
);
640 * @param avpl the avpl from which to try to extract the avp.
642 * Extracts the last avp from the avpl.
644 * Return value: a pointer to extracted avp if there's one, else NULL.
647 extern AVP
* extract_last_avp(AVPL
* avpl
) {
651 node
= avpl
->null
.prev
;
653 avpl
->null
.prev
->next
= &avpl
->null
;
654 avpl
->null
.prev
= node
->prev
;
659 g_slice_free(any_avp_type
,(any_avp_type
*)node
);
661 #ifdef _AVP_DEBUGGING
662 dbg_print(dbg_avpl
,4,dbg_fp
,"avpl: %p new len: %i",avpl
,avpl
->len
);
666 #ifdef _AVP_DEBUGGING
667 dbg_print(dbg_avpl_op
,5,dbg_fp
,"extract_last_avp: got avp: %p",avp
);
677 * @param avpl the avpl from which to try to extract the avp.
678 * @param avps_too whether or not it should delete the avps as well.
680 * Destroys an avpl and releases the resources it uses. If told to do
681 * so releases the avps as well.
684 extern void delete_avpl(AVPL
* avpl
, bool avps_too
) {
686 #ifdef _AVP_DEBUGGING
687 dbg_print(dbg_avpl
,3,dbg_fp
,"delete_avpl: %p",avpl
);
690 while(( avp
= extract_last_avp(avpl
))) {
696 scs_unsubscribe(avp_strings
,avpl
->name
);
697 g_slice_free(any_avp_type
,(any_avp_type
*)avpl
);
704 * @param avpl the avpl from which to try to get the avps.
705 * @param cookie variable in which to store the state between calls.
707 * Iterates on an avpl to get its avps.
709 * Return value: a pointer to the next avp if there's one, else NULL.
712 extern AVP
* get_next_avp(AVPL
* avpl
, void** cookie
) {
715 #ifdef _AVP_DEBUGGING
716 dbg_print(dbg_avpl_op
,5,dbg_fp
,"get_next_avp: avpl: %p avpn: %p",avpl
,*cookie
);
720 node
= (AVPN
*) *cookie
;
722 node
= avpl
->null
.next
;
725 *cookie
= node
->next
;
727 #ifdef _AVP_DEBUGGING
728 dbg_print(dbg_avpl_op
,5,dbg_fp
,"extract_last_avp: got avp: %p",node
->avp
);
736 * @param avpl the avpl to represent.
738 * Creates a newly allocated string containing a representation of an avpl.
740 * Return value: a pointer to the newly allocated string.
743 char* avpl_to_str(AVPL
* avpl
) {
745 GString
* s
= g_string_new("");
749 for(c
=avpl
->null
.next
; c
->avp
; c
= c
->next
) {
750 avp_s
= avp_to_str(c
->avp
);
751 g_string_append_printf(s
," %s;",avp_s
);
755 r
= g_string_free(s
,FALSE
);
757 /* g_strchug(r); ? */
761 extern char* avpl_to_dotstr(AVPL
* avpl
) {
763 GString
* s
= g_string_new("");
767 for(c
=avpl
->null
.next
; c
->avp
; c
= c
->next
) {
768 avp_s
= avp_to_str(c
->avp
);
769 g_string_append_printf(s
," .%s;",avp_s
);
773 r
= g_string_free(s
,FALSE
);
775 /* g_strchug(r); ? */
781 * @param dst the avpl in which to merge the avps.
782 * @param src the avpl from which to get the avps.
783 * @param copy_avps whether avps should be copied instead of referenced.
785 * Adds the avps of src that are not existent in dst into dst.
788 extern void merge_avpl(AVPL
* dst
, AVPL
* src
, bool copy_avps
) {
792 #ifdef _AVP_DEBUGGING
793 dbg_print(dbg_avpl_op
,3,dbg_fp
,"merge_avpl: %p %p",dst
,src
);
799 while (cs
->avp
&& cd
->avp
) {
801 int name_diff
= strcmp(cd
->avp
->n
, cs
->avp
->n
);
804 // dest < source, advance dest to find a better place to insert
806 } else if (name_diff
> 0) {
807 // dest > source, so it can be definitely inserted here.
808 insert_avp_before_node(dst
, cd
, cs
->avp
, copy_avps
);
811 // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
812 int value_diff
= strcmp(cd
->avp
->v
, cs
->avp
->v
);
814 if (value_diff
< 0) {
815 // dest < source, do not insert it yet
817 } else if (value_diff
> 0) {
818 // dest > source, insert AVP before the current dest AVP
819 insert_avp_before_node(dst
, cd
, cs
->avp
, copy_avps
);
822 // identical AVPs, do not create a duplicate.
828 // if there are remaining source AVPs while there are no more destination
829 // AVPs (cd now represents the NULL item, after the last item), append
830 // all remaining source AVPs to the end
832 insert_avp_before_node(dst
, cd
, cs
->avp
, copy_avps
);
836 #ifdef _AVP_DEBUGGING
837 dbg_print(dbg_avpl_op
,8,dbg_fp
,"merge_avpl: done");
845 * new_avpl_from_avpl:
846 * @param name the name of the new avpl.
847 * @param avpl the avpl from which to get the avps.
848 * @param copy_avps whether avps should be copied instead of referenced.
850 * Creates a new avpl containing the same avps as the given avpl
851 * It will either reference or copie the avps.
853 * Return value: a pointer to the newly allocated string.
856 extern AVPL
* new_avpl_from_avpl(const char* name
, AVPL
* avpl
, bool copy_avps
) {
857 AVPL
* newavpl
= new_avpl(name
);
862 #ifdef _AVP_DEBUGGING
863 dbg_print(dbg_avpl_op
,3,dbg_fp
,"new_avpl_from_avpl: %p from=%p name='%s'",newavpl
,avpl
,name
);
866 while(( avp
= get_next_avp(avpl
,&cookie
) )) {
868 copy
= avp_copy(avp
);
869 if ( ! insert_avp(newavpl
,copy
) ) {
873 insert_avp(newavpl
,avp
);
877 #ifdef _AVP_DEBUGGING
878 dbg_print(dbg_avpl_op
,8,dbg_fp
,"new_avpl_from_avpl: done");
886 * @param src an src to be compared against an "op" avp
887 * @param op the "op" avp that will be matched against the src avp
889 * Checks whether or not two avp's match.
891 * Return value: a pointer to the src avp if there's a match.
894 extern AVP
* match_avp(AVP
* src
, AVP
* op
) {
904 #ifdef _AVP_DEBUGGING
905 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
);
908 if ( src
->n
!= op
->n
) {
916 return src
->v
== op
->v
? src
: NULL
;
917 case AVP_OP_NOTEQUAL
:
918 return !( src
->v
== op
->v
) ? src
: NULL
;
920 return strncmp(src
->v
,op
->v
,strlen(op
->v
)) == 0 ? src
: NULL
;
922 splited
= g_strsplit(op
->v
,"|",0);
924 for (i
=0;splited
[i
];i
++) {
925 if(g_str_equal(splited
[i
],src
->v
)) {
939 fs
= g_ascii_strtod(src
->v
, NULL
);
940 fo
= g_ascii_strtod(op
->v
, NULL
);
943 if (fs
<fo
) return src
;
946 if (fs
>fo
) return src
;
950 /* does this work? */
951 ls
= (unsigned) strlen(src
->v
);
952 lo
= (unsigned) strlen(op
->v
);
957 p
= src
->v
+ ( ls
- lo
);
958 return g_str_equal(p
,op
->v
) ? src
: NULL
;
961 /* case AVP_OP_TRANSF: */
962 /* return do_transform(src,op); */
963 case AVP_OP_CONTAINS
:
964 return g_strrstr(src
->v
, op
->v
) ? src
: NULL
;
966 /* will never get here */
973 * new_avpl_loose_match:
974 * @param name the name of the resulting avpl
975 * @param src the data AVPL to be matched against a condition AVPL
976 * @param op the conditions AVPL that will be matched against the data AVPL
977 * @param copy_avps whether the avps in the resulting avpl should be copied
979 * Creates a new AVP list containing all data AVPs that matched any of the
980 * conditions AVPs. If there are no matches, an empty list will be returned.
982 * Note: Loose will always be considered a successful match, it matches zero or
985 extern AVPL
* new_avpl_loose_match(const char* name
,
990 AVPL
* newavpl
= new_avpl(scs_subscribe(avp_strings
, name
));
994 #ifdef _AVP_DEBUGGING
995 dbg_print(dbg_avpl_op
,3,dbg_fp
,"new_avpl_loose_match: %p src=%p op=%p name='%s'",newavpl
,src
,op
,name
);
1001 while (cs
->avp
&& co
->avp
) {
1002 int name_diff
= strcmp(co
->avp
->n
, cs
->avp
->n
);
1004 if (name_diff
< 0) {
1005 // op < source, op is not matching
1007 } else if (name_diff
> 0) {
1008 // op > source, source is not matching
1011 // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
1014 if (match_avp(cs
->avp
, cond
->avp
)) {
1015 insert_avp_before_node(newavpl
, newavpl
->null
.prev
->next
, cs
->avp
, copy_avps
);
1019 } while (cond
->avp
&& cond
->avp
->n
== cs
->avp
->n
);
1024 // return matches (possible none)
1029 * new_avpl_pairs_match:
1030 * @param name the name of the resulting avpl
1031 * @param src the data AVPL to be matched against a condition AVPL
1032 * @param op the conditions AVPL that will be matched against the data AVPL
1033 * @param strict true if every condition must have a matching data AVP, false if
1034 * it is also acceptable that only one of the condition AVPs for the same
1035 * attribute is matching.
1036 * @param copy_avps whether the avps in the resulting avpl should be copied
1038 * Creates an AVP list by matching pairs of conditions and data AVPs, returning
1039 * the data AVPs. If strict is true, then each condition must be paired with a
1040 * matching data AVP. If strict is false, then some conditions are allowed to
1041 * fail when other conditions for the same attribute do have a match. Note that
1042 * if the condition AVPL is empty, the result will be a match (an empty list).
1044 * Return value: a pointer to the newly created avpl containing the
1045 * matching avps or NULL if there is no match.
1047 extern AVPL
* new_avpl_pairs_match(const char* name
, AVPL
* src
, AVPL
* op
, bool strict
, bool copy_avps
) {
1051 const char *last_match
= NULL
;
1052 bool matched
= true;
1054 newavpl
= new_avpl(scs_subscribe(avp_strings
, name
));
1056 #ifdef _AVP_DEBUGGING
1057 dbg_print(dbg_avpl_op
,3,dbg_fp
,"%s: %p src=%p op=%p name='%s'",G_STRFUNC
,newavpl
,src
,op
,name
);
1060 cs
= src
->null
.next
;
1062 while (cs
->avp
&& co
->avp
) {
1063 int name_diff
= g_strcmp0(co
->avp
->n
, cs
->avp
->n
);
1064 const char *failed_match
= NULL
;
1066 if (name_diff
< 0) {
1067 // op < source, op has no data avp with same attribute.
1068 failed_match
= co
->avp
->n
;
1070 } else if (name_diff
> 0) {
1071 // op > source, the source avp is not matched by any condition
1074 // Matching attributes found, now try to find a matching data AVP for the condition.
1075 if (match_avp(cs
->avp
, co
->avp
)) {
1076 insert_avp_before_node(newavpl
, newavpl
->null
.prev
->next
, cs
->avp
, copy_avps
);
1077 last_match
= co
->avp
->n
;
1080 failed_match
= co
->avp
->n
;
1085 // condition did not match, check if we can continue matching.
1090 } else if (last_match
!= failed_match
) {
1091 // None of the conditions so far matched the attribute, check for other candidates
1092 if (!co
->avp
|| co
->avp
->n
!= last_match
) {
1100 // if there are any conditions remaining, then those could not be matched
1101 if (matched
&& strict
&& co
->avp
) {
1106 // there was a match, accept it
1109 // no match, only delete AVPs too if they were copied
1110 delete_avpl(newavpl
, copy_avps
);
1117 * new_avpl_from_match:
1118 * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
1119 * @param name the name of the resulting avpl
1120 * @param src the data AVPL to be matched against a condition AVPL
1121 * @param op the conditions AVPL that will be matched against the data AVPL
1123 * Matches the conditions AVPL against the original AVPL according to the mode.
1124 * If there is no match, NULL is returned. If there is actually a match, then
1125 * the matching AVPs (a subset of the data) are returned.
1127 extern AVPL
* new_avpl_from_match(avpl_match_mode mode
, const char* name
,AVPL
* src
, AVPL
* op
, bool copy_avps
) {
1132 avpl
= new_avpl_pairs_match(name
, src
, op
, true, copy_avps
);
1135 avpl
= new_avpl_loose_match(name
,src
,op
,copy_avps
);
1138 avpl
= new_avpl_pairs_match(name
, src
, op
, false, copy_avps
);
1141 // XXX this seems unused
1142 avpl
= new_avpl_from_avpl(name
,src
,copy_avps
);
1143 merge_avpl(avpl
, op
, copy_avps
);
1151 * delete_avpl_transform:
1152 * @param op a pointer to the avpl transformation object
1154 * Destroys an avpl transformation object and releases all the resources it
1158 extern void delete_avpl_transform(AVPL_Transf
* op
) {
1161 for (; op
; op
= next
) {
1167 delete_avpl(op
->match
,true);
1171 delete_avpl(op
->replace
,true);
1182 * @param src the source avpl for the transform operation.
1183 * @param op a pointer to the avpl transformation object to apply.
1185 * Applies the "op" transformation to an avpl, matches it and eventually
1186 * replaces or inserts the transformed avps.
1188 * Return value: whether the transformation was performed or not.
1190 extern void avpl_transform(AVPL
* src
, AVPL_Transf
* op
) {
1196 #ifdef _AVP_DEBUGGING
1197 dbg_print(dbg_avpl_op
,3,dbg_fp
,"avpl_transform: src=%p op=%p",src
,op
);
1200 for ( ; op
; op
= op
->next
) {
1202 avpl
= new_avpl_from_match(op
->match_mode
, src
->name
,src
, op
->match
, true);
1205 switch (op
->replace_mode
) {
1206 case AVPL_NO_REPLACE
:
1207 delete_avpl(avpl
,true);
1210 merge_avpl(src
,op
->replace
,true);
1211 delete_avpl(avpl
,true);
1214 cs
= src
->null
.next
;
1215 cm
= avpl
->null
.next
;
1216 // Removes AVPs from the source which are in the matched data.
1217 // Assume that the matched set is a subset of the source.
1218 while (cs
->avp
&& cm
->avp
) {
1219 if (cs
->avp
->n
== cm
->avp
->n
&& cs
->avp
->v
== cm
->avp
->v
) {
1222 cs
->prev
->next
= cs
->next
;
1223 cs
->next
->prev
= cs
->prev
;
1224 g_slice_free(any_avp_type
,(any_avp_type
*)cs
);
1229 // Current matched AVP is not equal to the current
1230 // source AVP. Since there must be a source AVP for
1231 // each matched AVP, advance current source and not
1237 merge_avpl(src
,op
->replace
,true);
1238 delete_avpl(avpl
,true);
1248 * @param name the name the loal will take.
1250 * Creates an empty list of avp lists.
1252 * Return value: a pointer to the newly created loal.
1254 extern LoAL
* new_loal(const char* name
) {
1255 LoAL
* new_loal_p
= (LoAL
*)g_slice_new(any_avp_type
);
1261 #ifdef _AVP_DEBUGGING
1262 dbg_print(dbg_avpl_op
,3,dbg_fp
,"new_loal_p: %p name=%s",new_loal_p
,name
);
1265 new_loal_p
->name
= scs_subscribe(avp_strings
,name
);
1266 new_loal_p
->null
.avpl
= NULL
;
1267 new_loal_p
->null
.next
= &new_loal_p
->null
;
1268 new_loal_p
->null
.prev
= &new_loal_p
->null
;
1269 new_loal_p
->len
= 0;
1275 * @param loal the loal on which to operate.
1276 * @param avpl the avpl to append.
1278 * Appends an avpl to a loal.
1281 extern void loal_append(LoAL
* loal
, AVPL
* avpl
) {
1282 LoALnode
* node
= (LoALnode
*)g_slice_new(any_avp_type
);
1284 #ifdef _AVP_DEBUGGING
1285 dbg_print(dbg_avpl_op
,3,dbg_fp
,"new_loal_node: %p",node
);
1289 node
->next
= &loal
->null
;
1290 node
->prev
= loal
->null
.prev
;
1292 loal
->null
.prev
->next
= node
;
1293 loal
->null
.prev
= node
;
1299 * extract_first_avpl:
1300 * @param loal the loal on which to operate.
1302 * Extracts the first avpl contained in a loal.
1304 * Return value: a pointer to the extracted avpl.
1307 extern AVPL
* extract_first_avpl(LoAL
* loal
) {
1311 #ifdef _AVP_DEBUGGING
1312 dbg_print(dbg_avpl_op
,3,dbg_fp
,"extract_first_avpl: from: %s",loal
->name
);
1315 node
= loal
->null
.next
;
1317 loal
->null
.next
->next
->prev
= &loal
->null
;
1318 loal
->null
.next
= node
->next
;
1325 g_slice_free(any_avp_type
,(any_avp_type
*)node
);
1327 #ifdef _AVP_DEBUGGING
1328 dbg_print(dbg_avpl_op
,3,dbg_fp
,"extract_first_avpl: got %s",avpl
->name
);
1329 dbg_print(dbg_avpl_op
,3,dbg_fp
,"delete_loal_node: %p",node
);
1337 * extract_first_avpl:
1338 * @param loal the loal on which to operate.
1340 * Extracts the last avpl contained in a loal.
1342 * Return value: a pointer to the extracted avpl.
1345 extern AVPL
* extract_last_avpl(LoAL
* loal
){
1349 node
= loal
->null
.prev
;
1351 loal
->null
.prev
->prev
->next
= &loal
->null
;
1352 loal
->null
.prev
= node
->prev
;
1359 g_slice_free(any_avp_type
,(any_avp_type
*)node
);
1360 #ifdef _AVP_DEBUGGING
1361 dbg_print(dbg_avpl_op
,3,dbg_fp
,"delete_loal_node: %p",node
);
1369 * extract_first_avpl:
1370 * @param loal the loal on which to operate.
1371 * @param cookie pointer to the pointer variable to contain the state between calls
1373 * At each call will return the following avpl from a loal. The given cookie
1374 * will be used to manatain the state between calls.
1376 * Return value: a pointer to the next avpl.
1379 extern AVPL
* get_next_avpl(LoAL
* loal
,void** cookie
) {
1382 #ifdef _AVP_DEBUGGING
1383 dbg_print(dbg_avpl_op
,3,dbg_fp
,"get_next_avpl: loal=%p node=%p",loal
,*cookie
);
1387 node
= (LoALnode
*) *cookie
;
1389 node
= loal
->null
.next
;
1392 *cookie
= node
->next
;
1399 * @param loal the loal to be deleted.
1400 * @param avpls_too whether avpls contained by the loal should be deleted as well
1401 * @param avps_too whether avps contained by the avpls should be also deleted
1403 * Destroys a loal and eventually desstroys avpls and avps.
1406 extern void delete_loal(LoAL
* loal
, bool avpls_too
, bool avps_too
) {
1409 #ifdef _AVP_DEBUGGING
1410 dbg_print(dbg_avpl_op
,3,dbg_fp
,"delete_loal: %p",loal
);
1413 while(( avpl
= extract_last_avpl(loal
) )) {
1415 delete_avpl(avpl
,avps_too
);
1419 scs_unsubscribe(avp_strings
,loal
->name
);
1420 g_slice_free(any_avp_type
,(any_avp_type
*)loal
);
1425 /****************************************************************************
1426 ******************* the following are used in load_loal_from_file
1427 ****************************************************************************/
1431 * Used by loal_from_file to handle errors while loading.
1433 static LoAL
* load_loal_error(FILE* fp
, LoAL
* loal
, AVPL
* curr
, int linenum
, const char* fmt
, ...) {
1439 va_start( list
, fmt
);
1440 desc
= ws_strdup_vprintf(fmt
, list
);
1444 err
= ws_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal
->name
,linenum
,desc
);
1446 err
= ws_strdup_printf("Error Loading LoAL at line: %i, %s",linenum
,desc
);
1448 ret
= new_loal(err
);
1454 if (loal
) delete_loal(loal
,true,true);
1455 if (curr
) delete_avpl(curr
,true);
1461 /* the maximum length allowed for a line */
1462 #define MAX_ITEM_LEN 8192
1464 /* this two ugly things are used for tokenizing */
1465 #define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1467 #define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1468 case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1469 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1470 case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1471 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1472 case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1473 case '7': case '8': case '9': case '.'
1478 * @param filename the file containing a loals text representation.
1480 * Given a filename it will attempt to load a loal containing a copy of
1481 * the avpls represented in the file.
1483 * Return value: if successful a pointer to the new populated loal, else NULL.
1486 extern LoAL
* loal_from_file(char* filename
) {
1490 uint32_t linenum
= 1;
1495 LoAL
*loal_error
, *loal
= new_loal(filename
);
1499 enum _load_loal_states
{
1507 linenum_buf
= (char*)g_malloc(MAX_ITEM_LEN
);
1508 name
= (char*)g_malloc(MAX_ITEM_LEN
);
1509 value
= (char*)g_malloc(MAX_ITEM_LEN
);
1512 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"MATE Will not run as root");
1519 if (( fp
= ws_fopen(filename
,"r") )) {
1520 while(( c
= (char) fgetc(fp
) )){
1524 report_read_failure(filename
,errno
);
1525 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"Error while reading '%f'",filename
);
1535 if ( i
>= MAX_ITEM_LEN
- 1 ) {
1536 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"Maximum item length exceeded");
1552 case ' ': case '\t':
1553 /* ignore whitespace at line start */
1556 /* ignore empty lines */
1564 snprintf(linenum_buf
,MAX_ITEM_LEN
,"%s:%u",filename
,linenum
);
1565 curr
= new_avpl(linenum_buf
);
1571 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"expecting name got: '%c'",c
);
1579 c
= (char) fgetc(fp
);
1580 if (c
!= '\n') ungetc(c
,fp
);
1592 loal_append(loal
,curr
);
1596 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"expecting name got: '%c'",c
);
1602 state
= BEFORE_NAME
;
1609 avp
= new_avp(name
,value
,op
);
1611 if (! insert_avp(curr
,avp
) ) {
1626 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"operator expected found new line");
1629 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"name or match operator expected found '%c'",c
);
1635 value
[i
++] = (char) fgetc(fp
);
1638 state
= BEFORE_NAME
;
1643 avp
= new_avp(name
,value
,op
);
1645 if (! insert_avp(curr
,avp
) ) {
1650 loal_error
= load_loal_error(fp
,loal
,curr
,linenum
,"';' expected found new line");
1660 g_free(linenum_buf
);
1667 report_open_failure(filename
,errno
,false);
1668 loal_error
= load_loal_error(NULL
,loal
,NULL
,0,"Cannot Open file '%s'",filename
);
1672 g_free(linenum_buf
);
1680 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1685 * indent-tabs-mode: t
1688 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1689 * :indentSize=8:tabSize=8:noTabs=false: