joes        2003/01/17 15:24:53

  Added:       src      apreq.c apreq.h apreq_tables.c apreq_tables.h
  Log:
  Import Frankentables.
  
  Revision  Changes    Path
  1.1                  httpd-apreq-2/src/apreq.c
  
  Index: apreq.c
  ===================================================================
  #include "apreq.h"
  #include "apr_time.h"
  #include "apr_file_info.h"
  
  #define MIN(a,b) ( (a) < (b) ? (a) : (b) )
  #define MAX(a,b) ( (a) > (b) ? (a) : (b) )
  
  apreq_value_t *apreq_value_make(apr_pool_t *p, const char *str, 
                                  const apr_ssize_t size)
  {
      apreq_value_t *v = apr_palloc(p, size + sizeof *v);
      memcpy(v->data,str,size);
      v->size = size;
      v->data[size] = 0;
      return v;
  }
  
  apreq_value_t *apreq_value_copy(apr_pool_t *p, 
                                 const apreq_value_t *val)
  {
      apreq_value_t *v;
      if (val == NULL)
          return NULL;
  
      v = apr_palloc(p, val->size + sizeof *v);
  
      *v = *val;
  
      if ( v->size > 0 )
          memcpy(v->data, val->data, v->size);
  
      return v;
  }
  
  apreq_value_t *apreq_value_merge(apr_pool_t *p,
                                   const apr_array_header_t *arr)
  {
      apr_ssize_t s;
      apreq_value_t *rv;
      apreq_value_t **a = (apreq_value_t **)arr->elts;
      int j, n = arr->nelts;
  
      if (n == 0)
          return NULL;
  
      while (*a == NULL && n > 0) {
          ++a;
          --n;
      }
      while (n > 0 && a[n-1] == NULL)
          --n;
  
      if (n == 0)
          return NULL;
  
      for (j=0, s=0; j < n; ++j)
          if (a[j] != NULL)
              s += a[j]->size + 2;
  
      rv = apr_palloc(p, s + sizeof *rv);
      rv->size = 0;
  
      memcpy(rv->data, a[0]->data, a[0]->size);
      rv->size = a[0]->size;
  
      for (j = 1; j < n; ++j) {
          rv->data[rv->size++] = ',';
          rv->data[rv->size++] = ' ';
          memcpy(rv->data, a[j]->data, a[j]->size);
          rv->size += a[j]->size;
      }
  
      rv->data[rv->size] = 0; /* nul-terminate rv->data */
      return rv;
  }
  
  char *apreq_expires(apr_pool_t *p, char *time_str, int type)
  {
      apr_time_t when;
      apr_time_exp_t tms;
      char sep = (type == APREQ_EXPIRES_HTTP) ? ' ' : '-';
  
      if (time_str == NULL) {
        return NULL;
      }
  
      when = apr_time_now();
      if ( strcasecmp(time_str,"now") != 0 ) 
          when += apreq_atod(time_str);
  
      if ( apr_time_exp_gmt(&tms, when) != APR_SUCCESS )
          return NULL;
  
      return apr_psprintf(p,
                       "%s, %.2d%c%s%c%.2d %.2d:%.2d:%.2d GMT",
                       apr_day_snames[tms.tm_wday],
                       tms.tm_mday, sep, apr_month_snames[tms.tm_mon], sep,
                       tms.tm_year + 1900,
                       tms.tm_hour, tms.tm_min, tms.tm_sec);
  }
  
  /* used for specifying file & byte sizes */
  
  apr_int64_t apreq_atol(const char *s) {
      apr_int64_t n = 0;
      char *p;
      if (s == NULL)
          return 0;
  
      n = apr_strtoi64(s, &p, 0);
  
      if (p == NULL)
          return n;
      while (apr_isspace(*p))
          ++p;
  
      switch (apr_tolower(*p)) {
        case 'g': return n * 1024*1024*1024;
        case 'm': return n * 1024*1024;
        case 'k': return n * 1024;
      }
  
      return n;
  }
  
  
  /* converts date offsets (e.g. "+3M") to seconds */
  
  apr_int64_t apreq_atod(const char *s) 
  {
      apr_int64_t n = 0;
      char *p;
      if (s == NULL)
          return 0;
      n = apr_strtoi64(s, &p, 0); /* XXX: what about overflow? */
  
      if (p == NULL)
          return n;
      while (apr_isspace(*p))
          ++p;
  
      switch (*p) {
        case 'Y': /* fall thru */
        case 'y': return n * 60*60*24*365;
        case 'M': return n * 60*60*24*30;
        case 'D': /* fall thru */
        case 'd': return n * 60*60*24;
        case 'H': /* fall thru */
        case 'h': return n * 60*60;
        case 'm': return n * 60;
        case 's': /* fall thru */
        default:
            return n;
      }
      /* should never get here */
      return -1;
  }
  
  
  /*
    search for a string in a fixed-length byte string.
    if partial is true, partial matches are allowed at the end of the buffer.
    returns NULL if not found, or a pointer to the start of the first match.
  */
  
  char *apreq_memmem(char* hay, apr_off_t hlen, 
                     const char* ndl, apr_off_t nlen, int part)
  {
      apr_off_t len = hlen;
      char *end = hay + hlen;
  
      while ( (hay = memchr(hay, ndl[0], len)) ) {
        len = end - hay;
  
        /* done if matches up to capacity of buffer */
        if ( memcmp(hay, ndl, MIN(nlen, len)) == 0 ) {
              if (part == 0 && len < nlen)
                  hay = NULL;     /* insufficient room for match */
            break;
          }
          --len;
          ++hay;
      }
  
      return hay;
  }
  
  apr_off_t apreq_index(char* hay, apr_off_t hlen, 
                     const char* ndl, apr_off_t nlen, int part)
  {
      apr_off_t len = hlen;
      char *end = hay + hlen;
      char *begin = hay;
  
      while ( (hay = memchr(hay, ndl[0], len)) ) {
        len = end - hay;
  
        /* done if matches up to capacity of buffer */
        if ( memcmp(hay, ndl, MIN(nlen, len)) == 0 ) {
              if (part == 0 && len < nlen)
                  hay = NULL;     /* insufficient room for match */
            break;
          }
          --len;
          ++hay;
      }
  
      return hay ? hay - begin : -1;
  }
  
  static const char c2x_table[] = "0123456789abcdef";
  static char x2c(const char *what)
  {
      register char digit;
  
  #if !APR_CHARSET_EBCDIC
      digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - 
'0'));
      digit *= 16;
      digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - 
