diff -ur freeradius-1.0.2-orig/man/man5/users.5 freeradius-1.0.2-substar/man/man5/users.5
--- freeradius-1.0.2-orig/man/man5/users.5	2004-01-22 11:30:19.000000000 -0500
+++ freeradius-1.0.2-substar/man/man5/users.5	2005-03-10 06:07:49.000000000 -0500
@@ -95,6 +95,25 @@
 As a reply item, it has an identical meaning, but the attribute is
 added to the reply items.
 
+
+.TP 0.5i
+.B "Attribute -= Value"
+As a reply item it means remove matching items from the reply list.
+.br
+Not allowed as a check item.
+
+.TP 0.5i
+.B "Attribute -* Value"
+As a reply item it means remove all "Attribute" attributes items from the reply list.
+.br
+Not allowed as a check item.
+
+.TP 0.5i
+.B "Attribute -~ Value"
+As a reply item it means remove regex matching items from the reply list.
+.br
+Not allowed as a check item.
+
 .TP 0.5i
 .B "Attribute != Value"
 As a check item, matches if the given attribute is in the request, AND
diff -ur freeradius-1.0.2-orig/src/include/token.h freeradius-1.0.2-substar/src/include/token.h
--- freeradius-1.0.2-orig/src/include/token.h	2004-02-26 14:04:20.000000000 -0500
+++ freeradius-1.0.2-substar/src/include/token.h	2005-03-10 06:12:57.000000000 -0500
@@ -35,6 +35,8 @@
 
   T_OP_ADD,			/* += */
   T_OP_SUB,			/* -= */
+  T_OP_SUB_ALL,			/* -* */
+  T_OP_SUB_REG,			/* -~ */
   T_OP_SET,			/* := */
   T_OP_EQ,			/* = */
   T_OP_NE,			/* != */
diff -ur freeradius-1.0.2-orig/src/lib/print.c freeradius-1.0.2-substar/src/lib/print.c
--- freeradius-1.0.2-orig/src/lib/print.c	2004-02-26 14:04:21.000000000 -0500
+++ freeradius-1.0.2-substar/src/lib/print.c	2005-03-10 06:13:12.000000000 -0500
@@ -226,6 +226,8 @@
   ";",
   "+=",
   "-=",
+  "-*",
+  "-~",
   ":=",
   "=",
   "!=",
diff -ur freeradius-1.0.2-orig/src/lib/token.c freeradius-1.0.2-substar/src/lib/token.c
--- freeradius-1.0.2-orig/src/lib/token.c	2003-09-12 15:25:29.000000000 -0400
+++ freeradius-1.0.2-substar/src/lib/token.c	2005-03-10 06:13:10.000000000 -0500
@@ -39,6 +39,8 @@
 	{ ",",	T_COMMA,	},
 	{ "+=",	T_OP_ADD,	},
 	{ "-=",	T_OP_SUB,	},
+	{ "-*", T_OP_SUB_ALL,	},
+	{ "-~", T_OP_SUB_REG,	},
 	{ ":=",	T_OP_SET,	},
 	{ "=*", T_OP_CMP_TRUE,  },
 	{ "!*", T_OP_CMP_FALSE, },
diff -ur freeradius-1.0.2-orig/src/main/valuepair.c freeradius-1.0.2-substar/src/main/valuepair.c
--- freeradius-1.0.2-orig/src/main/valuepair.c	2005-02-07 15:04:47.000000000 -0500
+++ freeradius-1.0.2-substar/src/main/valuepair.c	2005-03-10 06:06:21.000000000 -0500
@@ -458,7 +458,128 @@
 	return paircompare( 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 = paircompare(req,first,second,NULL,NULL);
+				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->strvalue,
+					REG_EXTENDED);
+					result = regexec(&reg, (char *)first->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->strvalue,
+						REG_EXTENDED);
+					result = regexec(&reg, (char *)second->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
 /*
  *	Compare a Connect-Info and a Connect-Rate
  */
@@ -753,6 +874,7 @@
 	 *	Point "tailto" to the end of the "to" list.
 	 */
 	tailto = to;
+	
 	for(i = *to; i; i = i->next) {
 		tailto = &i->next;
 	}
@@ -793,30 +915,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->strvalue[0] ||
-				    (strcmp((char *)found->strvalue,
-					    (char *)i->strvalue) == 0)){
-					pairdelete(to, found->attribute);
-
-					/*
-					 *	'tailto' may have been
-					 *	deleted...
-					 */
+ 				if (!i->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.
 			 */
