--- ../../cvs/cvs-20051029/radiusd/src/main/valuepair.c	2005-10-18 19:52:49.000000000 -0400
+++ src/main/valuepair.c	2005-11-05 19:32:46.000000000 -0500
@@ -298,8 +298,13 @@
 			 *	Didn't find it.  If we were *trying*
 			 *	to not find it, then we succeeded.
 			 */
-			if (check_item->operator == T_OP_CMP_FALSE)
-				return 0;
+			if (check_item->operator == T_OP_CMP_FALSE ||
+#ifdef HAVE_REGEX_H
+			    check_item->operator == T_OP_REG_NE ||
+#endif
+			    check_item->operator == T_OP_NE 
+			    )
+				continue;
 			else
 				return -1;
 		}
@@ -310,7 +315,10 @@
 		 */
 		if (check_item->operator == T_OP_CMP_FALSE)
 			return -1;
-
+		
+		/* no comparison needed if it exists */
+		if (check_item->operator == T_OP_CMP_TRUE) 
+			continue;
 
 		/*
 		 *	We've got to xlat the string before doing
@@ -342,14 +350,14 @@
 				radlog(L_INFO,  "Invalid operator for item %s: "
 						"reverting to '=='", check_item->name);
 				/*FALLTHRU*/
-		        case T_OP_CMP_TRUE:    /* compare always == 0 */
-		        case T_OP_CMP_FALSE:   /* compare always == 1 */
 			case T_OP_CMP_EQ:
 				if (compare != 0) result = -1;
 				break;
 
 			case T_OP_NE:
-				if (compare == 0) result = -1;
+				/* != only succeeds if NO reply values match*/
+				if (compare == 0) return -1;
+				result = -1; /*check the rest for no match */
 				break;
 
 			case T_OP_LT:
@@ -457,7 +465,9 @@
 				compare = regexec(&reg, (char *)auth_item->vp_strvalue,
 						0, NULL, 0);
 				regfree(&reg);
-				if (compare == 0) result = -1;
+				/* !~ only succeeds if NO matches are found in reply pair */
+				if (compare == 0) return -1;
+				result = -1; /*check the rest for no match */
 				break;
 #endif
 
@@ -487,7 +497,128 @@
 	return compare_pair( req, first, second, NULL, NULL );
 }
 