'0'));
  #else /*APR_CHARSET_EBCDIC*/
      char xstr[5];
      xstr[0]='0';
      xstr[1]='x';
      xstr[2]=what[0];
      xstr[3]=what[1];
      xstr[4]='\0';
      digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 0xFF & strtol(xstr, NULL, 
16));
  #endif /*APR_CHARSET_EBCDIC*/
      return (digit);
  }
  
  apr_off_t apreq_unescape(char *s)
  {
      register int badesc, badpath;
      char *x, *y;
  
      badesc = 0;
      badpath = 0;
      /* Initial scan for first '%'. Don't bother writing values before
       * seeing a '%' */
      y = strchr(s, '%');
      if (y == NULL) {
          return -1;
      }
  
      for (x = y; *y; ++x, ++y) {
          switch (*y) {
          case '+':
              *x = ' ';
              break;
          case '%':
            if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
                badesc = 1;
                *x = '%';
            }
            else {
                *x = x2c(y + 1);
                y += 2;
            }
              break;
          default:
              *x = *y;
          }
      }
      *x = '\0';
      if (badesc)
        return -1;
      else
        return x - s;
  }
  
  
  char *apreq_escape(apr_pool_t *p, char *str) 
  {
      char *esc = apr_palloc(p, 3 * strlen(str) + 1);
      char *d = esc;
      const unsigned char *s = (const unsigned char *)str;
      unsigned c;
      while((c = *s)) {
          if( apr_isalnum(c) ) {
              *d++ = c;
          } 
          else if ( c == ' ' ) {
              *d++ = '+';
          }
          else {
  #if APR_CHARSET_EBCDIC
              c = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)c);
  #endif
              *d++ = '%';
              *d++ = c2x_table[c >> 4];
              *d++ = c2x_table[c & 0xf];
          }
          ++s;
      }
      *d = 0;
      return esc;
  }
  
  /*
  
  char *apreq_script_name(request_rec *r)
  {
      char *tmp;
  
      if(r->path_info && *r->path_info) {
          int pi_start = ap_find_path_info(r->uri,r->path_info);
          tmp = apr_pstrndup(r->pool,r->uri,pi_start);
      }
      else {
          tmp = r->uri;
      }
  
      return tmp;
  }
  char *(apreq_script_path)(request_rec *r)
  {
      return apreq_script_path(r);
  }
  
  */
  
  static char *apreq_array_pstrcat(apr_pool_t *p,
                                   const apr_array_header_t *arr,
                                   const char *sep, int mode)
  {
      char *cp, *res, **strpp;
      apr_size_t len, size;
      int i;
  
      size = sep ? strlen(sep) : 0;
  
      if (arr->nelts <= 0 || arr->elts == NULL) {    /* Empty table? */
          return (char *) apr_pcalloc(p, 1);
      }
  
      /* Pass one --- find length of required string */
  
      len = 0;
      for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
          if (strpp && *strpp != NULL) {
              len += strlen(*strpp);
          }
          if (++i >= arr->nelts) {
              break;
          }
  
          len += size;
      }
  
      /* Allocate the required string */
  
      if (mode == APREQ_ESCAPE)
          len += 2 * len;
      else if(mode == APREQ_QUOTE)
          len += 2 * arr->nelts;
  
      res = (char *) apr_palloc(p, len + 1);
      cp = res;
  
      /* Pass two --- copy the argument strings into the result space */
  
      for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
          if (strpp && *strpp != NULL) {
              len = strlen(*strpp);
  
              if (mode == APREQ_ESCAPE) {
                  const unsigned char *s = (const unsigned char *)*strpp;
                  unsigned c;
                  while((c = *s)) {
                      if( apr_isalnum(c) ) {
                          *cp++ = c;
                      } else if ( c == ' ' ) {
                          *cp++ = '+';
                      }
                      else {
  #if APR_CHARSET_EBCDIC
                          c = apr_xlate_conv_byte(ap_hdrs_to_ascii, 
                                                  (unsigned char)c);
  #endif
                          *cp++ = '%';
                          *cp++ = c2x_table[c >> 4];
                          *cp++ = c2x_table[c & 0xf];
                      }
                      ++s;
                  }
              } 
              else if (mode == APREQ_QUOTE) {
                  *cp++ = '"';
                  memcpy(cp, *strpp, len);
                  cp += len;
                  *cp++ = '"';
  
              }
  
              else {
                  memcpy(cp, *strpp, len);
                  cp += len;
              }
  
          }
          if (++i >= arr->nelts) {
              break;
          }
          if (sep) {
              memcpy(cp,sep,size);
              cp += size;
          }
  
      }
  
      *cp = '\0';
  
      return res;
  }
  
  
  
  1.1                  httpd-apreq-2/src/apreq.h
  
  Index: apreq.h
  ===================================================================
  #ifndef APREQ_H
  #define APREQ_H
  
  #ifdef  __cplusplus
   extern "C" {
  #endif 
  
  #include "apr_tables.h" 
  #include "apr_lib.h"
  #include "apr_pools.h"
  #include "apr_strings.h"
  
  #define APREQ_DECLARE(d)        d
  
  /* default enctypes */
  #define APREQ_URL_ENCTYPE               "application/x-www-form-urlencoded"
  #define APREQ_URL_LENGTH                33
  #define APREQ_MFD_ENCTYPE               "multipart/form-data"
  #define APREQ_MFD_ENCTYPE_LENGTH        19
  #define APREQ_XML_ENCTYPE               "text/xml"
  #define APREQ_XML_ENCTYPE_LENGTH        8
  
  #define APREQ_EXPIRES_HTTP              1
  #define APREQ_EXPIRES_NSCOOKIE          2
  
  #define APREQ_DEFAULT_POST_MAX         -1 /* XXX: unsafe default ??? */
  #define APREQ_DEFAULT_NELTS             8
  
  /* string match modes:
   * full match: full needle must be found 
   * partial match: allow for partial matches at the end of haystack
   */
  #define APREQ_MATCH_FULL                0
  #define APREQ_MATCH_PART                1
  
  /* join modes */
  #define APREQ_ESCAPE                    1
  #define APREQ_URLENCODE                 2
  #define APREQ_QUOTE                     3
  
  /* status codes */
  #define APREQ_OK                  0
  #define APREQ_CONTINUE          100
  #define APREQ_ERROR             500
  
  typedef struct apreq_value_t {
      apr_ssize_t          size;
      unsigned char        flags; /* ??? */
      char                 data[1];
  } apreq_value_t;
  
  
  typedef apreq_value_t *(apreq_value_merge_t)(apr_pool_t *p,
                                               const apr_array_header_t *a);
  typedef apreq_value_t *(apreq_value_copy_t)(apr_pool_t *p,
                                              const apreq_value_t *v);
  
  #define apreq_attr_to_type(T,A,P) ( (T*) ((char*)(P)-offsetof(T,A)) )
  #define apreq_char_to_value(ptr)  apreq_attr_to_type(apreq_value_t, data, ptr)
  
  apreq_value_t *apreq_value_make(apr_pool_t *p, const char *str, 
                                  const apr_ssize_t size);
  apreq_value_t *apreq_value_copy(apr_pool_t *p, const apreq_value_t *val);
  apreq_value_t *apreq_value_merge(apr_pool_t *p, const apr_array_header_t 
*arr);
  
  /* apr_array_pstrcat, w/ string separator and APREQ_ESCAPE mode */
  char *apreq_pvcat(apr_pool_t *p, 
                    const apr_array_header_t *arr, 
                    const char *sep, 
                    int mode);
  
  char *apreq_memmem(char* h, apr_off_t hlen, 
                     const char* n, apr_off_t nlen, int part);
  
  apr_off_t apreq_index(char* h, apr_off_t hlen, 
                        const char* n, apr_off_t nlen, int part);
  
  /* url-escapes non-alphanumeric characters */
  char *apreq_escape(apr_pool_t *p, char *s);
  apr_off_t apreq_unescape(char *s);
  
  /* returns date string, (time_str is offset from "now") formatted
   * either as an NSCOOKIE or HTTP date */
  char *apreq_expires(apr_pool_t *p, char *time_str, int type);
  
  /* file sizes (KMG) to bytes */
  apr_int64_t apreq_atol(const char *s);
  /* "duration" strings (YMDhms) to seconds */
  apr_int64_t apreq_atod(const char *s);
  
  /* r->uri, omitting path info */
  
  
  /*
  char *apreq_script_name(void *r);
  char *(apreq_script_path)(void *r);
  #define apreq_script_path(r) 
ap_make_dirstr_parent(r->pool,apreq_script_name(r))
  */
  #ifdef __cplusplus
   }
  #endif
  
  #endif
  
  
  
  1.1                  httpd-apreq-2/src/apreq_tables.c
  
  Index: apreq_tables.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2003 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
   * ITS 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 consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /*
   * Resource allocation code... the code here is responsible for making
   * sure that nothing leaks.
   *
   * rst --- 4/95 --- 6/95
   */
  
  /*
  #include "apr_private.h"
  */
  #include "apr_general.h"
  #include "apr_pools.h"
  #include "apreq_tables.h"
  #include "apr_strings.h"
  #include "apr_lib.h"
  #if APR_HAVE_STDLIB_H
  #include <stdlib.h>
  #endif
  #if APR_HAVE_STRING_H
  #include <string.h>
  #endif
  #if APR_HAVE_STRINGS_H
  #include <strings.h>
  #endif
  
  
  
  
  /********************* table_entry structure ********************/
  
  /* private struct */
  
  typedef struct apreq_table_entry_t {
      const char    *key;
      apreq_value_t *val;
  
      enum { RED, BLACK } color;
      int tree[4];              /* LEFT RIGHT PARENT NEXT */
  } apreq_table_entry_t;
  
  /* LEFT & RIGHT must satisfy !LEFT==RIGHT , !RIGHT==LEFT */
  #define LEFT   0
  #define RIGHT  1
  #define PARENT 2
  #define NEXT   3
  
  
  
  /********************* table structure ********************/
  
  
  #define TABLE_HASH_SIZE 16
  #define TABLE_INDEX_MASK 0x0f
  
  /* TABLE_HASH must be compatible with table comparator t->cmp */
  /* this one is strcasecmp compatible */
  #define TABLE_HASH(key)  (TABLE_INDEX_MASK & *(unsigned char *)(key))
  
  /* coarse locking (maybe API worthy) */
  #define LOCK_TABLE(t)
  #define UNLOCK_TABLE(t)
  #define TABLE_IS_LOCKED(t)
  
  /* finer grained tree locks (internal) */
  #define LOCK_TREE(t,n)
  #define UNLOCK_TREE(t,n)
  #define TREE_IS_LOCKED(t,n)
  
  /* This may someday become public, but has hash+tree harmony embedded. */
  typedef int (apreq_table_cmp_t)(const char *, const char *);
  
  #define TF_LOCK      1
  #define TF_BALANCE   8
  #define TF_PROMOTE  16
  
  
  struct apreq_table_t {
      apr_array_header_t a;
      int ghosts;
  
      apreq_table_cmp_t   *cmp;
      apreq_value_merge_t *merge;
      apreq_value_copy_t  *copy;
  
      int root[TABLE_HASH_SIZE];
      unsigned long locks;
  
      unsigned char flags;
  
  #ifdef MAKE_TABLE_PROFILE
      int creator; /* XXX Is this the right type ??? */
  #endif
  };
  
  
  /***************************** tree macros ****************************/
  
  
  /* NEVER KILL AN ENTRY THAT'S STILL WITHIN THE FOREST */
  #define KILL_ENTRY(t,idx) do { (idx)[o].tree[PARENT] = idx; \
                                 (t)->ghosts++; } while (0)
  #define IS_DEAD(idx)     ( idx[o].tree[PARENT] == idx )
  #define IN_FOREST(t,idx) ( (idx[o].tree[PARENT] >= 0 && ! IS_DEAD(idx))\
                           || idx == (t)->root[TABLE_HASH(idx[o].key)] )
  
  
  /********************* (internal) tree operations ********************/
  
  
  /* MUST ensure n's parent exists before using LR(n) */
  #define LR(n) (  ( (n)[o].tree[PARENT][o].tree[LEFT] == (n) ) ? LEFT : RIGHT  
)
  
  #define PROMOTE(o,r,p)  do rotate(o,r,p,!LR(p)); while (o[p].tree[PARENT] >= 
