#! /bin/sh /usr/share/dpatch/dpatch-run
## 240-before-after-where-head-tail.dpatch by  <joe@nameserver3.ttec.com>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: No description.

@DPATCH@
diff -urNad freeradius-1.1.0~/src/modules/rlm_policy/evaluate.c freeradius-1.1.0/src/modules/rlm_policy/evaluate.c
--- freeradius-1.1.0~/src/modules/rlm_policy/evaluate.c	2005-11-27 12:22:35.000000000 -0500
+++ freeradius-1.1.0/src/modules/rlm_policy/evaluate.c	2005-11-27 12:22:35.000000000 -0500
@@ -882,6 +882,7 @@
 	VALUE_PAIR **vps = NULL;
 	VALUE_PAIR *vp, *head, **tail;
 	const policy_item_t *attr;
+	policy_lex_t this_how;
 
 	this = (const policy_attributes_t *) item;
 
@@ -932,20 +933,119 @@
 		tail = &(vp->next);
 	}
 
-	switch (this->how) {
+	this_how = this->how;
+retry_how:
+	switch (this_how) {
 	case POLICY_LEX_SET_EQUALS: /* dangerous: removes all previous things! */
 		pairfree(vps);
 		*vps = head;
 		break;
 
+	case POLICY_LEX_AFTER_TAIL_ASSIGN:
 	case POLICY_LEX_ASSIGN: /* 'union' */
-		pairmove(vps, head);
+		pairmove(vps, &head);
 		pairfree(&head);
 		break;
 
+	case POLICY_LEX_BEFORE_HEAD_ASSIGN:
+		pairmove(&head, &vps);
+		pairfree(vps);
+		*vps = head;
+		break;
+
+	case POLICY_LEX_AFTER_TAIL_EQUALS:
 	case POLICY_LEX_CONCAT_EQUALS:
 		pairadd(vps, head);
 		break;
+	
+	case POLICY_LEX_BEFORE_HEAD_EQUALS:
+		pairadd(&head, *vps);
+		*vps = head;
+		break;
+
+	case POLICY_LEX_BEFORE_WHERE_EQUALS:
+	case POLICY_LEX_AFTER_WHERE_EQUALS:
+	case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+	case POLICY_LEX_AFTER_WHERE_ASSIGN:
+		/* find location*/
+		{
+			VALUE_PAIR *vpprev = NULL, *vpnext = NULL, *lvp;
+
+			for(lvp = *vps; lvp; vpprev = lvp, lvp = lvp->next) {
+				vpnext = lvp->next;
+				lvp->next = NULL;
+				if (evaluate_condition(state, this->where_loc))
+					break;
+				lvp->next = vpnext;
+			}
+
+			if (lvp) { 
+				switch(this_how) {
+				case POLICY_LEX_BEFORE_WHERE_EQUALS:
+				case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+					if (vpprev) {
+						lvp->next = vpnext;
+						vpnext = lvp;
+						vpprev->next = NULL;
+						lvp = vpprev;
+					}
+				default: /* always reached */
+					break;
+				}
+
+				switch(this_how) {
+				case POLICY_LEX_BEFORE_WHERE_EQUALS:
+					if (vpprev) 
+						pairadd(&lvp, head);
+					else
+						*vps = lvp = head;
+					break;
+ 				case POLICY_LEX_AFTER_WHERE_EQUALS:
+ 					pairadd(&lvp, head);
+					break;
+				case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+					if (vpprev) {
+						pairmove(&lvp, &head);
+						pairfree(&head);
+					}
+					else
+						*vps = lvp = head;
+					break;
+				case POLICY_LEX_AFTER_WHERE_ASSIGN:
+					pairmove(&lvp, &head);
+					pairfree(&head);
+					break;
+				default:/*never reached*/
+					break;
+				}	
+				for( ; lvp && lvp->next; lvp = lvp->next);
+				if (lvp)
+					lvp->next = vpnext;
+				break;
+			}
+
+			switch(this_how) {
+				case POLICY_LEX_BEFORE_WHERE_EQUALS:
+					this_how = POLICY_LEX_BEFORE_HEAD_EQUALS;
+					break;
+				case POLICY_LEX_AFTER_WHERE_EQUALS:
+					this_how = POLICY_LEX_AFTER_TAIL_EQUALS;
+					break;
+				case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+					this_how = POLICY_LEX_BEFORE_HEAD_ASSIGN;
+					break;
+				case POLICY_LEX_AFTER_WHERE_ASSIGN:
+					this_how = POLICY_LEX_AFTER_TAIL_ASSIGN;
+					break;
+				default: /*never reached*/
+					break;
+			}
+			goto retry_how; 
+		}	
+		fprintf(stderr, "HUH?\n");
+		pairfree(&head);
+		return 0;
+		break;  /* never reach here */
 
 	default:
 		fprintf(stderr, "HUH?\n");
diff -urNad freeradius-1.1.0~/src/modules/rlm_policy/parse.c freeradius-1.1.0/src/modules/rlm_policy/parse.c
--- freeradius-1.1.0~/src/modules/rlm_policy/parse.c	2005-11-27 12:22:35.000000000 -0500
+++ freeradius-1.1.0/src/modules/rlm_policy/parse.c	2005-11-27 12:22:35.000000000 -0500
@@ -106,6 +106,14 @@
 	{ ">=", POLICY_LEX_GT },
 	{ "=~", POLICY_LEX_RX_EQUALS },
 	{ "!~", POLICY_LEX_RX_NOT_EQUALS },
+	{ "^=", POLICY_LEX_BEFORE_HEAD_ASSIGN },
+	{ "^==", POLICY_LEX_BEFORE_WHERE_ASSIGN },
+	{ "^.", POLICY_LEX_BEFORE_HEAD_EQUALS },
+	{ "^.=", POLICY_LEX_BEFORE_WHERE_EQUALS },
+	{ "$=", POLICY_LEX_AFTER_TAIL_ASSIGN },
+	{ "$==", POLICY_LEX_AFTER_WHERE_ASSIGN },
+	{ "$.", POLICY_LEX_AFTER_TAIL_EQUALS },
+	{ "$.=", POLICY_LEX_AFTER_WHERE_EQUALS },
 	{ ".=", POLICY_LEX_CONCAT_EQUALS },
 	{ ":=", POLICY_LEX_SET_EQUALS },
 	{ "-=", POLICY_LEX_MINUS_EQUALS },
@@ -219,6 +227,56 @@
 		}
 		*token = POLICY_LEX_BAD;
 		return input + 1;
+	
+	case '^':
+		if (input[1] == '.' ) {
+			if (input[2] == '=') {
+				*token = POLICY_LEX_BEFORE_WHERE_EQUALS;
+				return input + 3;
+			}
+			else {
+				*token = POLICY_LEX_BEFORE_HEAD_EQUALS;
+				return input + 2;
+			}
+		}
+		else if (input[1] == '=') {
+			if (input[2] == '=') {
+				*token = POLICY_LEX_BEFORE_WHERE_ASSIGN;
+				return input + 3;
+			}
+			else {
+				*token = POLICY_LEX_BEFORE_HEAD_ASSIGN;
+				return input + 2;
+			}
+		}
+			
+		*token = POLICY_LEX_BAD;
+		return input + 1;
+
+	case '$':
+		if (input[1] == '.' ) {
+			if (input[2] == '=') {
+				*token = POLICY_LEX_AFTER_WHERE_EQUALS;
+				return input + 3;
+			}
+			else {
+				*token = POLICY_LEX_AFTER_TAIL_EQUALS;
+				return input + 2;
+			}
+		}
+		else if (input[1] == '=') {
+			if (input[2] == '=') {
+				*token = POLICY_LEX_AFTER_WHERE_ASSIGN;
+				return input + 3;
+			}
+			else {
+				*token = POLICY_LEX_AFTER_TAIL_ASSIGN;
+				return input + 2;
+			}
+		}
+
+		*token = POLICY_LEX_BAD;
+		return input + 1;
 
 	case ':':
 		if (input[1] == '=') {
@@ -935,9 +993,31 @@
 	policy_lex_t token;
 	policy_attributes_t *this;
 	char buffer[32];
+
+	this = rad_malloc(sizeof(*this));
+	if (!this) {
+		fprintf(stderr, "%s[%d]: rad_malloc failed\n",
+			lexer->filename, lexer->lineno);
+		return 0;
+	}
+	memset(this, 0, sizeof(*this));
+	this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
 	
 	token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
 	switch (token) {
+	case POLICY_LEX_BEFORE_WHERE_EQUALS:
+	case POLICY_LEX_AFTER_WHERE_EQUALS:
+	case POLICY_LEX_BEFORE_WHERE_ASSIGN:
+	case POLICY_LEX_AFTER_WHERE_ASSIGN:
+		if (!parse_condition(lexer, &(this->where_loc))) {
+			rlm_policy_free_item((policy_item_t *)this);
+			return 0;
+		}
+		break;
+	case POLICY_LEX_BEFORE_HEAD_EQUALS:
+	case POLICY_LEX_AFTER_TAIL_EQUALS:
+	case POLICY_LEX_BEFORE_HEAD_ASSIGN:
+	case POLICY_LEX_AFTER_TAIL_ASSIGN:
 	case POLICY_LEX_ASSIGN:
 	case POLICY_LEX_SET_EQUALS:
 	case POLICY_LEX_CONCAT_EQUALS:
@@ -947,13 +1027,10 @@
 		fprintf(stderr, "%s[%d]: Unexpected token %s\n",
 			lexer->filename, lexer->lineno,
 			lrad_int2str(rlm_policy_tokens, token, "?"));
+		rlm_policy_free_item((policy_item_t *)this);
 		return 0;	/* unknown */
 	}
-	
-	this = rad_malloc(sizeof(*this));
-	memset(this, 0, sizeof(*this));
 
-	this->item.type = POLICY_TYPE_ATTRIBUTE_LIST;
 	this->item.lineno = lexer->lineno;
 	this->where = where;
 	this->how = token;
diff -urNad freeradius-1.1.0~/src/modules/rlm_policy/rlm_policy.h freeradius-1.1.0/src/modules/rlm_policy/rlm_policy.h
--- freeradius-1.1.0~/src/modules/rlm_policy/rlm_policy.h	2005-11-27 12:22:35.000000000 -0500
+++ freeradius-1.1.0/src/modules/rlm_policy/rlm_policy.h	2005-11-27 12:23:09.000000000 -0500
@@ -80,6 +80,14 @@
 	POLICY_LEX_CONCAT_EQUALS, /* .= */
 	POLICY_LEX_VARIABLE,	/* %{foo} */
 	POLICY_LEX_FUNCTION,	/* Hmmm... */
+	POLICY_LEX_BEFORE_HEAD_ASSIGN, /* ^= */
+	POLICY_LEX_BEFORE_WHERE_ASSIGN, /* ^== */
+	POLICY_LEX_BEFORE_HEAD_EQUALS, /* ^. */
+	POLICY_LEX_BEFORE_WHERE_EQUALS, /* ^.= */
+	POLICY_LEX_AFTER_TAIL_ASSIGN, /* $= */
+	POLICY_LEX_AFTER_WHERE_ASSIGN, /* $== */
+	POLICY_LEX_AFTER_TAIL_EQUALS, /* $. */
+	POLICY_LEX_AFTER_WHERE_EQUALS, /* $.= */
 	POLICY_LEX_DOUBLE_QUOTED_STRING,
 	POLICY_LEX_SINGLE_QUOTED_STRING,
 	POLICY_LEX_BACK_QUOTED_STRING,
@@ -157,6 +165,7 @@
 	policy_reserved_word_t	where; /* where to do it */
 	policy_lex_t		how; /* how to do */
 	policy_item_t		*attributes; /* things to do */
+	policy_item_t		*where_loc; /* search for location in list*/
 	/* FIXME: VALUE_PAIR *vps; */
 } policy_attributes_t;
 
