1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter/*
18251875Speter * Resource allocation code... the code here is responsible for making
19251875Speter * sure that nothing leaks.
20251875Speter *
21251875Speter * rst --- 4/95 --- 6/95
22251875Speter */
23251875Speter
24251875Speter#include "apr_private.h"
25251875Speter
26251875Speter#include "apr_general.h"
27251875Speter#include "apr_pools.h"
28251875Speter#include "apr_tables.h"
29251875Speter#include "apr_strings.h"
30251875Speter#include "apr_lib.h"
31251875Speter#if APR_HAVE_STDLIB_H
32251875Speter#include <stdlib.h>
33251875Speter#endif
34251875Speter#if APR_HAVE_STRING_H
35251875Speter#include <string.h>
36251875Speter#endif
37251875Speter#if APR_HAVE_STRINGS_H
38251875Speter#include <strings.h>
39251875Speter#endif
40251875Speter
41251875Speter#if (APR_POOL_DEBUG || defined(MAKE_TABLE_PROFILE)) && APR_HAVE_STDIO_H
42251875Speter#include <stdio.h>
43251875Speter#endif
44251875Speter
45251875Speter/*****************************************************************
46251875Speter * This file contains array and apr_table_t functions only.
47251875Speter */
48251875Speter
49251875Speter/*****************************************************************
50251875Speter *
51251875Speter * The 'array' functions...
52251875Speter */
53251875Speter
54251875Speterstatic void make_array_core(apr_array_header_t *res, apr_pool_t *p,
55251875Speter			    int nelts, int elt_size, int clear)
56251875Speter{
57251875Speter    /*
58251875Speter     * Assure sanity if someone asks for
59251875Speter     * array of zero elts.
60251875Speter     */
61251875Speter    if (nelts < 1) {
62251875Speter        nelts = 1;
63251875Speter    }
64251875Speter
65251875Speter    if (clear) {
66251875Speter        res->elts = apr_pcalloc(p, nelts * elt_size);
67251875Speter    }
68251875Speter    else {
69251875Speter        res->elts = apr_palloc(p, nelts * elt_size);
70251875Speter    }
71251875Speter
72251875Speter    res->pool = p;
73251875Speter    res->elt_size = elt_size;
74251875Speter    res->nelts = 0;		/* No active elements yet... */
75251875Speter    res->nalloc = nelts;	/* ...but this many allocated */
76251875Speter}
77251875Speter
78251875SpeterAPR_DECLARE(int) apr_is_empty_array(const apr_array_header_t *a)
79251875Speter{
80251875Speter    return ((a == NULL) || (a->nelts == 0));
81251875Speter}
82251875Speter
83251875SpeterAPR_DECLARE(apr_array_header_t *) apr_array_make(apr_pool_t *p,
84251875Speter						int nelts, int elt_size)
85251875Speter{
86251875Speter    apr_array_header_t *res;
87251875Speter
88251875Speter    res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t));
89251875Speter    make_array_core(res, p, nelts, elt_size, 1);
90251875Speter    return res;
91251875Speter}
92251875Speter
93251875SpeterAPR_DECLARE(void) apr_array_clear(apr_array_header_t *arr)
94251875Speter{
95251875Speter    arr->nelts = 0;
96251875Speter}
97251875Speter
98251875SpeterAPR_DECLARE(void *) apr_array_pop(apr_array_header_t *arr)
99251875Speter{
100251875Speter    if (apr_is_empty_array(arr)) {
101251875Speter        return NULL;
102251875Speter    }
103251875Speter
104251875Speter    return arr->elts + (arr->elt_size * (--arr->nelts));
105251875Speter}
106251875Speter
107251875SpeterAPR_DECLARE(void *) apr_array_push(apr_array_header_t *arr)
108251875Speter{
109251875Speter    if (arr->nelts == arr->nalloc) {
110251875Speter        int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2;
111251875Speter        char *new_data;
112251875Speter
113251875Speter        new_data = apr_palloc(arr->pool, arr->elt_size * new_size);
114251875Speter
115251875Speter        memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
116251875Speter        memset(new_data + arr->nalloc * arr->elt_size, 0,
117251875Speter               arr->elt_size * (new_size - arr->nalloc));
118251875Speter        arr->elts = new_data;
119251875Speter        arr->nalloc = new_size;
120251875Speter    }
121251875Speter
122251875Speter    ++arr->nelts;
123251875Speter    return arr->elts + (arr->elt_size * (arr->nelts - 1));
124251875Speter}
125251875Speter
126251875Speterstatic void *apr_array_push_noclear(apr_array_header_t *arr)
127251875Speter{
128251875Speter    if (arr->nelts == arr->nalloc) {
129251875Speter        int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2;
130251875Speter        char *new_data;
131251875Speter
132251875Speter        new_data = apr_palloc(arr->pool, arr->elt_size * new_size);
133251875Speter
134251875Speter        memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
135251875Speter        arr->elts = new_data;
136251875Speter        arr->nalloc = new_size;
137251875Speter    }
138251875Speter
139251875Speter    ++arr->nelts;
140251875Speter    return arr->elts + (arr->elt_size * (arr->nelts - 1));
141251875Speter}
142251875Speter
143251875SpeterAPR_DECLARE(void) apr_array_cat(apr_array_header_t *dst,
144251875Speter			       const apr_array_header_t *src)
145251875Speter{
146251875Speter    int elt_size = dst->elt_size;
147251875Speter
148251875Speter    if (dst->nelts + src->nelts > dst->nalloc) {
149251875Speter	int new_size = (dst->nalloc <= 0) ? 1 : dst->nalloc * 2;
150251875Speter	char *new_data;
151251875Speter
152251875Speter	while (dst->nelts + src->nelts > new_size) {
153251875Speter	    new_size *= 2;
154251875Speter	}
155251875Speter
156251875Speter	new_data = apr_pcalloc(dst->pool, elt_size * new_size);
157251875Speter	memcpy(new_data, dst->elts, dst->nalloc * elt_size);
158251875Speter
159251875Speter	dst->elts = new_data;
160251875Speter	dst->nalloc = new_size;
161251875Speter    }
162251875Speter
163251875Speter    memcpy(dst->elts + dst->nelts * elt_size, src->elts,
164251875Speter	   elt_size * src->nelts);
165251875Speter    dst->nelts += src->nelts;
166251875Speter}
167251875Speter
168251875SpeterAPR_DECLARE(apr_array_header_t *) apr_array_copy(apr_pool_t *p,
169251875Speter						const apr_array_header_t *arr)
170251875Speter{
171251875Speter    apr_array_header_t *res =
172251875Speter        (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t));
173251875Speter    make_array_core(res, p, arr->nalloc, arr->elt_size, 0);
174251875Speter
175251875Speter    memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts);
176251875Speter    res->nelts = arr->nelts;
177251875Speter    memset(res->elts + res->elt_size * res->nelts, 0,
178251875Speter           res->elt_size * (res->nalloc - res->nelts));
179251875Speter    return res;
180251875Speter}
181251875Speter
182251875Speter/* This cute function copies the array header *only*, but arranges
183251875Speter * for the data section to be copied on the first push or arraycat.
184251875Speter * It's useful when the elements of the array being copied are
185251875Speter * read only, but new stuff *might* get added on the end; we have the
186251875Speter * overhead of the full copy only where it is really needed.
187251875Speter */
188251875Speter
189251875Speterstatic APR_INLINE void copy_array_hdr_core(apr_array_header_t *res,
190251875Speter					   const apr_array_header_t *arr)
191251875Speter{
192251875Speter    res->elts = arr->elts;
193251875Speter    res->elt_size = arr->elt_size;
194251875Speter    res->nelts = arr->nelts;
195251875Speter    res->nalloc = arr->nelts;	/* Force overflow on push */
196251875Speter}
197251875Speter
198251875SpeterAPR_DECLARE(apr_array_header_t *)
199251875Speter    apr_array_copy_hdr(apr_pool_t *p,
200251875Speter		       const apr_array_header_t *arr)
201251875Speter{
202251875Speter    apr_array_header_t *res;
203251875Speter
204251875Speter    res = (apr_array_header_t *) apr_palloc(p, sizeof(apr_array_header_t));
205251875Speter    res->pool = p;
206251875Speter    copy_array_hdr_core(res, arr);
207251875Speter    return res;
208251875Speter}
209251875Speter
210251875Speter/* The above is used here to avoid consing multiple new array bodies... */
211251875Speter
212251875SpeterAPR_DECLARE(apr_array_header_t *)
213251875Speter    apr_array_append(apr_pool_t *p,
214251875Speter		      const apr_array_header_t *first,
215251875Speter		      const apr_array_header_t *second)
216251875Speter{
217251875Speter    apr_array_header_t *res = apr_array_copy_hdr(p, first);
218251875Speter
219251875Speter    apr_array_cat(res, second);
220251875Speter    return res;
221251875Speter}
222251875Speter
223251875Speter/* apr_array_pstrcat generates a new string from the apr_pool_t containing
224251875Speter * the concatenated sequence of substrings referenced as elements within
225251875Speter * the array.  The string will be empty if all substrings are empty or null,
226251875Speter * or if there are no elements in the array.
227251875Speter * If sep is non-NUL, it will be inserted between elements as a separator.
228251875Speter */
229251875SpeterAPR_DECLARE(char *) apr_array_pstrcat(apr_pool_t *p,
230251875Speter				     const apr_array_header_t *arr,
231251875Speter				     const char sep)
232251875Speter{
233251875Speter    char *cp, *res, **strpp;
234251875Speter    apr_size_t len;
235251875Speter    int i;
236251875Speter
237251875Speter    if (arr->nelts <= 0 || arr->elts == NULL) {    /* Empty table? */
238251875Speter        return (char *) apr_pcalloc(p, 1);
239251875Speter    }
240251875Speter
241251875Speter    /* Pass one --- find length of required string */
242251875Speter
243251875Speter    len = 0;
244251875Speter    for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
245251875Speter        if (strpp && *strpp != NULL) {
246251875Speter            len += strlen(*strpp);
247251875Speter        }
248251875Speter        if (++i >= arr->nelts) {
249251875Speter            break;
250251875Speter	}
251251875Speter        if (sep) {
252251875Speter            ++len;
253251875Speter	}
254251875Speter    }
255251875Speter
256251875Speter    /* Allocate the required string */
257251875Speter
258251875Speter    res = (char *) apr_palloc(p, len + 1);
259251875Speter    cp = res;
260251875Speter
261251875Speter    /* Pass two --- copy the argument strings into the result space */
262251875Speter
263251875Speter    for (i = 0, strpp = (char **) arr->elts; ; ++strpp) {
264251875Speter        if (strpp && *strpp != NULL) {
265251875Speter            len = strlen(*strpp);
266251875Speter            memcpy(cp, *strpp, len);
267251875Speter            cp += len;
268251875Speter        }
269251875Speter        if (++i >= arr->nelts) {
270251875Speter            break;
271251875Speter	}
272251875Speter        if (sep) {
273251875Speter            *cp++ = sep;
274251875Speter	}
275251875Speter    }
276251875Speter
277251875Speter    *cp = '\0';
278251875Speter
279251875Speter    /* Return the result string */
280251875Speter
281251875Speter    return res;
282251875Speter}
283251875Speter
284251875Speter
285251875Speter/*****************************************************************
286251875Speter *
287251875Speter * The "table" functions.
288251875Speter */
289251875Speter
290251875Speter#if APR_CHARSET_EBCDIC
291251875Speter#define CASE_MASK 0xbfbfbfbf
292251875Speter#else
293251875Speter#define CASE_MASK 0xdfdfdfdf
294251875Speter#endif
295251875Speter
296251875Speter#define TABLE_HASH_SIZE 32
297251875Speter#define TABLE_INDEX_MASK 0x1f
298251875Speter#define TABLE_HASH(key)  (TABLE_INDEX_MASK & *(unsigned char *)(key))
299251875Speter#define TABLE_INDEX_IS_INITIALIZED(t, i) ((t)->index_initialized & (1 << (i)))
300251875Speter#define TABLE_SET_INDEX_INITIALIZED(t, i) ((t)->index_initialized |= (1 << (i)))
301251875Speter
302251875Speter/* Compute the "checksum" for a key, consisting of the first
303251875Speter * 4 bytes, normalized for case-insensitivity and packed into
304251875Speter * an int...this checksum allows us to do a single integer
305251875Speter * comparison as a fast check to determine whether we can
306251875Speter * skip a strcasecmp
307251875Speter */
308251875Speter#define COMPUTE_KEY_CHECKSUM(key, checksum)    \
309251875Speter{                                              \
310251875Speter    const char *k = (key);                     \
311251875Speter    apr_uint32_t c = (apr_uint32_t)*k;         \
312251875Speter    (checksum) = c;                            \
313251875Speter    (checksum) <<= 8;                          \
314251875Speter    if (c) {                                   \
315251875Speter        c = (apr_uint32_t)*++k;                \
316251875Speter        checksum |= c;                         \
317251875Speter    }                                          \
318251875Speter    (checksum) <<= 8;                          \
319251875Speter    if (c) {                                   \
320251875Speter        c = (apr_uint32_t)*++k;                \
321251875Speter        checksum |= c;                         \
322251875Speter    }                                          \
323251875Speter    (checksum) <<= 8;                          \
324251875Speter    if (c) {                                   \
325251875Speter        c = (apr_uint32_t)*++k;                \
326251875Speter        checksum |= c;                         \
327251875Speter    }                                          \
328251875Speter    checksum &= CASE_MASK;                     \
329251875Speter}
330251875Speter
331251875Speter/** The opaque string-content table type */
332251875Speterstruct apr_table_t {
333251875Speter    /* This has to be first to promote backwards compatibility with
334251875Speter     * older modules which cast a apr_table_t * to an apr_array_header_t *...
335251875Speter     * they should use the apr_table_elts() function for most of the
336251875Speter     * cases they do this for.
337251875Speter     */
338251875Speter    /** The underlying array for the table */
339251875Speter    apr_array_header_t a;
340251875Speter#ifdef MAKE_TABLE_PROFILE
341251875Speter    /** Who created the array. */
342251875Speter    void *creator;
343251875Speter#endif
344251875Speter    /* An index to speed up table lookups.  The way this works is:
345251875Speter     *   - Hash the key into the index:
346251875Speter     *     - index_first[TABLE_HASH(key)] is the offset within
347251875Speter     *       the table of the first entry with that key
348251875Speter     *     - index_last[TABLE_HASH(key)] is the offset within
349251875Speter     *       the table of the last entry with that key
350251875Speter     *   - If (and only if) there is no entry in the table whose
351251875Speter     *     key hashes to index element i, then the i'th bit
352251875Speter     *     of index_initialized will be zero.  (Check this before
353251875Speter     *     trying to use index_first[i] or index_last[i]!)
354251875Speter     */
355251875Speter    apr_uint32_t index_initialized;
356251875Speter    int index_first[TABLE_HASH_SIZE];
357251875Speter    int index_last[TABLE_HASH_SIZE];
358251875Speter};
359251875Speter
360269847Speter/* keep state for apr_table_getm() */
361269847Spetertypedef struct
362269847Speter{
363269847Speter    apr_pool_t *p;
364269847Speter    const char *first;
365269847Speter    apr_array_header_t *merged;
366269847Speter} table_getm_t;
367269847Speter
368251875Speter/*
369251875Speter * NOTICE: if you tweak this you should look at is_empty_table()
370251875Speter * and table_elts() in alloc.h
371251875Speter */
372251875Speter#ifdef MAKE_TABLE_PROFILE
373251875Speterstatic apr_table_entry_t *do_table_push(const char *func, apr_table_t *t)
374251875Speter{
375251875Speter    if (t->a.nelts == t->a.nalloc) {
376251875Speter        fprintf(stderr, "%s: table created by %p hit limit of %u\n",
377251875Speter                func ? func : "table_push", t->creator, t->a.nalloc);
378251875Speter    }
379251875Speter    return (apr_table_entry_t *) apr_array_push_noclear(&t->a);
380251875Speter}
381251875Speter#if defined(__GNUC__) && __GNUC__ >= 2
382251875Speter#define table_push(t) do_table_push(__FUNCTION__, t)
383251875Speter#else
384251875Speter#define table_push(t) do_table_push(NULL, t)
385251875Speter#endif
386251875Speter#else /* MAKE_TABLE_PROFILE */
387251875Speter#define table_push(t)	((apr_table_entry_t *) apr_array_push_noclear(&(t)->a))
388251875Speter#endif /* MAKE_TABLE_PROFILE */
389251875Speter
390251875SpeterAPR_DECLARE(const apr_array_header_t *) apr_table_elts(const apr_table_t *t)
391251875Speter{
392251875Speter    return (const apr_array_header_t *)t;
393251875Speter}
394251875Speter
395251875SpeterAPR_DECLARE(int) apr_is_empty_table(const apr_table_t *t)
396251875Speter{
397251875Speter    return ((t == NULL) || (t->a.nelts == 0));
398251875Speter}
399251875Speter
400251875SpeterAPR_DECLARE(apr_table_t *) apr_table_make(apr_pool_t *p, int nelts)
401251875Speter{
402251875Speter    apr_table_t *t = apr_palloc(p, sizeof(apr_table_t));
403251875Speter
404251875Speter    make_array_core(&t->a, p, nelts, sizeof(apr_table_entry_t), 0);
405251875Speter#ifdef MAKE_TABLE_PROFILE
406251875Speter    t->creator = __builtin_return_address(0);
407251875Speter#endif
408251875Speter    t->index_initialized = 0;
409251875Speter    return t;
410251875Speter}
411251875Speter
412251875SpeterAPR_DECLARE(apr_table_t *) apr_table_copy(apr_pool_t *p, const apr_table_t *t)
413251875Speter{
414251875Speter    apr_table_t *new = apr_palloc(p, sizeof(apr_table_t));
415251875Speter
416251875Speter#if APR_POOL_DEBUG
417251875Speter    /* we don't copy keys and values, so it's necessary that t->a.pool
418251875Speter     * have a life span at least as long as p
419251875Speter     */
420251875Speter    if (!apr_pool_is_ancestor(t->a.pool, p)) {
421251875Speter	fprintf(stderr, "apr_table_copy: t's pool is not an ancestor of p\n");
422251875Speter	abort();
423251875Speter    }
424251875Speter#endif
425251875Speter    make_array_core(&new->a, p, t->a.nalloc, sizeof(apr_table_entry_t), 0);
426251875Speter    memcpy(new->a.elts, t->a.elts, t->a.nelts * sizeof(apr_table_entry_t));
427251875Speter    new->a.nelts = t->a.nelts;
428251875Speter    memcpy(new->index_first, t->index_first, sizeof(int) * TABLE_HASH_SIZE);
429251875Speter    memcpy(new->index_last, t->index_last, sizeof(int) * TABLE_HASH_SIZE);
430251875Speter    new->index_initialized = t->index_initialized;
431251875Speter    return new;
432251875Speter}
433251875Speter
434251875SpeterAPR_DECLARE(apr_table_t *) apr_table_clone(apr_pool_t *p, const apr_table_t *t)
435251875Speter{
436251875Speter    const apr_array_header_t *array = apr_table_elts(t);
437251875Speter    apr_table_entry_t *elts = (apr_table_entry_t *) array->elts;
438251875Speter    apr_table_t *new = apr_table_make(p, array->nelts);
439251875Speter    int i;
440251875Speter
441251875Speter    for (i = 0; i < array->nelts; i++) {
442251875Speter        apr_table_add(new, elts[i].key, elts[i].val);
443251875Speter    }
444251875Speter
445251875Speter    return new;
446251875Speter}
447251875Speter
448251875Speterstatic void table_reindex(apr_table_t *t)
449251875Speter{
450251875Speter    int i;
451251875Speter    int hash;
452251875Speter    apr_table_entry_t *next_elt = (apr_table_entry_t *) t->a.elts;
453251875Speter
454251875Speter    t->index_initialized = 0;
455251875Speter    for (i = 0; i < t->a.nelts; i++, next_elt++) {
456251875Speter        hash = TABLE_HASH(next_elt->key);
457251875Speter        t->index_last[hash] = i;
458251875Speter        if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
459251875Speter            t->index_first[hash] = i;
460251875Speter            TABLE_SET_INDEX_INITIALIZED(t, hash);
461251875Speter        }
462251875Speter    }
463251875Speter}
464251875Speter
465251875SpeterAPR_DECLARE(void) apr_table_clear(apr_table_t *t)
466251875Speter{
467251875Speter    t->a.nelts = 0;
468251875Speter    t->index_initialized = 0;
469251875Speter}
470251875Speter
471251875SpeterAPR_DECLARE(const char *) apr_table_get(const apr_table_t *t, const char *key)
472251875Speter{
473251875Speter    apr_table_entry_t *next_elt;
474251875Speter    apr_table_entry_t *end_elt;
475251875Speter    apr_uint32_t checksum;
476251875Speter    int hash;
477251875Speter
478251875Speter    if (key == NULL) {
479251875Speter	return NULL;
480251875Speter    }
481251875Speter
482251875Speter    hash = TABLE_HASH(key);
483251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
484251875Speter        return NULL;
485251875Speter    }
486251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
487251875Speter    next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];;
488251875Speter    end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
489251875Speter
490251875Speter    for (; next_elt <= end_elt; next_elt++) {
491251875Speter	if ((checksum == next_elt->key_checksum) &&
492251875Speter            !strcasecmp(next_elt->key, key)) {
493251875Speter	    return next_elt->val;
494251875Speter	}
495251875Speter    }
496251875Speter
497251875Speter    return NULL;
498251875Speter}
499251875Speter
500251875SpeterAPR_DECLARE(void) apr_table_set(apr_table_t *t, const char *key,
501251875Speter                                const char *val)
502251875Speter{
503251875Speter    apr_table_entry_t *next_elt;
504251875Speter    apr_table_entry_t *end_elt;
505251875Speter    apr_table_entry_t *table_end;
506251875Speter    apr_uint32_t checksum;
507251875Speter    int hash;
508251875Speter
509251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
510251875Speter    hash = TABLE_HASH(key);
511251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
512251875Speter        t->index_first[hash] = t->a.nelts;
513251875Speter        TABLE_SET_INDEX_INITIALIZED(t, hash);
514251875Speter        goto add_new_elt;
515251875Speter    }
516251875Speter    next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];;
517251875Speter    end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
518251875Speter    table_end =((apr_table_entry_t *) t->a.elts) + t->a.nelts;
519251875Speter
520251875Speter    for (; next_elt <= end_elt; next_elt++) {
521251875Speter	if ((checksum == next_elt->key_checksum) &&
522251875Speter            !strcasecmp(next_elt->key, key)) {
523251875Speter
524251875Speter            /* Found an existing entry with the same key, so overwrite it */
525251875Speter
526251875Speter            int must_reindex = 0;
527251875Speter            apr_table_entry_t *dst_elt = NULL;
528251875Speter
529251875Speter            next_elt->val = apr_pstrdup(t->a.pool, val);
530251875Speter
531251875Speter            /* Remove any other instances of this key */
532251875Speter            for (next_elt++; next_elt <= end_elt; next_elt++) {
533251875Speter                if ((checksum == next_elt->key_checksum) &&
534251875Speter                    !strcasecmp(next_elt->key, key)) {
535251875Speter                    t->a.nelts--;
536251875Speter                    if (!dst_elt) {
537251875Speter                        dst_elt = next_elt;
538251875Speter                    }
539251875Speter                }
540251875Speter                else if (dst_elt) {
541251875Speter                    *dst_elt++ = *next_elt;
542251875Speter                    must_reindex = 1;
543251875Speter                }
544251875Speter            }
545251875Speter
546251875Speter            /* If we've removed anything, shift over the remainder
547251875Speter             * of the table (note that the previous loop didn't
548251875Speter             * run to the end of the table, just to the last match
549251875Speter             * for the index)
550251875Speter             */
551251875Speter            if (dst_elt) {
552251875Speter                for (; next_elt < table_end; next_elt++) {
553251875Speter                    *dst_elt++ = *next_elt;
554251875Speter                }
555251875Speter                must_reindex = 1;
556251875Speter            }
557251875Speter            if (must_reindex) {
558251875Speter                table_reindex(t);
559251875Speter            }
560251875Speter            return;
561251875Speter        }
562251875Speter    }
563251875Speter
564251875Speteradd_new_elt:
565251875Speter    t->index_last[hash] = t->a.nelts;
566251875Speter    next_elt = (apr_table_entry_t *) table_push(t);
567251875Speter    next_elt->key = apr_pstrdup(t->a.pool, key);
568251875Speter    next_elt->val = apr_pstrdup(t->a.pool, val);
569251875Speter    next_elt->key_checksum = checksum;
570251875Speter}
571251875Speter
572251875SpeterAPR_DECLARE(void) apr_table_setn(apr_table_t *t, const char *key,
573251875Speter                                 const char *val)
574251875Speter{
575251875Speter    apr_table_entry_t *next_elt;
576251875Speter    apr_table_entry_t *end_elt;
577251875Speter    apr_table_entry_t *table_end;
578251875Speter    apr_uint32_t checksum;
579251875Speter    int hash;
580251875Speter
581251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
582251875Speter    hash = TABLE_HASH(key);
583251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
584251875Speter        t->index_first[hash] = t->a.nelts;
585251875Speter        TABLE_SET_INDEX_INITIALIZED(t, hash);
586251875Speter        goto add_new_elt;
587251875Speter    }
588251875Speter    next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];;
589251875Speter    end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
590251875Speter    table_end =((apr_table_entry_t *) t->a.elts) + t->a.nelts;
591251875Speter
592251875Speter    for (; next_elt <= end_elt; next_elt++) {
593251875Speter	if ((checksum == next_elt->key_checksum) &&
594251875Speter            !strcasecmp(next_elt->key, key)) {
595251875Speter
596251875Speter            /* Found an existing entry with the same key, so overwrite it */
597251875Speter
598251875Speter            int must_reindex = 0;
599251875Speter            apr_table_entry_t *dst_elt = NULL;
600251875Speter
601251875Speter            next_elt->val = (char *)val;
602251875Speter
603251875Speter            /* Remove any other instances of this key */
604251875Speter            for (next_elt++; next_elt <= end_elt; next_elt++) {
605251875Speter                if ((checksum == next_elt->key_checksum) &&
606251875Speter                    !strcasecmp(next_elt->key, key)) {
607251875Speter                    t->a.nelts--;
608251875Speter                    if (!dst_elt) {
609251875Speter                        dst_elt = next_elt;
610251875Speter                    }
611251875Speter                }
612251875Speter                else if (dst_elt) {
613251875Speter                    *dst_elt++ = *next_elt;
614251875Speter                    must_reindex = 1;
615251875Speter                }
616251875Speter            }
617251875Speter
618251875Speter            /* If we've removed anything, shift over the remainder
619251875Speter             * of the table (note that the previous loop didn't
620251875Speter             * run to the end of the table, just to the last match
621251875Speter             * for the index)
622251875Speter             */
623251875Speter            if (dst_elt) {
624251875Speter                for (; next_elt < table_end; next_elt++) {
625251875Speter                    *dst_elt++ = *next_elt;
626251875Speter                }
627251875Speter                must_reindex = 1;
628251875Speter            }
629251875Speter            if (must_reindex) {
630251875Speter                table_reindex(t);
631251875Speter            }
632251875Speter            return;
633251875Speter        }
634251875Speter    }
635251875Speter
636251875Speteradd_new_elt:
637251875Speter    t->index_last[hash] = t->a.nelts;
638251875Speter    next_elt = (apr_table_entry_t *) table_push(t);
639251875Speter    next_elt->key = (char *)key;
640251875Speter    next_elt->val = (char *)val;
641251875Speter    next_elt->key_checksum = checksum;
642251875Speter}
643251875Speter
644251875SpeterAPR_DECLARE(void) apr_table_unset(apr_table_t *t, const char *key)
645251875Speter{
646251875Speter    apr_table_entry_t *next_elt;
647251875Speter    apr_table_entry_t *end_elt;
648251875Speter    apr_table_entry_t *dst_elt;
649251875Speter    apr_uint32_t checksum;
650251875Speter    int hash;
651251875Speter    int must_reindex;
652251875Speter
653251875Speter    hash = TABLE_HASH(key);
654251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
655251875Speter        return;
656251875Speter    }
657251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
658251875Speter    next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];
659251875Speter    end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
660251875Speter    must_reindex = 0;
661251875Speter    for (; next_elt <= end_elt; next_elt++) {
662251875Speter	if ((checksum == next_elt->key_checksum) &&
663251875Speter            !strcasecmp(next_elt->key, key)) {
664251875Speter
665251875Speter            /* Found a match: remove this entry, plus any additional
666251875Speter             * matches for the same key that might follow
667251875Speter             */
668251875Speter            apr_table_entry_t *table_end = ((apr_table_entry_t *) t->a.elts) +
669251875Speter                t->a.nelts;
670251875Speter            t->a.nelts--;
671251875Speter            dst_elt = next_elt;
672251875Speter            for (next_elt++; next_elt <= end_elt; next_elt++) {
673251875Speter                if ((checksum == next_elt->key_checksum) &&
674251875Speter                    !strcasecmp(next_elt->key, key)) {
675251875Speter                    t->a.nelts--;
676251875Speter                }
677251875Speter                else {
678251875Speter                    *dst_elt++ = *next_elt;
679251875Speter                }
680251875Speter            }
681251875Speter
682251875Speter            /* Shift over the remainder of the table (note that
683251875Speter             * the previous loop didn't run to the end of the table,
684251875Speter             * just to the last match for the index)
685251875Speter             */
686251875Speter            for (; next_elt < table_end; next_elt++) {
687251875Speter                *dst_elt++ = *next_elt;
688251875Speter            }
689251875Speter            must_reindex = 1;
690251875Speter            break;
691251875Speter        }
692251875Speter    }
693251875Speter    if (must_reindex) {
694251875Speter        table_reindex(t);
695251875Speter    }
696251875Speter}
697251875Speter
698251875SpeterAPR_DECLARE(void) apr_table_merge(apr_table_t *t, const char *key,
699251875Speter				 const char *val)
700251875Speter{
701251875Speter    apr_table_entry_t *next_elt;
702251875Speter    apr_table_entry_t *end_elt;
703251875Speter    apr_uint32_t checksum;
704251875Speter    int hash;
705251875Speter
706251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
707251875Speter    hash = TABLE_HASH(key);
708251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
709251875Speter        t->index_first[hash] = t->a.nelts;
710251875Speter        TABLE_SET_INDEX_INITIALIZED(t, hash);
711251875Speter        goto add_new_elt;
712251875Speter    }
713251875Speter    next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];
714251875Speter    end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
715251875Speter
716251875Speter    for (; next_elt <= end_elt; next_elt++) {
717251875Speter	if ((checksum == next_elt->key_checksum) &&
718251875Speter            !strcasecmp(next_elt->key, key)) {
719251875Speter
720251875Speter            /* Found an existing entry with the same key, so merge with it */
721251875Speter	    next_elt->val = apr_pstrcat(t->a.pool, next_elt->val, ", ",
722251875Speter                                        val, NULL);
723251875Speter            return;
724251875Speter        }
725251875Speter    }
726251875Speter
727251875Speteradd_new_elt:
728251875Speter    t->index_last[hash] = t->a.nelts;
729251875Speter    next_elt = (apr_table_entry_t *) table_push(t);
730251875Speter    next_elt->key = apr_pstrdup(t->a.pool, key);
731251875Speter    next_elt->val = apr_pstrdup(t->a.pool, val);
732251875Speter    next_elt->key_checksum = checksum;
733251875Speter}
734251875Speter
735251875SpeterAPR_DECLARE(void) apr_table_mergen(apr_table_t *t, const char *key,
736251875Speter				  const char *val)
737251875Speter{
738251875Speter    apr_table_entry_t *next_elt;
739251875Speter    apr_table_entry_t *end_elt;
740251875Speter    apr_uint32_t checksum;
741251875Speter    int hash;
742251875Speter
743251875Speter#if APR_POOL_DEBUG
744251875Speter    {
745253734Speter	apr_pool_t *pool;
746253734Speter	pool = apr_pool_find(key);
747269847Speter	if ((pool != (apr_pool_t *)key)
748269847Speter            && (!apr_pool_is_ancestor(pool, t->a.pool))) {
749251875Speter	    fprintf(stderr, "apr_table_mergen: key not in ancestor pool of t\n");
750251875Speter	    abort();
751251875Speter	}
752253734Speter	pool = apr_pool_find(val);
753269847Speter	if ((pool != (apr_pool_t *)val)
754269847Speter            && (!apr_pool_is_ancestor(pool, t->a.pool))) {
755251875Speter	    fprintf(stderr, "apr_table_mergen: val not in ancestor pool of t\n");
756251875Speter	    abort();
757251875Speter	}
758251875Speter    }
759251875Speter#endif
760251875Speter
761251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
762251875Speter    hash = TABLE_HASH(key);
763251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
764251875Speter        t->index_first[hash] = t->a.nelts;
765251875Speter        TABLE_SET_INDEX_INITIALIZED(t, hash);
766251875Speter        goto add_new_elt;
767251875Speter    }
768251875Speter    next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];;
769251875Speter    end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
770251875Speter
771251875Speter    for (; next_elt <= end_elt; next_elt++) {
772251875Speter	if ((checksum == next_elt->key_checksum) &&
773251875Speter            !strcasecmp(next_elt->key, key)) {
774251875Speter
775251875Speter            /* Found an existing entry with the same key, so merge with it */
776251875Speter	    next_elt->val = apr_pstrcat(t->a.pool, next_elt->val, ", ",
777251875Speter                                        val, NULL);
778251875Speter            return;
779251875Speter        }
780251875Speter    }
781251875Speter
782251875Speteradd_new_elt:
783251875Speter    t->index_last[hash] = t->a.nelts;
784251875Speter    next_elt = (apr_table_entry_t *) table_push(t);
785251875Speter    next_elt->key = (char *)key;
786251875Speter    next_elt->val = (char *)val;
787251875Speter    next_elt->key_checksum = checksum;
788251875Speter}
789251875Speter
790251875SpeterAPR_DECLARE(void) apr_table_add(apr_table_t *t, const char *key,
791251875Speter			       const char *val)
792251875Speter{
793251875Speter    apr_table_entry_t *elts;
794251875Speter    apr_uint32_t checksum;
795251875Speter    int hash;
796251875Speter
797251875Speter    hash = TABLE_HASH(key);
798251875Speter    t->index_last[hash] = t->a.nelts;
799251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
800251875Speter        t->index_first[hash] = t->a.nelts;
801251875Speter        TABLE_SET_INDEX_INITIALIZED(t, hash);
802251875Speter    }
803251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
804251875Speter    elts = (apr_table_entry_t *) table_push(t);
805251875Speter    elts->key = apr_pstrdup(t->a.pool, key);
806251875Speter    elts->val = apr_pstrdup(t->a.pool, val);
807251875Speter    elts->key_checksum = checksum;
808251875Speter}
809251875Speter
810251875SpeterAPR_DECLARE(void) apr_table_addn(apr_table_t *t, const char *key,
811251875Speter				const char *val)
812251875Speter{
813251875Speter    apr_table_entry_t *elts;
814251875Speter    apr_uint32_t checksum;
815251875Speter    int hash;
816251875Speter
817251875Speter#if APR_POOL_DEBUG
818251875Speter    {
819251875Speter	if (!apr_pool_is_ancestor(apr_pool_find(key), t->a.pool)) {
820251875Speter	    fprintf(stderr, "apr_table_addn: key not in ancestor pool of t\n");
821251875Speter	    abort();
822251875Speter	}
823251875Speter	if (!apr_pool_is_ancestor(apr_pool_find(val), t->a.pool)) {
824251875Speter	    fprintf(stderr, "apr_table_addn: val not in ancestor pool of t\n");
825251875Speter	    abort();
826251875Speter	}
827251875Speter    }
828251875Speter#endif
829251875Speter
830251875Speter    hash = TABLE_HASH(key);
831251875Speter    t->index_last[hash] = t->a.nelts;
832251875Speter    if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
833251875Speter        t->index_first[hash] = t->a.nelts;
834251875Speter        TABLE_SET_INDEX_INITIALIZED(t, hash);
835251875Speter    }
836251875Speter    COMPUTE_KEY_CHECKSUM(key, checksum);
837251875Speter    elts = (apr_table_entry_t *) table_push(t);
838251875Speter    elts->key = (char *)key;
839251875Speter    elts->val = (char *)val;
840251875Speter    elts->key_checksum = checksum;
841251875Speter}
842251875Speter
843251875SpeterAPR_DECLARE(apr_table_t *) apr_table_overlay(apr_pool_t *p,
844251875Speter					     const apr_table_t *overlay,
845251875Speter					     const apr_table_t *base)
846251875Speter{
847251875Speter    apr_table_t *res;
848251875Speter
849251875Speter#if APR_POOL_DEBUG
850251875Speter    /* we don't copy keys and values, so it's necessary that
851251875Speter     * overlay->a.pool and base->a.pool have a life span at least
852251875Speter     * as long as p
853251875Speter     */
854251875Speter    if (!apr_pool_is_ancestor(overlay->a.pool, p)) {
855251875Speter	fprintf(stderr,
856251875Speter		"apr_table_overlay: overlay's pool is not an ancestor of p\n");
857251875Speter	abort();
858251875Speter    }
859251875Speter    if (!apr_pool_is_ancestor(base->a.pool, p)) {
860251875Speter	fprintf(stderr,
861251875Speter		"apr_table_overlay: base's pool is not an ancestor of p\n");
862251875Speter	abort();
863251875Speter    }
864251875Speter#endif
865251875Speter
866251875Speter    res = apr_palloc(p, sizeof(apr_table_t));
867251875Speter    /* behave like append_arrays */
868251875Speter    res->a.pool = p;
869251875Speter    copy_array_hdr_core(&res->a, &overlay->a);
870251875Speter    apr_array_cat(&res->a, &base->a);
871251875Speter    table_reindex(res);
872251875Speter    return res;
873251875Speter}
874251875Speter
875251875Speter/* And now for something completely abstract ...
876251875Speter
877251875Speter * For each key value given as a vararg:
878251875Speter *   run the function pointed to as
879251875Speter *     int comp(void *r, char *key, char *value);
880251875Speter *   on each valid key-value pair in the apr_table_t t that matches the vararg key,
881251875Speter *   or once for every valid key-value pair if the vararg list is empty,
882251875Speter *   until the function returns false (0) or we finish the table.
883251875Speter *
884251875Speter * Note that we restart the traversal for each vararg, which means that
885251875Speter * duplicate varargs will result in multiple executions of the function
886251875Speter * for each matching key.  Note also that if the vararg list is empty,
887251875Speter * only one traversal will be made and will cut short if comp returns 0.
888251875Speter *
889251875Speter * Note that the table_get and table_merge functions assume that each key in
890251875Speter * the apr_table_t is unique (i.e., no multiple entries with the same key).  This
891251875Speter * function does not make that assumption, since it (unfortunately) isn't
892251875Speter * true for some of Apache's tables.
893251875Speter *
894251875Speter * Note that rec is simply passed-on to the comp function, so that the
895251875Speter * caller can pass additional info for the task.
896251875Speter *
897251875Speter * ADDENDUM for apr_table_vdo():
898251875Speter *
899251875Speter * The caching api will allow a user to walk the header values:
900251875Speter *
901251875Speter * apr_status_t apr_cache_el_header_walk(apr_cache_el *el,
902251875Speter *    int (*comp)(void *, const char *, const char *), void *rec, ...);
903251875Speter *
904251875Speter * So it can be ..., however from there I use a  callback that use a va_list:
905251875Speter *
906251875Speter * apr_status_t (*cache_el_header_walk)(apr_cache_el *el,
907251875Speter *    int (*comp)(void *, const char *, const char *), void *rec, va_list);
908251875Speter *
909251875Speter * To pass those ...'s on down to the actual module that will handle walking
910251875Speter * their headers, in the file case this is actually just an apr_table - and
911251875Speter * rather than reimplementing apr_table_do (which IMHO would be bad) I just
912251875Speter * called it with the va_list. For mod_shmem_cache I don't need it since I
913251875Speter * can't use apr_table's, but mod_file_cache should (though a good hash would
914251875Speter * be better, but that's a different issue :).
915251875Speter *
916251875Speter * So to make mod_file_cache easier to maintain, it's a good thing
917251875Speter */
918251875SpeterAPR_DECLARE_NONSTD(int) apr_table_do(apr_table_do_callback_fn_t *comp,
919251875Speter                                     void *rec, const apr_table_t *t, ...)
920251875Speter{
921251875Speter    int rv;
922251875Speter
923251875Speter    va_list vp;
924251875Speter    va_start(vp, t);
925251875Speter    rv = apr_table_vdo(comp, rec, t, vp);
926251875Speter    va_end(vp);
927251875Speter
928251875Speter    return rv;
929251875Speter}
930251875Speter
931251875Speter/* XXX: do the semantics of this routine make any sense?  Right now,
932251875Speter * if the caller passed in a non-empty va_list of keys to search for,
933251875Speter * the "early termination" facility only terminates on *that* key; other
934251875Speter * keys will continue to process.  Note that this only has any effect
935251875Speter * at all if there are multiple entries in the table with the same key,
936251875Speter * otherwise the called function can never effectively early-terminate
937251875Speter * this function, as the zero return value is effectively ignored.
938251875Speter *
939251875Speter * Note also that this behavior is at odds with the behavior seen if an
940251875Speter * empty va_list is passed in -- in that case, a zero return value terminates
941251875Speter * the entire apr_table_vdo (which is what I think should happen in
942251875Speter * both cases).
943251875Speter *
944251875Speter * If nobody objects soon, I'm going to change the order of the nested
945251875Speter * loops in this function so that any zero return value from the (*comp)
946251875Speter * function will cause a full termination of apr_table_vdo.  I'm hesitant
947251875Speter * at the moment because these (funky) semantics have been around for a
948251875Speter * very long time, and although Apache doesn't seem to use them at all,
949251875Speter * some third-party vendor might.  I can only think of one possible reason
950251875Speter * the existing semantics would make any sense, and it's very Apache-centric,
951251875Speter * which is this: if (*comp) is looking for matches of a particular
952251875Speter * substring in request headers (let's say it's looking for a particular
953251875Speter * cookie name in the Set-Cookie headers), then maybe it wants to be
954251875Speter * able to stop searching early as soon as it finds that one and move
955251875Speter * on to the next key.  That's only an optimization of course, but changing
956251875Speter * the behavior of this function would mean that any code that tried
957251875Speter * to do that would stop working right.
958251875Speter *
959251875Speter * Sigh.  --JCW, 06/28/02
960251875Speter */
961251875SpeterAPR_DECLARE(int) apr_table_vdo(apr_table_do_callback_fn_t *comp,
962251875Speter                               void *rec, const apr_table_t *t, va_list vp)
963251875Speter{
964251875Speter    char *argp;
965251875Speter    apr_table_entry_t *elts = (apr_table_entry_t *) t->a.elts;
966251875Speter    int vdorv = 1;
967251875Speter
968251875Speter    argp = va_arg(vp, char *);
969251875Speter    do {
970251875Speter        int rv = 1, i;
971251875Speter        if (argp) {
972251875Speter            /* Scan for entries that match the next key */
973251875Speter            int hash = TABLE_HASH(argp);
974251875Speter            if (TABLE_INDEX_IS_INITIALIZED(t, hash)) {
975251875Speter                apr_uint32_t checksum;
976251875Speter                COMPUTE_KEY_CHECKSUM(argp, checksum);
977251875Speter                for (i = t->index_first[hash];
978251875Speter                     rv && (i <= t->index_last[hash]); ++i) {
979251875Speter                    if (elts[i].key && (checksum == elts[i].key_checksum) &&
980251875Speter                                        !strcasecmp(elts[i].key, argp)) {
981251875Speter                        rv = (*comp) (rec, elts[i].key, elts[i].val);
982251875Speter                    }
983251875Speter                }
984251875Speter            }
985251875Speter        }
986251875Speter        else {
987251875Speter            /* Scan the entire table */
988251875Speter            for (i = 0; rv && (i < t->a.nelts); ++i) {
989251875Speter                if (elts[i].key) {
990251875Speter                    rv = (*comp) (rec, elts[i].key, elts[i].val);
991251875Speter                }
992251875Speter            }
993251875Speter        }
994251875Speter        if (rv == 0) {
995251875Speter            vdorv = 0;
996251875Speter        }
997251875Speter    } while (argp && ((argp = va_arg(vp, char *)) != NULL));
998251875Speter
999251875Speter    return vdorv;
1000251875Speter}
1001251875Speter
1002251875Speterstatic apr_table_entry_t **table_mergesort(apr_pool_t *pool,
1003251875Speter                                           apr_table_entry_t **values,
1004251875Speter                                           apr_size_t n)
1005251875Speter{
1006251875Speter    /* Bottom-up mergesort, based on design in Sedgewick's "Algorithms
1007251875Speter     * in C," chapter 8
1008251875Speter     */
1009251875Speter    apr_table_entry_t **values_tmp =
1010251875Speter        (apr_table_entry_t **)apr_palloc(pool, n * sizeof(apr_table_entry_t*));
1011251875Speter    apr_size_t i;
1012251875Speter    apr_size_t blocksize;
1013251875Speter
1014251875Speter    /* First pass: sort pairs of elements (blocksize=1) */
1015251875Speter    for (i = 0; i + 1 < n; i += 2) {
1016251875Speter        if (strcasecmp(values[i]->key, values[i + 1]->key) > 0) {
1017251875Speter            apr_table_entry_t *swap = values[i];
1018251875Speter            values[i] = values[i + 1];
1019251875Speter            values[i + 1] = swap;
1020251875Speter        }
1021251875Speter    }
1022251875Speter
1023251875Speter    /* Merge successively larger blocks */
1024251875Speter    blocksize = 2;
1025251875Speter    while (blocksize < n) {
1026251875Speter        apr_table_entry_t **dst = values_tmp;
1027251875Speter        apr_size_t next_start;
1028251875Speter        apr_table_entry_t **swap;
1029251875Speter
1030251875Speter        /* Merge consecutive pairs blocks of the next blocksize.
1031251875Speter         * Within a block, elements are in sorted order due to
1032251875Speter         * the previous iteration.
1033251875Speter         */
1034251875Speter        for (next_start = 0; next_start + blocksize < n;
1035251875Speter             next_start += (blocksize + blocksize)) {
1036251875Speter
1037251875Speter            apr_size_t block1_start = next_start;
1038251875Speter            apr_size_t block2_start = block1_start + blocksize;
1039251875Speter            apr_size_t block1_end = block2_start;
1040251875Speter            apr_size_t block2_end = block2_start + blocksize;
1041251875Speter            if (block2_end > n) {
1042251875Speter                /* The last block may be smaller than blocksize */
1043251875Speter                block2_end = n;
1044251875Speter            }
1045251875Speter            for (;;) {
1046251875Speter
1047251875Speter                /* Merge the next two blocks:
1048251875Speter                 * Pick the smaller of the next element from
1049251875Speter                 * block 1 and the next element from block 2.
1050251875Speter                 * Once either of the blocks is emptied, copy
1051251875Speter                 * over all the remaining elements from the
1052251875Speter                 * other block
1053251875Speter                 */
1054251875Speter                if (block1_start == block1_end) {
1055251875Speter                    for (; block2_start < block2_end; block2_start++) {
1056251875Speter                        *dst++ = values[block2_start];
1057251875Speter                    }
1058251875Speter                    break;
1059251875Speter                }
1060251875Speter                else if (block2_start == block2_end) {
1061251875Speter                    for (; block1_start < block1_end; block1_start++) {
1062251875Speter                        *dst++ = values[block1_start];
1063251875Speter                    }
1064251875Speter                    break;
1065251875Speter                }
1066251875Speter                if (strcasecmp(values[block1_start]->key,
1067251875Speter                               values[block2_start]->key) > 0) {
1068251875Speter                    *dst++ = values[block2_start++];
1069251875Speter                }
1070251875Speter                else {
1071251875Speter                    *dst++ = values[block1_start++];
1072251875Speter                }
1073251875Speter            }
1074251875Speter        }
1075251875Speter
1076251875Speter        /* If n is not a multiple of 2*blocksize, some elements
1077251875Speter         * will be left over at the end of the array.
1078251875Speter         */
1079251875Speter        for (i = dst - values_tmp; i < n; i++) {
1080251875Speter            values_tmp[i] = values[i];
1081251875Speter        }
1082251875Speter
1083251875Speter        /* The output array of this pass becomes the input
1084251875Speter         * array of the next pass, and vice versa
1085251875Speter         */
1086251875Speter        swap = values_tmp;
1087251875Speter        values_tmp = values;
1088251875Speter        values = swap;
1089251875Speter
1090251875Speter        blocksize += blocksize;
1091251875Speter    }
1092251875Speter
1093251875Speter    return values;
1094251875Speter}
1095251875Speter
1096251875SpeterAPR_DECLARE(void) apr_table_compress(apr_table_t *t, unsigned flags)
1097251875Speter{
1098251875Speter    apr_table_entry_t **sort_array;
1099251875Speter    apr_table_entry_t **sort_next;
1100251875Speter    apr_table_entry_t **sort_end;
1101251875Speter    apr_table_entry_t *table_next;
1102251875Speter    apr_table_entry_t **last;
1103251875Speter    int i;
1104251875Speter    int dups_found;
1105251875Speter
1106251875Speter    if (t->a.nelts <= 1) {
1107251875Speter        return;
1108251875Speter    }
1109251875Speter
1110251875Speter    /* Copy pointers to all the table elements into an
1111251875Speter     * array and sort to allow for easy detection of
1112251875Speter     * duplicate keys
1113251875Speter     */
1114251875Speter    sort_array = (apr_table_entry_t **)
1115251875Speter        apr_palloc(t->a.pool, t->a.nelts * sizeof(apr_table_entry_t*));
1116251875Speter    sort_next = sort_array;
1117251875Speter    table_next = (apr_table_entry_t *)t->a.elts;
1118251875Speter    i = t->a.nelts;
1119251875Speter    do {
1120251875Speter        *sort_next++ = table_next++;
1121251875Speter    } while (--i);
1122251875Speter
1123251875Speter    /* Note: the merge is done with mergesort instead of quicksort
1124251875Speter     * because mergesort is a stable sort and runs in n*log(n)
1125251875Speter     * time regardless of its inputs (quicksort is quadratic in
1126251875Speter     * the worst case)
1127251875Speter     */
1128251875Speter    sort_array = table_mergesort(t->a.pool, sort_array, t->a.nelts);
1129251875Speter
1130251875Speter    /* Process any duplicate keys */
1131251875Speter    dups_found = 0;
1132251875Speter    sort_next = sort_array;
1133251875Speter    sort_end = sort_array + t->a.nelts;
1134251875Speter    last = sort_next++;
1135251875Speter    while (sort_next < sort_end) {
1136251875Speter        if (((*sort_next)->key_checksum == (*last)->key_checksum) &&
1137251875Speter            !strcasecmp((*sort_next)->key, (*last)->key)) {
1138251875Speter            apr_table_entry_t **dup_last = sort_next + 1;
1139251875Speter            dups_found = 1;
1140251875Speter            while ((dup_last < sort_end) &&
1141251875Speter                   ((*dup_last)->key_checksum == (*last)->key_checksum) &&
1142251875Speter                   !strcasecmp((*dup_last)->key, (*last)->key)) {
1143251875Speter                dup_last++;
1144251875Speter            }
1145251875Speter            dup_last--; /* Elements from last through dup_last, inclusive,
1146251875Speter                         * all have the same key
1147251875Speter                         */
1148251875Speter            if (flags == APR_OVERLAP_TABLES_MERGE) {
1149251875Speter                apr_size_t len = 0;
1150251875Speter                apr_table_entry_t **next = last;
1151251875Speter                char *new_val;
1152251875Speter                char *val_dst;
1153251875Speter                do {
1154251875Speter                    len += strlen((*next)->val);
1155251875Speter                    len += 2; /* for ", " or trailing null */
1156251875Speter                } while (++next <= dup_last);
1157251875Speter                new_val = (char *)apr_palloc(t->a.pool, len);
1158251875Speter                val_dst = new_val;
1159251875Speter                next = last;
1160251875Speter                for (;;) {
1161251875Speter                    strcpy(val_dst, (*next)->val);
1162251875Speter                    val_dst += strlen((*next)->val);
1163251875Speter                    next++;
1164251875Speter                    if (next > dup_last) {
1165251875Speter                        *val_dst = 0;
1166251875Speter                        break;
1167251875Speter                    }
1168251875Speter                    else {
1169251875Speter                        *val_dst++ = ',';
1170251875Speter                        *val_dst++ = ' ';
1171251875Speter                    }
1172251875Speter                }
1173251875Speter                (*last)->val = new_val;
1174251875Speter            }
1175251875Speter            else { /* overwrite */
1176251875Speter                (*last)->val = (*dup_last)->val;
1177251875Speter            }
1178251875Speter            do {
1179251875Speter                (*sort_next)->key = NULL;
1180251875Speter            } while (++sort_next <= dup_last);
1181251875Speter        }
1182251875Speter        else {
1183251875Speter            last = sort_next++;
1184251875Speter        }
1185251875Speter    }
1186251875Speter
1187251875Speter    /* Shift elements to the left to fill holes left by removing duplicates */
1188251875Speter    if (dups_found) {
1189251875Speter        apr_table_entry_t *src = (apr_table_entry_t *)t->a.elts;
1190251875Speter        apr_table_entry_t *dst = (apr_table_entry_t *)t->a.elts;
1191251875Speter        apr_table_entry_t *last_elt = src + t->a.nelts;
1192251875Speter        do {
1193251875Speter            if (src->key) {
1194251875Speter                *dst++ = *src;
1195251875Speter            }
1196251875Speter        } while (++src < last_elt);
1197251875Speter        t->a.nelts -= (int)(last_elt - dst);
1198251875Speter    }
1199251875Speter
1200251875Speter    table_reindex(t);
1201251875Speter}
1202251875Speter
1203251875Speterstatic void apr_table_cat(apr_table_t *t, const apr_table_t *s)
1204251875Speter{
1205251875Speter    const int n = t->a.nelts;
1206251875Speter    register int idx;
1207251875Speter
1208251875Speter    apr_array_cat(&t->a,&s->a);
1209251875Speter
1210251875Speter    if (n == 0) {
1211251875Speter        memcpy(t->index_first,s->index_first,sizeof(int) * TABLE_HASH_SIZE);
1212251875Speter        memcpy(t->index_last, s->index_last, sizeof(int) * TABLE_HASH_SIZE);
1213251875Speter        t->index_initialized = s->index_initialized;
1214251875Speter        return;
1215251875Speter    }
1216251875Speter
1217251875Speter    for (idx = 0; idx < TABLE_HASH_SIZE; ++idx) {
1218251875Speter        if (TABLE_INDEX_IS_INITIALIZED(s, idx)) {
1219251875Speter            t->index_last[idx] = s->index_last[idx] + n;
1220251875Speter            if (!TABLE_INDEX_IS_INITIALIZED(t, idx)) {
1221251875Speter                t->index_first[idx] = s->index_first[idx] + n;
1222251875Speter            }
1223251875Speter        }
1224251875Speter    }
1225251875Speter
1226251875Speter    t->index_initialized |= s->index_initialized;
1227251875Speter}
1228251875Speter
1229251875SpeterAPR_DECLARE(void) apr_table_overlap(apr_table_t *a, const apr_table_t *b,
1230251875Speter				    unsigned flags)
1231251875Speter{
1232251875Speter    if (a->a.nelts + b->a.nelts == 0) {
1233251875Speter        return;
1234251875Speter    }
1235251875Speter
1236251875Speter#if APR_POOL_DEBUG
1237251875Speter    /* Since the keys and values are not copied, it's required that
1238251875Speter     * b->a.pool has a lifetime at least as long as a->a.pool. */
1239251875Speter    if (!apr_pool_is_ancestor(b->a.pool, a->a.pool)) {
1240251875Speter        fprintf(stderr, "apr_table_overlap: b's pool is not an ancestor of a's\n");
1241251875Speter        abort();
1242251875Speter    }
1243251875Speter#endif
1244251875Speter
1245251875Speter    apr_table_cat(a, b);
1246251875Speter
1247251875Speter    apr_table_compress(a, flags);
1248251875Speter}
1249269847Speter
1250269847Speterstatic int table_getm_do(void *v, const char *key, const char *val)
1251269847Speter{
1252269847Speter    table_getm_t *state = (table_getm_t *) v;
1253269847Speter
1254269847Speter    if (!state->first) {
1255269847Speter        /**
1256269847Speter         * The most common case is a single header, and this is covered by
1257269847Speter         * a fast path that doesn't allocate any memory. On the second and
1258269847Speter         * subsequent header, an array is created and the array concatenated
1259269847Speter         * together to form the final value.
1260269847Speter         */
1261269847Speter        state->first = val;
1262269847Speter    }
1263269847Speter    else {
1264269847Speter        const char **elt;
1265269847Speter        if (!state->merged) {
1266269847Speter            state->merged = apr_array_make(state->p, 10, sizeof(const char *));
1267269847Speter            elt = apr_array_push(state->merged);
1268269847Speter            *elt = state->first;
1269269847Speter        }
1270269847Speter        elt = apr_array_push(state->merged);
1271269847Speter        *elt = val;
1272269847Speter    }
1273269847Speter    return 1;
1274269847Speter}
1275269847Speter
1276269847SpeterAPR_DECLARE(const char *) apr_table_getm(apr_pool_t *p, const apr_table_t *t,
1277269847Speter        const char *key)
1278269847Speter{
1279269847Speter    table_getm_t state;
1280269847Speter
1281269847Speter    state.p = p;
1282269847Speter    state.first = NULL;
1283269847Speter    state.merged = NULL;
1284269847Speter
1285269847Speter    apr_table_do(table_getm_do, &state, t, key, NULL);
1286269847Speter
1287269847Speter    if (!state.first) {
1288269847Speter        return NULL;
1289269847Speter    }
1290269847Speter    else if (!state.merged) {
1291269847Speter        return state.first;
1292269847Speter    }
1293269847Speter    else {
1294269847Speter        return apr_array_pstrcat(p, state.merged, ',');
1295269847Speter    }
1296269847Speter}
1297