#ifndef lint
static char copyright[] =
-"$Id: options.c,v 1.32 1998/06/25 03:02:50 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
+"$Id: options.c,v 1.33 1998/11/05 18:42:47 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#define DHCP_OPTION_DATA
#include "dhcpd.h"
-static void do_option_set PROTO ((struct option_cache **,
+static void do_option_set PROTO ((pair *,
struct option_cache *,
enum statement_op));
/* Parse all available options out of the specified packet. */
-void parse_options (packet)
+int parse_options (packet)
struct packet *packet;
{
+ int i;
+ struct option_cache *op = (struct option_cache *)0;
+
/* Initially, zero all option pointers. */
- memset (packet -> options, 0, sizeof (packet -> options));
+ memset (&packet -> options, 0, sizeof (packet -> options));
/* If we don't see the magic cookie, there's nothing to parse. */
if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
packet -> options_valid = 0;
- return;
+ return 1;
}
/* Go through the options field, up to the end of the packet
or the End field. */
- parse_option_buffer (packet, &packet -> raw -> options [4],
- packet -> packet_length - DHCP_FIXED_NON_UDP - 4);
+ if (!parse_option_buffer (packet, &packet -> raw -> options [4],
+ (packet -> packet_length -
+ DHCP_FIXED_NON_UDP - 4)))
+ return 0;
+
/* If we parsed a DHCP Option Overload option, parse more
options out of the buffer(s) containing them. */
- if (packet -> options_valid
- && packet -> options [DHO_DHCP_OPTION_OVERLOAD].data) {
- if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)
- parse_option_buffer
- (packet,
- (unsigned char *)packet -> raw -> file,
- sizeof packet -> raw -> file);
- if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)
- parse_option_buffer
- (packet,
- (unsigned char *)packet -> raw -> sname,
- sizeof packet -> raw -> sname);
+ if (packet -> options_valid &&
+ (op = lookup_option (packet -> options.dhcp_hash,
+ DHO_DHCP_OPTION_OVERLOAD))) {
+ if (op -> data.data [0] & 1) {
+ if (!parse_option_buffer
+ (packet, (unsigned char *)packet -> raw -> file,
+ sizeof packet -> raw -> file))
+ return 0;
+ }
+ if (op -> data.data [0] & 2) {
+ if (!parse_option_buffer
+ (packet,
+ (unsigned char *)packet -> raw -> sname,
+ sizeof packet -> raw -> sname))
+ return 0;
+ }
}
+ return 1;
}
/* Parse options out of the specified buffer, storing addresses of option
values in packet -> options and setting packet -> options_valid if no
errors are encountered. */
-void parse_option_buffer (packet, buffer, length)
+int parse_option_buffer (packet, buffer, length)
struct packet *packet;
unsigned char *buffer;
int length;
{
- unsigned char *s, *t;
+ unsigned char *t;
unsigned char *end = buffer + length;
- int len;
+ int len, offset;
int code;
+ struct option_cache *op = (struct option_cache *)0;
+ struct buffer *bp = (struct buffer *)0;
- for (s = buffer; *s != DHO_END && s < end; ) {
- code = s [0];
+ if (!buffer_allocate (&bp, length, "parse_option_buffer")) {
+ warn ("parse_option_buffer: no memory for option buffer.");
+ return 0;
+ }
+ memcpy (bp -> data, buffer, length);
+
+ for (offset = 0; buffer [offset] != DHO_END && offset < length; ) {
+ code = buffer [offset];
/* Pad options don't have a length - just skip them. */
if (code == DHO_PAD) {
- ++s;
+ ++offset;
continue;
}
+
/* All other fields (except end, see above) have a
one-byte length. */
- len = s [1];
+ len = buffer [offset + 1];
/* If the length is outrageous, the options are bad. */
- if (s + len + 2 > end) {
+ if (offset + len + 2 > length) {
warn ("Option %s length %d overflows input buffer.",
dhcp_options [code].name,
len);
- packet -> options_valid = 0;
- return;
+ buffer_dereference (&bp, "parse_option_buffer");
+ return 0;
}
/* If this is a Relay Agent Information option, we must
handle it specially. */
if (code == DHO_DHCP_AGENT_OPTIONS) {
- if (!parse_agent_information_option (packet,
- len, &s [2])) {
+ if (!parse_agent_information_option
+ (packet, len, buffer + offset + 2)) {
warn ("malformed agent information option.");
+ buffer_dereference (&bp,
+ "parse_option_buffer");
+ return 0;
}
-
- /* If we haven't seen this option before, just make
- space for it and copy it there. */
- } else if (!packet -> options [code].data) {
- if (!(t = (unsigned char *)malloc (len + 1)))
- error ("Can't allocate storage for option %s.",
- dhcp_options [code].name);
- /* Copy and NUL-terminate the option (in case it's an
- ASCII string. */
- memcpy (t, &s [2], len);
- t [len] = 0;
- packet -> options [code].len = len;
- packet -> options [code].data = t;
} else {
- /* If it's a repeat, concatenate it to whatever
- we last saw. This is really only required
- for clients, but what the heck... */
- t = (unsigned char *)
- malloc (len
- + packet -> options [code].len
- + 1);
- if (!t)
- error ("Can't expand storage for option %s.",
- dhcp_options [code].name);
- memcpy (t, packet -> options [code].data,
- packet -> options [code].len);
- memcpy (t + packet -> options [code].len,
- &s [2], len);
- packet -> options [code].len += len;
- t [packet -> options [code].len] = 0;
- free (packet -> options [code].data);
- packet -> options [code].data = t;
+ if (!option_cache_allocate (&op,
+ "parse_option_buffer")) {
+ warn ("Can't allocate storage for option %s.",
+ dhcp_options [code].name);
+ buffer_dereference (&bp,
+ "parse_option_buffer");
+ return 0;
+ }
+
+ /* Reference buffer copy to option cache. */
+ op -> data.buffer = (struct buffer *)0;
+ buffer_reference (&op -> data.buffer, bp,
+ "parse_option_buffer");
+
+ /* Point option cache into buffer. */
+ op -> data.data = &bp -> data [offset + 2];
+ op -> data.len = len;
+
+ /* NUL terminate (we can get away with this
+ because we allocated one more than the
+ buffer size, and because the byte following
+ the end of an option is always the code of
+ the next option, which we're getting out of
+ the *original* buffer. */
+ bp -> data [offset + 2 + len] = 0;
+ op -> data.terminated = 1;
+
+ op -> option = &dhcp_options [code];
+ /* Now store the option. */
+ save_option (packet -> options.dhcp_hash, op);
+
+ /* And let go of our reference. */
+ option_cache_dereference (&op,
+ "parse_option_buffer");
}
- s += len + 2;
+ offset += len + 2;
}
packet -> options_valid = 1;
+ buffer_dereference (&bp, "parse_option_buffer");
+ return 1;
}
/* Parse a Relay Agent Information option and put it at the end of the
if (op + 1 == max || op + op [1] + 2 > max)
return 0;
/* Make space for this suboption. */
- t = (struct option_tag *)malloc (op [1] + 1 + sizeof *t);
+ t = (struct option_tag *)
+ dmalloc (op [1] + 1 + sizeof *t,
+ "parse_agent_information_option");
if (!t)
error ("can't allocate space for option tag data.");
}
/* Make an agent options structure to put on the list. */
- a = (struct agent_options *)malloc (sizeof *a);
+ a = (struct agent_options *)dmalloc (sizeof *a,
+ "parse_agent_information_option");
if (!a)
error ("can't allocate space for agent option structure.");
/* Find the tail of the list. */
- for (tail = &packet -> agent_options; *tail; tail = &((*tail) -> next))
+ for (tail = &packet -> options.agent_options;
+ *tail; tail = &((*tail) -> next))
;
*tail = a;
a -> next = (struct agent_options *)0;
int terminate;
int bootpp;
{
- unsigned char priority_list [300];
+#define PRIORITY_COUNT 300
+ int priority_list [PRIORITY_COUNT];
int priority_len;
unsigned char buffer [4096]; /* Really big buffer... */
int main_buffer_size;
int mainbufix, bufix, agentix;
int option_size;
int length;
+ int i;
+ struct option_cache *op;
+ struct data_string ds;
+ pair pp;
+
+ memset (&ds, 0, sizeof ds);
/* If there's a Maximum Message Size option in the incoming packet
and no alternate maximum message size has been specified, take the
one in the packet. */
- if (!mms &&
- inpacket && inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data) {
- mms = getUShort (inpacket ->
- options [DHO_DHCP_MAX_MESSAGE_SIZE].data);
+ if (!mms && inpacket &&
+ (op = lookup_option (inpacket -> options.dhcp_hash,
+ DHO_DHCP_MAX_MESSAGE_SIZE))) {
+ evaluate_option_cache (&ds, inpacket,
+ &inpacket -> options, op);
+ if (ds.len >= sizeof (u_int16_t))
+ mms = getUShort (ds.data);
+ data_string_forget (&ds, "cons_options");
}
/* If the client has provided a maximum DHCP message size,
priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
priority_list [priority_len++] = DHO_DHCP_LEASE_TIME;
priority_list [priority_len++] = DHO_DHCP_MESSAGE;
+ priority_list [priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
/* If the client has provided a list of options that it wishes
returned, use it to prioritize. Otherwise, prioritize
based on the default priority list. */
- if (inpacket &&
- inpacket -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
- int prlen = (inpacket ->
- options [DHO_DHCP_PARAMETER_REQUEST_LIST].len);
- if (prlen + priority_len > sizeof priority_list)
- prlen = (sizeof priority_list) - priority_len;
-
- memcpy (&priority_list [priority_len],
- inpacket -> options
- [DHO_DHCP_PARAMETER_REQUEST_LIST].data, prlen);
- priority_len += prlen;
+ if (inpacket)
+ op = lookup_option (inpacket -> options.dhcp_hash,
+ DHO_DHCP_PARAMETER_REQUEST_LIST);
+ else
+ op = (struct option_cache *)0;
+
+ if (op)
+ evaluate_option_cache (&ds, inpacket,
+ &inpacket -> options, op);
+
+ if (ds.len > 0) {
+ data_string_truncate (&ds,
+ (PRIORITY_COUNT - priority_len));
+
+ for (i = 0; i < ds.len; i++)
+ priority_list [priority_len++] = ds.data [i];
+ data_string_forget (&ds, "cons_options");
} else {
- memcpy (&priority_list [priority_len],
- dhcp_option_default_priority_list,
- sizeof_dhcp_option_default_priority_list);
- priority_len += sizeof_dhcp_option_default_priority_list;
+ /* First, hardcode some more options that ought to be
+ sent first... */
+ priority_list [priority_len++] = DHO_SUBNET_MASK;
+ priority_list [priority_len++] = DHO_ROUTERS;
+ priority_list [priority_len++] = DHO_DOMAIN_NAME_SERVERS;
+ priority_list [priority_len++] = DHO_HOST_NAME;
+
+ /* Now just tack on the list of all the options we have,
+ and any duplicates will be eliminated. */
+ for (i = 0; i < OPTION_HASH_SIZE; i++) {
+ for (pp = options -> dhcp_hash [i];
+ pp; pp = pp -> cdr) {
+ op = (struct option_cache *)(pp -> car);
+ if (priority_len < PRIORITY_COUNT)
+ priority_list [priority_len++] =
+ op -> option -> code;
+ }
+ }
}
/* Copy the options into the big buffer... */
(main_buffer_size - 7 +
((overload & 1) ? DHCP_FILE_LEN : 0) +
((overload & 2) ? DHCP_SNAME_LEN : 0)),
- options -> dhcp_options,
+ options,
priority_list, priority_len,
main_buffer_size,
(main_buffer_size +
mainbufix += option_size;
if (mainbufix < main_buffer_size) {
agentix = mainbufix;
- outpacket -> options [mainbufix++]
- = DHO_END;
+ outpacket -> options [mainbufix++] = DHO_END;
} else
agentix = mainbufix;
length = DHCP_FIXED_NON_UDP + mainbufix;
} else {
- outpacket -> options [mainbufix++] =
- DHO_DHCP_OPTION_OVERLOAD;
+ outpacket -> options [mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
outpacket -> options [mainbufix++] = 1;
if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN)
outpacket -> options [mainbufix++] = 3;
/* Cycle through the options, appending them to the
buffer. */
for (a = options -> agent_options; a; a = a -> next) {
- if (agentix + a -> length + 3 + DHCP_FIXED_LEN <=
- dhcp_max_agent_option_packet_length) {
- outpacket ->
- options [agentix++] = DHO_DHCP_AGENT_OPTIONS;
- outpacket -> options [agentix++] = a -> length;
- for (o = a -> first; o; o = o -> next) {
- memcpy (&outpacket -> options [agentix],
- o -> data, o -> data [1] + 2);
- agentix += o -> data [1] + 2;
+ if (agentix + a -> length + 3 + DHCP_FIXED_LEN <=
+ dhcp_max_agent_option_packet_length) {
+ outpacket -> options [agentix++]
+ = DHO_DHCP_AGENT_OPTIONS;
+ outpacket -> options [agentix++] = a -> length;
+ for (o = a -> first; o; o = o -> next) {
+ memcpy (&outpacket -> options [agentix],
+ o -> data, o -> data [1] + 2);
+ agentix += o -> data [1] + 2;
+ }
}
- }
}
/* Reterminate the packet. */
first_cutoff, second_cutoff, terminate)
unsigned char *buffer;
int buflen;
- struct option_cache **options;
- unsigned char *priority_list;
+ struct option_state *options;
+ int *priority_list;
int priority_len;
int first_cutoff, second_cutoff;
int terminate;
{
int bufix = 0;
- int option_stored [256];
int i;
int ix;
int tto;
struct data_string od;
+ struct option_cache *oc;
- /* Zero out the stored-lengths array. */
- memset (option_stored, 0, sizeof option_stored);
+ memset (&od, 0, sizeof od);
+
+ /* Eliminate duplicate options in the parameter request list.
+ There's got to be some clever knuthian way to do this:
+ Eliminate all but the first occurance of a value in an array
+ of values without otherwise disturbing the order of the array. */
+ for (i = 0; i < priority_len - 1; i++) {
+ tto = 0;
+ for (ix = i + 1; ix < priority_len + tto; ix++) {
+ if (tto)
+ priority_list [ix - tto] =
+ priority_list [ix];
+ if (priority_list [i] == priority_list [ix]) {
+ tto++;
+ priority_len--;
+ }
+ }
+ }
/* Copy out the options in the order that they appear in the
priority list... */
int length;
/* If no data is available for this option, skip it. */
- if (!options [code]) {
+ if (!(oc = lookup_option (options -> dhcp_hash, code))) {
continue;
}
- /* The client could ask for things that are mandatory,
- in which case we should avoid storing them twice... */
- if (option_stored [code])
- continue;
- option_stored [code] = 1;
-
/* Find the value of the option... */
- od = evaluate_data_expression ((struct packet *)0,
- options [code] -> expression);
- if (!od.len)
+ evaluate_option_cache (&od, (struct packet *)0,
+ (struct option_state *)0, oc);
+ if (!od.len) {
continue;
+ }
/* We should now have a constant length for the option. */
length = od.len;
ix += incr;
bufix += 2 + incr;
}
- if (od.buffer)
- dfree (od.buffer, "store_options");
+ data_string_forget (&od, "store_options");
}
return bufix;
}
struct hardware *hfrom;
{
struct packet tp;
+ int i;
+ struct option_cache *op;
memset (&tp, 0, sizeof tp);
tp.raw = packet;
note ("Discarding packet with bogus hardware address length.");
return;
}
- parse_options (&tp);
+ if (!parse_options (&tp)) {
+ option_state_dereference (&tp.options);
+ return;
+ }
+
if (tp.options_valid &&
- tp.options [DHO_DHCP_MESSAGE_TYPE].data)
- tp.packet_type =
- tp.options [DHO_DHCP_MESSAGE_TYPE].data [0];
+ (op = lookup_option (tp.options.dhcp_hash,
+ DHO_DHCP_MESSAGE_TYPE))) {
+ struct data_string dp;
+ memset (&dp, 0, sizeof dp);
+ evaluate_option_cache (&dp, &tp, &tp.options, op);
+ if (dp.len > 0)
+ tp.packet_type = dp.data [0];
+ else
+ tp.packet_type = 0;
+ data_string_forget (&dp, "do_packet");
+ }
+
if (tp.packet_type)
dhcp (&tp);
else
bootp (&tp);
- /* XXX what about freeing the options ?!? */
+ option_state_dereference (&tp.options);
}
-struct data_string dhcp_option_lookup (packet, code)
- struct packet *packet;
+int dhcp_option_lookup (result, options, code)
+ struct data_string *result;
+ struct option_state *options;
int code;
{
- struct data_string result;
+ struct option_cache *oc;
- result.len = packet -> options [code].len;
- result.data = packet -> options [code].data;
- result.terminated = 1;
- result.buffer = (unsigned char *)0;
- return result;
+ if (!(oc = lookup_option (options -> dhcp_hash, code)))
+ return 0;
+ if (!evaluate_option_cache (result, (struct packet *)0, options, oc))
+ return 0;
+ return 1;
}
-struct data_string agent_suboption_lookup (packet, code)
- struct packet *packet;
+int agent_suboption_lookup (result, options, code)
+ struct data_string *result;
+ struct option_state *options;
int code;
{
struct agent_options *ao;
struct option_tag *t;
- struct data_string result;
-
- memset (&result, 0, sizeof result);
/* Find the last set of agent options and consider it definitive. */
+ for (ao = options -> agent_options; ao -> next; ao = ao -> next)
+ ;
if (ao) {
- for (ao = packet -> agent_options; ao -> next; ao = ao -> next)
- ;
- for (t = ao -> first; t; t = t -> next)
+ for (t = ao -> first; t; t = t -> next) {
if (t -> data [0] == code) {
- result.len = t -> data [1];
- result.data = &t -> data [2];
- break;
+ result -> len = t -> data [1];
+ if (!buffer_allocate (&result -> buffer,
+ result -> len + 1,
+ "agent_suboption_lookup"
+ )) {
+ result -> len = 0;
+ buffer_dereference
+ (&result -> buffer,
+ "agent_suboption_lookup");
+ return 0;
+ }
+ result -> data = &result -> buffer -> data [0];
+ memcpy (result -> data,
+ &t -> data [2], result -> len);
+ result -> data [result -> len] = 0;
+ result -> terminated = 1;
+ return 1;
}
+ }
}
- return result;
+ return 0;
}
-struct data_string server_option_lookup (packet, code)
- struct packet *packet;
+int server_option_lookup (result, options, code)
+ struct data_string *result;
+ struct option_state *options;
int code;
{
- struct data_string result;
-
- result.len = 0;
- result.data = (unsigned char *)0;
- result.terminated = 0;
- result.buffer = (unsigned char *)0;
- return result;
+ return 0;
}
void dhcp_option_set (options, option, op)
{
struct option_cache *thecache;
- do_option_set (&options -> server_options
- [option -> option -> code], option, op);
+ do_option_set (options -> dhcp_hash, option, op);
}
void server_option_set (options, option, op)
struct option_cache *option;
enum statement_op op;
{
- do_option_set (&options -> server_options
- [option -> option -> code], option, op);
+ do_option_set (options -> server_hash, option, op);
}
-static void do_option_set (thecache, option, op)
- struct option_cache **thecache;
+static void do_option_set (hash, option, op)
+ pair *hash;
struct option_cache *option;
enum statement_op op;
{
- struct option_cache *oc;
+ struct option_cache *oc, *noc;
switch (op) {
case if_statement:
break;
case default_option_statement:
- if (*thecache)
+ oc = lookup_option (hash, option -> option -> code);
+ if (oc)
break;
- *thecache = option;
+ save_option (hash, option);
break;
case supersede_option_statement:
- /* Free any ephemeral state associated with the
- current value. */
- if (*thecache)
- free_oc_ephemeral_state (*thecache);
- *thecache = option;
+ /* Install the option, replacing any existing version. */
+ save_option (hash, option);
break;
case append_option_statement:
case prepend_option_statement:
- if (!*thecache) {
- *thecache = option;
+ oc = lookup_option (hash, option -> option -> code);
+ if (!oc) {
+ save_option (hash, option);
break;
}
- if (((*thecache) -> expression -> flags) & EXPR_EPHEMERAL)
- oc = *thecache;
- else {
- oc = new_option_cache ("do_option_set");
- if (!oc) {
- warn ("can't allocate option cache!");
+ /* If it's not an expression, make it into one. */
+ if (!oc -> expression && oc -> data.len) {
+ if (!expression_allocate (&oc -> expression,
+ "do_option_set")) {
+ warn ("Can't allocate const expression.");
+ break;
+ }
+ oc -> expression -> op = expr_const_data;
+ data_string_copy
+ (&oc -> expression -> data.const_data,
+ &oc -> data, "do_option_set");
+ data_string_forget (&oc -> data, "do_option_set");
+ }
+ noc = (struct option_cache *)0;
+ if (!option_cache_allocate (&noc, "do_option_set"))
+ break;
+ if (op == append_option_statement) {
+ if (!make_concat (&noc -> expression,
+ oc -> expression,
+ option -> expression)) {
+ option_cache_dereference (&noc,
+ "do_option_set");
+ break;
+ }
+ } else {
+ if (!make_concat (&noc -> expression,
+ option -> expression,
+ oc -> expression)) {
+ option_cache_dereference (&noc,
+ "do_option_set");
break;
}
- *oc = **thecache;
}
- if (op == append_option_statement)
- oc -> expression =
- make_concat ((*thecache) -> expression,
- option -> expression);
+ noc -> option = oc -> option;
+ save_option (hash, noc);
+ option_cache_dereference (&noc, "do_option_set");
+ break;
+ }
+}
+
+struct option_cache *lookup_option (hash, code)
+ pair *hash;
+ int code;
+{
+ int hashix;
+ pair bptr;
+
+ hashix = ((code & 31) + ((code >> 5) & 31)) % 17;
+ for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
+ if (((struct option_cache *)(bptr -> car)) -> option -> code ==
+ code)
+ return (struct option_cache *)(bptr -> car);
+ }
+ return (struct option_cache *)0;
+}
+
+void save_option (hash, oc)
+ pair *hash;
+ struct option_cache *oc;
+{
+ int hashix;
+ pair bptr;
+
+ /* Try to find an existing option matching the new one. */
+ hashix = ((oc -> option -> code & 31) +
+ ((oc -> option -> code >> 5) & 31)) % 17;
+ for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
+ if (((struct option_cache *)(bptr -> car)) -> option -> code ==
+ oc -> option -> code)
+ break;
+ }
+
+ /* If we find one, dereference it and put the new one in its place. */
+ if (bptr) {
+ option_cache_dereference ((struct option_cache **)&bptr -> car,
+ "save_option");
+ option_cache_reference ((struct option_cache **)&bptr -> car,
+ oc, "save_option");
+ } else {
+ /* Otherwise, just put the new one at the head of the list. */
+ bptr = new_pair ("save_option");
+ if (!bptr) {
+ warn ("No memory for option_cache reference.");
+ return;
+ }
+ bptr -> cdr = hash [hashix];
+ bptr -> car = 0;
+ option_cache_reference ((struct option_cache **)&bptr -> car,
+ oc, "save_option");
+ hash [hashix] = bptr;
+ }
+}
+
+void delete_option (hash, code)
+ pair *hash;
+ int code;
+{
+ int hashix;
+ pair bptr, prev = (pair)0;
+
+ /* Try to find an existing option matching the new one. */
+ hashix = ((code & 31) +
+ ((code >> 5) & 31)) % 17;
+ for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
+ if (((struct option_cache *)(bptr -> car)) -> option -> code
+ == code)
+ break;
+ prev = bptr;
+ }
+ /* If we found one, wipe it out... */
+ if (bptr) {
+ if (prev)
+ prev -> cdr = bptr -> cdr;
else
- oc -> expression =
- make_concat (option -> expression,
- (*thecache) -> expression);
+ hash [hashix] = bptr -> cdr;
+ option_cache_dereference
+ ((struct option_cache **)(&bptr -> car),
+ "delete_option");
+ free_pair (bptr, "delete_option");
+ }
+}
- oc -> expression -> flags |= EXPR_EPHEMERAL;
- *thecache = oc;
- break;
+extern struct option_cache *free_option_caches; /* XXX */
+
+int option_cache_dereference (ptr, name)
+ struct option_cache **ptr;
+ char *name;
+{
+ if (!ptr || !*ptr) {
+ warn ("Null pointer in option_cache_dereference: %s", name);
+ abort ();
+ }
+
+ (*ptr) -> refcnt--;
+ if (!(*ptr) -> refcnt) {
+ if ((*ptr) -> data.buffer)
+ data_string_forget (&(*ptr) -> data, name);
+ if ((*ptr) -> expression)
+ expression_dereference (&(*ptr) -> expression, name);
+ /* Put it back on the free list... */
+ (*ptr) -> expression = (struct expression *)free_option_caches;
+ free_option_caches = *ptr;
}
+ *ptr = (struct option_cache *)0;
+ return 1;
+
}