+/*
+ * 	Compare ONE attribute from a (potential) list.
+ * 	Return the first matching one.
+ */
+
+static VALUE_PAIR * paircmpmatchone(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
+{
+	VALUE_PAIR *fnext = NULL;
+	VALUE_PAIR *snext = NULL;
+	int result = 0;
+	
+	for(; second; second = second->next){
+		for(; first; first = first->next){
+			if(first->attribute == second->attribute){
+				/* No matter what, compare only THESE items in chain */
+				fnext = first->next;
+				first->next = NULL;
+				snext = second->next;
+				second->next = NULL;
+				result = simplepaircmp(req, first, second);
+				first->next = fnext;
+				second->next = snext;
+				
+				if(!result)
+					return first;
+			}
+		}	
+	}
+	return NULL;
+}	
+
+#ifdef HAVE_REGEX_H
+/*
+ * 	Regex Compare ONE attribute from a (potential) list.
+ * 	Return the first matching one.
+ */
+
+static VALUE_PAIR * pairregmatchone(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
+{
+	int result = 0;
+	regex_t reg;
+	
+	for(; second; second = second->next){
+		for(; first; first = first->next){
+			if(first->attribute == second->attribute) {
+				if (second->operator == T_OP_REG_EQ ||
+				    second->operator == T_OP_REG_NE ||
+				    second->operator == T_OP_SUB_REG)
+				{	
+					regcomp(&reg, (char *)second->vp_strvalue,
+					REG_EXTENDED);
+					result = regexec(&reg, (char *)first->vp_strvalue,
+						0, NULL, 0);
+					regfree(&reg);
+				
+					if(!result)
+						return first;
+				}
+				if (first->operator == T_OP_REG_EQ ||
+				    first->operator == T_OP_REG_NE ||
+				    first->operator == T_OP_SUB_REG)
+				{
+
+					regcomp(&reg, (char *)first->vp_strvalue,
+						REG_EXTENDED);
+					result = regexec(&reg, (char *)second->vp_strvalue,
+							0, NULL, 0);
+					regfree(&reg);
+
+					if(!result)
+						return second;
+				}
+			}
+		}	
+	}
+	return NULL;
+}	
+#endif
+
+/*
+ * 	Deletes matching attributes.
+ */
+static void pairdeletematch(REQUEST *req,VALUE_PAIR **first,VALUE_PAIR *second)
+{
+	VALUE_PAIR *vp, *i;
+
+	while((vp = paircmpmatchone(req,*first,second))){
+		if(*first == vp) /* new head of chain */
+			*first = vp->next;
+		else
+			for(i = *first; i; i = i->next){
+				if(i->next == vp){ /* dis-own */
+					i->next = vp->next;
+					break;
+				}
+			}
+		pairbasicfree(vp);
+	}
+}
+
+#ifdef HAVE_REGEX_H
+/*
+ * 	deletes matching regex pairs
+*/
+static void pairdeleteregmatch(REQUEST *req,VALUE_PAIR **first,VALUE_PAIR *second)
+{
+	VALUE_PAIR *vp, *i;
 
+	while((vp = pairregmatchone(req,*first,second))){
+		if(*first == vp) /* new head of chain */
+			*first = vp->next;
+		else
+			for(i = *first; i; i = i->next){
+				if(i->next == vp){ /* dis-own */
+					i->next = vp->next;
+					break;
+				}
+			}
+		pairbasicfree(vp);
+	}
+}
+#endif
 /*
  *	Move pairs, replacing/over-writing them, and doing xlat.
  */
@@ -505,6 +636,7 @@
 	 *	Point "tailto" to the end of the "to" list.
 	 */
 	tailto = to;
+	
 	for(i = *to; i; i = i->next) {
 		tailto = &i->next;
 	}
@@ -545,30 +677,57 @@
 		switch (i->operator) {
 
 			/*
-			 *  If a similar attribute is found,
-			 *  delete it.
+			 *  If a similar attribute are found,
+			 *  delete them.
 			 */
 		case T_OP_SUB:		/* -= */
 			if (found) {
-				if (!i->vp_strvalue[0] ||
-				    (strcmp((char *)found->vp_strvalue,
-					    (char *)i->vp_strvalue) == 0)){
-					pairdelete(to, found->attribute);
-
-					/*
-					 *	'tailto' may have been
-					 *	deleted...
-					 */
+ 				if (!i->vp_strvalue[0])
+					goto delete_all;
+				
+				pairdeletematch(req,to,i);
+				/*
+				 *	'tailto' may have been
+				 *	deleted...
+				 */
+				tailto = to;
+				for(j = *to; j; j = j->next) {
+ 					tailto = &j->next;
+				}
+			
+			}
+			tailfrom = i;
+			continue;
+			break;
+		case T_OP_SUB_ALL:	/* -* */
+			if (found) {
+delete_all:		
+				pairdelete(to, found->attribute);
 					tailto = to;
 					for(j = *to; j; j = j->next) {
 						tailto = &j->next;
 					}
-				}
 			}
 			tailfrom = i;
 			continue;
 			break;
 
+#ifdef HAVE_REGEX_H
+		case T_OP_SUB_REG:	/* -~ */
+			if (found) {
+				
+				pairdeleteregmatch(req,to,i);
+					
+				tailto = to;
+				for(j = *to; j; j = j->next) {
+ 					tailto = &j->next;
+				}
+
+			tailfrom = i;
+			continue;
+			break;
+		}
+#endif
 			/*
 			 *  Add it, if it's not already there.
 			 */
