aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mate/mate_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mate/mate_util.c')
-rw-r--r--plugins/mate/mate_util.c438
1 files changed, 175 insertions, 263 deletions
diff --git a/plugins/mate/mate_util.c b/plugins/mate/mate_util.c
index 70f2d80a6b..0dff2acae5 100644
--- a/plugins/mate/mate_util.c
+++ b/plugins/mate/mate_util.c
@@ -28,15 +28,6 @@
#include <wsutil/file_util.h>
#include <wsutil/ws_printf.h> /* ws_g_warning */
-/***************************************************************************
-* ADDRDIFF
-***************************************************************************
-* This is a macro that computes the difference between the raw address
-* values of two pointers (rather than the difference between the pointers)
-* as a ptrdiff_t.
-***************************************************************************/
-#define ADDRDIFF(p,q) (((char *)(void *)(p)) - ((char *)(void *)(q)))
-
/***************************************************************************
* dbg_print
@@ -439,6 +430,38 @@ extern void rename_avpl(AVPL* avpl, gchar* name) {
}
/**
+ * insert_avp_before_node:
+ * @param avpl the avpl in which to insert.
+ * @param next_node the next node before which the new avpn has to be inserted.
+ * @param avp the avp to be inserted.
+ * @param copy_avp whether the original AVP or a copy thereof must be inserted.
+ *
+ * Pre-condition: the avp is sorted before before_avp and does not already exist
+ * in the avpl.
+ */
+static void insert_avp_before_node(AVPL* avpl, AVPN* next_node, AVP *avp, gboolean copy_avp) {
+ AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type);
+
+ new_avp_val->avp = copy_avp ? avp_copy(avp) : avp;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %p",new_avp_val);
+ dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %p in %p before %p;",avp,avpl,next_node);
+#endif
+
+ new_avp_val->next = next_node;
+ new_avp_val->prev = next_node->prev;
+ next_node->prev->next = new_avp_val;
+ next_node->prev = new_avp_val;
+
+ avpl->len++;
+
+#ifdef _AVP_DEBUGGING
+ dbg_print(dbg_avpl,4,dbg_fp,"avpl: %p new len: %i",avpl,avpl->len);
+#endif
+}
+
+/**
* insert_avp:
* @param avpl the avpl in which to insert.
* @param avp the avp to be inserted.
@@ -451,55 +474,40 @@ extern void rename_avpl(AVPL* avpl, gchar* name) {
* it is not inserted.
**/
extern gboolean insert_avp(AVPL* avpl, AVP* avp) {
- AVPN* new_avp_val = (AVPN*)g_slice_new(any_avp_type);
AVPN* c;
- new_avp_val->avp = avp;
-
#ifdef _AVP_DEBUGGING
- dbg_print(dbg_avpl_op,7,dbg_fp,"new_avpn: %X",new_avp_val);
dbg_print(dbg_avpl_op,4,dbg_fp,"insert_avp: %X %X %s%c%s;",avpl,avp,avp->n,avp->o,avp->v);
#endif
/* get to the insertion point */
- for(c=avpl->null.next; c->avp; c = c->next) {
+ for (c=avpl->null.next; c->avp; c = c->next) {
+ int name_diff = strcmp(avp->n, c->avp->n);
- if ( avp->n == c->avp->n ) {
+ if (name_diff == 0) {
+ int value_diff = strcmp(avp->v, c->avp->v);
- if (avp->v > c->avp->v) {
+ if (value_diff < 0) {
break;
}
- if (avp->v == c->avp->v) {
- if (avp->o == AVP_OP_EQUAL) {
-#ifdef _AVP_DEBUGGING
- dbg_print(dbg_avpl_op,7,dbg_fp,"delete_avpn: %X",new_avp_val);
-#endif
- g_slice_free(any_avp_type,(any_avp_type*)new_avp_val);
+ if (value_diff == 0) {
+ // ignore duplicate values, prevents (a=1, a=1)
+ // note that this is also used to insert
+ // conditions AVPs, so really check if the name,
+ // value and operator are all equal.
+ if (c->avp->o == avp->o && avp->o == AVP_OP_EQUAL) {
return FALSE;
}
}
}
- if (avp->n > c->avp->n) {
+ if (name_diff < 0) {
break;
}
}
-#ifdef _AVP_DEBUGGING
- dbg_print(dbg_avpl,5,dbg_fp,"insert_avp: inserting %X in %X before %X;",avp,avpl,c);
-#endif
-
- new_avp_val->next = c;
- new_avp_val->prev = c->prev;
- c->prev->next = new_avp_val;
- c->prev = new_avp_val;
-
- avpl->len++;
-
-#ifdef _AVP_DEBUGGING
- dbg_print(dbg_avpl,4,dbg_fp,"avpl: %X new len: %i",avpl,avpl->len);
-#endif
+ insert_avp_before_node(avpl, c, avp, FALSE);
return TRUE;
}
@@ -786,14 +794,10 @@ extern gchar* avpl_to_dotstr(AVPL* avpl) {
*
* Adds the avps of src that are not existent in dst into dst.
*
- * Return value: a pointer to the newly allocated string.
- *
**/
extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
AVPN* cd = NULL;
AVPN* cs = NULL;
- ptrdiff_t c;
- AVP* copy;
#ifdef _AVP_DEBUGGING
dbg_print(dbg_avpl_op,3,dbg_fp,"merge_avpl: %X %X",dst,src);
@@ -802,43 +806,43 @@ extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
cs = src->null.next;
cd = dst->null.next;
- while(cs->avp) {
+ while (cs->avp && cd->avp) {
- if(cd->avp) {
- c = ADDRDIFF(cd->avp->n,cs->avp->n);
- } else {
- c = -1;
- }
-
- if (c > 0) {
- if (cd->avp) cd = cd->next;
- } else if (c < 0) {
- if (copy_avps) {
- copy = avp_copy(cs->avp);
- if ( ! insert_avp(dst,copy) ) {
- delete_avp(copy);
- }
- } else {
- insert_avp(dst,cs->avp);
- }
+ int name_diff = strcmp(cd->avp->n, cs->avp->n);
+ if (name_diff < 0) {
+ // dest < source, advance dest to find a better place to insert
+ cd = cd->next;
+ } else if (name_diff > 0) {
+ // dest > source, so it can be definitely inserted here.
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
cs = cs->next;
} else {
- if ( ! cd->avp || ! (cd->avp->v == cs->avp->v) ) {
- if (copy_avps) {
- copy = avp_copy(cs->avp);
- if ( ! insert_avp(dst,copy) ) {
- delete_avp(copy);
- }
- } else {
- insert_avp(dst,cs->avp);
- }
+ // attribute names are equal. Ignore duplicate values but ensure that other values are sorted.
+ int value_diff = strcmp(cd->avp->v, cs->avp->v);
+
+ if (value_diff < 0) {
+ // dest < source, do not insert it yet
+ cd = cd->next;
+ } else if (value_diff > 0) {
+ // dest > source, insert AVP before the current dest AVP
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ } else {
+ // identical AVPs, do not create a duplicate.
+ cs = cs->next;
}
- cs = cs->next;
- if (cd->avp) cd = cd->next;
}
}
+ // if there are remaing source AVPs while there are no more destination
+ // AVPs (cd now represents the NULL item, after the last item), append
+ // all remaining source AVPs to the end
+ while (cs->avp) {
+ insert_avp_before_node(dst, cd, cs->avp, copy_avps);
+ cs = cs->next;
+ }
+
#ifdef _AVP_DEBUGGING
dbg_print(dbg_avpl_op,8,dbg_fp,"merge_avpl: done");
#endif
@@ -848,7 +852,7 @@ extern void merge_avpl(AVPL* dst, AVPL* src, gboolean copy_avps) {
/**
- * merge_avpl:
+ * new_avpl_from_avpl:
* @param name the name of the new avpl.
* @param avpl the avpl from which to get the avps.
* @param copy_avps whether avps should be copied instead of referenced.
@@ -975,20 +979,19 @@ extern AVP* match_avp(AVP* src, AVP* op) {
-/* TODO: rename me */
/**
* new_avpl_loose_match:
* @param name the name of the resulting avpl
- * @param src avpl to be matched agains an "op" avpl
- * @param op the "op" avpl that will be matched against the src avpl
+ * @param src the data AVPL to be matched against a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
* @param copy_avps whether the avps in the resulting avpl should be copied
*
- * creates an avp list containing any avps in src matching any avps in op
- * it will eventually create an empty list in none match
+ * Creates a new AVP list containing all data AVPs that matched any of the
+ * conditions AVPs. If there are no matches, an empty list will be returned.
*
- * Return value: a pointer to the newly created avpl containing the
- * matching avps.
- **/
+ * Note: Loose will always be considered a successful match, it matches zero or
+ * more conditions.
+ */
extern AVPL* new_avpl_loose_match(const gchar* name,
AVPL* src,
AVPL* op,
@@ -997,9 +1000,6 @@ extern AVPL* new_avpl_loose_match(const gchar* name,
AVPL* newavpl = new_avpl(scs_subscribe(avp_strings, name));
AVPN* co = NULL;
AVPN* cs = NULL;
- ptrdiff_t c;
- AVP* m;
- AVP* copy;
#ifdef _AVP_DEBUGGING
dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_loose_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
@@ -1008,241 +1008,147 @@ extern AVPL* new_avpl_loose_match(const gchar* name,
cs = src->null.next;
co = op->null.next;
- while(1) {
-
- if (!co->avp) {
- return newavpl;
- }
-
- if (!cs->avp) {
- return newavpl;
- }
-
-
- c = ADDRDIFF(co->avp->n, cs->avp->n);
-
- if ( c > 0 ) {
- if (co->avp) co = co->next;
- } else if (c < 0) {
- if (cs->avp) cs = cs->next;
+ while (cs->avp && co->avp) {
+ int name_diff = strcmp(co->avp->n, cs->avp->n);
+
+ if (name_diff < 0) {
+ // op < source, op is not matching
+ co = co->next;
+ } else if (name_diff > 0) {
+ // op > source, source is not matching
+ cs = cs->next;
} else {
- m = match_avp(cs->avp,co->avp);
- if(m) {
-
- if (copy_avps) {
- copy = avp_copy(m);
- if ( ! insert_avp(newavpl,copy) ) {
- delete_avp(copy);
- }
- } else {
- insert_avp(newavpl,m);
+ // attribute match found, let's see if there is any condition (op) that accepts this data AVP.
+ AVPN *cond = co;
+ do {
+ if (match_avp(cs->avp, cond->avp)) {
+ insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
+ break;
}
-
-
- }
-
- if (cs->avp) cs = cs->next;
-
+ cond = cond->next;
+ } while (cond->avp && cond->avp->n == cs->avp->n);
+ cs = cs->next;
}
}
-#ifdef _AVP_DEBUGGING
- dbg_print(dbg_avpl_op,6,dbg_fp,"new_avpl_loose_match: done!");
-#endif
-
- return NULL;
+ // return matches (possible none)
+ return newavpl;
}
-/* TODO: rename me */
/**
-* new_avpl_every_match:
+* new_avpl_pairs_match:
* @param name the name of the resulting avpl
- * @param src avpl to be matched agains an "op" avpl
- * @param op the "op" avpl that will be matched against the src avpl
+ * @param src the data AVPL to be matched against a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
+ * @param strict TRUE if every condition must have a matching data AVP, FALSE if
+ * it is also acceptable that only one of the condition AVPs for the same
+ * attribute is matching.
* @param copy_avps whether the avps in the resulting avpl should be copied
*
- * creates an avp list containing any avps in src matching every avp in op
- * it will not create a list if there is not a match for every attribute in op
+ * Creates an AVP list by matching pairs of conditions and data AVPs, returning
+ * the data AVPs. If strict is TRUE, then each condition must be paired with a
+ * matching data AVP. If strict is FALSE, then some conditions are allowed to
+ * fail when other conditions for the same attribute do have a match. Note that
+ * if the condition AVPL is empty, the result will be a match (an empty list).
*
* Return value: a pointer to the newly created avpl containing the
- * matching avps.
- **/
-extern AVPL* new_avpl_every_match(const gchar* name, AVPL* src, AVPL* op, gboolean copy_avps) {
+ * matching avps or NULL if there is no match.
+ */
+extern AVPL* new_avpl_pairs_match(const gchar* name, AVPL* src, AVPL* op, gboolean strict, gboolean copy_avps) {
AVPL* newavpl;
AVPN* co = NULL;
AVPN* cs = NULL;
- ptrdiff_t c;
- AVP* m;
- AVP* copy;
- gboolean matches;
+ const gchar *last_match = NULL;
+ gboolean matched = TRUE;
#ifdef _AVP_DEBUGGING
- dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_every_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
+ dbg_print(dbg_avpl_op,3,dbg_fp,"%s: %p src=%p op=%p name='%s'",G_STRFUNC,newavpl,src,op,name);
#endif
- if (src->len == 0) return NULL;
newavpl = new_avpl(scs_subscribe(avp_strings, name));
- if (op->len == 0)
- return newavpl;
-
- matches = TRUE;
-
cs = src->null.next;
co = op->null.next;
- while(1) {
-
- if (!co->avp) {
- break;
- }
-
- if (!cs->avp) {
- break;
- }
-
- c = ADDRDIFF(co->avp->n,cs->avp->n);
-
- if ( c > 0 ) {
- delete_avpl(newavpl,TRUE);
- return NULL;
- } else if (c < 0) {
+ while (cs->avp && co->avp) {
+ int name_diff = strcmp(co->avp->n, cs->avp->n);
+ const gchar *failed_match = NULL;
+
+ if (name_diff < 0) {
+ // op < source, op has no data avp with same attribute.
+ failed_match = co->avp->n;
+ co = co->next;
+ } else if (name_diff > 0) {
+ // op > source, the source avp is not matched by any condition
cs = cs->next;
- if (! cs->avp ) {
- break;
- }
} else {
- m = match_avp(cs->avp,co->avp);
-
- if(m) {
- matches++;
+ // Matching attributes found, now try to find a matching data AVP for the condition.
+ if (match_avp(cs->avp, co->avp)) {
+ insert_avp_before_node(newavpl, newavpl->null.prev, cs->avp, copy_avps);
+ last_match = co->avp->n;
cs = cs->next;
- co = co->next;
+ } else {
+ failed_match = co->avp->n;
+ }
+ co = co->next;
+ }
- if (copy_avps) {
- copy = avp_copy(m);
- if ( ! insert_avp(newavpl,copy) ) {
- delete_avp(copy);
- }
- } else {
- insert_avp(newavpl,m);
+ // condition did not match, check if we can continue matching.
+ if (failed_match) {
+ if (strict) {
+ matched = FALSE;
+ break;
+ } else if (last_match != failed_match) {
+ // None of the conditions so far matched the attribute, check for other candidates
+ if (!co->avp || co->avp->n != last_match) {
+ matched = FALSE;
+ break;
}
-
- } else {
- cs = cs->next;
}
}
+ }
+ // if there are any conditions remaining, then those could not be matched
+ if (matched && strict && co->avp) {
+ matched = FALSE;
}
- if (matches) {
+ if (matched) {
+ // there was a match, accept it
return newavpl;
} else {
- delete_avpl(newavpl,TRUE);
+ // no match, only delete AVPs too if they were copied
+ delete_avpl(newavpl, copy_avps);
return NULL;
}
}
-/* TODO: rename me */
/**
- * new_avpl_exact_match:
+ * new_avpl_from_match:
+ * @param mode The matching method, one of AVPL_STRICT, AVPL_LOOSE, AVPL_EVERY.
* @param name the name of the resulting avpl
- * @param src avpl to be matched agains an "op" avpl
- * @param op the "op" avpl that will be matched against the src avpl
- * @param copy_avps whether the avps in the resulting avpl should be copied
- *
- * creates an avp list containing every avp in src matching every avp in op
- * it will not create a list unless every avp in op is matched only once
- * to every avp in op.
+ * @param src the data AVPL to be matched agains a condition AVPL
+ * @param op the conditions AVPL that will be matched against the data AVPL
*
- * Return value: a pointer to the newly created avpl containing the
- * matching avps.
- **/
-extern AVPL* new_avpl_exact_match(const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps) {
- AVPL* newavpl = new_avpl(name);
- AVPN* co = NULL;
- AVPN* cs = NULL;
- ptrdiff_t c;
- AVP* m;
- AVP* copy;
-
-#ifdef _AVP_DEBUGGING
- dbg_print(dbg_avpl_op,3,dbg_fp,"new_avpl_every_match: %X src=%X op=%X name='%s'",newavpl,src,op,name);
-#endif
-
- if (op->len == 0)
- return newavpl;
-
- if (src->len == 0) {
- delete_avpl(newavpl,FALSE);
- return NULL;
- }
-
- cs = src->null.next;
- co = op->null.next;
- while(1) {
-
- c = ADDRDIFF(co->avp->n,cs->avp->n);
-
- if ( c > 0 ) {
- delete_avpl(newavpl,TRUE);
- return NULL;
- } else if (c < 0) {
- cs = cs->next;
- if (! cs->avp ) {
- delete_avpl(newavpl,TRUE);
- return NULL;
- }
- } else {
- m = match_avp(cs->avp,co->avp);
-
- if(m) {
- cs = cs->next;
- co = co->next;
-
- if (copy_avps) {
- copy = avp_copy(m);
- if ( ! insert_avp(newavpl,copy) ) {
- delete_avp(copy);
- }
- } else {
- insert_avp(newavpl,m);
- }
-
-
- if (!co->avp) {
- return newavpl;
- }
- if (!cs->avp) {
- delete_avpl(newavpl,TRUE);
- return NULL;
- }
- } else {
- delete_avpl(newavpl,TRUE);
- return NULL;
- }
- }
-
- }
-
- /* should never be reached */
- return NULL;
-}
-
+ * Matches the conditions AVPL against the original AVPL according to the mode.
+ * If there is no match, NULL is returned. If there is actually a match, then
+ * the matching AVPs (a subset of the data) are returned.
+ */
extern AVPL* new_avpl_from_match(avpl_match_mode mode, const gchar* name,AVPL* src, AVPL* op, gboolean copy_avps) {
AVPL* avpl = NULL;
switch (mode) {
case AVPL_STRICT:
- avpl = new_avpl_exact_match(name,src,op,copy_avps);
+ avpl = new_avpl_pairs_match(name, src, op, TRUE, copy_avps);
break;
case AVPL_LOOSE:
avpl = new_avpl_loose_match(name,src,op,copy_avps);
break;
case AVPL_EVERY:
- avpl = new_avpl_every_match(name,src,op,copy_avps);
+ avpl = new_avpl_pairs_match(name, src, op, FALSE, copy_avps);
break;
case AVPL_NO_MATCH:
+ // XXX this seems unused
avpl = new_avpl_from_avpl(name,src,copy_avps);
merge_avpl(avpl, op, copy_avps);
break;
@@ -1317,8 +1223,10 @@ extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
case AVPL_REPLACE:
cs = src->null.next;
cm = avpl->null.next;
- while(cs->avp) {
- if (cm->avp && cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
+ // Removes AVPs from the source which are in the matched data.
+ // Assume that the matched set is a subset of the source.
+ while (cs->avp && cm->avp) {
+ if (cs->avp->n == cm->avp->n && cs->avp->v == cm->avp->v) {
n = cs->next;
cs->prev->next = cs->next;
@@ -1328,6 +1236,10 @@ extern void avpl_transform(AVPL* src, AVPL_Transf* op) {
cs = n;
cm = cm->next;
} else {
+ // Current matched AVP is not equal to the current
+ // source AVP. Since there must be a source AVP for
+ // each matched AVP, advance current source and not
+ // the match AVP.
cs = cs->next;
}
}