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
15 #include "mate_util.h"
16 #include <wsutil/file_util.h>
19 /***************************************************************************
21 ***************************************************************************
22 * This is the debug facility of the thing.
23 ***************************************************************************/
26 * which: a pointer to the current level of debugging for a feature
27 * how: the level over which this message should be printed out
28 * where: the file on which to print (g_message if null)
29 * fmt, ...: what to print
32 void dbg_print(const gint* which, gint how, FILE* where, const gchar* fmt, ... ) {
33 static gchar debug_buffer[DEBUG_BUFFER_SIZE];
36 if ( ! which || *which < how ) return;
38 va_start( list, fmt );
39 g_vsnprintf(debug_buffer,DEBUG_BUFFER_SIZE,fmt,list);
43 g_message("%s", debug_buffer);
45 fputs(debug_buffer,where);
52 /***************************************************************************
54 ***************************************************************************
55 * Strings repeat more often than don't. In order to save memory
56 * we'll keep only one copy of each as key to a hash with a count of
57 * subscribers as value.
58 ***************************************************************************/
63 * Initializes the scs hash.
66 struct _scs_collection {
67 GHashTable* hash; /* key: a string value: guint number of subscribers */
70 /* ToDo? free any string,ctr entries pointed to by the hash table ??
71 * XXX: AFAIKT destroy_scs_collection() might be called only when reading a
72 * mate config file. Since reading a new config file can apparently currently
73 * only be done once after starting Wireshark, in theory this fcn
74 * currently should never be called since there will never be an existing
75 * scs_collection to be destroyed.
77 static void destroy_scs_collection(SCS_collection* c) {
78 if (c->hash) g_hash_table_destroy(c->hash);
81 static SCS_collection* scs_init(void) {
82 SCS_collection* c = (SCS_collection *)g_malloc(sizeof(SCS_collection));
84 c->hash = g_hash_table_new(g_str_hash,g_str_equal);
92 * @param c the scs hash
95 * Checks if the given string exists already and if so it increases the count of
96 * subsscribers and returns a pointer to the stored string. If not It will copy
97 * the given string store it in the hash and return the pointer to the copy.
98 * Remember, containment is handled internally, take care of your own strings.
100 * Return value: a pointer to the subscribed string.
102 gchar* scs_subscribe(SCS_collection* c, const gchar* s) {
107 g_hash_table_lookup_extended(c->hash,(gconstpointer)s,(gpointer *)&orig,(gpointer *)&ip);
112 ip = g_slice_new(guint);
117 if (len <= SCS_SMALL_SIZE) {
118 len = SCS_SMALL_SIZE;
119 } else if (len <= SCS_MEDIUM_SIZE) {
120 len = SCS_MEDIUM_SIZE;
121 } else if (len <= SCS_LARGE_SIZE) {
122 len = SCS_LARGE_SIZE;
123 } else if (len < SCS_HUGE_SIZE) {
127 g_warning("mate SCS: string truncated due to huge size");
130 orig = (gchar *)g_slice_alloc(len);
131 g_strlcpy(orig,s,len);
133 g_hash_table_insert(c->hash,orig,ip);
141 * @param c the scs hash
144 * decreases the count of subscribers, if zero frees the internal copy of
147 void scs_unsubscribe(SCS_collection* c, gchar* s) {
152 g_hash_table_lookup_extended(c->hash,(gconstpointer)s,(gpointer *)&orig,(gpointer *)&ip);
156 g_hash_table_remove(c->hash,orig);
160 if (len < SCS_SMALL_SIZE) {
161 len = SCS_SMALL_SIZE;
162 } else if (len < SCS_MEDIUM_SIZE) {
163 len = SCS_MEDIUM_SIZE;
164 } else if (len < SCS_LARGE_SIZE) {
165 len = SCS_LARGE_SIZE;
170 g_slice_free1(len, orig);
171 g_slice_free(guint,ip);
177 g_warning("unsubscribe: not subscribed");
182 * scs_subscribe_printf:
183 * @param fmt a format string ...
185 * Formats the input and subscribes it.
187 * Return value: the stored copy of the formated string.
190 gchar* scs_subscribe_printf(SCS_collection* c, gchar* fmt, ...) {
192 static gchar buf[SCS_HUGE_SIZE];
194 va_start( list, fmt );
195 g_vsnprintf(buf, SCS_HUGE_SIZE, fmt, list);
198 return scs_subscribe(c,buf);
201 /***************************************************************************
203 ***************************************************************************
204 * The Thing operates mainly on avps, avpls and loals
205 * - attribute value pairs (two strings: the name and the value and an operator)
206 * - avp lists a somehow sorted list of avps
207 * - loal (list of avp lists) an arbitrarily sorted list of avpls
210 ***************************************************************************/
213 typedef union _any_avp_type {
222 static SCS_collection* avp_strings = NULL;
224 #ifdef _AVP_DEBUGGING
225 static FILE* dbg_fp = NULL;
227 static int dbg_level = 0;
228 static int* dbg = &dbg_level;
230 static int dbg_avp_level = 0;
231 static int* dbg_avp = &dbg_avp_level;
233 static int dbg_avp_op_level = 0;
234 static int* dbg_avp_op = &dbg_avp_op_level;
236 static int dbg_avpl_level = 0;
237 static int* dbg_avpl = &dbg_avpl_level;
239 static int dbg_avpl_op_level = 0;
240 static int* dbg_avpl_op = &dbg_avpl_op_level;
244 * @param fp the file in which to send debugging output.
245 * @param general a pointer to the level of debugging of facility "general"
246 * @param avp a pointer to the level of debugging of facility "avp"
247 * @param avp_op a pointer to the level of debugging of facility "avp_op"
248 * @param avpl a pointer to the level of debugging of facility "avpl"
249 * @param avpl_op a pointer to the level of debugging of facility "avpl_op"
251 * If enabled sets up the debug facilities for the avp library.
254 extern void setup_avp_debug(FILE* fp, int* general, int* avp, int* avp_op, int* avpl, int* avpl_op) {
260 dbg_avpl_op = avpl_op;
263 #endif /* _AVP_DEBUGGING */
268 * (Re)Initializes the avp library.
271 extern void avp_init(void) {
273 if (avp_strings) destroy_scs_collection(avp_strings);
274 avp_strings = scs_init();
279 * new_avp_from_finfo:
280 * @param name the name the avp will have.
281 * @param finfo the field_info from which to fetch the data.
283 * Creates an avp from a field_info record.
285 * Return value: a pointer to the newly created avp.
288 extern AVP* new_avp_from_finfo(const gchar* name, field_info* finfo) {
289 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
293 new_avp_val->n = scs_subscribe(avp_strings, name);
295 repr = fvalue_to_string_repr(NULL, &finfo->value,FTREPR_DISPLAY,finfo->hfinfo->display);
298 value = scs_subscribe(avp_strings, repr);
299 wmem_free(NULL, repr);
300 #ifdef _AVP_DEBUGGING
301 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: from string: %s",value);
304 #ifdef _AVP_DEBUGGING
305 dbg_print (dbg_avp,2,dbg_fp,"new_avp_from_finfo: a proto: %s",finfo->hfinfo->abbrev);
307 value = scs_subscribe(avp_strings, "");
310 new_avp_val->v = value;
312 new_avp_val->o = '=';
314 #ifdef _AVP_DEBUGGING
315 dbg_print (dbg_avp,1,dbg_fp,"new_avp_from_finfo: %X %s%c%s;",(guint32) new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
324 * @param name the name the avp will have.
325 * @param value the value the avp will have.
326 * @param o the operator of this avp.
328 * Creates an avp given every parameter.
330 * Return value: a pointer to the newly created avp.
333 extern AVP* new_avp(const gchar* name, const gchar* value, gchar o) {
334 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
336 new_avp_val->n = scs_subscribe(avp_strings, name);
337 new_avp_val->v = scs_subscribe(avp_strings, value);
340 #ifdef _AVP_DEBUGGING
341 dbg_print(dbg_avp,1,dbg_fp,"new_avp_val: %X %s%c%s;",(guint32) new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
349 * @param avp the avp to delete.
351 * Destroys an avp and releases the resources it uses.
354 extern void delete_avp(AVP* avp) {
355 #ifdef _AVP_DEBUGGING
356 dbg_print(dbg_avp,1,dbg_fp,"delete_avp: %X %s%c%s;",(guint32) avp,avp->n,avp->o,avp->v);
359 scs_unsubscribe(avp_strings, avp->n);
360 scs_unsubscribe(avp_strings, avp->v);
361 g_slice_free(any_avp_type,(any_avp_type*)avp);
367 * @param from the avp to be copied.
369 * Creates an avp whose name op and value are copies of the given one.
371 * Return value: a pointer to the newly created avp.
374 extern AVP* avp_copy(AVP* from) {
375 AVP* new_avp_val = (AVP*)g_slice_new(any_avp_type);
377 new_avp_val->n = scs_subscribe(avp_strings, from->n);
378 new_avp_val->v = scs_subscribe(avp_strings, from->v);
379 new_avp_val->o = from->o;
381 #ifdef _AVP_DEBUGGING
382 dbg_print(dbg_avp,1,dbg_fp,"copy_avp: %X %s%c%s;",(guint32) new_avp_val,new_avp_val->n,new_avp_val->o,new_avp_val->v);
390 * @param name the name the avpl will have.
392 * Creates an empty avpl.
394 * Return value: a pointer to the newly created avpl.
397 extern AVPL* new_avpl(const gchar* name) {
398 AVPL* new_avpl_p = (AVPL*)g_slice_new(any_avp_type);
400 #ifdef _AVP_DEBUGGING
401 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpl_p: %X name=%s",new_avpl_p,name);
404 new_avpl_p->name = name ? scs_subscribe(avp_strings, name) : scs_subscribe(avp_strings, "");
406 new_avpl_p->null.avp = NULL;
407 new_avpl_p->null.next = &new_avpl_p->null;
408 new_avpl_p->null.prev = &new_avpl_p->null;
414 extern void rename_avpl(AVPL* avpl, gchar* name) {
415 scs_unsubscribe(avp_strings,avpl->name);
416 avpl->name = scs_subscribe(avp_strings,name);
420 * insert_avp_before_node:
421 * @param avpl the avpl in which to insert.
422 * @param next_node the next node before which the new avpn has to be inserted.
423 * @param avp the avp to be inserted.
424 * @param copy_avp whether the original AVP or a copy thereof must be inserted.
426 * Pre-condition: the avp is sorted before before_avp and does not already exist
429 static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, gboolean copy_avp) {
430 AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type);
432 new_avp_val->avp = copy_avp ? avp_copy(avp) : avp;
434 #ifdef _AVP_DEBUGGING
435 dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %p",new_avp_val);
436 dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %p in %p before %p;",avp,avpl,next_node);
439 new_avp_val->next = next_node;
440 new_avp_val->prev = next_node->prev;
441 next_node->prev->next = new_avp_val;
442 next_node->prev = new_avp_val;
446 #ifdef _AVP_DEBUGGING
447 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
453 * @param avpl the avpl in which to insert.
454 * @param avp the avp to be inserted.
456 * Inserts the given AVP into the given AVPL if an identical one isn't yet there.
458 * Return value: whether it was inserted or not.
460 * BEWARE: Check the return value, you might need to delete the avp if
461 * it is not inserted.
463 extern gboolean insert_avp(AVPL* avpl, AVP* avp) {
466 #ifdef _AVP_DEBUGGING
467 dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %X %X %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
470 /* get to the insertion point */
471 for (c=avpl->null.next; c->avp; c = c->next) {
472 int name_diff = strcmp(avp->n, c->avp->n);
474 if (name_diff == 0) {
475 int value_diff = strcmp(avp->v, c->avp->v);
477 if (value_diff < 0) {
481 if (value_diff == 0) {
482 // ignore duplicate values, prevents (a=1, a=1)
483 // note that this is also used to insert
484 // conditions AVPs, so really check if the name,
485 // value and operator are all equal.
486 if (c->avp->o == avp->o && avp->o == AVP_OP_EQUAL) {
497 insert_avp_before_node(avpl, c, avp, FALSE);
504 * @param avpl the avpl from which to try to get the avp.
505 * @param name the name of the avp we are looking for.
506 * @param cookie variable in which to store the state between calls.
508 * Gets pointer to the next avp whose name is given; uses cookie to store its
509 * state between calls.
511 * Return value: a pointer to the next matching avp if there's one, else NULL.
514 extern AVP* get_avp_by_name(AVPL* avpl, gchar* name, void** cookie) {
516 AVPN* start = (AVPN*) *cookie;
518 #ifdef _AVP_DEBUGGING
519 dbg_print(dbg_avpl_op,7,dbg_fp,"get_avp_by_name: entering: %X %s %X",avpl,name,*cookie);
522 name = scs_subscribe(avp_strings, name);
524 if (!start) start = avpl->null.next;
526 for ( curr = start; curr->avp; curr = curr->next ) {
527 if ( curr->avp->n == name ) {
534 #ifdef _AVP_DEBUGGING
535 dbg_print(dbg_avpl_op,5,dbg_fp,"get_avp_by_name: got avp: %X",curr);
538 scs_unsubscribe(avp_strings, name);
544 * extract_avp_by_name:
545 * @param avpl the avpl from which to try to extract the avp.
546 * @param name the name of the avp we are looking for.
548 * Extracts from the avpl the next avp whose name is given;
550 * Return value: a pointer to extracted avp if there's one, else NULL.
553 extern AVP* extract_avp_by_name(AVPL* avpl, gchar* name) {
557 #ifdef _AVP_DEBUGGING
558 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_avp_by_name: entering: %X %s",avpl,name);
561 name = scs_subscribe(avp_strings, name);
563 for ( curr = avpl->null.next; curr->avp; curr = curr->next ) {
564 if ( curr->avp->n == name ) {
569 scs_unsubscribe(avp_strings, name);
571 if( ! curr->avp ) return NULL;
573 curr->next->prev = curr->prev;
574 curr->prev->next = curr->next;
578 g_slice_free(any_avp_type,(any_avp_type*)curr);
582 #ifdef _AVP_DEBUGGING
583 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
586 #ifdef _AVP_DEBUGGING
587 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_avp_by_name: got avp: %X",avp);
596 * @param avpl the avpl from which to try to extract the avp.
598 * Extracts the fisrt avp from the avpl.
600 * Return value: a pointer to extracted avp if there's one, else NULL.
603 extern AVP* extract_first_avp(AVPL* avpl) {
607 #ifdef _AVP_DEBUGGING
608 dbg_print(dbg_avpl_op,7,dbg_fp,"extract_first_avp: %X",avpl);
611 node = avpl->null.next;
613 avpl->null.next->prev = &avpl->null;
614 avpl->null.next = node->next;
619 g_slice_free(any_avp_type,(any_avp_type*)node);
621 #ifdef _AVP_DEBUGGING
622 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
626 #ifdef _AVP_DEBUGGING
627 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_first_avp: got avp: %X",avp);
637 * @param avpl the avpl from which to try to extract the avp.
639 * Extracts the last avp from the avpl.
641 * Return value: a pointer to extracted avp if there's one, else NULL.
644 extern AVP* extract_last_avp(AVPL* avpl) {
648 node = avpl->null.prev;
650 avpl->null.prev->next = &avpl->null;
651 avpl->null.prev = node->prev;
656 g_slice_free(any_avp_type,(any_avp_type*)node);
658 #ifdef _AVP_DEBUGGING
659 dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
663 #ifdef _AVP_DEBUGGING
664 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",avp);
674 * @param avpl the avpl from which to try to extract the avp.
675 * @param avps_too whether or not it should delete the avps as well.
677 * Destroys an avpl and releases the resources it uses. If told to do
678 * so releases the avps as well.
681 extern void delete_avpl(AVPL* avpl, gboolean avps_too) {
683 #ifdef _AVP_DEBUGGING
684 dbg_print(dbg_avpl,3,dbg_fp,"delete_avpl: %X",avpl);
687 while(( avp = extract_last_avp(avpl))) {
693 scs_unsubscribe(avp_strings,avpl->name);
694 g_slice_free(any_avp_type,(any_avp_type*)avpl);
701 * @param avpl the avpl from which to try to get the avps.
702 * @param cookie variable in which to store the state between calls.
704 * Iterates on an avpl to get its avps.
706 * Return value: a pointer to the next avp if there's one, else NULL.
709 extern AVP* get_next_avp(AVPL* avpl, void** cookie) {
712 #ifdef _AVP_DEBUGGING
713 dbg_print(dbg_avpl_op,5,dbg_fp,"get_next_avp: avpl: %X avpn: %X",avpl,*cookie);
717 node = (AVPN*) *cookie;
719 node = avpl->null.next;
722 *cookie = node->next;
724 #ifdef _AVP_DEBUGGING
725 dbg_print(dbg_avpl_op,5,dbg_fp,"extract_last_avp: got avp: %X",node->avp);
733 * @param avpl the avpl to represent.
735 * Creates a newly allocated string containing a representation of an avpl.
737 * Return value: a pointer to the newly allocated string.
740 gchar* avpl_to_str(AVPL* avpl) {
742 GString* s = g_string_new("");
746 for(c=avpl->null.next; c->avp; c = c->next) {
747 avp_s = avp_to_str(c->avp);
748 g_string_append_printf(s," %s;",avp_s);
752 r = g_string_free(s,FALSE);
754 /* g_strchug(r); ? */
758 extern gchar* avpl_to_dotstr(AVPL* avpl) {
760 GString* s = g_string_new("");
764 for(c=avpl->null.next; c->avp; c = c->next) {
765 avp_s = avp_to_str(c->avp);
766 g_string_append_printf(s," .%s;",avp_s);
770 r = g_string_free(s,FALSE);
772 /* g_strchug(r); ? */
778 * @param dst the avpl in which to merge the avps.
779 * @param src the avpl from which to get the avps.
780 * @param copy_avps whether avps should be copied instead of referenced.
782 * Adds the avps of src that are not existent in dst into dst.
785 extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
789 #ifdef _AVP_DEBUGGING
790 dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %X %X",dst,src);
796 while (cs->avp && cd->avp) {
798 int name_diff = strcmp(cd->avp->n, cs->avp->n);
801 // dest < source, advance dest to find a better place to insert
803 } else if (name_diff > 0) {
804 // dest > source, so it can be definitely inserted here.
805 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
808 // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
809 int value_diff = strcmp(cd->avp->v, cs->avp->v);
811 if (value_diff < 0) {
812 // dest < source, do not insert it yet
814 } else if (value_diff > 0) {
815 // dest > source, insert AVP before the current dest AVP
816 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
819 // identical AVPs, do not create a duplicate.
825 // if there are remaing source AVPs while there are no more destination
826 // AVPs (cd now represents the NULL item, after the last item), append
827 // all remaining source AVPs to the end
829 insert_avp_before_node(dst, cd, cs->avp, copy_avps);
833 #ifdef _AVP_DEBUGGING
834 dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
842 * new_avpl_from_avpl:
843 * @param name the name of the new avpl.
844 * @param avpl the avpl from which to get the avps.
845 * @param copy_avps whether avps should be copied instead of referenced.
847 * Creates a new avpl containing the same avps as the given avpl
848 * It will either reference or copie the avps.
850 * Return value: a pointer to the newly allocated string.
853 extern AVPL* new_avpl_from_avpl(const gchar* name, AVPL* avpl, gboolean copy_avps) {
854 AVPL* newavpl = new_avpl(name);
859 #ifdef _AVP_DEBUGGING
860 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_from_avpl: %X from=%X name='%s'",newavpl,avpl,name);
863 while(( avp = get_next_avp(avpl,&cookie) )) {
865 copy = avp_copy(avp);
866 if ( ! insert_avp(newavpl,copy) ) {
870 insert_avp(newavpl,avp);
874 #ifdef _AVP_DEBUGGING
875 dbg_print(dbg_avpl_op,8,dbg_fp,"new_avpl_from_avpl: done");
883 * @param src an src to be compared agains an "op" avp
884 * @param op the "op" avp that will be matched against the src avp
886 * Checks whether or not two avp's match.
888 * Return value: a pointer to the src avp if there's a match.
891 extern AVP* match_avp(AVP* src, AVP* op) {
899 gboolean lower = FALSE;
901 #ifdef _AVP_DEBUGGING
902 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);
905 if ( src->n != op->n ) {
913 return src->v == op->v ? src : NULL;
914 case AVP_OP_NOTEQUAL:
915 return !( src->v == op->v) ? src : NULL;
917 return strncmp(src->v,op->v,strlen(op->v)) == 0 ? src : NULL;
919 splited = g_strsplit(op->v,"|",0);
921 for (i=0;splited[i];i++) {
922 if(g_str_equal(splited[i],src->v)) {
936 fs = (float) g_ascii_strtod(src->v, NULL);
937 fo = (float) g_ascii_strtod(op->v, NULL);
940 if (fs<fo) return src;
943 if (fs>fo) return src;
947 /* does this work? */
948 ls = (guint) strlen(src->v);
949 lo = (guint) strlen(op->v);
954 p = src->v + ( ls - lo );
955 return g_str_equal(p,op->v) ? src : NULL;
958 /* case AVP_OP_TRANSF: */
959 /* return do_transform(src,op); */
960 case AVP_OP_CONTAINS:
961 return g_strrstr(src->v, op->v) ? src : NULL;;
963 /* will never get here */
970 * new_avpl_loose_match:
971 * @param name the name of the resulting avpl
972 * @param src the data AVPL to be matched against a condition AVPL
973 * @param op the conditions AVPL that will be matched against the data AVPL
974 * @param copy_avps whether the avps in the resulting avpl should be copied
976 * Creates a new AVP list containing all data AVPs that matched any of the
977 * conditions AVPs. If there are no matches, an empty list will be returned.
979 * Note: Loose will always be considered a successful match, it matches zero or
982 extern AVPL* new_avpl_loose_match(const gchar* name,
985 gboolean copy_avps) {
987 AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
991 #ifdef _AVP_DEBUGGING
992 dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
998 while (cs->avp && co->avp) {
999 int name_diff = strcmp(co->avp->n, cs->avp->n);
1001 if (name_diff < 0) {
1002 // op < source, op is not matching
1004 } else if (name_diff > 0) {
1005 // op > source, source is not matching
1008 // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
1011 if (match_avp(cs->avp, cond->avp)) {
1012 insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
1016 } while (cond->avp && cond->avp->n == cs->avp->n);
1021 // return matches (possible none)
1026 * new_avpl_pairs_match:
1027 * @param name the name of the resulting avpl
1028 * @param src the data AVPL to be matched against a condition AVPL
1029 * @param op the conditions AVPL that will be matched against the data AVPL
1030 * @param strict TRUE if every condition must have a matching data AVP, FALSE if
1031 * it is also acceptable that only one of the condition AVPs for the same
1032 * attribute is matching.
1033 * @param copy_avps whether the avps in the resulting avpl should be copied
1035 * Creates an AVP list by matching pairs of conditions and data AVPs, returning
1036 * the data AVPs. If strict is TRUE, then each condition must be paired with a
1037 * matching data AVP. If strict is FALSE, then some conditions are allowed to
1038 * fail when other conditions for the same attribute do have a match. Note that
1039 * if the condition AVPL is empty, the result will be a match (an empty list).
1041 * Return value: a pointer to the newly created avpl containing the
1042 * matching avps or NULL if there is no match.
1044 extern AVPL* new_avpl_pairs_match(const gchar* name, AVPL* src, AVPL* op, gboolean strict, gboolean copy_avps) {
1048 const gchar *last_match = NULL;
1049 gboolean matched = TRUE;
1051 #ifdef _AVP_DEBUGGING
1052 dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC,newavpl,src,op,name);
1055 newavpl = new_avpl(scs_subscribe(avp_strings, name));
1057 cs = src->null.next;
1059 while (cs->avp && co->avp) {
1060 int name_diff = g_strcmp0(co->avp->n, cs->avp->n);
1061 const gchar *failed_match = NULL;
1063 if (name_diff < 0) {
1064 // op < source, op has no data avp with same attribute.
1065 failed_match = co->avp->n;
1067 } else if (name_diff > 0) {
1068 // op > source, the source avp is not matched by any condition
1071 // Matching attributes found, now try to find a matching data AVP for the condition.
1072 if (match_avp(cs->avp, co->avp)) {
1073 insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
1074 last_match = co->avp->n;
1077 failed_match = co->avp->n;
1082 // condition did not match, check if we can continue matching.
1087 } else if (last_match != failed_match) {
1088 // None of the conditions so far matched the attribute, check for other candidates
1089 if (!co->avp || co->avp->n != last_match) {
1097 // if there are any conditions remaining, then those could not be matched
1098 if (matched && strict && co->avp) {
1103 // there was a match, accept it
1106 // no match, only delete AVPs too if they were copied
1107 delete_avpl(newavpl, copy_avps);
1114 * new_avpl_from_match:
1115 * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
1116 * @param name the name of the resulting avpl
1117 * @param src the data AVPL to be matched agains a condition AVPL
1118 * @param op the conditions AVPL that will be matched against the data AVPL
1120 * Matches the conditions AVPL against the original AVPL according to the mode.
1121 * If there is no match, NULL is returned. If there is actually a match, then
1122 * the matching AVPs (a subset of the data) are returned.
1124 extern AVPL* new_avpl_from_match(avpl_match_mode mode, const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps) {
1129 avpl = new_avpl_pairs_match(name, src, op, TRUE, copy_avps);
1132 avpl = new_avpl_loose_match(name,src,op,copy_avps);
1135 avpl = new_avpl_pairs_match(name, src, op, FALSE, copy_avps);
1138 // XXX this seems unused
1139 avpl = new_avpl_from_avpl(name,src,copy_avps);
1140 merge_avpl(avpl, op, copy_avps);
1148 * delete_avpl_transform:
1149 * @param op a pointer to the avpl transformation object
1151 * Destroys an avpl transformation object and releases all the resources it
1155 extern void delete_avpl_transform(AVPL_Transf* op) {
1158 for (; op ; op = next) {
1164 delete_avpl(op->match,TRUE);
1168 delete_avpl(op->replace,TRUE);
1179 * @param src the source avpl for the transform operation.
1180 * @param op a pointer to the avpl transformation object to apply.
1182 * Applies the "op" transformation to an avpl, matches it and eventually
1183 * replaces or inserts the transformed avps.
1185 * Return value: whether the transformation was performed or not.
1187 extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
1193 #ifdef _AVP_DEBUGGING
1194 dbg_print(dbg_avpl_op,3,dbg_fp,"avpl_transform: src=%X op=%X",src,op);
1197 for ( ; op ; op = op->next) {
1199 avpl = new_avpl_from_match(op->match_mode, src->name,src, op->match, TRUE);
1202 switch (op->replace_mode) {
1203 case AVPL_NO_REPLACE:
1204 delete_avpl(avpl,TRUE);
1207 merge_avpl(src,op->replace,TRUE);
1208 delete_avpl(avpl,TRUE);
1211 cs = src->null.next;
1212 cm = avpl->null.next;
1213 // Removes AVPs from the source which are in the matched data.
1214 // Assume that the matched set is a subset of the source.
1215 while (cs->avp && cm->avp) {
1216 if (cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
1219 cs->prev->next = cs->next;
1220 cs->next->prev = cs->prev;
1221 g_slice_free(any_avp_type,(any_avp_type*)cs);
1226 // Current matched AVP is not equal to the current
1227 // source AVP. Since there must be a source AVP for
1228 // each matched AVP, advance current source and not
1234 merge_avpl(src,op->replace,TRUE);
1235 delete_avpl(avpl,TRUE);
1245 * @param name the name the loal will take.
1247 * Creates an empty list of avp lists.
1249 * Return value: a pointer to the newly created loal.
1251 extern LoAL* new_loal(const gchar* name) {
1252 LoAL* new_loal_p = (LoAL*)g_slice_new(any_avp_type);
1258 #ifdef _AVP_DEBUGGING
1259 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_p: %X name=%s",new_loal_p,name);
1262 new_loal_p->name = scs_subscribe(avp_strings,name);
1263 new_loal_p->null.avpl = NULL;
1264 new_loal_p->null.next = &new_loal_p->null;
1265 new_loal_p->null.prev = &new_loal_p->null;
1266 new_loal_p->len = 0;
1272 * @param loal the loal on which to operate.
1273 * @param avpl the avpl to append.
1275 * Appends an avpl to a loal.
1278 extern void loal_append(LoAL* loal, AVPL* avpl) {
1279 LoALnode* node = (LoALnode*)g_slice_new(any_avp_type);
1281 #ifdef _AVP_DEBUGGING
1282 dbg_print(dbg_avpl_op,3,dbg_fp,"new_loal_node: %X",node);
1286 node->next = &loal->null;
1287 node->prev = loal->null.prev;
1289 loal->null.prev->next = node;
1290 loal->null.prev = node;
1296 * extract_first_avpl:
1297 * @param loal the loal on which to operate.
1299 * Extracts the first avpl contained in a loal.
1301 * Return value: a pointer to the extracted avpl.
1304 extern AVPL* extract_first_avpl(LoAL* loal) {
1308 #ifdef _AVP_DEBUGGING
1309 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: from: %s",loal->name);
1312 node = loal->null.next;
1314 loal->null.next->next->prev = &loal->null;
1315 loal->null.next = node->next;
1322 g_slice_free(any_avp_type,(any_avp_type*)node);
1324 #ifdef _AVP_DEBUGGING
1325 dbg_print(dbg_avpl_op,3,dbg_fp,"extract_first_avpl: got %s",avpl->name);
1326 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1334 * extract_first_avpl:
1335 * @param loal the loal on which to operate.
1337 * Extracts the last avpl contained in a loal.
1339 * Return value: a pointer to the extracted avpl.
1342 extern AVPL* extract_last_avpl(LoAL* loal){
1346 node = loal->null.prev;
1348 loal->null.prev->prev->next = &loal->null;
1349 loal->null.prev = node->prev;
1356 g_slice_free(any_avp_type,(any_avp_type*)node);
1357 #ifdef _AVP_DEBUGGING
1358 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal_node: %X",node);
1366 * extract_first_avpl:
1367 * @param loal the loal on which to operate.
1368 * @param cookie pointer to the pointer variable to contain the state between calls
1370 * At each call will return the following avpl from a loal. The given cookie
1371 * will be used to manatain the state between calls.
1373 * Return value: a pointer to the next avpl.
1376 extern AVPL* get_next_avpl(LoAL* loal,void** cookie) {
1379 #ifdef _AVP_DEBUGGING
1380 dbg_print(dbg_avpl_op,3,dbg_fp,"get_next_avpl: loal=%X node=%X",loal,*cookie);
1384 node = (LoALnode*) *cookie;
1386 node = loal->null.next;
1389 *cookie = node->next;
1396 * @param loal the loal to be deleted.
1397 * @param avpls_too whether avpls contained by the loal should be deleted as well
1398 * @param avps_too whether avps contained by the avpls should be also deleted
1400 * Destroys a loal and eventually desstroys avpls and avps.
1403 extern void delete_loal(LoAL* loal, gboolean avpls_too, gboolean avps_too) {
1406 #ifdef _AVP_DEBUGGING
1407 dbg_print(dbg_avpl_op,3,dbg_fp,"delete_loal: %X",loal);
1410 while(( avpl = extract_last_avpl(loal) )) {
1412 delete_avpl(avpl,avps_too);
1416 scs_unsubscribe(avp_strings,loal->name);
1417 g_slice_free(any_avp_type,(any_avp_type*)loal);
1422 /****************************************************************************
1423 ******************* the following are used in load_loal_from_file
1424 ****************************************************************************/
1428 * Used by loal_from_file to handle errors while loading.
1430 static LoAL* load_loal_error(FILE* fp, LoAL* loal, AVPL* curr, int linenum, const gchar* fmt, ...) {
1436 va_start( list, fmt );
1437 desc = g_strdup_vprintf(fmt, list);
1441 err = g_strdup_printf("Error Loading LoAL from file: in %s at line: %i, %s",loal->name,linenum,desc);
1443 err = g_strdup_printf("Error Loading LoAL at line: %i, %s",linenum,desc);
1445 ret = new_loal(err);
1451 if (loal) delete_loal(loal,TRUE,TRUE);
1452 if (curr) delete_avpl(curr,TRUE);
1458 /* the maximum length allowed for a line */
1459 #define MAX_ITEM_LEN 8192
1461 /* this two ugly things are used for tokenizing */
1462 #define AVP_OP_CHAR '=': case '^': case '$': case '~': case '<': case '>': case '?': case '|': case '&' : case '!'
1464 #define AVP_NAME_CHAR 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J':\
1465 case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T':\
1466 case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd':\
1467 case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':\
1468 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x':\
1469 case 'y': case 'z': case '_': case '0': case '1': case '2': case '3': case '4': case '5': case '6':\
1470 case '7': case '8': case '9': case '.'
1475 * @param filename the file containing a loals text representation.
1477 * Given a filename it will attempt to load a loal containing a copy of
1478 * the avpls represented in the file.
1480 * Return value: if successful a pointer to the new populated loal, else NULL.
1483 extern LoAL* loal_from_file(gchar* filename) {
1487 guint32 linenum = 1;
1492 LoAL *loal_error, *loal = new_loal(filename);
1496 enum _load_loal_states {
1504 linenum_buf = (gchar*)g_malloc(MAX_ITEM_LEN);
1505 name = (gchar*)g_malloc(MAX_ITEM_LEN);
1506 value = (gchar*)g_malloc(MAX_ITEM_LEN);
1509 loal_error = load_loal_error(fp,loal,curr,linenum,"MATE Will not run as root");
1516 if (( fp = ws_fopen(filename,"r") )) {
1517 while(( c = (gchar) fgetc(fp) )){
1521 report_read_failure(filename,errno);
1522 loal_error = load_loal_error(fp,loal,curr,linenum,"Error while reading '%f'",filename);
1532 if ( i >= MAX_ITEM_LEN - 1 ) {
1533 loal_error = load_loal_error(fp,loal,curr,linenum,"Maximum item length exceeded");
1549 case ' ': case '\t':
1550 /* ignore whitespace at line start */
1553 /* ignore empty lines */
1561 g_snprintf(linenum_buf,MAX_ITEM_LEN,"%s:%u",filename,linenum);
1562 curr = new_avpl(linenum_buf);
1568 loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1576 c = (gchar) fgetc(fp);
1577 if (c != '\n') ungetc(c,fp);
1589 loal_append(loal,curr);
1593 loal_error = load_loal_error(fp,loal,curr,linenum,"expecting name got: '%c'",c);
1599 state = BEFORE_NAME;
1606 avp = new_avp(name,value,op);
1608 if (! insert_avp(curr,avp) ) {
1623 loal_error = load_loal_error(fp,loal,curr,linenum,"operator expected found new line");
1626 loal_error = load_loal_error(fp,loal,curr,linenum,"name or match operator expected found '%c'",c);
1632 value[i++] = (gchar) fgetc(fp);
1635 state = BEFORE_NAME;
1640 avp = new_avp(name,value,op);
1642 if (! insert_avp(curr,avp) ) {
1647 loal_error = load_loal_error(fp,loal,curr,linenum,"';' expected found new line");
1657 g_free(linenum_buf);
1664 report_open_failure(filename,errno,FALSE);
1665 loal_error = load_loal_error(NULL,loal,NULL,0,"Cannot Open file '%s'",filename);
1669 g_free(linenum_buf);
1677 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1682 * indent-tabs-mode: t
1685 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1686 * :indentSize=8:tabSize=8:noTabs=false: