/* tree.c

   Routines for manipulating parse trees... */

/*
 * Copyright (c) 1995-2001 Internet Software Consortium.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of The Internet Software Consortium nor the names
 *    of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This software has been written for the Internet Software Consortium
 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
 * To learn more about the Internet Software Consortium, see
 * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
 * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
 * ``http://www.nominum.com''.
 */

#ifndef lint
static char copyright[] =
"$Id: tree.c,v 1.101.2.5 2001/10/04 20:38:22 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
#endif /* not lint */

#include "dhcpd.h"
#include <omapip/omapip_p.h>

struct binding_scope *global_scope;

static int do_host_lookup PROTO ((struct data_string *,
				  struct dns_host_entry *));

#ifdef NSUPDATE
struct __res_state resolver_state;
int resolver_inited = 0;
#endif

pair cons (car, cdr)
	caddr_t car;
	pair cdr;
{
	pair foo = (pair)dmalloc (sizeof *foo, MDL);
	if (!foo)
		log_fatal ("no memory for cons.");
	foo -> car = car;
	foo -> cdr = cdr;
	return foo;
}

int make_const_option_cache (oc, buffer, data, len, option, file, line)
	struct option_cache **oc;
	struct buffer **buffer;
	u_int8_t *data;
	unsigned len;
	struct option *option;
	const char *file;
	int line;
{
	struct buffer *bp;

	if (buffer) {
		bp = *buffer;
		*buffer = 0;
	} else {
		bp = (struct buffer *)0;
		if (!buffer_allocate (&bp, len, file, line)) {
			log_error ("%s(%d): can't allocate buffer.",
				   file, line);
			return 0;
		}
	}

	if (!option_cache_allocate (oc, file, line)) {
		log_error ("%s(%d): can't allocate option cache.", file, line);
		buffer_dereference (&bp, file, line);
		return 0;
	}

	(*oc) -> data.len = len;
	(*oc) -> data.buffer = bp;
	(*oc) -> data.data = &bp -> data [0];
	(*oc) -> data.terminated = 0;
	if (data)
		memcpy (&bp -> data [0], data, len);
	(*oc) -> option = option;
	return 1;
}

int make_host_lookup (expr, name)
	struct expression **expr;
	const char *name;
{
	if (!expression_allocate (expr, MDL)) {
		log_error ("No memory for host lookup tree node.");
		return 0;
	}
	(*expr) -> op = expr_host_lookup;
	if (!enter_dns_host (&((*expr) -> data.host_lookup), name)) {
		expression_dereference (expr, MDL);
		return 0;
	}
	return 1;
}

int enter_dns_host (dh, name)
	struct dns_host_entry **dh;
	const char *name;
{
	/* XXX This should really keep a hash table of hostnames
	   XXX and just add a new reference to a hostname that
	   XXX already exists, if possible, rather than creating
	   XXX a new structure. */
	if (!dns_host_entry_allocate (dh, name, MDL)) {
		log_error ("Can't allocate space for new host.");
		return 0;
	}
	return 1;
}

int make_const_data (struct expression **expr, const unsigned char *data,
		     unsigned len, int terminated, int allocate,
		     const char *file, int line)
{
	struct expression *nt;

	if (!expression_allocate (expr, file, line)) {
		log_error ("No memory for make_const_data tree node.");
		return 0;
	}
	nt = *expr;

	if (len) {
		if (allocate) {
			if (!buffer_allocate (&nt -> data.const_data.buffer,
					      len + terminated, file, line)) {
				log_error ("Can't allocate const_data buffer");
				expression_dereference (expr, file, line);
				return 0;
			}
			nt -> data.const_data.data =
				&nt -> data.const_data.buffer -> data [0];
			memcpy (nt -> data.const_data.buffer -> data,
				data, len + terminated);
		} else 
			nt -> data.const_data.data = data;
		nt -> data.const_data.terminated = terminated;
	} else
		nt -> data.const_data.data = 0;

	nt -> op = expr_const_data;
	nt -> data.const_data.len = len;
	return 1;
}

int make_const_int (expr, val)
	struct expression **expr;
	unsigned long val;
{
	if (!expression_allocate (expr, MDL)) {
		log_error ("No memory for make_const_int tree node.");
		return 0;
	}

	(*expr) -> op = expr_const_int;
	(*expr) -> data.const_int = val;
	return 1;
}

int make_concat (expr, left, right)
	struct expression **expr;
	struct expression *left, *right;
{
	/* If we're concatenating a null tree to a non-null tree, just
	   return the non-null tree; if both trees are null, return
	   a null tree. */
	if (!left) {
		if (!right)
			return 0;
		expression_reference (expr, right, MDL);
		return 1;
	}
	if (!right) {
		expression_reference (expr, left, MDL);
		return 1;
	}
			
	/* Otherwise, allocate a new node to concatenate the two. */
	if (!expression_allocate (expr, MDL)) {
		log_error ("No memory for concatenation expression node.");
		return 0;
	}
		
	(*expr) -> op = expr_concat;
	expression_reference (&(*expr) -> data.concat [0], left, MDL);
	expression_reference (&(*expr) -> data.concat [1], right, MDL);
	return 1;
}

int make_encapsulation (expr, name)
	struct expression **expr;
	struct data_string *name;
{
	/* Allocate a new node to store the encapsulation. */
	if (!expression_allocate (expr, MDL)) {
		log_error ("No memory for encapsulation expression node.");
		return 0;
	}
		
	(*expr) -> op = expr_encapsulate;
	data_string_copy (&(*expr) -> data.encapsulate, name, MDL);
	return 1;
}

int make_substring (new, expr, offset, length)
	struct expression **new;
	struct expression *expr;
	struct expression *offset;
	struct expression *length;
{
	/* Allocate an expression node to compute the substring. */
	if (!expression_allocate (new, MDL)) {
		log_error ("no memory for substring expression.");
		return 0;
	}
	(*new) -> op = expr_substring;
	expression_reference (&(*new) -> data.substring.expr, expr, MDL);
	expression_reference (&(*new) -> data.substring.offset, offset, MDL);
	expression_reference (&(*new) -> data.substring.len, length, MDL);
	return 1;
}

int make_limit (new, expr, limit)
	struct expression **new;
	struct expression *expr;
	int limit;
{
	struct expression *rv;

	/* Allocate a node to enforce a limit on evaluation. */
	if (!expression_allocate (new, MDL))
		log_error ("no memory for limit expression");
	(*new) -> op = expr_substring;
	expression_reference (&(*new) -> data.substring.expr, expr, MDL);

	/* Offset is a constant 0. */
	if (!expression_allocate (&(*new) -> data.substring.offset, MDL)) {
		log_error ("no memory for limit offset expression");
		expression_dereference (new, MDL);
		return 0;
	}
	(*new) -> data.substring.offset -> op = expr_const_int;
	(*new) -> data.substring.offset -> data.const_int = 0;

	/* Length is a constant: the specified limit. */
	if (!expression_allocate (&(*new) -> data.substring.len, MDL)) {
		log_error ("no memory for limit length expression");
		expression_dereference (new, MDL);
		return 0;
	}
	(*new) -> data.substring.len -> op = expr_const_int;
	(*new) -> data.substring.len -> data.const_int = limit;

	return 1;
}

int option_cache (struct option_cache **oc, struct data_string *dp,
		  struct expression *expr, struct option *option,
		  const char *file, int line)
{
	if (!option_cache_allocate (oc, file, line))
		return 0;
	if (dp)
		data_string_copy (&(*oc) -> data, dp, file, line);
	if (expr)
		expression_reference (&(*oc) -> expression, expr, file, line);
	(*oc) -> option = option;
	return 1;
}

int make_let (result, name)
	struct executable_statement **result;
	const char *name;
{
	if (!(executable_statement_allocate (result, MDL)))
		return 0;
	
	(*result) -> op = let_statement;
	(*result) -> data.let.name = dmalloc (strlen (name) + 1, MDL);
	if (!(*result) -> data.let.name) {
		executable_statement_dereference (result, MDL);
		return 0;
	}
	strcpy ((*result) -> data.let.name, name);
	return 1;
}
		
static int do_host_lookup (result, dns)
	struct data_string *result;
	struct dns_host_entry *dns;
{
	struct hostent *h;
	unsigned i, count;
	unsigned new_len;

#ifdef DEBUG_EVAL
	log_debug ("time: now = %d  dns = %d  diff = %d",
	       cur_time, dns -> timeout, cur_time - dns -> timeout);
#endif

	/* If the record hasn't timed out, just copy the data and return. */
	if (cur_time <= dns -> timeout) {
#ifdef DEBUG_EVAL
		log_debug ("easy copy: %d %s",
		       dns -> data.len,
		       (dns -> data.len > 4
			? inet_ntoa (*(struct in_addr *)(dns -> data.data))
			: 0));
#endif
		data_string_copy (result, &dns -> data, MDL);
		return 1;
	}
#ifdef DEBUG_EVAL
	log_debug ("Looking up %s", dns -> hostname);
#endif

	/* Otherwise, look it up... */
	h = gethostbyname (dns -> hostname);
	if (!h) {
#ifndef NO_H_ERRNO
		switch (h_errno) {
		      case HOST_NOT_FOUND:
#endif
			log_error ("%s: host unknown.", dns -> hostname);
#ifndef NO_H_ERRNO
			break;
		      case TRY_AGAIN:
			log_error ("%s: temporary name server failure",
				   dns -> hostname);
			break;
		      case NO_RECOVERY:
			log_error ("%s: name server failed", dns -> hostname);
			break;
		      case NO_DATA:
			log_error ("%s: no A record associated with address",
				   dns -> hostname);
		}
#endif /* !NO_H_ERRNO */

		/* Okay to try again after a minute. */
		dns -> timeout = cur_time + 60;
		data_string_forget (&dns -> data, MDL);
		return 0;
	}

#ifdef DEBUG_EVAL
	log_debug ("Lookup succeeded; first address is %s",
	       inet_ntoa (h -> h_addr_list [0]));
#endif

	/* Count the number of addresses we got... */
	for (count = 0; h -> h_addr_list [count]; count++)
		;
	
	/* Dereference the old data, if any. */
	data_string_forget (&dns -> data, MDL);

	/* Do we need to allocate more memory? */
	new_len = count * h -> h_length;
	if (!buffer_allocate (&dns -> data.buffer, new_len, MDL))
	{
		log_error ("No memory for %s.", dns -> hostname);
		return 0;
	}

	dns -> data.data = &dns -> data.buffer -> data [0];
	dns -> data.len = new_len;
	dns -> data.terminated = 0;

	/* Addresses are conveniently stored one to the buffer, so we
	   have to copy them out one at a time... :'( */
	for (i = 0; i < count; i++) {
		memcpy (&dns -> data.buffer -> data [h -> h_length * i],
			h -> h_addr_list [i], (unsigned)(h -> h_length));
	}
#ifdef DEBUG_EVAL
	log_debug ("dns -> data: %x  h -> h_addr_list [0]: %x",
		   *(int *)(dns -> buffer), h -> h_addr_list [0]);
#endif

	/* XXX Set the timeout for an hour from now.
	   XXX This should really use the time on the DNS reply. */
	dns -> timeout = cur_time + 3600;

#ifdef DEBUG_EVAL
	log_debug ("hard copy: %d %s", dns -> data.len,
	       (dns -> data.len > 4
		? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0));
#endif
	data_string_copy (result, &dns -> data, MDL);
	return 1;
}

