diff -ur --exclude=CVS radiusd/man/man5/users.5 radiusd-jm/man/man5/users.5
--- radiusd/man/man5/users.5	Thu Apr 17 09:18:16 2003
+++ radiusd-jm/man/man5/users.5	Sun Dec 14 12:41:03 2003
@@ -95,6 +95,19 @@
 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 check item, matches if the given attribute is in the request, AND
diff -ur --exclude=CVS radiusd/src/include/libradius.h radiusd-jm/src/include/libradius.h
--- radiusd/src/include/libradius.h	Thu Nov 20 20:15:15 2003
+++ radiusd-jm/src/include/libradius.h	Sun Dec 14 11:16:45 2003
@@ -238,11 +238,13 @@
 /* valuepair.c */
 VALUE_PAIR	*paircreate(int attr, int type);
 void		pairfree(VALUE_PAIR **);
-void            pairbasicfree(VALUE_PAIR *pair);
+void		pairbasicfree(VALUE_PAIR *);
 VALUE_PAIR	*pairfind(VALUE_PAIR *, int);
 void		pairdelete(VALUE_PAIR **, int);
 void		pairadd(VALUE_PAIR **, VALUE_PAIR *);
 void            pairreplace(VALUE_PAIR **first, VALUE_PAIR *add);
+VALUE_PAIR	*pairmatchone(REQUEST *,VALUE_PAIR *,VALUE_PAIR *);
+void		pairdeletematch(REQUEST *,VALUE_PAIR **,VALUE_PAIR *);
 VALUE_PAIR	*paircopy(VALUE_PAIR *vp);
 VALUE_PAIR	*paircopy2(VALUE_PAIR *vp, int attr);
 void		pairmove(VALUE_PAIR **to, VALUE_PAIR **from);
@@ -251,7 +253,7 @@
 VALUE_PAIR	*pairmake(const char *attribute, const char *value, int operator);
 VALUE_PAIR	*pairread(char **ptr, LRAD_TOKEN *eol);
 LRAD_TOKEN	userparse(char *buffer, VALUE_PAIR **first_pair);
-VALUE_PAIR     *readvp2(FILE *fp, int *pfiledone, const char *errprefix);
+VALUE_PAIR	*readvp2(FILE *fp, int *pfiledone, const char *errprefix);
 
 /*
  *	Error functions.
diff -ur --exclude=CVS radiusd/src/include/token.h radiusd-jm/src/include/token.h
--- radiusd/src/include/token.h	Thu Nov 21 12:37:16 2002
+++ radiusd-jm/src/include/token.h	Sun Dec 14 12:45:53 2003
@@ -35,6 +35,7 @@
   
   T_OP_ADD,			/* += */
   T_OP_SUB,			/* -= */
+  T_OP_SUB_ALL,			/* -* */
   T_OP_SET,			/* := */
   T_OP_EQ,			/* = */
   T_OP_NE,			/* != */
diff -ur --exclude=CVS radiusd/src/lib/print.c radiusd-jm/src/lib/print.c
--- radiusd/src/lib/print.c	Thu Oct 23 17:16:48 2003
+++ radiusd-jm/src/lib/print.c	Sun Dec 14 02:42:13 2003
@@ -222,6 +222,7 @@
   ";",
   "+=",
   "-=",
+  "-*",
   ":=",
   "=",
   "!=",
diff -ur --exclude=CVS radiusd/src/lib/token.c radiusd-jm/src/lib/token.c
--- radiusd/src/lib/token.c	Fri Sep 12 15:25:29 2003
+++ radiusd-jm/src/lib/token.c	Sun Dec 14 00:26:12 2003
@@ -51,6 +51,7 @@
 	{ "<",	T_OP_LT,	},
 	{ "#",	T_HASH,		},
 	{ ";",	T_SEMICOLON,	},
+	{ "-*", T_OP_SUB_ALL,	},
 	{ NULL, 0,		},
 };
 
diff -ur --exclude=CVS radiusd/src/main/valuepair.c radiusd-jm/src/main/valuepair.c
--- radiusd/src/main/valuepair.c	Sun Dec 14 12:36:56 2003
+++ radiusd-jm/src/main/valuepair.c	Sun Dec 14 11:15:54 2003
@@ -385,6 +385,58 @@
 	return paircompare( req, first, second, NULL, NULL );
 }
 
+/*
+ * 	Compare ONE attribute from a (potential) list.
+ * 	Return the first matching one.
+ */
+
+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->next; second = second->next){
+		for(first;first->next;first = first->next){
+			if(first->attribute == second->attribute){
+				/* No matter what, compair 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;
+}	
+
+
+/*
+ * 	Deletes matching attributes.
+ */
+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->next){
+				if(i->next == vp){ /* dis-own */
+					i->next = vp->next;
+					break;
+				}
+			}
+		pairbasicfree(vp);
+	}
+}
 
 /*
  *	Compare a Connect-Info and a Connect-Rate
@@ -681,6 +733,7 @@
 	 *	Point "tailto" to the end of the "to" list.
 	 */
 	tailto = to;
+	
 	for(i = *to; i; i = i->next) {
 		tailto = &i->next;
 	}
@@ -721,25 +774,37 @@
 		switch (i->operator) {
 			
 			/*
-			 *  If a similar attribute is found,
-			 *  delete it.
+			 *  If similar attributes 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...
-					 */
-					tailto = to;
-					for(j = *to; j; j = j->next) {
-						tailto = &j->next;
-					}
+				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;