0)
  
  static APR_INLINE void rotate(apreq_table_entry_t *o, 
                                int *root,
                                const int pivot, 
                                const int direction)
  {
      const int child   = pivot[o].tree[!direction];
      const int parent  = pivot[o].tree[PARENT];
  
      pivot[o].tree[!direction] = child[o].tree[direction];
  
      if (pivot[o].tree[!direction] >= 0)
          pivot[o].tree[!direction][o].tree[PARENT] = pivot;
  
      (parent >= 0) ? parent[o].tree[LR(pivot)] : *root = child;
  
      child[o].tree[PARENT]      = parent;
      child[o].tree[direction]   = pivot;
      pivot[o].tree[PARENT]      = child;
  
  }
  
  
  static APR_INLINE int search(apreq_table_cmp_t *cmp,
                               apreq_table_entry_t *o, 
                               int *elt,
                               const char *key) 
  {
      int idx = *elt;
      if ( idx < 0)
          return 0;
      while (1) {
          int direction = (*cmp)(key,idx[o].key);
  
          if (direction < 0 && idx[o].tree[LEFT] >= 0)
              idx = idx[o].tree[LEFT];
          else if (direction > 0 && idx[o].tree[RIGHT] >= 0)
              idx = idx[o].tree[RIGHT];
          else {
              *elt = idx;
              return direction;
          }
      }
      return 0;
  }
  
  /* XXX Are these macros really needed? */
  #define TREE_PUSH      1
  #define TREE_REPLACE   2
  
  #define TREE_POP       1
  #define TREE_DROP      2
  
  static int insert(apreq_table_cmp_t *cmp,
                    apreq_table_entry_t *o, int *root, int x,
                    apreq_table_entry_t *elt,
                    unsigned flags )
  {
      int idx = elt - o;
      int s = search(cmp, o, &x, elt->key);
  
      if (s == 0) { /* found */
          if (x < 0) { /* empty tree */
              *root = idx;
              elt->tree[LEFT]   = -1;
              elt->tree[RIGHT]  = -1;
              elt->tree[PARENT] = -1;
              return -1;
          }
  
          switch (flags & 3) {
              int parent;
  
          case TREE_PUSH:
              parent = x;
  
              while (parent[o].tree[NEXT] >= 0)
                  parent = parent[o].tree[NEXT];
  
              parent[o].tree[NEXT]  = idx;
              elt->tree[PARENT]      = -1;
              elt->tree[RIGHT]       = -1;
              elt->tree[LEFT]        = -1;
              return x;
  
          case TREE_REPLACE:
              parent = x[o].tree[PARENT];
  
              if (x[o].tree[LEFT] >= 0)
                  x[o].tree[LEFT][o].tree[PARENT] = idx;
  
              if (x[o].tree[RIGHT] >= 0)
                  x[o].tree[RIGHT][o].tree[PARENT] = idx;
  
              (parent >= 0) ? parent[o].tree[LR(x)] : *root = idx;
  
              elt->tree[PARENT] = parent;
              elt->tree[RIGHT]  = x[o].tree[RIGHT];
              elt->tree[LEFT]   = x[o].tree[LEFT];
              elt->color        = x[o].color;
  
              return x;
  
          default:
              return -1;
          }
      }
  
  
      /* The element wasn't in the tree, so add it */
      x[o].tree[s < 0 ? LEFT : RIGHT] = idx;
      elt->tree[PARENT] =  x;
      elt->tree[RIGHT]  = -1;
      elt->tree[LEFT]   = -1;
  
      elt->color = RED;
  
  
      if (flags & TF_BALANCE) {
          while (idx[o].tree[PARENT] >= 0 && idx[o].tree[PARENT][o].color == 
RED)
          {
          /* parent , grandparent, & parent_sibling nodes all exist */
  
              int parent = idx[o].tree[PARENT];
              int grandparent = parent[o].tree[PARENT];
              register const int parent_direction = LR(parent);
              int parent_sibling = grandparent[o].tree[!parent_direction];
  
              if (parent_sibling[o].color == RED) {
                  parent[o].color            = BLACK;
                  parent_sibling[o].color    = BLACK;
                  grandparent[o].color       = RED;
                  idx                        = grandparent;
                  parent                     = idx[o].tree[PARENT];
              }
              else {  /* parent_sibling->color == BLACK */
  
                  if ( LR(idx) != parent_direction ) { /* opposite direction */
                      rotate(o, root, parent, parent_direction); /* demotes idx 
*/
                      idx           = parent;
                      parent        = idx[o].tree[PARENT]; /* idx's old sibling 
*/
                      /* grandparent is unchanged */
                  }
  
                  parent[o].color      = BLACK;
                  grandparent[o].color = RED;
                  rotate(o, root, grandparent, !parent_direction);
              }
          }
      }
      (*root)[o].color = BLACK;
      return -1;
  }
  
  
  static void delete(apreq_table_entry_t *o, 
                     int *root,  const int idx, unsigned flags)
  {
      int x,y;
  
      if ((flags & TREE_POP) && idx[o].tree[NEXT] >= 0) {
          /* pop elt off the stack */
          x = idx[o].tree[PARENT];
          y = idx[o].tree[NEXT];
          idx[o].tree[NEXT] = 0;
  
          y[o].tree[PARENT] = x;
          y[o].tree[LEFT]   = idx[o].tree[LEFT];
          y[o].tree[RIGHT]  = idx[o].tree[RIGHT];
          y[o].color        = idx[o].color;
  
          (x >= 0) ? x[o].tree[LR(idx)] : *root = y;
  
          idx[o].tree[PARENT] = -1;
          idx[o].tree[LEFT]   = -1;
          idx[o].tree[RIGHT]  = -1;
  
          return;
      }
  
      if (idx[o].tree[LEFT] < 0 || idx[o].tree[RIGHT] < 0)
          y = idx;
      else {
          y = idx[o].tree[RIGHT];
          while (y[o].tree[LEFT] >= 0)
              y = y[o].tree[LEFT];
      }
  
      /* x is y's only child */
      x = (y[o].tree[LEFT] >= 0) ? y[o].tree[LEFT] : y[o].tree[RIGHT];
  
      /* remove y from the parent chain */
      x[o].tree[PARENT] = y[o].tree[PARENT];
  
      (y[o].tree[PARENT] >= 0) ?
          y[o].tree[PARENT][o].tree[LR(y)] : *root = x;
  
      if (y != idx) {     /* swap y[o] with idx[o] */
          y[o].tree[LEFT] = idx[o].tree[LEFT];
          y[o].tree[RIGHT] = idx[o].tree[RIGHT];
          y[o].tree[PARENT] = idx[o].tree[PARENT];
          
          (y[o].tree[PARENT] >= 0) ?
              y[o].tree[PARENT][o].tree[LR(y)] : *root = y;
  
          idx[o].tree[LEFT] = -1;
          idx[o].tree[RIGHT] = -1;
          idx[o].tree[PARENT] = -1;
      }
  
      if (y[o].color == RED) {
          y[o].color = idx[o].color;
          return;
      }
  
      /* rebalance tree (standard double-black promotion) */
  
      x[o].color = idx[o].color; /* should this be y[o].color ??? */
  
      if (flags & TF_BALANCE) {
          while (x != *root && x[o].color == BLACK) 
          {
              /* x has a grandparent, parent & sibling */
              int parent = x[o].tree[PARENT];
              int grandparent = parent[o].tree[PARENT];
              register const int direction = LR(x);
              int sibling = parent[o].tree[!direction];
  
              if (sibling[o].color == RED) {
                  sibling[o].color = BLACK;
                  parent[o].color  = RED;
                  rotate(o, root, parent, !direction); /* demote x */
                  grandparent = sibling;
                  sibling = parent[o].tree[!direction];
              }
  
              if (sibling[o].tree[direction][o].color == BLACK && 
                  sibling[o].tree[!direction][o].color == BLACK) 
              {
                  sibling[o].color = RED;
                  x = parent;
              }
              else {
                  if (sibling[o].tree[!direction][o].color == BLACK) {
                      sibling[o].tree[direction][o].color = BLACK;
                      sibling[o].color = RED;
                      rotate(o, root, sibling, !direction); /* demote sibling */
                      sibling = parent[o].tree[!direction];
                  }
                  sibling[o].color = parent[o].color;
                  parent[o].color = BLACK;
                  sibling[o].tree[!direction][o].color = BLACK;
                  rotate(o, root, parent, direction); /* promote x */
                  *root = x;
              }
  
          }
      }
      x[o].color = BLACK;
  }
  
  static int combine(apreq_table_cmp_t *cmp,
                     apreq_table_entry_t *o, int a, 
                     int b, const int n)
  {
      if (a < 0)
          return b;
      else if (b < 0)
          return a;
      else {
          int parent = a[o].tree[PARENT];
          int left = b[o].tree[LEFT] + n;
          int right = b[o].tree[RIGHT] + n;
          parent[o].tree[LR(a)] = b;
          a[o].tree[PARENT] = -1;
          b = insert(cmp,o,&a,b,b+o,TREE_PUSH);
          if (a != b)
              PROMOTE(o,&a,b);
          b[o].tree[PARENT] = parent;
          b[o].tree[LEFT]  = combine(cmp, o, b[o].tree[LEFT],  left,  n);
          b[o].tree[RIGHT] = combine(cmp, o, b[o].tree[RIGHT], right, n);
          return b;
      }
  }
  
  
  /*****************************************************************
   *
   * The "apreq_table" API.
   */
  
  
  /* static void make_array_core(apr_array_header_t *res, apr_pool_t *p,
                            int nelts, int elt_size, int clear) */
  
  #define make_array_core(a,p,n,s,c) do {        \
      if (c)                                     \
          (a)->elts = apr_pcalloc(p, (n) * (s)); \
      else                                       \
          (a)->elts = apr_palloc( p, (n) * (s)); \
      (a)->pool = p;                             \
      (a)->elt_size = s;                         \
      (a)->nelts = 0;                            \
      (a)->nalloc = n;                           \
    } while (0)
  
  #define v2c(ptr) ( (ptr) ? (ptr)->data : NULL )
  
  /*
   * XXX: if you tweak this you should look at is_empty_table() and table_elts()
   * in alloc.h
   */
  
  static void *apr_array_push_noclear(apr_array_header_t *arr)
  {
      if (arr->nelts == arr->nalloc) {
          int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2;
          char *new_data;
  
          new_data = apr_palloc(arr->pool, arr->elt_size * new_size);
  
          memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
          arr->elts = new_data;
          arr->nalloc = new_size;
      }
  
      ++arr->nelts;
      return arr->elts + (arr->elt_size * (arr->nelts - 1));
  }
  
  #ifdef MAKE_TABLE_PROFILE
  static apreq_table_entry_t *table_push(apreq_table_t *t)
  {
      if (t->a.nelts == t->a.nalloc) {
          return NULL;
      }
      return (apreq_table_entry_t *) apr_array_push_noclear(&t->a);
  }
  #else /* MAKE_TABLE_PROFILE */
  #define table_push(t) ((apreq_table_entry_t *)apr_array_push_noclear(&(t)->a))
  #endif /* MAKE_TABLE_PROFILE */
  
  
  
  /********************* table ctors ********************/
  
  
  static apreq_value_t *collapse(apr_pool_t *p,
                                 const apr_array_header_t *arr)
  {
      apreq_value_t **a = (apreq_value_t **)arr->elts;
  
      if (arr->nelts == 0)
          return NULL;
      else 
          return a[arr->nelts - 1];
  }             
  
  
  APREQ_DECLARE(apreq_table_t *) apreq_table_make(apr_pool_t *p, int nelts)
  {
      apreq_table_t *t = apr_palloc(p, sizeof *t);
  
      make_array_core(&t->a, p, nelts, sizeof(apreq_table_entry_t), 0);
  #ifdef MAKE_TABLE_PROFILE
      t->creator = __builtin_return_address(0);
  #endif
  
      /* XXX: is memset(*,-1,*) portable ??? */
      memset(t->root, -1, TABLE_HASH_SIZE * sizeof(int));
  
      t->cmp    = &strcasecmp;
      t->merge  = &apreq_value_merge;
      t->copy   = &apreq_value_copy;
      t->flags  = 0;
      t->ghosts = 0;
      return t;
  }
  
  APREQ_DECLARE(apreq_table_t *) apreq_table_copy(apr_pool_t *p, 
                                                const apreq_table_t *t)
  {
      apreq_table_t *new = apr_palloc(p, sizeof *new);
  
  #ifdef POOL_DEBUG
      /* we don't copy keys and values, so it's necessary that t->a.pool
       * have a life span at least as long as p
       */
      if (!apr_pool_is_ancestor(t->a.pool, p)) {
        fprintf(stderr, "copy_table: t's pool is not an ancestor of p\n");
        abort();
      }
  #endif
  
      make_array_core(&new->a, p, t->a.nalloc, sizeof(apreq_table_entry_t), 0);
      memcpy(new->a.elts, t->a.elts, t->a.nelts * sizeof(apreq_table_entry_t));
      new->a.nelts = t->a.nelts;
      memcpy(new->root, t->root, TABLE_HASH_SIZE * sizeof(int));
      new->merge = t->merge;
      new->copy = t->copy;
      new->cmp = t->cmp;
      new->flags=t->flags;
      return new;
  }
  
  APREQ_DECLARE(apr_table_t *)apreq_table_export(apreq_table_t *t, apr_pool_t 
*p)
  {
      int idx;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
      apr_table_t *s = apr_table_make(p, t->a.nelts);
  
      for (idx = 0; idx < t->a.nelts; ++idx) {
          if (! IS_DEAD(idx))
              apr_table_addn(s, apr_pstrdup(p,idx[o].key), 
                                t->copy(p,idx[o].val)->data);
      }
  
      return s;
  }
  
  APREQ_DECLARE(apreq_table_t *)apreq_table_import(apr_pool_t *p, 
                                                   apr_table_t *s, 
                                                   unsigned f) 
  {
      apreq_table_t *t = apreq_table_make(p,APREQ_DEFAULT_NELTS);
      const apr_array_header_t *a = apr_table_elts(s);
      const apr_table_entry_t *e = (const apr_table_entry_t *)a->elts;
      const apr_table_entry_t *end = e + a->nelts;
  
      t->flags = f;
  
      for ( ; e < end; e++)
          apreq_table_addv(t, apr_pstrdup(p,e->key), 
                           e->val ? apreq_value_make(p, e->val, strlen(e->val)) 
                                  : NULL);
  
      return t;
  }
  
  
  
  /********************* unary table ops ********************/
  
  
  
  APREQ_DECLARE(void) apreq_table_clear(apreq_table_t *t)
  {
      memset(t->root,-1,TABLE_HASH_SIZE * sizeof(int));
      t->a.nelts = 0;
  }
  
  APREQ_DECLARE(int) apreq_table_nelts(const apreq_table_t *t)
  {
      return t->a.nelts - t->ghosts;
  }
  
  APREQ_DECLARE(int) apreq_is_empty_table(const apreq_table_t *t)
  {
      return t != NULL && t->a.nelts > t->ghosts;
  }
  
  
  APREQ_DECLARE(apreq_value_copy_t *)apreq_table_copier(apreq_table_t *t, 
                                                        apreq_value_copy_t *c)
  {
      if (c != NULL)
          t->copy = c;
      return t->copy;
  }
  
  APREQ_DECLARE(apreq_value_merge_t *)apreq_table_merger(apreq_table_t *t, 
                                                         apreq_value_merge_t *m)
  {
      if (m != NULL)
          t->merge = m;
      return t->merge;
  }
  
  APREQ_DECLARE(void) apreq_table_cache_lookups(apreq_table_t *t, const int on)
  {
      if (on)
          t->flags |=  TF_PROMOTE;
      else
          t->flags &= ~TF_PROMOTE;
  }
  
  APREQ_DECLARE(void) apreq_table_balance(apreq_table_t *t, const int on)
  {
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
      if (on) {
          int idx;
          if (t->flags & TF_BALANCE)
              return;
  
          memset(t->root,-1,TABLE_HASH_SIZE * sizeof(int));
          for (idx = 0; idx < t->a.nelts; ++idx)
              insert(t->cmp, o, &t->root[TABLE_HASH(idx[o].key)], -1, &idx[o], 
                     TF_BALANCE | TREE_PUSH);
  
          t->flags &= ~TF_PROMOTE;
          t->flags |= TF_BALANCE;
      }
      else {
          t->flags &= ~TF_BALANCE;
      }
  }
  
  APREQ_DECLARE(void) apreq_table_normalize(apreq_table_t *t)
  {
      if (t->merge) {
          int idx;
          apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
          apr_array_header_t *a = apr_array_make(t->a.pool, APREQ_DEFAULT_NELTS,
                                                 sizeof (apreq_value_t *));
          
          for (idx = 0; idx < t->a.nelts; ++idx) {
              int j = idx[o].tree[NEXT];
  
              if ( j >= 0 && IN_FOREST(t,idx) ) {
  
                  LOCK_TABLE(t);
  
                  idx[o].tree[NEXT] = -1;
                  a->nelts = 0;
                  *(apreq_value_t **)apr_array_push(a) = idx[o].val;
  
                  for ( ; j >= 0; j = j[o].tree[NEXT]) {
                      *(apreq_value_t **)apr_array_push(a) = j[o].val;
                      KILL_ENTRY(t,j);
                  }
  
                  idx[o].val = t->merge(t->a.pool, a);
  
                  UNLOCK_TABLE(t);
              }
          }
      }
  }
  
  static int bury_table_entries(apreq_table_t *t, apr_array_header_t *q)
  {
      /* EXPENSIVE: ~O(4 * t->a.nelts * q->nelts) */
  
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
      int j, n, m, *p = (int *)q->elts;
      int reindex = 0;
  
      /* add sentinel */
      *(int *)apr_array_push(q) = t->a.nelts; 
      q->nelts--;
  
      LOCK_TABLE(t);
  
      /* remove entries O(t->nelts) */
      for ( j=1, n=0; n < q->nelts; ++j, ++n )
          for (m = p[n]+1; m < p[n+1]; ++m) {
              m[o-j] = m[o];
          }
  
      t->a.nelts -= q->nelts;
  
      /* reindex trees */
  
  #define REINDEX(P) for (n=0; P > p[n]; ++n) P--; reindex += n
  
      for (j=0; j < TABLE_HASH_SIZE; ++j) {
          REINDEX( t->root[j] );
      }
  
      for ( j=0; j < t->a.nelts; ++j ) {
          REINDEX( j[o].tree[LEFT]   );
          REINDEX( j[o].tree[RIGHT]  );
          REINDEX( j[o].tree[PARENT] );
          REINDEX( j[o].tree[NEXT]   );
      }
  
  #undef REINDEX
  
      UNLOCK_TABLE(t);
      return reindex;
  }
  
  APREQ_DECLARE(int) apreq_table_ghosts(apreq_table_t *t)
  {
      return t->ghosts;
  }
  APREQ_DECLARE(int) apreq_table_exorcise(apreq_table_t *t)
  {
      int rv = 0;
  
      if (t->ghosts == 0)
          return 0;
  
      if (t->ghosts < t->a.nelts) {
          apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
          /* first eliminate trailing ghosts */
          while (IS_DEAD(t->a.nelts - 1)) {
              t->a.nelts--;
              t->ghosts--;
          }
  
          /* bury any remaining ghosts */
          if (t->ghosts) {
              int idx;
              apr_array_header_t *a = apr_array_make(t->a.pool, 
                                                     t->ghosts + 1, 
                                                     sizeof(int));
              for (idx = 0; idx < t->a.nelts; ++idx)
                  if (IS_DEAD(idx))
                      *(int *)apr_array_push(a) = idx;
  
              rv = bury_table_entries(t,a);
          }
      }
      else { /* nothing but ghosts */
          t->a.nelts = 0;
      }
  
      t->ghosts = 0;
      return rv;
  }
  
  
  
  /********************* accessors ********************/
  
  
  
  APREQ_DECLARE(const char *) apreq_table_get(apreq_table_t *t, 
                                              const char *key)
  {
      int *root = &t->root[TABLE_HASH(key)];
      int idx = *root;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
      if ( idx < 0 || search(t->cmp,o,&idx,key) )
        return NULL;
  
      if (t->flags & TF_PROMOTE && idx != *root) {
          /* modifies tree, which is why t isn't declared as const */
          PROMOTE(o,root,idx);
      }
      return v2c(idx[o].val);
  }
  
  APREQ_DECLARE(apr_array_header_t *) apreq_table_keys(const apreq_table_t *t,
                                                       apr_pool_t *p)
  {
      int i;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
      apr_array_header_t *a = apr_array_make(p, t->a.nelts, 
                                             sizeof(char *));
  
      for (i = 0; i < t->a.nelts; ++i)
          if (IN_FOREST(t,i))
              *(const char **)apr_array_push(a) = i[o].key;
  
      return a;
  }
  
  APREQ_DECLARE(apr_array_header_t *) apreq_table_values(apreq_table_t *t,
                                                         const char *key,
                                                         apr_pool_t *p)
  {
      int i;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
      apr_array_header_t *a = apr_array_make(p, key ? APREQ_DEFAULT_NELTS : 
                                                      t->a.nelts - t->ghosts,
                                                      sizeof(apreq_value_t *));
  
      if (key == NULL) {  /* fetch all values */
          for (i = 0; i < t->a.nelts; ++i)
              if (IN_FOREST(t,i))
                  *(apreq_value_t **)apr_array_push(a) = i[o].val;
      }
      else {
          i = t->root[TABLE_HASH(key)];
          if ( i>=0 && search(t->cmp,o,&i,key) == 0 )
             for ( ; i>=0; i = i[o].tree[NEXT] )
                  *(apreq_value_t **)apr_array_push(a) = i[o].val;
  
          if (t->flags & TF_PROMOTE && i != t->root[TABLE_HASH(key)]) {
              /* modifies tree, so t cannot be declared const */
              int *root = &t->root[TABLE_HASH(key)];
              PROMOTE(o,root,i);
          }
  
      }
  
      return a;
  }
  
  
  
  /********************* table_set* & table_unset ********************/
  
  
  
  APREQ_DECLARE(void) apreq_table_set(apreq_table_t *t, const char *key, 
                                    const char *val)
  {
      if (key && val)
          apreq_table_setb(t, key, strlen(key), val, strlen(val));
      else if (key != NULL)
          apreq_table_setb(t, key, strlen(key), val, 0);
  }
  
  APREQ_DECLARE(void) apreq_table_setb(apreq_table_t *t, 
                                     const char *key, int klen,
                                     const char *val, int vlen)
  {
      apreq_value_t *k, *v;
      apr_pool_t *p = t->a.pool;
  
      if (key == NULL)
          return;
  
      k = apreq_value_make(p, key, klen);
      v = (val == NULL) ? NULL : apreq_value_make(p, val, vlen);
      apreq_table_setv(t, k->data, v);
  }
  
  APREQ_DECLARE(void) apreq_table_setn(apreq_table_t *t, 
                                     const char *key,
                                     const char *val)
  {
      apreq_table_setv(t,key,apreq_char_to_value(val));
  }
  
  APREQ_DECLARE(void) apreq_table_setv(apreq_table_t *t, 
                                       const char *key,
                                       apreq_value_t *val)
  {
      int idx = t->root[TABLE_HASH(key)];
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
  #ifdef POOL_DEBUG
      {
        if (!apr_pool_is_ancestor(apr_pool_find(key), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
        if (!apr_pool_is_ancestor(apr_pool_find(val), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
      }
  #endif
  
      if (idx >= 0 && search(t->cmp,o,&idx,key) == 0) {
          int n;
          idx[o].val = val;
  
          for ( n=idx[o].tree[NEXT]; n>=0; n=n[o].tree[NEXT] )
              KILL_ENTRY(t,n);
  
          idx[o].tree[NEXT] = -1;        
      }
      else {
          apreq_table_entry_t *e = table_push(t);
          e->key = key;
          e->val = val;
          e->tree[NEXT] = -1;
          insert(t->cmp, o,&t->root[TABLE_HASH(key)],idx,e,TREE_PUSH);
      }
  }
  
  APREQ_DECLARE(void) apreq_table_unset(apreq_table_t *t, const char *key)
  {
      int idx = t->root[TABLE_HASH(key)];
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
      if (idx >= 0 && search(t->cmp,o,&idx,key) == 0) {
          int n;
  
          LOCK_TABLE(t);
  
          delete(o,&t->root[TABLE_HASH(key)],idx,TREE_DROP);
  
          for ( n=idx; n>=0; n=n[o].tree[NEXT] )
              KILL_ENTRY(t,n);
  
          UNLOCK_TABLE(t);
      }
  }
  
  
  
  /********************* table_merge* *********************/
  
  
  
  APREQ_DECLARE(void) apreq_table_merge(apreq_table_t *t,
                                      const char *key,
                                      const char *val)
  {
      if (key && val)
          apreq_table_mergeb(t, key, strlen(key), val, strlen(val));
      else if (key != NULL)
          apreq_table_mergeb(t, key, strlen(key), val, 0);
  }
  
  APREQ_DECLARE(void) apreq_table_mergeb(apreq_table_t *t,
                                       const char *key, int klen,
                                       const char *val, int vlen)
  {
      apreq_value_t *k, *v;
      apr_pool_t *p = t->a.pool;
  
      if (key == NULL)
          return;
  
      k = apreq_value_make(p, key, klen);
      v = (val == NULL) ? NULL : apreq_value_make(p, val, vlen);
      apreq_table_mergev(t, k->data, v);
  }
  
  
  APREQ_DECLARE(void) apreq_table_mergen(apreq_table_t *t,
                                       const char *key,
                                       const char *val)
  {
      apreq_table_mergev(t,key,apreq_char_to_value(val));
  }
  
  APREQ_DECLARE(void) apreq_table_mergev(apreq_table_t *t,
                                         const char *key,
                                         apreq_value_t *val)
  {
      int idx = t->root[TABLE_HASH(key)];
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
  #ifdef POOL_DEBUG
      {
        if (!apr_pool_is_ancestor(apr_pool_find(key), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
        if (!apr_pool_is_ancestor(apr_pool_find(val), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
      }
  #endif
  
      if (t->merge && idx >= 0 && search(t->cmp,o,&idx,key) == 0) {
          int n;
          apr_array_header_t *arr = apr_array_make(t->a.pool, 
                                                   APREQ_DEFAULT_NELTS, 
                                                   sizeof(apreq_value_t *));
  
          if (idx[o].val)
              *(apreq_value_t **)apr_array_push(arr) = idx[o].val;
  
          for (n = idx[o].tree[NEXT]; n >= 0; n = n[o].tree[NEXT]) {
              KILL_ENTRY(t,n);
  
              if (n[o].val)
                  *(apreq_value_t **)apr_array_push(arr) = n[o].val;
          }
  
          if (val)
              *(apreq_value_t **)apr_array_push(arr) = val;
  
          if (arr->nelts > 1)
              idx[o].val = t->merge(t->a.pool, arr);
      }
  
      else { /* not found */
          apreq_table_entry_t *e = table_push(t);
          e->key = key;
          e->val = val;
          e->tree[NEXT] = -1;
          insert(t->cmp,o,&t->root[TABLE_HASH(key)],idx,e,TREE_PUSH);
      }
  }
  
  
  
  /********************* table_add* *********************/
  
  
  
  APREQ_DECLARE(void) apreq_table_add(apreq_table_t *t, 
                                     const char *key, 
                                     const char *val)
  {
      if (key && val)
          apreq_table_addb(t, key, strlen(key), val, strlen(val));
      else if (key != NULL)
          apreq_table_addb(t, key, strlen(key), val, 0);
  }
  
  APREQ_DECLARE(void) apreq_table_addb(apreq_table_t *t, 
                                     const char *key, int klen, 
                                     const char *val, int vlen)
  {
      apreq_value_t *k, *v;
      apr_pool_t *p = t->a.pool;
  
      if (key == NULL)
          return;
  
      k = apreq_value_make(p, key, klen);
      v = (val == NULL) ? NULL : apreq_value_make(p, val, vlen);
      apreq_table_addv(t, k->data, v);
  }
  
  APREQ_DECLARE(void) apreq_table_addn(apreq_table_t *t, 
                                     const char *key, 
                                     const char *val)
  {
      apreq_table_addv(t, key, apreq_char_to_value(val));
  }
  
  APREQ_DECLARE(void) apreq_table_addv(apreq_table_t *t, 
                                     const char *key, 
                                     apreq_value_t *val)
  {
      apreq_table_entry_t *elt = table_push(t);
      int *root = &t->root[TABLE_HASH(key)];
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
  #ifdef POOL_DEBUG
      {
        if (!apr_pool_is_ancestor(apr_pool_find(key), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
        if (!apr_pool_is_ancestor(apr_pool_find(val), t->a.pool)) {
            fprintf(stderr, "table_set: key not in ancestor pool of t\n");
            abort();
        }
      }
  #endif
  
      elt->key = key;
      elt->val = val;
      elt->tree[NEXT] = -1;
      insert(t->cmp, o, root, *root, elt,TREE_PUSH);
  }
  
  
  
  /********************* binary operations ********************/
  
  
  
  APREQ_DECLARE(void) apreq_table_cat(apreq_table_t *t, const apreq_table_t *s)
  {
      const int n = t->a.nelts;
      int idx;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
      apr_array_cat(&t->a, &s->a);
  
      LOCK_TABLE(t);
  
      if (t->flags & TF_BALANCE) {
          for (idx = n; idx < t->a.nelts; ++idx) {
              const unsigned char hash = TABLE_HASH(idx[o].key);
  
              if (idx[o].tree[PARENT] == idx - n) { /* ghost from s */
                  KILL_ENTRY(t,idx);
                  continue;
              }
  
              if (idx[o].tree[NEXT] >= 0)
                  idx[o].tree[NEXT] += n;
              
              if (t->root[hash] < 0) {
                  if (idx[o].tree[LEFT] >= 0)
                      idx[o].tree[LEFT] += n;
                  if (idx[o].tree[RIGHT] >= 0)
                      idx[o].tree[RIGHT] += n;
                  if (idx[o].tree[PARENT] >= 0)
                      idx[o].tree[PARENT] += n;
  
                  t->root[hash] = idx;
              }
              else if ( idx[o].tree[PARENT] >= 0 || 
                        s->root[hash] == idx-n ) 
              {
                  insert(t->cmp, o, &t->root[hash], t->root[hash], idx+o, 
                         TREE_PUSH | TF_BALANCE);
              }
          }
      }
      else {
          for (idx = 0; idx < TABLE_HASH_SIZE; ++idx)
              t->root[idx] = combine(t->cmp, o,t->root[idx],s->root[idx],n);
      }
  
      UNLOCK_TABLE(t);
  }
  
  APREQ_DECLARE(apreq_table_t *) apreq_table_overlay(apr_pool_t *p,
                                                     const apreq_table_t 
*overlay,
                                                     const apreq_table_t *base)
  {
      apreq_table_t *t = apr_palloc(p, sizeof *t);
  
  #ifdef POOL_DEBUG
      /* we don't copy keys and values, so it's necessary that
       * overlay->a.pool and base->a.pool have a life span at least
       * as long as p
       */
      if (!apr_pool_is_ancestor(overlay->a.pool, p)) {
        fprintf(stderr,
                "overlay_tables: overlay's pool is not an ancestor of p\n");
        abort();
      }
      if (!apr_pool_is_ancestor(base->a.pool, p)) {
        fprintf(stderr,
                "overlay_tables: base's pool is not an ancestor of p\n");
        abort();
      }
  #endif
  
      if (overlay->cmp != base->cmp)
          return NULL;
  
      t->a.pool = p;
      t->a.elt_size = sizeof(apreq_table_entry_t);
      t->copy = overlay->copy;
      t->cmp = overlay->cmp;
      t->merge = overlay->merge;
      t->flags = overlay->flags & base->flags;
      t->ghosts = overlay->ghosts;
  
      memcpy(t->root, overlay->root, TABLE_HASH_SIZE * sizeof(int));
  
      /* copy overlay array */
      t->a.elts = overlay->a.elts;
      t->a.nelts = overlay->a.nelts;
      t->a.nalloc = overlay->a.nelts;
      (void)apr_array_push(&t->a); /* induce copying */
      t->a.nelts--;
  
  
      if (base->a.nelts) {
          if (t->a.nelts)
              apreq_table_cat(t, base);
          else
              memcpy(t->root, base->root, TABLE_HASH_SIZE * sizeof(int));
      }
  
      return t;
  }
  
  APREQ_DECLARE(void) apreq_table_overlap(apreq_table_t *a, 
                                        const apreq_table_t *b,
                                        const unsigned flags)
  {
  
      const int m = a->a.nelts;
      const int n = b->a.nelts;
      apr_pool_t *p = b->a.pool;
  
      if (m + n == 0 || a->cmp != b->cmp)
          return; /* ERROR !?!? */
  
      /* copy (extend) a using b's pool */
      if (a->a.pool != p) {
          make_array_core(&a->a, p, m+n, sizeof(apreq_table_entry_t), 0);
      }
  
      apreq_table_cat(a, b);
  
      if (flags == APR_OVERLAP_TABLES_SET) {
          apreq_value_merge_t *f = a->merge;
          a->merge = collapse;
          apreq_table_normalize(a);
          a->merge = f;
      }
      else
          apreq_table_normalize(a);
  }
  
  
  /********************* iterators ********************/
  
  APREQ_DECLARE(apr_status_t) apreq_table_fetch(const apreq_table_t *t,
                                                const char **key,
                                                const apreq_value_t **val,
                                                int *off)
  {
      int idx = *off;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
      if (idx < 0 || idx >= t->a.nelts - t->ghosts) {
          *key = NULL;
          *val = NULL;
          return APR_EGENERAL;
      }
  
      if (t->ghosts) {   /* count ghosts: O(N) */
          int n;
  
          for (n=0; n <= idx; ++n)
              if ( IS_DEAD(n) )
                  ++idx;
  
          *off = idx;
      }
  
      *key = idx[o].key;
      *val = idx[o].val;
  
      return APR_SUCCESS;
  }
  
  APREQ_DECLARE(apr_status_t) apreq_table_first(const apreq_table_t *t,
                                                const char **key,
                                                const apreq_value_t **val,
                                                int *off)
  {
      *off = -1;
      return apreq_table_next(t,key,val,off);
  }
  
  APREQ_DECLARE(apr_status_t) apreq_table_next(const apreq_table_t *t,
                                               const char **key, 
                                               const apreq_value_t **val,
                                               int *off)
  {
      int idx = *off;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
      if (idx < -1 || idx >= t->a.nelts) {
          *key = NULL;
          *val = NULL;
          return APR_EGENERAL;
      }
  
      do { 
          if (++idx == t->a.nelts) {
              *off = idx;
              *key = NULL;
              *val = NULL;
              return APR_EGENERAL;
          }
      } while ( IS_DEAD(idx) );
  
      *off = idx;
      *key = idx[o].key;
      *val = idx[o].val;
      return APR_SUCCESS;
  }
  
  APREQ_DECLARE(apr_status_t) apreq_table_last(const apreq_table_t *t, 
                                               const char **key,
                                               const apreq_value_t **val, 
                                               int *off)
  {
      *off = t->a.nelts;
      return apreq_table_prev(t,key,val,off);
  }
  
  APREQ_DECLARE(apr_status_t) apreq_table_prev(const apreq_table_t *t, 
                                               const char **key, 
                                               const apreq_value_t **val,
                                               int *off)
  {
      int idx = *off;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
  
      if (idx <= 0 || idx > t->a.nelts) {
          *key = NULL;
          *val = NULL;
          return APR_EGENERAL;
      }
  
      do { 
          if (--idx == -1) {
              *off = idx;
              *key = NULL;
              *val = NULL;
              return APR_EGENERAL;
          }
      } while ( IS_DEAD(idx) );
  
      *off = idx;
      *key = idx[o].key;
      *val = idx[o].val;
      return APR_SUCCESS;
  }
  
  /* And now for something completely abstract ...
  
   * For each key value given as a vararg:
   *   run the function pointed to as
   *     int comp(void *r, char *key, char *value);
   *   on each valid key-value pair in the apr_table_t t that matches the 
vararg key,
   *   or once for every valid key-value pair if the vararg list is empty,
   *   until the function returns false (0) or we finish the table.
   *
   * Note that we restart the traversal for each vararg, which means that
   * duplicate varargs will result in multiple executions of the function
   * for each matching key.  Note also that if the vararg list is empty,
   * only one traversal will be made and will cut short if comp returns 0.
   *
   * Note that the table_get and table_merge functions assume that each key in
   * the apr_table_t is unique (i.e., no multiple entries with the same key).  
This
   * function does not make that assumption, since it (unfortunately) isn't
   * true for some of Apache's tables.
   *
   * Note that rec is simply passed-on to the comp function, so that the
   * caller can pass additional info for the task.
   *
   * ADDENDUM for apr_table_vdo():
   * 
   * The caching api will allow a user to walk the header values:
   *
   * apr_status_t apr_cache_el_header_walk(apr_cache_el *el, 
   *    int (*comp)(void *, const char *, const char *), void *rec, ...);
   *
   * So it can be ..., however from there I use a  callback that use a va_list:
   *
   * apr_status_t (*cache_el_header_walk)(apr_cache_el *el, 
   *    int (*comp)(void *, const char *, const char *), void *rec, va_list);
   *
   * To pass those ...'s on down to the actual module that will handle walking
   * their headers, in the file case this is actually just an apr_table - and
   * rather than reimplementing apr_table_do (which IMHO would be bad) I just
   * called it with the va_list. For mod_shmem_cache I don't need it since I
   * can't use apr_table's, but mod_file_cache should (though a good hash would
   * be better, but that's a different issue :). 
   *
   * So to make mod_file_cache easier to maintain, it's a good thing
   */
  APREQ_DECLARE(int) apreq_table_do(apreq_table_do_callback_fn_t *comp,
                                    void *rec, const apreq_table_t *t, ...)
  {
      int rv;
  
      va_list vp;
      va_start(vp, t);
      rv = apreq_table_vdo(comp, rec, t, vp);
      va_end(vp);
  
      return rv;
  } 
  
  /* XXX: do the semantics of this routine make any sense?  Right now,
   * if the caller passed in a non-empty va_list of keys to search for,
   * the "early termination" facility only terminates on *that* key; other
   * keys will continue to process.  Note that this only has any effect
   * at all if there are multiple entries in the table with the same key,
   * otherwise the called function can never effectively early-terminate
   * this function, as the zero return value is effectively ignored.
   *
   * Note also that this behavior is at odds with the behavior seen if an
   * empty va_list is passed in -- in that case, a zero return value terminates
   * the entire apr_table_vdo (which is what I think should happen in
   * both cases).
   *
   * If nobody objects soon, I'm going to change the order of the nested
   * loops in this function so that any zero return value from the (*comp)
   * function will cause a full termination of apr_table_vdo.  I'm hesitant
   * at the moment because these (funky) semantics have been around for a
   * very long time, and although Apache doesn't seem to use them at all,
   * some third-party vendor might.  I can only think of one possible reason
   * the existing semantics would make any sense, and it's very Apache-centric,
   * which is this: if (*comp) is looking for matches of a particular
   * substring in request headers (let's say it's looking for a particular
   * cookie name in the Set-Cookie headers), then maybe it wants to be
   * able to stop searching early as soon as it finds that one and move
   * on to the next key.  That's only an optimization of course, but changing
   * the behavior of this function would mean that any code that tried
   * to do that would stop working right.
   *
   * Sigh.  --JCW, 06/28/02
   */
  APREQ_DECLARE(int) apreq_table_vdo(apr_table_do_callback_fn_t *comp,
                                   void *rec, const apreq_table_t *t, va_list 
vp)
  {
      char *argp;
      apreq_table_entry_t *o = (apreq_table_entry_t *)t->a.elts;
      int vdorv = 1;
  
      argp = va_arg(vp, char *);
      do {
          int rv = 1, idx;
          if (argp) {     /* Scan for entries that match the next key */
              idx = t->root[TABLE_HASH(argp)];
              if ( search(t->cmp,o,&idx,argp) == 0 )
                  while (idx >= 0) {
                      rv = (*comp) (rec, idx[o].key, v2c(idx[o].val));
                      idx = idx[o].tree[NEXT];
                  }
          }
          else {          /* Scan the entire table */
              for (idx = 0; rv && (idx < t->a.nelts); ++idx)
                  /* if (idx[o].key) */
                  if (! IS_DEAD(idx) )
                      rv = (*comp) (rec, idx[o].key, v2c(idx[o].val));
          }
          if (rv == 0) {
              vdorv = 0;
          }
      } while (argp && ((argp = va_arg(vp, char *)) != NULL));
  
      return vdorv;
  }
  
  
  
  1.1                  httpd-apreq-2/src/apreq_tables.h
  
  Index: apreq_tables.h
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000-2002 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
   * ITS 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 consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  #ifndef APREQ_TABLES_H
  #define APREQ_TABLES_H
  
  #include "apr_tables.h"
  #include "apreq.h"
  #include <stddef.h>
  #if APR_HAVE_STDARG_H
  #include <stdarg.h>     /* for va_list */
  #endif
  
  #ifdef __cplusplus
  extern "C" {
  #endif /* __cplusplus */
  
  /*
   * Define the structures used by the APR general-purpose library.
   */
  
  /**
   * @file apreq_tables.h
   * @brief APR Table library
   */
  /**
   * @defgroup APREQ_Table Table routines
   * @ingroup APREQ
   *
   * Memory allocation stuff, like pools, arrays, and tables.  Pools
   * and tables are opaque structures to applications, but arrays are
   * published.
   * @{
   */
  /** the table abstract data type */
  typedef struct apreq_table_t apreq_table_t;
  
  /**
   * Make a new table
   * @param p The pool to allocate the pool out of
   * @param nelts The number of elements in the initial table.
   * @return The new table.
   * @warning This table can only store text data
   */
  APREQ_DECLARE(apreq_table_t *) apreq_table_make(apr_pool_t *p, int nelts);
  
  /**
   * Create a new table and copy another table into it
   * @param p The pool to allocate the new table out of
   * @param t The table to copy
   * @return A copy of the table passed in
   */
  APREQ_DECLARE(apreq_table_t *) apreq_table_copy(apr_pool_t *p,
                                              const apreq_table_t *t);
  
  
  APREQ_DECLARE(apr_table_t *)apreq_table_export(apreq_table_t *t, 
                                               apr_pool_t *p);
  APREQ_DECLARE(apreq_table_t *)apreq_table_import(apr_pool_t *p, 
                                                 apr_table_t *s, 
                                                 unsigned f);
  
  
  /**
   * Delete all of the elements from a table
   * @param t The table to clear
   */
  APREQ_DECLARE(void) apreq_table_clear(apreq_table_t *t);
  
  APREQ_DECLARE(int) apreq_table_nelts(const apreq_table_t *t);
  APREQ_DECLARE(int) apreq_is_empty_table(const apreq_table_t *t);
  
  APREQ_DECLARE(apreq_value_copy_t *) apreq_table_copier(apreq_table_t *t, 
                                             apreq_value_copy_t *c);
  APREQ_DECLARE(apreq_value_merge_t *) apreq_table_merger(apreq_table_t *t, 
                                             apreq_value_merge_t *m);
  
  APREQ_DECLARE(void) apreq_table_cache_lookups(apreq_table_t *t, 
                                                const int on);
  
  APREQ_DECLARE(void) apreq_table_balance(apreq_table_t *t, const int on);
  APREQ_DECLARE(void) apreq_table_normalize(apreq_table_t *t);
  
  APREQ_DECLARE(int) apreq_table_ghosts(apreq_table_t *t);
  APREQ_DECLARE(int) apreq_table_exorcise(apreq_table_t *t);
  
  
  /**
   * Get the value associated with a given key from the table.  After this call,
   * The data is still in the table
   * @param t The table to search for the key
   * @param key The key to search for
   * @return The value associated with the key
   */
  APREQ_DECLARE(const char*) apreq_table_get(apreq_table_t *t, 
                                             const char *key);
  
  
  APREQ_DECLARE(apr_array_header_t *) apreq_table_keys(const 
                                                       apreq_table_t *t,
                                                       apr_pool_t *p);
  
  APREQ_DECLARE(apr_array_header_t *) apreq_table_values(apreq_table_t *t,
                                                         const char *key,
                                                         apr_pool_t *p);
  
  
  /**
   * Add a key/value pair to a table, if another element already exists with the
   * same key, this will over-write the old data.
   * @param t The table to add the data to.
   * @param key The key fo use
   * @param val The value to add
   * @remark When adding data, this function makes a copy of both the key and 
the
   *         value.
   */
  APREQ_DECLARE(void) apreq_table_set(apreq_table_t *t, const char *key, 
                                    const char *val);
  
  APREQ_DECLARE(void) apreq_table_setb(apreq_table_t *t, const char *key, 
                                       int klen, const char *val, int vlen);
  
  /**
   * Add a key/value pair to a table, if another element already exists with the
   * same key, this will over-write the old data.
   * @param t The table to add the data to.
   * @param key The key to use
   * @param val The value to add
   * @warning When adding data, this function does not make a copy of the key 
or 
   *          the value, so care should be taken to ensure that the values will 
   *          not change after they have been added..
   */
  APREQ_DECLARE(void) apreq_table_setn(apreq_table_t *t, const char *key, 
                                       const char *val);
  
  APREQ_DECLARE(void) apreq_table_setv(apreq_table_t *t, const char *key, 
                                       apreq_value_t *val);
  
  
  /**
   * Remove data from the table
   * @param t The table to remove data from
   * @param key The key of the data being removed
   */
  APREQ_DECLARE(void) apreq_table_unset(apreq_table_t *t, const char *key);
  
  /**
   * Add data to a table by merging the value with data that has already been 
   * stored
   * @param t The table to search for the data
   * @param key The key to merge data for
   * @param val The data to add
   * @remark If the key is not found, then this function acts like apr_table_add
   */
  
  APREQ_DECLARE(void) apreq_table_merge(apreq_table_t *t, const char *key, 
                                        const char *val);
  
  APREQ_DECLARE(void) apreq_table_mergeb(apreq_table_t *t,
                                         const char *key, int klen,
                                         const char *val, int vlen);
  
  /**
   * Add data to a table by merging the value with data that has already been 
   * stored
   * @param t The table to search for the data
   * @param key The key to merge data for
   * @param val The data to add
   * @remark If the key is not found, then this function acts like 
apr_table_addn
   */
  APREQ_DECLARE(void) apreq_table_mergen(apreq_table_t *t, const char *key,
                                       const char *val);
  
  APREQ_DECLARE(void) apreq_table_mergev(apreq_table_t *t,
                                         const char *key,
                                         apreq_value_t *val);
  
  /**
   * Add data to a table, regardless of whether there is another element with 
the
   * same key.
   * @param t The table to add to
   * @param key The key to use
   * @param val The value to add.
   * @remark When adding data, this function makes a copy of both the key and 
the
   *         value.
   */
  APREQ_DECLARE(void) apreq_table_add(apreq_table_t *t, 
                                      const char *key, const char *val);
  APREQ_DECLARE(void) apreq_table_addb(apreq_table_t *t, 
                                       const char *key, int klen, 
                                       const char *val, int vlen);
  
  /**
   * Add data to a table, regardless of whether there is another element with 
the
   * same key.
   * @param t The table to add to
   * @param key The key to use
   * @param val The value to add.
   * @remark When adding data, this function does not make a copy of the key or 
the
   *         value, so care should be taken to ensure that the values will not 
   *         change after they have been added..
   */
  APREQ_DECLARE(void) apreq_table_addn(apreq_table_t *t, const char *key, 
                                       const char *val);
  APREQ_DECLARE(void) apreq_table_addv(apreq_table_t *t, 
                                       const char *key, 
                                       apreq_value_t *val);
  
  APREQ_DECLARE(void) apreq_table_cat(apreq_table_t *t, const apreq_table_t *s);
  /**
   * Merge two tables into one new table
   * @param p The pool to use for the new table
   * @param over The first table to put in the new table
   * @param under The table to add at the end of the new table
   * @return A new table containing all of the data from the two passed in
   */
  APREQ_DECLARE(apreq_table_t *) apreq_table_overlay(apr_pool_t *p,
                                                     const apreq_table_t *over,
                                                     const apreq_table_t 
*under);
  
  /**
   * For each element in table b, either use setn or mergen to add the data
   * to table a.  Which method is used is determined by the flags passed in.
   * @param a The table to add the data to.
   * @param b The table to iterate over, adding its data to table a
   * @param flags How to add the table to table a.  One of:
   *          APR_OVERLAP_TABLES_SET        Use apr_table_setn
   *          APR_OVERLAP_TABLES_MERGE      Use apr_table_mergen
   * @remark  This function is highly optimized, and uses less memory and CPU 
cycles
   *          than a function that just loops through table b calling other 
functions.
   */
  /**
   *<PRE>
   * Conceptually, apr_table_overlap does this:
   *
   *  apr_array_header_t *barr = apr_table_elts(b);
   *  apr_table_entry_t *belt = (apr_table_entry_t *)barr->elts;
   *  int i;
   *
   *  for (i = 0; i < barr->nelts; ++i) {
   *      if (flags & APR_OVERLAP_TABLES_MERGE) {
   *          apr_table_mergen(a, belt[i].key, belt[i].val);
   *      }
   *      else {
   *          apr_table_setn(a, belt[i].key, belt[i].val);
   *      }
   *  }
   *
   *  Except that it is more efficient (less space and cpu-time) especially
   *  when b has many elements.
   *
   *  Notice the assumptions on the keys and values in b -- they must be
   *  in an ancestor of a's pool.  In practice b and a are usually from
   *  the same pool.
   * </PRE>
   */
  
  APREQ_DECLARE(void) apreq_table_overlap(apreq_table_t *a, 
                                          const apreq_table_t *b,
                                          const unsigned flags);
  
  
  
  /* iterator API */
  
  APREQ_DECLARE(apr_status_t) apreq_table_fetch(const apreq_table_t *t,
                                                const char **key,
                                                const apreq_value_t **val,
                                                int *off);
  
  
  APREQ_DECLARE(apr_status_t) apreq_table_first(const apreq_table_t *t,
                                                const char **key,
                                                const apreq_value_t **val,
                                                int *off);
  APREQ_DECLARE(apr_status_t) apreq_table_next(const apreq_table_t *t,
                                               const char **key, 
                                               const apreq_value_t **val,
                                               int *off);
  APREQ_DECLARE(apr_status_t) apreq_table_last(const apreq_table_t *t, 
                                               const char **key,
                                               const apreq_value_t **val, 
                                               int *off);
  APREQ_DECLARE(apr_status_t) apreq_table_prev(const apreq_table_t *t, 
                                               const char **key, 
                                               const apreq_value_t **val,
                                               int *off);
  /**
   * Declaration prototype for the iterator callback function of apr_table_do()
   * and apr_table_vdo().
   * @param rec The data passed as the first argument to apr_table_[v]do()
   * @param key The key from this iteration of the table
   * @param key The value from this iteration of the table
   * @remark Iteration continues while this callback function returns non-zero.
   * To export the callback function for apr_table_[v]do() it must be declared 
   * in the _NONSTD convention.
   */
  typedef int (apreq_table_do_callback_fn_t)(void *rec, const char *key, 
                                             const char *val);
  
  /** 
   * Iterate over a table running the provided function once for every
   * element in the table.  If there is data passed in as a vararg, then the 
   * function is only run on those elements whose key matches something in 
   * the vararg.  If the vararg is NULL, then every element is run through the
   * function.  Iteration continues while the function returns non-zero.
   * @param comp The function to run
   * @param rec The data to pass as the first argument to the function
   * @param t The table to iterate over
   * @param ... The vararg.  If this is NULL, then all elements in the table are
   *            run through the function, otherwise only those whose key matches
   *            are run.
   * @return FALSE if one of the comp() iterations returned zero; TRUE if all
   *            iterations returned non-zero
   * @see apr_table_do_callback_fn_t
   */
  APREQ_DECLARE(int) apreq_table_do(apreq_table_do_callback_fn_t *comp,
                                    void *rec, 
                                    const apreq_table_t *t, ...);
  
  /** 
   * Iterate over a table running the provided function once for every
   * element in the table.  If there is data passed in as a vararg, then the 
   * function is only run on those element's whose key matches something in 
   * the vararg.  If the vararg is NULL, then every element is run through the
   * function.  Iteration continues while the function returns non-zero.
   * @param comp The function to run
   * @param rec The data to pass as the first argument to the function
   * @param t The table to iterate over
   * @param vp The vararg table.  If this is NULL, then all elements in the 
   *                table are run through the function, otherwise only those 
   *                whose key matches are run.
   * @return FALSE if one of the comp() iterations returned zero; TRUE if all
   *            iterations returned non-zero
   * @see apr_table_do_callback_fn_t
   */
  APREQ_DECLARE(int) apreq_table_vdo(apreq_table_do_callback_fn_t *comp,
                                   void *rec, 
                                   const apreq_table_t *t, va_list);
  
  
  /** @} */
  #ifdef __cplusplus
  }
  #endif
  
  #endif        /* ! APR_TABLES_H */
  
  
  

Reply via email to