int evaluate_expression (result, packet, lease, client_state,
			 in_options, cfg_options, scope, expr, file, line)
	struct binding_value **result;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct expression *expr;
	const char *file;
	int line;
{
	struct binding_value *bv;
	int status;
	struct binding *binding;

	bv = (struct binding_value *)0;

	if (expr -> op == expr_variable_reference) {
		if (!scope || !*scope)
			return 0;

		binding = find_binding (*scope, expr -> data.variable);

		if (binding && binding -> value) {
			if (result)
				binding_value_reference (result,
							 binding -> value,
							 file, line);
			return 1;
		} else
			return 0;
	} else if (expr -> op == expr_funcall) {
		struct string_list *s;
		struct expression *arg;
		struct binding_scope *ns;
		struct binding *nb;

		if (!scope || !*scope) {
			log_error ("%s: no such function.",
				   expr -> data.funcall.name);
			return 0;
		}

		binding = find_binding (*scope, expr -> data.funcall.name);

		if (!binding || !binding -> value) {
			log_error ("%s: no such function.",
				   expr -> data.funcall.name);
			return 0;
		}
		if (binding -> value -> type != binding_function) {
			log_error ("%s: not a function.",
				   expr -> data.funcall.name);
			return 0;
		}

		/* Create a new binding scope in which to define
		   the arguments to the function. */
		ns = (struct binding_scope *)0;
		if (!binding_scope_allocate (&ns, MDL)) {
			log_error ("%s: can't allocate argument scope.",
				   expr -> data.funcall.name);
			return 0;
		}

		arg = expr -> data.funcall.arglist;
		s = binding -> value -> value.fundef -> args;
		while (arg && s) {
			nb = dmalloc (sizeof *nb, MDL);
			if (!nb) {
			      blb:
				binding_scope_dereference (&ns, MDL);
				return 0;
			} else {
				memset (nb, 0, sizeof *nb);
				nb -> name = dmalloc (strlen (s -> string) + 1,
						      MDL);
				if (nb -> name)
					strcpy (nb -> name, s -> string);
				else {
					dfree (nb, MDL);
					nb = (struct binding *)0;
					goto blb;
				}
			}
			evaluate_expression (&nb -> value, packet, lease,
					     client_state,
					     in_options, cfg_options, scope,
					     arg -> data.arg.val, file, line);
			nb -> next = ns -> bindings;
			ns -> bindings = nb;
			arg = arg -> data.arg.next;
			s = s -> next;
		}
		if (arg) {
			log_error ("%s: too many arguments.",
				   expr -> data.funcall.name);
			binding_scope_dereference (&ns, MDL);
			return 0;
		}
		if (s) {
			log_error ("%s: too few arguments.",
				   expr -> data.funcall.name);
			binding_scope_dereference (&ns, MDL);
			return 0;
		}

		if (scope && *scope)
			binding_scope_reference (&ns -> outer, *scope, MDL);

		status = (execute_statements
			  (&bv, packet,
			   lease, client_state, in_options, cfg_options, &ns,
			   binding -> value -> value.fundef -> statements));
		binding_scope_dereference (&ns, MDL);

		if (!bv)
			return 1;
        } else if (is_boolean_expression (expr)) {
		if (!binding_value_allocate (&bv, MDL))
			return 0;
		bv -> type = binding_boolean;
		status = (evaluate_boolean_expression
			  (&bv -> value.boolean, packet, lease, client_state,
			   in_options, cfg_options, scope, expr));
	} else if (is_numeric_expression (expr)) {
		if (!binding_value_allocate (&bv, MDL))
			return 0;
		bv -> type = binding_numeric;
		status = (evaluate_numeric_expression
			  (&bv -> value.intval, packet, lease, client_state,
			   in_options, cfg_options, scope, expr));
	} else if (is_data_expression  (expr)) {
		if (!binding_value_allocate (&bv, MDL))
			return 0;
		bv -> type = binding_data;
		status = (evaluate_data_expression
			  (&bv -> value.data, packet, lease, client_state,
			   in_options, cfg_options, scope, expr, MDL));
	} else if (is_dns_expression (expr)) {
#if defined (NSUPDATE)
		if (!binding_value_allocate (&bv, MDL))
			return 0;
		bv -> type = binding_dns;
		status = (evaluate_dns_expression
			  (&bv -> value.dns, packet, lease, client_state,
			   in_options, cfg_options, scope, expr));
#endif
	} else {
		log_error ("%s: invalid expression type: %d",
			   "evaluate_expression", expr -> op);
		return 0;
	}
	if (result && status)
		binding_value_reference (result, bv, file, line);
	binding_value_dereference (&bv, MDL);

	return status;
}

int binding_value_dereference (struct binding_value **v,
			       const char *file, int line)
{
	struct binding_value *bv = *v;

	*v = (struct binding_value *)0;

	/* Decrement the reference count.   If it's nonzero, we're
	   done. */
	--(bv -> refcnt);
	rc_register (file, line, v, bv, bv -> refcnt, 1);
	if (bv -> refcnt > 0)
		return 1;
	if (bv -> refcnt < 0) {
		log_error ("%s(%d): negative refcnt!", file, line);
#if defined (DEBUG_RC_HISTORY)
		dump_rc_history (bv);
#endif
#if defined (POINTER_DEBUG)
		abort ();
#else
		return 0;
#endif
	}

	switch (bv -> type) {
	      case binding_boolean:
	      case binding_numeric:
		break;
	      case binding_data:
		if (bv -> value.data.buffer)
			data_string_forget (&bv -> value.data, file, line);
		break;
	      case binding_dns:
#if defined (NSUPDATE)
		if (bv -> value.dns) {
			if (bv -> value.dns -> r_data) {
				dfree (bv -> value.dns -> r_data_ephem, MDL);
				bv -> value.dns -> r_data = (unsigned char *)0;
				bv -> value.dns -> r_data_ephem =
					(unsigned char *)0;
			}
			minires_freeupdrec (bv -> value.dns);
		}
		break;
#endif
	      default:
		log_error ("%s(%d): invalid binding type: %d",
			   file, line, bv -> type);
		return 0;
	}
	dfree (bv, file, line);
	return 1;
}

#if defined (NSUPDATE)
int evaluate_dns_expression (result, packet, lease, client_state, in_options,
			     cfg_options, scope, expr)
	ns_updrec **result;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct expression *expr;
{
	ns_updrec *foo;
	unsigned long ttl = 0;
	char *tname;
	struct data_string name, data;
	int r0, r1, r2, r3;

	if (!result || *result) {
		log_error ("evaluate_dns_expression called with non-null %s",
			   "result pointer");
#if defined (POINTER_DEBUG)
		abort ();
#else
		return 0;
#endif
	}
		
	switch (expr -> op) {
#if defined (NSUPDATE)
	      case expr_ns_add:
		r0 = evaluate_numeric_expression (&ttl, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.ns_add.ttl);
		goto nsfinish;

	      case expr_ns_exists:
		ttl = 1;

	      case expr_ns_delete:
	      case expr_ns_not_exists:
		r0 = 1;
	      nsfinish:
		memset (&name, 0, sizeof name);
		r1 = evaluate_data_expression (&name, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.ns_add.rrname,
					       MDL);
		if (r1) {
			/* The result of the evaluation may or may not
			   be NUL-terminated, but we need it
			   terminated for sure, so we have to allocate
			   a buffer and terminate it. */
			tname = dmalloc (name.len + 1, MDL);
			if (!tname) {
				r2 = 0;
				r1 = 0;
				data_string_forget (&name, MDL);
			} else {
				memcpy (tname, name.data, name.len);
				tname [name.len] = 0;
				memset (&data, 0, sizeof data);
				r2 = evaluate_data_expression
					(&data, packet, lease, client_state,
					 in_options, cfg_options, scope,
					 expr -> data.ns_add.rrdata, MDL);
			}
		} else
			r2 = 0;
		if (r0 && r1 && (r2 || expr -> op != expr_ns_add)) {
		    *result = minires_mkupdrec (((expr -> op == expr_ns_add ||
						  expr -> op == expr_ns_delete)
						 ? S_UPDATE : S_PREREQ),
						tname,
						expr -> data.ns_add.rrclass,
						expr -> data.ns_add.rrtype,
						ttl);
		    if (!*result) {
			  ngood:
			    if (r2) {
				data_string_forget (&data, MDL);
				r2 = 0;
			    }
		    } else {
			if (data.len) {
				/* As a special case, if we get exactly
				   four bytes of data, it's an IP address
				   represented as a 32-bit quantity, which
				   is actually what we *should* be getting
				   here.   Because res_mkupdrec is currently
				   broken and expects a dotted quad, convert
				   it.   This should be fixed when the new
				   resolver is merged. */
				if (data.len == 4) {
				    (*result) -> r_data_ephem =
					    dmalloc (16, MDL);
				    if (!(*result) -> r_data_ephem)
					goto dpngood;
				    (*result) -> r_data =
					    (*result) -> r_data_ephem;
				    sprintf ((char *)(*result) -> r_data_ephem,
					     "%d.%d.%d.%d",
					     data.data [0], data.data [1],
					     data.data [2], data.data [3]);
				    (*result) -> r_size = 
					    strlen ((const char *)
						    (*result) -> r_data);
				} else {
				    (*result) -> r_size = data.len;
				    (*result) -> r_data_ephem =
					    dmalloc (data.len, MDL);
				    if (!(*result) -> r_data_ephem) {
				      dpngood: /* double plus ungood. */
					minires_freeupdrec (*result);
					*result = 0;
					goto ngood;
				    }
				    (*result) -> r_data =
					    (*result) -> r_data_ephem;
				    memcpy ((*result) -> r_data_ephem,
					    data.data, data.len);
				}
			} else {
				(*result) -> r_data = 0;
				(*result) -> r_size = 0;
			}
			switch (expr -> op) {
			      case expr_ns_add:
				(*result) -> r_opcode = ADD;
				break;
			      case expr_ns_delete:
				(*result) -> r_opcode = DELETE;
				break;
			      case expr_ns_exists:
				(*result) -> r_opcode = YXRRSET;
				break;
			      case expr_ns_not_exists:
				(*result) -> r_opcode = NXRRSET;
				break;

				/* Can't happen, but satisfy gcc. */
			      default:
				break;
			}
		    }
		}
		if (r1) {
			data_string_forget (&name, MDL);
			dfree (tname, MDL);
		}
		if (r2)
			data_string_forget (&data, MDL);
		/* One flaw in the thinking here: an IP address and an
		   ASCII string both look like data expressions, but
		   for A records, we want an ASCII string, not a
		   binary IP address.  Do I need to turn binary IP
		   addresses into a seperate type?  */
		return (r0 && r1 &&
			(r2 || expr -> op != expr_ns_add) && *result);

#else
	      case expr_ns_add:
	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
		return 0;
#endif
	      case expr_funcall:
		log_error ("%s: dns values for functions not supported.",
			   expr -> data.funcall.name);
		break;

	      case expr_variable_reference:
		log_error ("%s: dns values for variables not supported.",
			   expr -> data.variable);
		break;

	      case expr_check:
	      case expr_equal:
	      case expr_not_equal:
	      case expr_and:
	      case expr_or:
	      case expr_not:
	      case expr_match:
	      case expr_static:
	      case expr_known:
	      case expr_exists:
	      case expr_variable_exists:
		log_error ("Boolean opcode in evaluate_dns_expression: %d",
		      expr -> op);
		return 0;

	      case expr_none:
	      case expr_substring:
	      case expr_suffix:
	      case expr_option:
	      case expr_hardware:
	      case expr_const_data:
	      case expr_packet:
	      case expr_concat:
	      case expr_encapsulate:
	      case expr_host_lookup:
	      case expr_encode_int8:
	      case expr_encode_int16:
	      case expr_encode_int32:
	      case expr_binary_to_ascii:
	      case expr_reverse:
	      case expr_filename:
	      case expr_sname:
	      case expr_pick_first_value:
	      case expr_host_decl_name:
	      case expr_config_option:
	      case expr_leased_address:
	      case expr_null:
		log_error ("Data opcode in evaluate_dns_expression: %d",
		      expr -> op);
		return 0;

	      case expr_extract_int8:
	      case expr_extract_int16:
	      case expr_extract_int32:
	      case expr_const_int:
	      case expr_lease_time:
	      case expr_dns_transaction:
	      case expr_add:
	      case expr_subtract:
	      case expr_multiply:
	      case expr_divide:
	      case expr_remainder:
	      case expr_binary_and:
	      case expr_binary_or:
	      case expr_binary_xor:
	      case expr_client_state:
		log_error ("Numeric opcode in evaluate_dns_expression: %d",
		      expr -> op);
		return 0;

	      case expr_function:
		log_error ("Function opcode in evaluate_dns_expression: %d",
		      expr -> op);
		return 0;

	      case expr_arg:
		break;
	}

	log_error ("Bogus opcode in evaluate_dns_expression: %d",
		   expr -> op);
	return 0;
}
#endif /* defined (NSUPDATE) */

int evaluate_boolean_expression (result, packet, lease, client_state,
				 in_options, cfg_options, scope, expr)
	int *result;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct expression *expr;
{
	struct data_string left, right;
	struct data_string rrtype, rrname, rrdata;
	unsigned long ttl;
	int srrtype, srrname, srrdata, sttl;
	int bleft, bright;
	int sleft, sright;
	struct binding *binding;
	struct binding_value *bv, *obv;

	switch (expr -> op) {
	      case expr_check:
		*result = check_collection (packet, lease,
					    expr -> data.check);
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: check (%s) returns %s",
			   expr -> data.check -> name,
			   *result ? "true" : "false");
#endif
		return 1;

	      case expr_equal:
	      case expr_not_equal:
		bv = obv = (struct binding_value *)0;
		sleft = evaluate_expression (&bv, packet, lease, client_state,
					     in_options, cfg_options, scope,
					     expr -> data.equal [0], MDL);
		sright = evaluate_expression (&obv, packet, lease,
					      client_state, in_options,
					      cfg_options, scope,
					      expr -> data.equal [1], MDL);
		if (sleft && sright) {
		    if (bv -> type != obv -> type)
			*result = expr -> op == expr_not_equal;
		    else {
			switch (obv -> type) {
			  case binding_boolean:
			    if (bv -> value.boolean == obv -> value.boolean)
				*result = expr -> op == expr_equal;
			    else
				*result = expr -> op == expr_not_equal;
			    break;

			  case binding_data:
			    if ((bv -> value.data.len ==
				 obv -> value.data.len) &&
				!memcmp (bv -> value.data.data,
					 obv -> value.data.data,
					 obv -> value.data.len))
				*result = expr -> op == expr_equal;
			    else
				*result = expr -> op == expr_not_equal;
			    break;

			  case binding_numeric:
			    if (bv -> value.intval == obv -> value.intval)
				*result = expr -> op == expr_equal;
			    else
				*result = expr -> op == expr_not_equal;
			    break;

			  case binding_dns:
#if defined (NSUPDATE)
			    /* XXX This should be a comparison for equal
			       XXX values, not for identity. */
			    if (bv -> value.dns == obv -> value.dns)
				*result = expr -> op == expr_equal;
			    else
				*result = expr -> op == expr_not_equal;
#else
				*result = expr -> op == expr_not_equal;
#endif
			    break;

			  case binding_function:
			    if (bv -> value.fundef == obv -> value.fundef)
				*result = expr -> op == expr_equal;
			    else
				*result = expr -> op == expr_not_equal;
			    break;
			  default:
			    *result = expr -> op == expr_not_equal;
			    break;
			}
		    }
		} else if (!sleft && !sright)
		    *result = expr -> op == expr_equal;
		else
		    *result = expr -> op == expr_not_equal;

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: %sequal = %s",
			   expr -> op == expr_not_equal ? "not" : "",
			   (*result ? "true" : "false"));
#endif
		if (sleft)
			binding_value_dereference (&bv, MDL);
		if (sright)
			binding_value_dereference (&obv, MDL);
		return 1;

	      case expr_and:
		sleft = evaluate_boolean_expression (&bleft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		if (sleft && bleft)
			sright = evaluate_boolean_expression
				(&bright, packet, lease, client_state,
				 in_options, cfg_options,
				 scope, expr -> data.and [1]);
		else
			sright = bright = 0;

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: and (%s, %s) = %s",
		      sleft ? (bleft ? "true" : "false") : "NULL",
		      sright ? (bright ? "true" : "false") : "NULL",
		      ((sleft && sright)
		       ? (bleft && bright ? "true" : "false") : "NULL"));
#endif
		if (sleft && sright) {
			*result = bleft && bright;
			return 1;
		}
		return 0;

	      case expr_or:
		bleft = bright = 0;
		sleft = evaluate_boolean_expression (&bleft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.or [0]);
		if (!sleft || !bleft)
			sright = evaluate_boolean_expression
				(&bright, packet, lease, client_state,
				 in_options, cfg_options,
				 scope, expr -> data.or [1]);
		else
			sright = 0;
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: or (%s, %s) = %s",
		      sleft ? (bleft ? "true" : "false") : "NULL",
		      sright ? (bright ? "true" : "false") : "NULL",
		      ((sleft || sright)
		       ? (bleft || bright ? "true" : "false") : "NULL"));
#endif
		if (sleft || sright) {
			*result = bleft || bright;
			return 1;
		}
		return 0;

	      case expr_not:
		sleft = evaluate_boolean_expression (&bleft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.not);
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: not (%s) = %s",
		      sleft ? (bleft ? "true" : "false") : "NULL",
		      ((sleft && sright)
		       ? (!bleft ? "true" : "false") : "NULL"));

#endif
		if (sleft) {
			*result = !bleft;
			return 1;
		}
		return 0;

	      case expr_exists:
		memset (&left, 0, sizeof left);
		if (!in_options ||
		    !get_option (&left, expr -> data.exists -> universe,
				 packet, lease, client_state,
				 in_options, cfg_options, in_options,
				 scope, expr -> data.exists -> code, MDL))
			*result = 0;
		else {
			*result = 1;
			data_string_forget (&left, MDL);
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: exists %s.%s = %s",
			   expr -> data.option -> universe -> name,
			   expr -> data.option -> name,
			   *result ? "true" : "false");
#endif
		return 1;

	      case expr_known:
		if (!packet) {
#if defined (DEBUG_EXPRESSIONS)
			log_debug ("bool: known = NULL");
#endif
			return 0;
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: known = %s",
			  packet -> known ? "true" : "false");
#endif
		*result = packet -> known;
		return 1;

	      case expr_static:
		if (!lease || !(lease -> flags & STATIC_LEASE)) {
#if defined (DEBUG_EXPRESSIONS)
			log_debug ("bool: static = false (%s %s %s %d)",
				   lease ? "y" : "n",
				   (lease && (lease -> flags & STATIC_LEASE)
				    ? "y" : "n"),
				   piaddr (lease -> ip_addr),
				   lease ? lease -> flags : 0);
#endif
			*result = 0;
			return 1;
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("bool: static = true");
#endif
		*result = 1;
		return 1;

	      case expr_variable_exists:
		if (scope && *scope) {
			binding = find_binding (*scope, expr -> data.variable);

			if (binding) {
				if (binding -> value)
					*result = 1;
				else
					*result = 0;
			} else
				*result = 0;
		} else
			*result = 0;
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("boolean: %s? = %s", expr -> data.variable,
			   *result ? "true" : "false");
#endif
		return 1;

	      case expr_variable_reference:
		if (scope && *scope) {
		    binding = find_binding (*scope, expr -> data.variable);

		    if (binding && binding -> value) {
			if (binding -> value -> type ==
			    binding_boolean) {
				*result = binding -> value -> value.boolean;
				sleft = 1;
			} else {
				log_error ("binding type %d in %s.",
					   binding -> value -> type,
					   "evaluate_boolean_expression");
				sleft = 0;
			}
		    } else
			    sleft = 0;
		} else
			sleft = 0;
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("boolean: %s = %s", expr -> data.variable,
			   sleft ? (*result ? "true" : "false") : "NULL");
#endif
		return sleft;

	      case expr_funcall:
		bv = (struct binding_value *)0;
		sleft = evaluate_expression (&bv, packet, lease, client_state,
					  in_options, cfg_options,
					  scope, expr, MDL);
		if (sleft) {
			if (bv -> type != binding_boolean)
				log_error ("%s() returned type %d in %s.",
					   expr -> data.funcall.name,
					   bv -> type,
					   "evaluate_boolean_expression");
			else
				*result = bv -> value.boolean;
			binding_value_dereference (&bv, MDL);
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("boolean: %s() = %s", expr -> data.funcall.name,
			   sleft ? (*result ? "true" : "false") : "NULL");
#endif
		break;

	      case expr_none:
	      case expr_match:
	      case expr_substring:
	      case expr_suffix:
	      case expr_option:
	      case expr_hardware:
	      case expr_const_data:
	      case expr_packet:
	      case expr_concat:
	      case expr_encapsulate:
	      case expr_host_lookup:
	      case expr_encode_int8:
	      case expr_encode_int16:
	      case expr_encode_int32:
	      case expr_binary_to_ascii:
	      case expr_reverse:
	      case expr_pick_first_value:
	      case expr_host_decl_name:
	      case expr_config_option:
	      case expr_leased_address:
	      case expr_null:
	      case expr_filename:
	      case expr_sname:
		log_error ("Data opcode in evaluate_boolean_expression: %d",
		      expr -> op);
		return 0;

	      case expr_extract_int8:
	      case expr_extract_int16:
	      case expr_extract_int32:
	      case expr_const_int:
	      case expr_lease_time:
	      case expr_dns_transaction:
	      case expr_add:
	      case expr_subtract:
	      case expr_multiply:
	      case expr_divide:
	      case expr_remainder:
	      case expr_binary_and:
	      case expr_binary_or:
	      case expr_binary_xor:
	      case expr_client_state:
		log_error ("Numeric opcode in evaluate_boolean_expression: %d",
		      expr -> op);
		return 0;

	      case expr_ns_add:
	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
		log_error ("dns opcode in evaluate_boolean_expression: %d",
		      expr -> op);
		return 0;

	      case expr_function:
		log_error ("function definition in evaluate_boolean_expr");
		return 0;

	      case expr_arg:
		break;
	}

	log_error ("Bogus opcode in evaluate_boolean_expression: %d",
		   expr -> op);
	return 0;
}

int evaluate_data_expression (result, packet, lease, client_state,
			      in_options, cfg_options, scope, expr, file, line)
	struct data_string *result;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct expression *expr;
	const char *file;
	int line;
{
	struct data_string data, other;
	unsigned long offset, len, i;
	int s0, s1, s2, s3;
	int status;
	struct binding *binding;
	char *s;
	struct binding_value *bv;

	switch (expr -> op) {
		/* Extract N bytes starting at byte M of a data string. */
	      case expr_substring:
		memset (&data, 0, sizeof data);
		s0 = evaluate_data_expression (&data, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.substring.expr,
					       MDL);

		/* Evaluate the offset and length. */
		s1 = evaluate_numeric_expression
			(&offset, packet, lease, client_state, in_options,
			 cfg_options, scope, expr -> data.substring.offset);
		s2 = evaluate_numeric_expression (&len, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.substring.len);

		if (s0 && s1 && s2) {
			/* If the offset is after end of the string,
			   return an empty string.  Otherwise, do the
			   adjustments and return what's left. */
			if (data.len > offset) {
				data_string_copy (result, &data, file, line);
				result -> len -= offset;
				if (result -> len > len) {
					result -> len = len;
					result -> terminated = 0;
				}
				result -> data += offset;
			}
			s3 = 1;
		} else
			s3 = 0;

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: substring (%s, %s, %s) = %s",
		      s0 ? print_hex_1 (data.len, data.data, 30) : "NULL",
		      s1 ? print_dec_1 (offset) : "NULL",
		      s2 ? print_dec_2 (len) : "NULL",
		      (s3 ? print_hex_2 (result -> len, result -> data, 30)
		          : "NULL"));
#endif
		if (s0)
			data_string_forget (&data, MDL);
		if (s3)
			return 1;
		return 0;


		/* Extract the last N bytes of a data string. */
	      case expr_suffix:
		memset (&data, 0, sizeof data);
		s0 = evaluate_data_expression (&data, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.suffix.expr, MDL);
		/* Evaluate the length. */
		s1 = evaluate_numeric_expression (&len, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.suffix.len);
		if (s0 && s1) {
			data_string_copy (result, &data, file, line);

			/* If we are returning the last N bytes of a
			   string whose length is <= N, just return
			   the string - otherwise, compute a new
			   starting address and decrease the
			   length. */
			if (data.len > len) {
				result -> data += data.len - len;
				result -> len = len;
			}
			data_string_forget (&data, MDL);
		}

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: suffix (%s, %s) = %s",
		      s0 ? print_hex_1 (data.len, data.data, 30) : "NULL",
		      s1 ? print_dec_1 (len) : "NULL",
		      ((s0 && s1)
		       ? print_hex_2 (result -> len, result -> data, 30)
		       : "NULL"));
#endif
		return s0 && s1;

		/* Extract an option. */
	      case expr_option:
		if (in_options)
		    s0 = get_option (result,
				     expr -> data.option -> universe,
				     packet, lease, client_state,
				     in_options, cfg_options, in_options,
				     scope, expr -> data.option -> code,
				     file, line);
		else
			s0 = 0;

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: option %s.%s = %s",
		      expr -> data.option -> universe -> name,
		      expr -> data.option -> name,
		      s0 ? print_hex_1 (result -> len, result -> data, 60)
		      : "NULL");
#endif
		return s0;

	      case expr_config_option:
		if (cfg_options)
		    s0 = get_option (result,
				     expr -> data.option -> universe,
				     packet, lease, client_state,
				     in_options, cfg_options, cfg_options,
				     scope, expr -> data.option -> code,
				     file, line);
		else
			s0 = 0;

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: config-option %s.%s = %s",
		      expr -> data.option -> universe -> name,
		      expr -> data.option -> name,
		      s0 ? print_hex_1 (result -> len, result -> data, 60)
		      : "NULL");
#endif
		return s0;

		/* Combine the hardware type and address. */
	      case expr_hardware:
		/* On the client, hardware is our hardware. */
		if (client_state) {
			memset (result, 0, sizeof *result);
			result -> data =
				client_state -> interface -> hw_address.hbuf;
			result -> len =
				client_state -> interface -> hw_address.hlen;
#if defined (DEBUG_EXPRESSIONS)
			log_debug ("data: hardware = %s",
				   print_hex_1 (result -> len,
						result -> data, 60));
#endif
			return 1;
		}

		/* The server cares about the client's hardware address,
		   so only in the case where we are examining a packet can
		   we return anything. */
		if (!packet || !packet -> raw) {
			log_error ("data: hardware: raw packet not available");
			return 0;
		}
		if (packet -> raw -> hlen > sizeof packet -> raw -> chaddr) {
			log_error ("data: hardware: invalid hlen (%d)\n",
				   packet -> raw -> hlen);
			return 0;
		}
		result -> len = packet -> raw -> hlen + 1;
		if (buffer_allocate (&result -> buffer, result -> len,
				     file, line)) {
			result -> data = &result -> buffer -> data [0];
			result -> buffer -> data [0] = packet -> raw -> htype;
			memcpy (&result -> buffer -> data [1],
				packet -> raw -> chaddr,
				packet -> raw -> hlen);
			result -> terminated = 0;
		} else {
			log_error ("data: hardware: no memory for buffer.");
			return 0;
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: hardware = %s",
		      print_hex_1 (result -> len, result -> data, 60));
#endif
		return 1;

		/* Extract part of the raw packet. */
	      case expr_packet:
		if (!packet || !packet -> raw) {
			log_error ("data: packet: raw packet not available");
			return 0;
		}

		s0 = evaluate_numeric_expression (&offset, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.packet.offset);
		s1 = evaluate_numeric_expression (&len,
						  packet, lease, client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.packet.len);
		if (s0 && s1 && offset < packet -> packet_length) {
			if (offset + len > packet -> packet_length)
				result -> len =
					packet -> packet_length - offset;
			else
				result -> len = len;
			if (buffer_allocate (&result -> buffer,
					     result -> len, file, line)) {
				result -> data = &result -> buffer -> data [0];
				memcpy (result -> buffer -> data,
					(((unsigned char *)(packet -> raw))
					 + offset), result -> len);
				result -> terminated = 0;
			} else {
				log_error ("data: packet: no buffer memory.");
				return 0;
			}
			s2 = 1;
		} else
			s2 = 0;
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: packet (%ld, %ld) = %s",
		      offset, len,
		      s2 ? print_hex_1 (result -> len,
					result -> data, 60) : NULL);
#endif
		return s2;

		/* The encapsulation of all defined options in an
		   option space... */
	      case expr_encapsulate:
		if (cfg_options)
			s0 = option_space_encapsulate
				(result, packet, lease, client_state,
				 in_options, cfg_options, scope,
				 &expr -> data.encapsulate);
		else
			s0 = 0;

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: encapsulate (%s) = %s",
			  expr -> data.encapsulate.data,
			  s0 ? print_hex_1 (result -> len,
					    result -> data, 60) : "NULL");
#endif
		return s0;

		/* Some constant data... */
	      case expr_const_data:
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: const = %s",
		      print_hex_1 (expr -> data.const_data.len,
				   expr -> data.const_data.data, 60));
#endif
		data_string_copy (result,
				  &expr -> data.const_data, file, line);
		return 1;

		/* Hostname lookup... */
	      case expr_host_lookup:
		s0 = do_host_lookup (result, expr -> data.host_lookup);
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: DNS lookup (%s) = %s",
		      expr -> data.host_lookup -> hostname,
		      (s0
		       ? print_dotted_quads (result -> len, result -> data)
		       : "NULL"));
#endif
		return s0;

		/* Concatenation... */
	      case expr_concat:
		memset (&data, 0, sizeof data);
		s0 = evaluate_data_expression (&data, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.concat [0], MDL);
		memset (&other, 0, sizeof other);
		s1 = evaluate_data_expression (&other, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.concat [1], MDL);

		if (s0 && s1) {
		    result -> len = data.len + other.len;
		    if (!buffer_allocate (&result -> buffer,
					  (result -> len + other.terminated),
					  file, line)) {
				log_error ("data: concat: no memory");
				result -> len = 0;
				data_string_forget (&data, MDL);
				data_string_forget (&other, MDL);
				return 0;
			}
			result -> data = &result -> buffer -> data [0];
			memcpy (result -> buffer -> data, data.data, data.len);
			memcpy (&result -> buffer -> data [data.len],
				other.data, other.len + other.terminated);
		}

		if (s0)
			data_string_forget (&data, MDL);
		if (s1)
			data_string_forget (&other, MDL);
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: concat (%s, %s) = %s",
		      s0 ? print_hex_1 (data.len, data.data, 20) : "NULL",
		      s1 ? print_hex_2 (other.len, other.data, 20) : "NULL",
		      ((s0 && s1)
		       ? print_hex_3 (result -> len, result -> data, 30)
		       : "NULL"));
#endif
		return s0 && s1;

	      case expr_encode_int8:
		s0 = evaluate_numeric_expression (&len, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.encode_int);
		if (s0) {
			result -> len = 1;
			if (!buffer_allocate (&result -> buffer,
					      1, file, line)) {
				log_error ("data: encode_int8: no memory");
				result -> len = 0;
				s0 = 0;
			} else {
				result -> data = &result -> buffer -> data [0];
				result -> buffer -> data [0] = len;
			}
		} else
			result -> len = 0;

#if defined (DEBUG_EXPRESSIONS)
		if (!s0)
			log_debug ("data: encode_int8 (NULL) = NULL");
		else
			log_debug ("data: encode_int8 (%ld) = %s", len,
				  print_hex_2 (result -> len,
					       result -> data, 20));
#endif
		return s0;
			
		
	      case expr_encode_int16:
		s0 = evaluate_numeric_expression (&len, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.encode_int);
		if (s0) {
			result -> len = 2;
			if (!buffer_allocate (&result -> buffer, 2,
					      file, line)) {
				log_error ("data: encode_int16: no memory");
				result -> len = 0;
				s0 = 0;
			} else {
				result -> data = &result -> buffer -> data [0];
				putUShort (result -> buffer -> data, len);
			}
		} else
			result -> len = 0;

#if defined (DEBUG_EXPRESSIONS)
		if (!s0)
			log_debug ("data: encode_int16 (NULL) = NULL");
		else
			log_debug ("data: encode_int16 (%ld) = %s", len,
				  print_hex_2 (result -> len,
					       result -> data, 20));
#endif
		return s0;

	      case expr_encode_int32:
		s0 = evaluate_numeric_expression (&len, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.encode_int);
		if (s0) {
			result -> len = 4;
			if (!buffer_allocate (&result -> buffer, 4,
					      file, line)) {
				log_error ("data: encode_int32: no memory");
				result -> len = 0;
				s0 = 0;
			} else {
				result -> data = &result -> buffer -> data [0];
				putULong (result -> buffer -> data, len);
			}
		} else
			result -> len = 0;

#if defined (DEBUG_EXPRESSIONS)
		if (!s0)
			log_debug ("data: encode_int32 (NULL) = NULL");
		else
			log_debug ("data: encode_int32 (%ld) = %s", len,
				  print_hex_2 (result -> len,
					       result -> data, 20));
#endif
		return s0;

	      case expr_binary_to_ascii:
		/* Evaluate the base (offset) and width (len): */
		s0 = evaluate_numeric_expression
			(&offset, packet, lease, client_state, in_options,
			 cfg_options, scope, expr -> data.b2a.base);
		s1 = evaluate_numeric_expression (&len, packet, lease,
						  client_state,
						  in_options, cfg_options,
						  scope,
						  expr -> data.b2a.width);

		/* Evaluate the seperator string. */
		memset (&data, 0, sizeof data);
		s2 = evaluate_data_expression (&data, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.b2a.seperator,
					       MDL);

		/* Evaluate the data to be converted. */
		memset (&other, 0, sizeof other);
		s3 = evaluate_data_expression (&other, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.b2a.buffer, MDL);

		if (s0 && s1 && s2 && s3) {
			unsigned buflen, i;

			if (len != 8 && len != 16 && len != 32) {
				log_info ("binary_to_ascii: %s %ld!",
					  "invalid width", len);
				goto b2a_out;
			}
			len /= 8;

			/* The buffer must be a multiple of the number's
			   width. */
			if (other.len % len) {
				log_info ("binary-to-ascii: %s %d %s %ld!",
					  "length of buffer", other.len,
					  "not a multiple of width", len);
				status = 0;
				goto b2a_out;
			}

			/* Count the width of the output. */
			buflen = 0;
			for (i = 0; i < other.len; i += len) {
				if (len == 1) {
					if (offset == 8) {
						if (other.data [i] < 8)
							buflen++;
						else if (other.data [i] < 64)
							buflen += 2;
						else
							buflen += 3;
					} else if (offset == 10) {
						if (other.data [i] < 10)
							buflen++;
						else if (other.data [i] < 100)
							buflen += 2;
						else
							buflen += 3;
					} else if (offset == 16) {
						if (other.data [i] < 16)
							buflen++;
						else
							buflen += 2;
					} else
						buflen += (converted_length
							   (&other.data [i],
							    offset, 1));
				} else
					buflen += (converted_length
						   (&other.data [i],
						    offset, len));
				if (i + len != other.len)
					buflen += data.len;
			}

			if (!buffer_allocate (&result -> buffer,
					      buflen + 1, file, line)) {
				log_error ("data: binary-to-ascii: no memory");
				status = 0;
				goto b2a_out;
			}
			result -> data = &result -> buffer -> data [0];
			result -> len = buflen;
			result -> terminated = 1;

			buflen = 0;
			for (i = 0; i < other.len; i += len) {
				buflen += (binary_to_ascii
					   (&result -> buffer -> data [buflen],
					    &other.data [i], offset, len));
				if (i + len != other.len) {
					memcpy (&result ->
						buffer -> data [buflen],
						data.data, data.len);
					buflen += data.len;
				}
			}
			/* NUL terminate. */
			result -> buffer -> data [buflen] = 0;
			status = 1;
		} else
			status = 0;

	      b2a_out:
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: binary-to-ascii (%s, %s, %s, %s) = %s",
		      s0 ? print_dec_1 (offset) : "NULL",
		      s1 ? print_dec_2 (len) : "NULL",
		      s2 ? print_hex_1 (data.len, data.data, 30) : "NULL",
		      s3 ? print_hex_2 (other.len, other.data, 30) : "NULL",
		      (status ? print_hex_3 (result -> len, result -> data, 30)
		          : "NULL"));
#endif
		if (s2)
			data_string_forget (&data, MDL);
		if (s3)
			data_string_forget (&other, MDL);
		if (status)
			return 1;
		return 0;

	      case expr_reverse:
		/* Evaluate the width (len): */
		s0 = evaluate_numeric_expression
			(&len, packet, lease, client_state, in_options,
			 cfg_options, scope, expr -> data.reverse.width);

		/* Evaluate the data. */
		memset (&data, 0, sizeof data);
		s1 = evaluate_data_expression (&data, packet, lease,
					       client_state,
					       in_options, cfg_options, scope,
					       expr -> data.reverse.buffer,
					       MDL);

		if (s0 && s1) {
			char *upper;
			int i;

			/* The buffer must be a multiple of the number's
			   width. */
			if (data.len % len) {
				log_info ("reverse: %s %d %s %ld!",
					  "length of buffer", data.len,
					  "not a multiple of width", len);
				status = 0;
				goto reverse_out;
			}

			/* XXX reverse in place?   I don't think we can. */
			if (!buffer_allocate (&result -> buffer,
					      data.len, file, line)) {
				log_error ("data: reverse: no memory");
				status = 0;
				goto reverse_out;
			}
			result -> data = &result -> buffer -> data [0];
			result -> len = data.len;
			result -> terminated = 0;

			for (i = 0; i < data.len; i += len) {
				memcpy (&result -> buffer -> data [i],
					&data.data [data.len - i - len], len);
			}
			status = 1;
		} else
			status = 0;

	      reverse_out:
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: reverse (%s, %s) = %s",
		      s0 ? print_dec_1 (len) : "NULL",
		      s1 ? print_hex_1 (data.len, data.data, 30) : "NULL",
		      (status ? print_hex_3 (result -> len, result -> data, 30)
		          : "NULL"));
#endif
		if (s0)
			data_string_forget (&data, MDL);
		if (status)
			return 1;
		return 0;

	      case expr_leased_address:
		if (!lease) {
			log_error ("data: leased_address: not available");
			return 0;
		}
		result -> len = lease -> ip_addr.len;
		if (buffer_allocate (&result -> buffer, result -> len,
				     file, line)) {
			result -> data = &result -> buffer -> data [0];
			memcpy (&result -> buffer -> data [0],
				lease -> ip_addr.iabuf, lease -> ip_addr.len);
			result -> terminated = 0;
		} else {
			log_error ("data: leased-address: no memory.");
			return 0;
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: leased-address = %s",
		      print_hex_1 (result -> len, result -> data, 60));
#endif
		return 1;

	      case expr_pick_first_value:
		memset (&data, 0, sizeof data);
		if ((evaluate_data_expression
		     (result, packet,
		      lease, client_state, in_options, cfg_options,
		      scope, expr -> data.pick_first_value.car, MDL))) {
#if defined (DEBUG_EXPRESSIONS)
			log_debug ("data: pick_first_value (%s, xxx)",
				   print_hex_1 (result -> len,
						result -> data, 40));
#endif
			return 1;
		}

		if (expr -> data.pick_first_value.cdr &&
		    (evaluate_data_expression
		     (result, packet,
		      lease, client_state, in_options, cfg_options,
		      scope, expr -> data.pick_first_value.cdr, MDL))) {
#if defined (DEBUG_EXPRESSIONS)
			log_debug ("data: pick_first_value (NULL, %s)",
				   print_hex_1 (result -> len,
						result -> data, 40));
#endif
			return 1;
		}

#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: pick_first_value (NULL, NULL) = NULL");
#endif
		return 0;

	      case expr_host_decl_name:
		if (!lease || !lease -> host) {
			log_error ("data: host_decl_name: not available");
			return 0;
		}
		result -> len = strlen (lease -> host -> name);
		if (buffer_allocate (&result -> buffer,
				     result -> len + 1, file, line)) {
			result -> data = &result -> buffer -> data [0];
			strcpy ((char *)&result -> buffer -> data [0],
				lease -> host -> name);
			result -> terminated = 1;
		} else {
			log_error ("data: host-decl-name: no memory.");
			return 0;
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: host-decl-name = %s", lease -> host -> name);
#endif
		return 1;

	      case expr_null:
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: null = NULL");
#endif
		return 0;

	      case expr_variable_reference:
		if (scope && *scope) {
		    binding = find_binding (*scope, expr -> data.variable);

		    if (binding && binding -> value) {
			if (binding -> value -> type == binding_data) {
			    data_string_copy (result,
					      &binding -> value -> value.data,
					      file, line);
			    s0 = 1;
			} else if (binding -> value -> type != binding_data) {
			    log_error ("binding type %d in %s.",
				       binding -> value -> type,
				       "evaluate_data_expression");
			    s0 = 0;
			} else
			    s0 = 0;
		    } else
			s0 = 0;
		} else
		    s0 = 0;
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: %s = %s", expr -> data.variable,
			   s0 ? print_hex_1 (result -> len,
					     result -> data, 50) : "NULL");
#endif
		return s0;

	      case expr_funcall:
		bv = (struct binding_value *)0;
		s0 = evaluate_expression (&bv, packet, lease, client_state,
					  in_options, cfg_options,
					  scope, expr, MDL);
		if (s0) {
			if (bv -> type != binding_data)
				log_error ("%s() returned type %d in %s.",
					   expr -> data.funcall.name,
					   bv -> type,
					   "evaluate_data_expression");
			else
				data_string_copy (result, &bv -> value.data,
						  file, line);
			binding_value_dereference (&bv, MDL);
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: %s = %s", expr -> data.funcall.name,
			   s0 ? print_hex_1 (result -> len,
					     result -> data, 50) : "NULL");
#endif
		break;

		/* Extract the filename. */
	      case expr_filename:
		if (packet && packet -> raw -> file [0]) {
			char *fn =
				memchr (packet -> raw -> file, 0,
					sizeof packet -> raw -> file);
			if (!fn)
				fn = ((char *)packet -> raw -> file +
				      sizeof packet -> raw -> file);
			result -> len = fn - &(packet -> raw -> file [0]);
			if (buffer_allocate (&result -> buffer,
					     result -> len + 1, file, line)) {
				result -> data = &result -> buffer -> data [0];
				memcpy (&result -> buffer -> data [0],
					packet -> raw -> file,
					result -> len);
				result -> buffer -> data [result -> len] = 0;
				result -> terminated = 1;
				s0 = 1;
			} else {
				log_error ("data: filename: no memory.");
				s0 = 0;
			}
		} else
			s0 = 0;

#if defined (DEBUG_EXPRESSIONS)
		log_info ("data: filename = \"%s\"",
			  s0 ? (const char *)(result -> data) : "NULL");
#endif
		return s0;

		/* Extract the server name. */
	      case expr_sname:
		if (packet && packet -> raw -> sname [0]) {
			char *fn =
				memchr (packet -> raw -> sname, 0,
					sizeof packet -> raw -> sname);
			if (!fn)
				fn = ((char *)packet -> raw -> sname + 
				      sizeof packet -> raw -> sname);
			result -> len = fn - &packet -> raw -> sname [0];
			if (buffer_allocate (&result -> buffer,
					     result -> len + 1, file, line)) {
				result -> data = &result -> buffer -> data [0];
				memcpy (&result -> buffer -> data [0],
					packet -> raw -> sname,
					result -> len);
				result -> buffer -> data [result -> len] = 0;
				result -> terminated = 1;
				s0 = 1;
			} else {
				log_error ("data: sname: no memory.");
				s0 = 0;
			}
		} else
			s0 = 0;

#if defined (DEBUG_EXPRESSIONS)
		log_info ("data: sname = \"%s\"",
			  s0 ? (const char *)(result -> data) : "NULL");
#endif
		return s0;

	      case expr_check:
	      case expr_equal:
	      case expr_not_equal:
	      case expr_and:
	      case expr_or:
	      case expr_not:
	      case expr_match:
	      case expr_static:
	      case expr_known:
	      case expr_none:
	      case expr_exists:
	      case expr_variable_exists:
		log_error ("Boolean opcode in evaluate_data_expression: %d",
		      expr -> op);
		return 0;

	      case expr_extract_int8:
	      case expr_extract_int16:
	      case expr_extract_int32:
	      case expr_const_int:
	      case expr_lease_time:
	      case expr_dns_transaction:
	      case expr_add:
	      case expr_subtract:
	      case expr_multiply:
	      case expr_divide:
	      case expr_remainder:
	      case expr_binary_and:
	      case expr_binary_or:
	      case expr_binary_xor:
	      case expr_client_state:
		log_error ("Numeric opcode in evaluate_data_expression: %d",
		      expr -> op);
		return 0;

	      case expr_ns_add:
	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
		log_error ("dns update opcode in evaluate_data_expression: %d",
		      expr -> op);
		return 0;

	      case expr_function:
		log_error ("function definition in evaluate_data_expression");
		return 0;

	      case expr_arg:
		break;
	}

	log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op);
	return 0;
}	

int evaluate_numeric_expression (result, packet, lease, client_state,
				 in_options, cfg_options, scope, expr)
	unsigned long *result;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct expression *expr;
{
	struct data_string data;
	int status, sleft, sright;
#if defined (NSUPDATE)
	ns_updrec *nut;
	ns_updque uq;
#endif
	struct expression *cur, *next;
	struct binding *binding;
	struct binding_value *bv;
	unsigned long ileft, iright;

	switch (expr -> op) {
	      case expr_check:
	      case expr_equal:
	      case expr_not_equal:
	      case expr_and:
	      case expr_or:
	      case expr_not:
	      case expr_match:
	      case expr_static:
	      case expr_known:
	      case expr_none:
	      case expr_exists:
	      case expr_variable_exists:
		log_error ("Boolean opcode in evaluate_numeric_expression: %d",
		      expr -> op);
		return 0;

	      case expr_substring:
	      case expr_suffix:
	      case expr_option:
	      case expr_hardware:
	      case expr_const_data:
	      case expr_packet:
	      case expr_concat:
	      case expr_encapsulate:
	      case expr_host_lookup:
	      case expr_encode_int8:
	      case expr_encode_int16:
	      case expr_encode_int32:
	      case expr_binary_to_ascii:
	      case expr_reverse:
	      case expr_filename:
	      case expr_sname:
	      case expr_pick_first_value:
	      case expr_host_decl_name:
	      case expr_config_option:
	      case expr_leased_address:
	      case expr_null:
		log_error ("Data opcode in evaluate_numeric_expression: %d",
		      expr -> op);
		return 0;

	      case expr_extract_int8:
		memset (&data, 0, sizeof data);
		status = evaluate_data_expression
			(&data, packet, lease, client_state, in_options,
			 cfg_options, scope, expr -> data.extract_int, MDL);
		if (status)
			*result = data.data [0];
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("num: extract_int8 (%s) = %s",
		      status ? print_hex_1 (data.len, data.data, 60) : "NULL",
		      status ? print_dec_1 (*result) : "NULL" );
#endif
		if (status) data_string_forget (&data, MDL);
		return status;

	      case expr_extract_int16:
		memset (&data, 0, sizeof data);
		status = (evaluate_data_expression
			  (&data, packet, lease, client_state, in_options,
			   cfg_options, scope, expr -> data.extract_int, MDL));
		if (status && data.len >= 2)
			*result = getUShort (data.data);
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("num: extract_int16 (%s) = %ld",
		      ((status && data.len >= 2) ?
		       print_hex_1 (data.len, data.data, 60) : "NULL"),
		      *result);
#endif
		if (status) data_string_forget (&data, MDL);
		return (status && data.len >= 2);

	      case expr_extract_int32:
		memset (&data, 0, sizeof data);
		status = (evaluate_data_expression
			  (&data, packet, lease, client_state, in_options,
			   cfg_options, scope, expr -> data.extract_int, MDL));
		if (status && data.len >= 4)
			*result = getULong (data.data);
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("num: extract_int32 (%s) = %ld",
		      ((status && data.len >= 4) ?
		       print_hex_1 (data.len, data.data, 60) : "NULL"),
		      *result);
#endif
		if (status) data_string_forget (&data, MDL);
		return (status && data.len >= 4);

	      case expr_const_int:
		*result = expr -> data.const_int;
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("number: CONSTANT = %ld", *result);
#endif
		return 1;

	      case expr_lease_time:
		if (!lease) {
			log_error ("data: leased_lease: not available");
			return 0;
		}
		if (lease -> ends < cur_time) {
			log_error ("%s %lu when it is now %lu",
				   "data: lease_time: lease ends at",
				   (long)(lease -> ends), (long)cur_time);
			return 0;
		}
		*result = lease -> ends - cur_time;
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("number: lease-time = (%lu - %lu) = %ld",
			   lease -> ends,
			   cur_time, *result);
#endif
		return 1;
 
	      case expr_dns_transaction:
#if !defined (NSUPDATE)
		return 0;
#else
		if (!resolver_inited) {
			minires_ninit (&resolver_state);
			resolver_inited = 1;
			resolver_state.retrans = 1;
			resolver_state.retry = 1;
		}
		ISC_LIST_INIT (uq);
		cur = expr;
		do {
		    next = cur -> data.dns_transaction.cdr;
		    nut = 0;
		    status = (evaluate_dns_expression
			      (&nut, packet,
			       lease, client_state, in_options, cfg_options,
			       scope, cur -> data.dns_transaction.car));
		    if (!status)
			    goto dns_bad;
		    ISC_LIST_APPEND (uq, nut, r_link);
		    cur = next;
		} while (next);

		/* Do the update and record the error code, if there was
		   an error; otherwise set it to NOERROR. */
		*result = minires_nupdate (&resolver_state,
					   ISC_LIST_HEAD (uq));
		status = 1;

		print_dns_status ((int)*result, &uq);

	      dns_bad:
		while (!ISC_LIST_EMPTY (uq)) {
			ns_updrec *tmp = ISC_LIST_HEAD (uq);
			ISC_LIST_UNLINK (uq, tmp, r_link);
			if (tmp -> r_data_ephem) {
				dfree (tmp -> r_data_ephem, MDL);
				tmp -> r_data = (unsigned char *)0;
				tmp -> r_data_ephem = (unsigned char *)0;
			}
			minires_freeupdrec (tmp);
		}
		return status;
#endif /* NSUPDATE */

	      case expr_variable_reference:
		if (scope && *scope) {
		    binding = find_binding (*scope, expr -> data.variable);

		    if (binding && binding -> value) {
			if (binding -> value -> type == binding_numeric) {
				*result = binding -> value -> value.intval;
			    status = 1;
			} else {
				log_error ("binding type %d in %s.",
					   binding -> value -> type,
					   "evaluate_numeric_expression");
				status = 0;
			}
		    } else
			status = 0;
		} else
		    status = 0;
#if defined (DEBUG_EXPRESSIONS)
		if (status)
			log_debug ("numeric: %s = %ld",
				   expr -> data.variable, *result);
		else
			log_debug ("numeric: %s = NULL",
				   expr -> data.variable);
#endif
		return status;

	      case expr_funcall:
		bv = (struct binding_value *)0;
		status = evaluate_expression (&bv, packet, lease,
					      client_state,
					      in_options, cfg_options,
					      scope, expr, MDL);
		if (status) {
			if (bv -> type != binding_numeric)
				log_error ("%s() returned type %d in %s.",
					   expr -> data.funcall.name,
					   bv -> type,
					   "evaluate_numeric_expression");
			else
				*result = bv -> value.intval;
			binding_value_dereference (&bv, MDL);
		}
#if defined (DEBUG_EXPRESSIONS)
		log_debug ("data: %s = %ld", expr -> data.funcall.name,
			   status ? *result : 0);
#endif
		break;

	      case expr_add:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright)
			log_debug ("num: %ld + %ld = %ld",
				   ileft, iright, ileft + iright);
		else if (sleft)
			log_debug ("num: %ld + NULL = NULL", ileft);
		else
			log_debug ("num: NULL + %ld = NULL", iright);
#endif
		if (sleft && sright) {
			*result = ileft + iright;
			return 1;
		}
		return 0;

	      case expr_subtract:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright)
			log_debug ("num: %ld - %ld = %ld",
				   ileft, iright, ileft - iright);
		else if (sleft)
			log_debug ("num: %ld - NULL = NULL", ileft);
		else
			log_debug ("num: NULL - %ld = NULL", iright);
#endif
		if (sleft && sright) {
			*result = ileft - iright;
			return 1;
		}
		return 0;

	      case expr_multiply:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright)
			log_debug ("num: %ld * %ld = %ld",
				   ileft, iright, ileft * iright);
		else if (sleft)
			log_debug ("num: %ld * NULL = NULL", ileft);
		else
			log_debug ("num: NULL * %ld = NULL", iright);
#endif
		if (sleft && sright) {
			*result = ileft * iright;
			return 1;
		}
		return 0;

	      case expr_divide:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright) {
			if (iright != 0)
				log_debug ("num: %ld / %ld = %ld",
					   ileft, iright, ileft / iright);
			else
				log_debug ("num: %ld / %ld = NULL",
					   ileft, iright);
		} else if (sleft)
			log_debug ("num: %ld / NULL = NULL", ileft);
		else
			log_debug ("num: NULL / %ld = NULL", iright);
#endif
		if (sleft && sright && iright) {
			*result = ileft / iright;
			return 1;
		}
		return 0;

	      case expr_remainder:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright) {
			if (iright != 0)
				log_debug ("num: %ld %% %ld = %ld",
					   ileft, iright, ileft % iright);
			else
				log_debug ("num: %ld %% %ld = NULL",
					   ileft, iright);
		} else if (sleft)
			log_debug ("num: %ld %% NULL = NULL", ileft);
		else
			log_debug ("num: NULL %% %ld = NULL", iright);
#endif
		if (sleft && sright && iright) {
			*result = ileft % iright;
			return 1;
		}
		return 0;

	      case expr_binary_and:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright)
			log_debug ("num: %ld | %ld = %ld",
				   ileft, iright, ileft & iright);
		else if (sleft)
			log_debug ("num: %ld & NULL = NULL", ileft);
		else
			log_debug ("num: NULL & %ld = NULL", iright);
#endif
		if (sleft && sright) {
			*result = ileft & iright;
			return 1;
		}
		return 0;

	      case expr_binary_or:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright)
			log_debug ("num: %ld | %ld = %ld",
				   ileft, iright, ileft | iright);
		else if (sleft)
			log_debug ("num: %ld | NULL = NULL", ileft);
		else
			log_debug ("num: NULL | %ld = NULL", iright);
#endif
		if (sleft && sright) {
			*result = ileft | iright;
			return 1;
		}
		return 0;

	      case expr_binary_xor:
		sleft = evaluate_numeric_expression (&ileft, packet, lease,
						     client_state,
						     in_options, cfg_options,
						     scope,
						     expr -> data.and [0]);
		sright = evaluate_numeric_expression (&iright, packet, lease,
						      client_state,
						      in_options, cfg_options,
						      scope,
						      expr -> data.and [1]);

#if defined (DEBUG_EXPRESSIONS)
		if (sleft && sright)
			log_debug ("num: %ld ^ %ld = %ld",
				   ileft, iright, ileft ^ iright);
		else if (sleft)
			log_debug ("num: %ld ^ NULL = NULL", ileft);
		else
			log_debug ("num: NULL ^ %ld = NULL", iright);
#endif
		if (sleft && sright) {
			*result = ileft ^ iright;
			return 1;
		}
		return 0;

	      case expr_client_state:
		if (client_state) {
#if defined (DEBUG_EXPRESSIONS)
			log_debug ("num: client-state = %d",
				   client_state -> state);
#endif
			*result = client_state -> state;
			return 1;
		} else {
#if defined (DEBUG_EXPRESSIONS)
			log_debug ("num: client-state = NULL");
#endif
			return 0;
		}

	      case expr_ns_add:
	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
		log_error ("dns opcode in evaluate_numeric_expression: %d",
		      expr -> op);
		return 0;

	      case expr_function:
		log_error ("function definition in evaluate_numeric_expr");
		return 0;

	      case expr_arg:
		break;
	}

	log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op);
	return 0;
}

/* Return data hanging off of an option cache structure, or if there
   isn't any, evaluate the expression hanging off of it and return the
   result of that evaluation.   There should never be both an expression
   and a valid data_string. */

int evaluate_option_cache (result, packet, lease, client_state,
			   in_options, cfg_options, scope, oc, file, line)
	struct data_string *result;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct option_cache *oc;
	const char *file;
	int line;
{
	if (oc -> data.len) {
		data_string_copy (result, &oc -> data, file, line);
		return 1;
	}
	if (!oc -> expression)
		return 0;
	return evaluate_data_expression (result, packet, lease, client_state,
					 in_options, cfg_options, scope,
					 oc -> expression, file, line);
}

/* Evaluate an option cache and extract a boolean from the result,
   returning the boolean.   Return false if there is no data. */

int evaluate_boolean_option_cache (ignorep, packet,
				   lease, client_state, in_options,
				   cfg_options, scope, oc, file, line)
	int *ignorep;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct option_cache *oc;
	const char *file;
	int line;
{
	struct data_string ds;
	int result;

	/* So that we can be called with option_lookup as an argument. */
	if (!oc || !in_options)
		return 0;
	
	memset (&ds, 0, sizeof ds);
	if (!evaluate_option_cache (&ds, packet,
				    lease, client_state, in_options,
				    cfg_options, scope, oc, file, line))
		return 0;

	if (ds.len) {
		result = ds.data [0];
		if (result == 2) {
			result = 0;
			*ignorep = 1;
		} else
			*ignorep = 0;
	} else
		result = 0;
	data_string_forget (&ds, MDL);
	return result;
}
		

/* Evaluate a boolean expression and return the result of the evaluation,
   or FALSE if it failed. */

int evaluate_boolean_expression_result (ignorep, packet, lease, client_state,
					in_options, cfg_options, scope, expr)
	int *ignorep;
	struct packet *packet;
	struct lease *lease;
	struct client_state *client_state;
	struct option_state *in_options;
	struct option_state *cfg_options;
	struct binding_scope **scope;
	struct expression *expr;
{
	int result;

	/* So that we can be called with option_lookup as an argument. */
	if (!expr)
		return 0;
	
	if (!evaluate_boolean_expression (&result, packet, lease, client_state,
					  in_options, cfg_options,
					  scope, expr))
		return 0;

	if (result == 2) {
		*ignorep = 1;
		result = 0;
	} else
		*ignorep = 0;
	return result;
}
		

/* Dereference an expression node, and if the reference count goes to zero,
   dereference any data it refers to, and then free it. */
void expression_dereference (eptr, file, line)
	struct expression **eptr;
	const char *file;
	int line;
{
	struct expression *expr = *eptr;

	/* Zero the pointer. */
	*eptr = (struct expression *)0;

	/* Decrement the reference count.   If it's nonzero, we're
	   done. */
	--(expr -> refcnt);
	rc_register (file, line, eptr, expr, expr -> refcnt, 1);
	if (expr -> refcnt > 0)
		return;
	if (expr -> refcnt < 0) {
		log_error ("%s(%d): negative refcnt!", file, line);
#if defined (DEBUG_RC_HISTORY)
		dump_rc_history (expr);
#endif
#if defined (POINTER_DEBUG)
		abort ();
#else
		return;
#endif
	}

	/* Dereference subexpressions. */
	switch (expr -> op) {
		/* All the binary operators can be handled the same way. */
	      case expr_equal:
	      case expr_not_equal:
	      case expr_concat:
	      case expr_and:
	      case expr_or:
	      case expr_add:
	      case expr_subtract:
	      case expr_multiply:
	      case expr_divide:
	      case expr_remainder:
	      case expr_binary_and:
	      case expr_binary_or:
	      case expr_binary_xor:
	      case expr_client_state:
		if (expr -> data.equal [0])
			expression_dereference (&expr -> data.equal [0],
						file, line);
		if (expr -> data.equal [1])
			expression_dereference (&expr -> data.equal [1],
						file, line);
		break;

	      case expr_substring:
		if (expr -> data.substring.expr)
			expression_dereference (&expr -> data.substring.expr,
						file, line);
		if (expr -> data.substring.offset)
			expression_dereference (&expr -> data.substring.offset,
						file, line);
		if (expr -> data.substring.len)
			expression_dereference (&expr -> data.substring.len,
						file, line);
		break;

	      case expr_suffix:
		if (expr -> data.suffix.expr)
			expression_dereference (&expr -> data.suffix.expr,
						file, line);
		if (expr -> data.suffix.len)
			expression_dereference (&expr -> data.suffix.len,
						file, line);
		break;

	      case expr_not:
		if (expr -> data.not)
			expression_dereference (&expr -> data.not, file, line);
		break;

	      case expr_packet:
		if (expr -> data.packet.offset)
			expression_dereference (&expr -> data.packet.offset,
						file, line);
		if (expr -> data.packet.len)
			expression_dereference (&expr -> data.packet.len,
						file, line);
		break;

	      case expr_extract_int8:
	      case expr_extract_int16:
	      case expr_extract_int32:
		if (expr -> data.extract_int)
			expression_dereference (&expr -> data.extract_int,
						file, line);
		break;

	      case expr_encode_int8:
	      case expr_encode_int16:
	      case expr_encode_int32:
		if (expr -> data.encode_int)
			expression_dereference (&expr -> data.encode_int,
						file, line);
		break;

	      case expr_encapsulate:
	      case expr_const_data:
		data_string_forget (&expr -> data.const_data, file, line);
		break;

	      case expr_host_lookup:
		if (expr -> data.host_lookup)
			dns_host_entry_dereference (&expr -> data.host_lookup,
						    file, line);
		break;

	      case expr_binary_to_ascii:
		if (expr -> data.b2a.base)
			expression_dereference (&expr -> data.b2a.base,
						file, line);
		if (expr -> data.b2a.width)
			expression_dereference (&expr -> data.b2a.width,
						file, line);
		if (expr -> data.b2a.seperator)
			expression_dereference (&expr -> data.b2a.seperator,
						file, line);
		if (expr -> data.b2a.buffer)
			expression_dereference (&expr -> data.b2a.buffer,
						file, line);
		break;

	      case expr_pick_first_value:
		if (expr -> data.pick_first_value.car)
		    expression_dereference (&expr -> data.pick_first_value.car,
					    file, line);
		if (expr -> data.pick_first_value.cdr)
		    expression_dereference (&expr -> data.pick_first_value.cdr,
					    file, line);
		break;

	      case expr_reverse:
		if (expr -> data.reverse.width)
			expression_dereference (&expr -> data.reverse.width,
						file, line);
		if (expr -> data.reverse.buffer)
			expression_dereference
				(&expr -> data.reverse.buffer, file, line);
		break;

	      case expr_dns_transaction:
		if (expr -> data.dns_transaction.car)
		    expression_dereference (&expr -> data.dns_transaction.car,
					    file, line);
		if (expr -> data.dns_transaction.cdr)
		    expression_dereference (&expr -> data.dns_transaction.cdr,
					    file, line);
		break;

	      case expr_ns_add:
		if (expr -> data.ns_add.rrname)
		    expression_dereference (&expr -> data.ns_add.rrname,
					    file, line);
		if (expr -> data.ns_add.rrdata)
		    expression_dereference (&expr -> data.ns_add.rrdata,
					    file, line);
		if (expr -> data.ns_add.ttl)
		    expression_dereference (&expr -> data.ns_add.ttl,
					    file, line);
		break;

	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
		if (expr -> data.ns_delete.rrname)
		    expression_dereference (&expr -> data.ns_delete.rrname,
					    file, line);
		if (expr -> data.ns_delete.rrdata)
		    expression_dereference (&expr -> data.ns_delete.rrdata,
					    file, line);
		break;

	      case expr_variable_reference:
	      case expr_variable_exists:
		if (expr -> data.variable)
			dfree (expr -> data.variable, file, line);
		break;

	      case expr_funcall:
		if (expr -> data.funcall.name)
			dfree (expr -> data.funcall.name, file, line);
		if (expr -> data.funcall.arglist)
			expression_dereference (&expr -> data.funcall.arglist,
						file, line);
		break;

	      case expr_arg:
		if (expr -> data.arg.val)
			expression_dereference (&expr -> data.arg.val,
						file, line);
		if (expr -> data.arg.next)
			expression_dereference (&expr -> data.arg.next,
						file, line);
		break;

	      case expr_function:
		fundef_dereference (&expr -> data.func, file, line);
		break;

		/* No subexpressions. */
	      case expr_leased_address:
	      case expr_lease_time:
	      case expr_filename:
	      case expr_sname:
	      case expr_const_int:
	      case expr_check:
	      case expr_option:
	      case expr_hardware:
	      case expr_exists:
	      case expr_known:
	      case expr_null:
		break;

	      default:
		break;
	}
	free_expression (expr, MDL);
}

int is_dns_expression (expr)
	struct expression *expr;
{
      return (expr -> op == expr_ns_add ||
	      expr -> op == expr_ns_delete ||
	      expr -> op == expr_ns_exists ||
	      expr -> op == expr_ns_not_exists);
}

int is_boolean_expression (expr)
	struct expression *expr;
{
	return (expr -> op == expr_check ||
		expr -> op == expr_exists ||
		expr -> op == expr_variable_exists ||
		expr -> op == expr_equal ||
		expr -> op == expr_not_equal ||
		expr -> op == expr_and ||
		expr -> op == expr_or ||
		expr -> op == expr_not ||
		expr -> op == expr_known ||
		expr -> op == expr_static);
}

int is_data_expression (expr)
	struct expression *expr;
{
	return (expr -> op == expr_substring ||
		expr -> op == expr_suffix ||
		expr -> op == expr_option ||
		expr -> op == expr_hardware ||
		expr -> op == expr_const_data ||
		expr -> op == expr_packet ||
		expr -> op == expr_concat ||
		expr -> op == expr_encapsulate ||
		expr -> op == expr_encode_int8 ||
		expr -> op == expr_encode_int16 ||
		expr -> op == expr_encode_int32 ||
		expr -> op == expr_host_lookup ||
		expr -> op == expr_binary_to_ascii ||
		expr -> op == expr_filename ||
		expr -> op == expr_sname ||
		expr -> op == expr_reverse ||
		expr -> op == expr_pick_first_value ||
		expr -> op == expr_host_decl_name ||
		expr -> op == expr_leased_address ||
		expr -> op == expr_config_option ||
		expr -> op == expr_null);
}

int is_numeric_expression (expr)
	struct expression *expr;
{
	return (expr -> op == expr_extract_int8 ||
		expr -> op == expr_extract_int16 ||
		expr -> op == expr_extract_int32 ||
		expr -> op == expr_const_int ||
		expr -> op == expr_lease_time ||
		expr -> op == expr_dns_transaction ||
		expr -> op == expr_add ||
		expr -> op == expr_subtract ||
		expr -> op == expr_multiply ||
		expr -> op == expr_divide ||
		expr -> op == expr_remainder ||
		expr -> op == expr_binary_and ||
		expr -> op == expr_binary_or ||
		expr -> op == expr_binary_xor ||
		expr -> op == expr_client_state);
}

int is_compound_expression (expr)
	struct expression *expr;
{
	return (expr -> op == expr_ns_add ||
		expr -> op == expr_ns_delete ||
		expr -> op == expr_ns_exists ||
		expr -> op == expr_ns_not_exists ||
		expr -> op == expr_substring ||
		expr -> op == expr_suffix ||
		expr -> op == expr_option ||
		expr -> op == expr_concat ||
		expr -> op == expr_encode_int8 ||
		expr -> op == expr_encode_int16 ||
		expr -> op == expr_encode_int32 ||
		expr -> op == expr_binary_to_ascii ||
		expr -> op == expr_reverse ||
		expr -> op == expr_pick_first_value ||
		expr -> op == expr_config_option ||
		expr -> op == expr_extract_int8 ||
		expr -> op == expr_extract_int16 ||
		expr -> op == expr_extract_int32 ||
		expr -> op == expr_dns_transaction);
}

static int op_val PROTO ((enum expr_op));

static int op_val (op)
	enum expr_op op;
{
	switch (op) {
	      case expr_none:
	      case expr_match:
	      case expr_static:
	      case expr_check:
	      case expr_substring:
	      case expr_suffix:
	      case expr_concat:
	      case expr_encapsulate:
	      case expr_host_lookup:
	      case expr_not:
	      case expr_option:
	      case expr_hardware:
	      case expr_packet:
	      case expr_const_data:
	      case expr_extract_int8:
	      case expr_extract_int16:
	      case expr_extract_int32:
	      case expr_encode_int8:
	      case expr_encode_int16:
	      case expr_encode_int32:
	      case expr_const_int:
	      case expr_exists:
	      case expr_variable_exists:
	      case expr_known:
	      case expr_binary_to_ascii:
	      case expr_reverse:
	      case expr_filename:
	      case expr_sname:
	      case expr_pick_first_value:
	      case expr_host_decl_name:
	      case expr_config_option:
	      case expr_leased_address:
	      case expr_lease_time:
	      case expr_dns_transaction:
	      case expr_null:
	      case expr_variable_reference:
	      case expr_ns_add:
	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
	      case expr_arg:
	      case expr_funcall:
	      case expr_function:
		/* XXXDPN: Need to assign sane precedences to these. */
	      case expr_binary_and:
	      case expr_binary_or:
	      case expr_binary_xor:
	      case expr_client_state:
		return 100;

	      case expr_equal:
	      case expr_not_equal:
		return 3;

	      case expr_and:
	      case expr_multiply:
	      case expr_divide:
	      case expr_remainder:
		return 1;

	      case expr_or:
	      case expr_add:
	      case expr_subtract:
		return 2;
	}
	return 100;
}

int op_precedence (op1, op2)
	enum expr_op op1, op2;
{
	int ov1, ov2;

	return op_val (op1) - op_val (op2);
}

enum expression_context expression_context (struct expression *expr)
{
	if (is_data_expression (expr))
		return context_data;
	if (is_numeric_expression (expr))
		return context_numeric;
	if (is_boolean_expression (expr))
		return context_boolean;
	if (is_dns_expression (expr))
		return context_dns;
	return context_any;
}

enum expression_context op_context (op)
	enum expr_op op;
{
	switch (op) {
/* XXX Why aren't these specific? */
	      case expr_none:
	      case expr_match:
	      case expr_static:
	      case expr_check:
	      case expr_substring:
	      case expr_suffix:
	      case expr_concat:
	      case expr_encapsulate:
	      case expr_host_lookup:
	      case expr_not:
	      case expr_option:
	      case expr_hardware:
	      case expr_packet:
	      case expr_const_data:
	      case expr_extract_int8:
	      case expr_extract_int16:
	      case expr_extract_int32:
	      case expr_encode_int8:
	      case expr_encode_int16:
	      case expr_encode_int32:
	      case expr_const_int:
	      case expr_exists:
	      case expr_variable_exists:
	      case expr_known:
	      case expr_binary_to_ascii:
	      case expr_reverse:
	      case expr_filename:
	      case expr_sname:
	      case expr_pick_first_value:
	      case expr_host_decl_name:
	      case expr_config_option:
	      case expr_leased_address:
	      case expr_lease_time:
	      case expr_null:
	      case expr_variable_reference:
	      case expr_ns_add:
	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
	      case expr_dns_transaction:
	      case expr_arg:
	      case expr_funcall:
	      case expr_function:
		return context_any;

	      case expr_equal:
	      case expr_not_equal:
		return context_data;

	      case expr_and:
		return context_boolean;

	      case expr_or:
		return context_boolean;

	      case expr_add:
	      case expr_subtract:
	      case expr_multiply:
	      case expr_divide:
	      case expr_remainder:
	      case expr_binary_and:
	      case expr_binary_or:
	      case expr_binary_xor:
	      case expr_client_state:
		return context_numeric;
	}
	return context_any;
}

int write_expression (file, expr, col, indent, firstp)
	FILE *file;
	struct expression *expr;
	int col;
	int indent;
	int firstp;
{
	struct expression *e;
	const char *s;
	char obuf [65];
	int scol;
	int width;

	/* If this promises to be a fat expression, start a new line. */
	if (!firstp && is_compound_expression (expr)) {
		indent_spaces (file, indent);
		col = indent;
	}

	switch (expr -> op) {
	      case expr_none:
		col = token_print_indent (file, col, indent, "", "", "null");
		break;
		
	      case expr_check:
		col = token_print_indent (file, col, indent, "", "", "check");
		col = token_print_indent_concat (file, col, indent,
						 " ", "", "\"",
						 expr -> data.check -> name,
						 "\"", (char *)0);
		break;

	      case expr_not_equal:
		s = "!=";
		goto binary;

	      case expr_equal:
		s = "=";
	      binary:
		col = write_expression (file, expr -> data.equal [0],
					col, indent, 1);
		col = token_print_indent (file, col, indent, " ", " ", s);
		col = write_expression (file, expr -> data.equal [1],
					col, indent + 2, 0);
		break;

	      case expr_substring:
		col = token_print_indent (file, col, indent, "", "",
					  "substring");
		col = token_print_indent (file, col, indent, " ", "", "(");
		scol = col;
		col = write_expression (file, expr -> data.substring.expr,
					col, scol, 1);
		col = token_print_indent (file, col, indent, "", " ", ",");
		col = write_expression (file, expr -> data.substring.offset,
					col, indent, 0);
		col = token_print_indent (file, col, scol, "", " ", ",");
		col = write_expression (file, expr -> data.substring.len,
					col, scol, 0);
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      case expr_suffix:
		col = token_print_indent (file, col, indent, "", "", "suffix");
		col = token_print_indent (file, col, indent, " ", "", "(");
		scol = col;
		col = write_expression (file, expr -> data.suffix.expr,
					col, scol, 1);
		col = token_print_indent (file, col, scol, "", " ", ",");
		col = write_expression (file, expr -> data.suffix.len,
					col, scol, 0);
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      case expr_concat:
		e = expr;
		col = token_print_indent (file, col, indent, "", "",
					  "concat");
		col = token_print_indent (file, col, indent, " ", "", "(");
		scol = col;
		firstp = 1;
	      concat_again:
		col = write_expression (file, e -> data.concat [0],
					col, scol, firstp);
		firstp = 0;
		if (!e -> data.concat [1])
			goto no_concat_cdr;
		col = token_print_indent (file, col, scol, "", " ", ",");
		if (e -> data.concat [1] -> op == expr_concat) {
			e = e -> data.concat [1];
			goto concat_again;
		}
		col = write_expression (file, e -> data.concat [1],
					col, scol, 0);
	      no_concat_cdr:
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      case expr_host_lookup:
		col = token_print_indent (file, col, indent, "", "",
					  "gethostbyname");
		col = token_print_indent (file, col, indent, " ", "", "(");
		col = token_print_indent_concat
			(file, col, indent, "", "",
			 "\"", expr -> data.host_lookup -> hostname, "\"",
			 (char *)0);
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      case expr_add:
		s = "+";
		goto binary;

	      case expr_subtract:
		s = "-";
		goto binary;

	      case expr_multiply:
		s = "*";
		goto binary;

	      case expr_divide:
		s = "/";
		goto binary;

	      case expr_remainder:
		s = "%";
		goto binary;

	      case expr_binary_and:
		s = "&";
		goto binary;

	      case expr_binary_or:
		s = "|";
		goto binary;

	      case expr_binary_xor:
		s = "^";
		goto binary;

	      case expr_and:
		s = "and";
		goto binary;

	      case expr_or:
		s = "or";
		goto binary;

	      case expr_not:
		col = token_print_indent (file, col, indent, "", " ", "not");
		col = write_expression (file,
					expr -> data.not, col, indent + 2, 1);
		break;

	      case expr_option:
		s = "option";

	      print_option_name:
		col = token_print_indent (file, col, indent, "", "", s);

		if (expr -> data.option -> universe != &dhcp_universe) {
			col = token_print_indent (file, col, indent,
						  " ", "",
						  (expr -> data.option -> 
						   universe -> name));
			col = token_print_indent (file, col, indent, "", "",
						  ".");
			col = token_print_indent (file, col, indent, "", "",
						  expr -> data.option -> name);
		} else {
			col = token_print_indent (file, col, indent, " ", "",
						  expr -> data.option -> name);
		}
		break;

	      case expr_hardware:	
		col = token_print_indent (file, col, indent, "", "",
					  "hardware");
		break;

	      case expr_packet:
		col = token_print_indent (file, col, indent, "", "",
					  "packet");
		col = token_print_indent (file, col, indent, " ", "", "(");
		scol = col;
		col = write_expression (file, expr -> data.packet.offset,
					col, indent, 1);
		col = token_print_indent (file, col, scol, "", " ", ",");
		col = write_expression (file, expr -> data.packet.len,
					col, scol, 0);
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      case expr_const_data:
		col = token_indent_data_string (file, col, indent, "", "",
						&expr -> data.const_data);
		break;

	      case expr_extract_int8:
		width = 8;
	      extract_int:
		col = token_print_indent (file, col, indent, "", "",
					  "extract-int");
		col = token_print_indent (file, col, indent, " ", "", "(");
		scol = col;
		col = write_expression (file, expr -> data.extract_int,
					col, indent, 1);
		col = token_print_indent (file, col, scol, "", " ", ",");
		sprintf (obuf, "%d", width);
		col = token_print_indent (file, col, scol, " ", "", obuf);
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      case expr_extract_int16:
		width = 16;
		goto extract_int;

	      case expr_extract_int32:
		width = 32;
		goto extract_int;

	      case expr_encode_int8:
		width = 8;
	      encode_int:
		col = token_print_indent (file, col, indent, "", "",
					  "encode-int");
		col = token_print_indent (file, col, indent, " ", "", "(");
		scol = col;
		col = write_expression (file, expr -> data.extract_int,
					col, indent, 1);
		col = token_print_indent (file, col, scol, "", " ", ",");
		sprintf (obuf, "%d", width);
		col = token_print_indent (file, col, scol, " ", "", obuf);
		col = token_print_indent (file, col, indent, "", "",
					  ")");
		break;

	      case expr_encode_int16:
		width = 16;
		goto encode_int;

	      case expr_encode_int32:
		width = 32;
		goto encode_int;

	      case expr_const_int:
		sprintf (obuf, "%lu", expr -> data.const_int);
		col = token_print_indent (file, col, indent, "", "", obuf);
		break;

	      case expr_exists:
		s = "exists";
		goto print_option_name;

	      case expr_encapsulate:
		col = token_print_indent (file, col, indent, "", "",
					  "encapsulate");
		col = token_indent_data_string (file, col, indent, " ", "",
						&expr -> data.encapsulate);
		break;

	      case expr_known:
		col = token_print_indent (file, col, indent, "", "", "known");
		break;

	      case expr_reverse:
		col = token_print_indent (file, col, indent, "", "",
					  "reverse");
		col = token_print_indent (file, col, indent, " ", "", "(");
		scol = col;
		col = write_expression (file, expr -> data.reverse.width,
					col, scol, 1);
		col = token_print_indent (file, col, scol, "", " ", ",");
		col = write_expression (file, expr -> data.reverse.buffer,
					col, scol, 0);
		col = token_print_indent (file, col, indent, "", "",
					  ")");
		break;

	      case expr_leased_address:
		col = token_print_indent (file, col, indent, "", "",
					  "leased-address");
		break;

	      case expr_client_state:
		col = token_print_indent (file, col, indent, "", "",
					  "client-state");
		break;

	      case expr_binary_to_ascii:
		col = token_print_indent (file, col, indent, "", "",
					  "binary-to-ascii");
		col = token_print_indent (file, col, indent, " ", "",
					  "(");
		scol = col;
		col = write_expression (file, expr -> data.b2a.base,
					col, scol, 1);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.b2a.width,
					col, scol, 0);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.b2a.seperator,
					col, scol, 0);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.b2a.buffer,
					col, scol, 0);
		col = token_print_indent (file, col, indent, "", "",
					  ")");
		break;

	      case expr_config_option:
		s = "config-option";
		goto print_option_name;

	      case expr_host_decl_name:
		col = token_print_indent (file, col, indent, "", "",
					  "host-decl-name");
		break;

	      case expr_pick_first_value:
		e = expr;
		col = token_print_indent (file, col, indent, "", "",
					  "concat");
		col = token_print_indent (file, col, indent, " ", "",
					  "(");
		scol = col;
		firstp = 1;
	      pick_again:
		col = write_expression (file,
					e -> data.pick_first_value.car,
					col, scol, firstp);
		firstp = 0;
		/* We're being very lisp-like right now - instead of
                   representing this expression as (first middle . last) we're
                   representing it as (first middle last), which means that the
                   tail cdr is always nil.  Apologies to non-wisp-lizards - may
                   this obscure way of describing the problem motivate you to
                   learn more about the one true computing language. */
		if (!e -> data.pick_first_value.cdr)
			goto no_pick_cdr;
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		if (e -> data.pick_first_value.cdr -> op ==
		    expr_pick_first_value) {
			e = e -> data.pick_first_value.cdr;
			goto pick_again;
		}
		col = write_expression (file,
					e -> data.pick_first_value.cdr,
					col, scol, 0);
	      no_pick_cdr:
		col = token_print_indent (file, col, indent, "", "",
					  ")");
		break;

	      case expr_lease_time:
		col = token_print_indent (file, col, indent, "", "",
					  "lease-time");
		break;

	      case expr_dns_transaction:
		col = token_print_indent (file, col, indent, "", "",
					  "ns-update");
		col = token_print_indent (file, col, indent, " ", "",
					  "(");
		scol = 0;
		for (e = expr;
		     e && e -> op == expr_dns_transaction;
		     e = e -> data.dns_transaction.cdr) {
			if (!scol) {
				scol = col;
				firstp = 1;
			} else
				firstp = 0;
			col = write_expression (file,
						e -> data.dns_transaction.car,
						col, scol, firstp);
			if (e -> data.dns_transaction.cdr)
				col = token_print_indent (file, col, scol,
							  "", " ", ",");
		}
		if (e)
			col = write_expression (file, e, col, scol, 0);
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      case expr_ns_add:
		col = token_print_indent (file, col, indent, "", "",
					  "update");
		col = token_print_indent (file, col, indent, " ", "",
					  "(");
		scol = col;
		sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
		col = token_print_indent (file, col, scol, "", "", obuf);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
		col = token_print_indent (file, col, scol, "", "", obuf);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.ns_add.rrname,
					col, scol, 0);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.ns_add.rrdata,
					col, scol, 0);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.ns_add.ttl,
					col, scol, 0);
		col = token_print_indent (file, col, indent, "", "",
					  ")");
		break;

	      case expr_ns_delete:
		col = token_print_indent (file, col, indent, "", "",
					  "delete");
		col = token_print_indent (file, col, indent, " ", "",
					  "(");
	      finish_ns_small:
		scol = col;
		sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
		col = token_print_indent (file, col, scol, "", "", obuf);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
		col = token_print_indent (file, col, scol, "", "", obuf);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.ns_add.rrname,
					col, scol, 0);
		col = token_print_indent (file, col, scol, "", " ",
					  ",");
		col = write_expression (file, expr -> data.ns_add.rrdata,
					col, scol, 0);
		col = token_print_indent (file, col, indent, "", "",
					  ")");
		break;

	      case expr_ns_exists:
		col = token_print_indent (file, col, indent, "", "",
					  "exists");
		col = token_print_indent (file, col, indent, " ", "",
					  "(");
		goto finish_ns_small;

	      case expr_ns_not_exists:
		col = token_print_indent (file, col, indent, "", "",
					  "not exists");
		col = token_print_indent (file, col, indent, " ", "",
					  "(");
		goto finish_ns_small;

	      case expr_static:
		col = token_print_indent (file, col, indent, "", "",
					  "static");
		break;

	      case expr_null:
		col = token_print_indent (file, col, indent, "", "", "null");
		break;

	      case expr_variable_reference:
		col = token_print_indent (file, indent, indent, "", "",
					  expr -> data.variable);
		break;

	      case expr_variable_exists:
		col = token_print_indent (file, indent, indent, "", "",
					  "defined");
		col = token_print_indent (file, col, indent, " ", "", "(");
		col = token_print_indent (file, col, indent, "", "",
					  expr -> data.variable);
		col = token_print_indent (file, col, indent, "", "", ")");
		break;

	      default:
		log_fatal ("invalid expression type in print_expression: %d",
			   expr -> op);
	}
	return col;
}

struct binding *find_binding (struct binding_scope *scope, const char *name)
{
	struct binding *bp;
	struct binding_scope *s;

	for (s = scope; s; s = s -> outer) {
		for (bp = s -> bindings; bp; bp = bp -> next) {
			if (!strcasecmp (name, bp -> name)) {
				return bp;
			}
		}
	}
	return (struct binding *)0;
}

int free_bindings (struct binding_scope *scope, const char *file, int line)
{
	struct binding *bp, *next;

	for (bp = scope -> bindings; bp; bp = next) {
		next = bp -> next;
		if (bp -> name)
			dfree (bp -> name, file, line);
		if (bp -> value)
			binding_value_dereference (&bp -> value, file, line);
		dfree (bp, file, line);
	}
	scope -> bindings = (struct binding *)0;
	return 1;
}

int binding_scope_dereference (ptr, file, line)
	struct binding_scope **ptr;
	const char *file;
	int line;
{
	int i;
	struct binding_scope *binding_scope;

	if (!ptr || !*ptr) {
		log_error ("%s(%d): null pointer", file, line);
#if defined (POINTER_DEBUG)
		abort ();
#else
		return 0;
#endif
	}

	binding_scope = *ptr;
	*ptr = (struct binding_scope *)0;
	--binding_scope -> refcnt;
	rc_register (file, line, ptr,
		     binding_scope, binding_scope -> refcnt, 1);
	if (binding_scope -> refcnt > 0)
		return 1;

	if (binding_scope -> refcnt < 0) {
		log_error ("%s(%d): negative refcnt!", file, line);
#if defined (DEBUG_RC_HISTORY)
		dump_rc_history (binding_scope);
#endif
#if defined (POINTER_DEBUG)
		abort ();
#else
		return 0;
#endif
	}

	free_bindings (binding_scope, file, line);
	if (binding_scope -> outer)
		binding_scope_dereference (&binding_scope -> outer, MDL);
	dfree (binding_scope, file, line);
	return 1;
}

int fundef_dereference (ptr, file, line)
	struct fundef **ptr;
	const char *file;
	int line;
{
	struct fundef *bp = *ptr;
	struct string_list *sp, *next;

	if (!ptr) {
		log_error ("%s(%d): null pointer", file, line);
#if defined (POINTER_DEBUG)
		abort ();
#else
		return 0;
#endif
	}

	if (!bp) {
		log_error ("%s(%d): null pointer", file, line);
#if defined (POINTER_DEBUG)
		abort ();
#else
		return 0;
#endif
	}

	bp -> refcnt--;
	rc_register (file, line, ptr, bp, bp -> refcnt, 1);
	if (bp -> refcnt < 0) {
		log_error ("%s(%d): negative refcnt!", file, line);
#if defined (DEBUG_RC_HISTORY)
		dump_rc_history (bp);
#endif
#if defined (POINTER_DEBUG)
		abort ();
#else
		return 0;
#endif
	}
	if (!bp -> refcnt) {
		for (sp = bp -> args; sp; sp = next) {
			next = sp -> next;
			dfree (sp, file, line);
		}
		if (bp -> statements)
			executable_statement_dereference (&bp -> statements,
							  file, line);
		dfree (bp, file, line);
	}
	*ptr = (struct fundef *)0;
	return 1;
}

#if defined (NOTYET)		/* Post 3.0 final. */
int data_subexpression_length (int *rv,
			       struct expression *expr)
{
	int crhs, clhs, llhs, lrhs;
	switch (expr -> op) {
	      case expr_substring:
		if (expr -> data.substring.len &&
		    expr -> data.substring.len -> op == expr_const_int) {
			(*rv =
			 (int)expr -> data.substring.len -> data.const_int);
			return 1;
		}
		return 0;

	      case expr_packet:
	      case expr_suffix:
		if (expr -> data.suffix.len &&
		    expr -> data.suffix.len -> op == expr_const_int) {
			(*rv =
			 (int)expr -> data.suffix.len -> data.const_int);
			return 1;
		}
		return 0;

	      case expr_concat:
		clhs = data_subexpression_length (&llhs,
						  expr -> data.concat [0]);
		crhs = data_subexpression_length (&lrhs,
						  expr -> data.concat [1]);
		if (crhs == 0 || clhs == 0)
			return 0;
		*rv = llhs + lrhs;
		return 1;
		break;

	      case expr_hardware:
		return 0;

	      case expr_const_data:
		*rv = expr -> data.const_data.len;
		return 2;

	      case expr_reverse:
		return data_subexpression_length (rv,
						  expr -> data.reverse.buffer);

	      case expr_leased_address:
	      case expr_lease_time:
		*rv = 4;
		return 2;

	      case expr_pick_first_value:
		clhs = data_subexpression_length (&llhs,
						  expr -> data.concat [0]);
		crhs = data_subexpression_length (&lrhs,
						  expr -> data.concat [1]);
		if (crhs == 0 || clhs == 0)
			return 0;
		if (llhs > lrhs)
			*rv = llhs;
		else
			*rv = lrhs;
		return 1;
			
	      case expr_binary_to_ascii:
	      case expr_config_option:
	      case expr_host_decl_name:
	      case expr_encapsulate:
	      case expr_filename:
	      case expr_sname:
	      case expr_host_lookup:
	      case expr_option:
	      case expr_none:
	      case expr_match:
	      case expr_check:
	      case expr_equal:
	      case expr_and:
	      case expr_or:
	      case expr_not:
	      case expr_extract_int8:
	      case expr_extract_int16:
	      case expr_extract_int32:
	      case expr_encode_int8:
	      case expr_encode_int16:
	      case expr_encode_int32:
	      case expr_const_int:
	      case expr_exists:
	      case expr_known:
	      case expr_dns_transaction:
	      case expr_static:
	      case expr_ns_add:
	      case expr_ns_delete:
	      case expr_ns_exists:
	      case expr_ns_not_exists:
	      case expr_not_equal:
	      case expr_null:
	      case expr_variable_exists:
	      case expr_variable_reference:
	      case expr_arg:
	      case expr_funcall:
	      case expr_function:
	      case expr_add:
	      case expr_subtract:
	      case expr_multiply:
	      case expr_divide:
	      case expr_remainder:
	      case expr_binary_and:
	      case expr_binary_or:
	      case expr_binary_xor:
	      case expr_client_state:
		return 0;
	}
	return 0;
}

int expr_valid_for_context (struct expression *expr,
			    enum expression_context context)
{
	/* We don't know at parse time what type of value a function may
	   return, so we can't flag an error on it. */
	if (expr -> op == expr_funcall ||
	    expr -> op == expr_variable_reference)
		return 1;

	switch (context) {
	      case context_any:
		return 1;

	      case context_boolean:
		if (is_boolean_expression (expr))
			return 1;
		return 0;

	      case context_data:
		if (is_data_expression (expr))
			return 1;
		return 0;

	      case context_numeric:
		if (is_numeric_expression (expr))
			return 1;
		return 0;

	      case context_dns:
		if (is_dns_expression (expr)) {
			return 1;
		}
		return 0;

	      case context_data_or_numeric:
		if (is_numeric_expression (expr) ||
		    is_data_expression (expr)) {
			return 1;
		}
		return 0;

	      case context_function:
		if (expr -> op == expr_function)
			return 1;
		return 0;
	}
	return 0;
}
#endif /* NOTYET */

struct binding *create_binding (struct binding_scope **scope, const char *name)
{
	struct binding *binding;

	if (!*scope) {
		if (!binding_scope_allocate (scope, MDL))
			return (struct binding *)0;
	}

	binding = find_binding (*scope, name);
	if (!binding) {
		binding = dmalloc (sizeof *binding, MDL);
		if (!binding)
			return (struct binding *)0;

		memset (binding, 0, sizeof *binding);
		binding -> name = dmalloc (strlen (name) + 1, MDL);
		if (!binding -> name) {
			dfree (binding, MDL);
			return (struct binding *)0;
		}
		strcpy (binding -> name, name);

		binding -> next = (*scope) -> bindings;
		(*scope) -> bindings = binding;
	}

	return binding;
}


int bind_ds_value (struct binding_scope **scope,
		   const char *name,
		   struct data_string *value)
{
	struct binding *binding;

	binding = create_binding (scope, name);
	if (!binding)
		return 0;

	if (binding -> value)
		binding_value_dereference (&binding -> value, MDL);

	if (!binding_value_allocate (&binding -> value, MDL))
		return 0;

	data_string_copy (&binding -> value -> value.data, value, MDL);
	binding -> value -> type = binding_data;

	return 1;
}


int find_bound_string (struct data_string *value,
		       struct binding_scope *scope,
		       const char *name)
{
	struct binding *binding;

	binding = find_binding (scope, name);
	if (!binding ||
	    !binding -> value ||
	    binding -> value -> type != binding_data)
		return 0;

	if (binding -> value -> value.data.terminated) {
		data_string_copy (value, &binding -> value -> value.data, MDL);
	} else {
		buffer_allocate (&value -> buffer,
				 binding -> value -> value.data.len,
				 MDL);
		if (!value -> buffer)
			return 0;

		memcpy (value -> buffer -> data,
			binding -> value -> value.data.data,
			binding -> value -> value.data.len);
		value -> data = value -> buffer -> data;
		value -> len = binding -> value -> value.data.len;
	}

	return 1;
}

int unset (struct binding_scope *scope, const char *name)
{
	struct binding *binding;

	binding = find_binding (scope, name);
	if (binding) {
		if (binding -> value)
			binding_value_dereference
				(&binding -> value, MDL);
		return 1;
	}
	return 0;
}

/* vim: set tabstop=8: */
