The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define mpxs_Apache2__Directive_conftree() ap_conftree

/* XXX: this is only useful for <Perl> at the moment */
static MP_INLINE SV *mpxs_Apache2__Directive_as_string(pTHX_
                                                      ap_directive_t *self)
{
    ap_directive_t *d;
    SV *sv = newSVpv("", 0);

    for (d = self->first_child; d; d = d->next) {
        sv_catpv(sv, d->directive);
        sv_catpv(sv, " ");
        sv_catpv(sv, d->args);
        sv_catpv(sv, "\n");
    }

    return sv;
}


/* Adds an entry to a hash, vivifying hash/array for multiple entries */
static void hash_insert(pTHX_ HV *hash, const char *key,
                        int keylen, const char *args,
                        int argslen, SV *value)
{
    HV *subhash;
    AV *args_array;
    SV **hash_ent = hv_fetch(hash, key, keylen, 0);

    if (value) {
        if (!hash_ent) {
            subhash = newHV();
            (void)hv_store(hash, key, keylen, newRV_noinc((SV *)subhash), 0);
        }
        else {
            subhash = (HV *)SvRV(*hash_ent);
        }

        (void)hv_store(subhash, args, argslen, value, 0);
    }
    else {
        if (hash_ent) {
            if (SvROK(*hash_ent) && (SVt_PVAV == SvTYPE(SvRV(*hash_ent)))) {
                args_array = (AV *)SvRV(*hash_ent);
            }
            else {
                args_array = newAV();
                av_push(args_array, newSVsv(*hash_ent));
                (void)hv_store(hash, key, keylen,
                               newRV_noinc((SV *)args_array), 0);
            }
            av_push(args_array, newSVpv(args, argslen));
        }
        else {
            (void)hv_store(hash, key, keylen, newSVpv(args, argslen), 0);
        }
    }
}

static MP_INLINE SV *mpxs_Apache2__Directive_as_hash(pTHX_
                                                    ap_directive_t *tree)
{
    const char *directive;
    int directive_len;
    const char *args;
    int args_len;

    HV *hash = newHV();
    SV *subtree;

    while (tree) {
        directive = tree->directive;
        directive_len = strlen(directive);
        args = tree->args;
        args_len = strlen(args);

        if (tree->first_child) {

            /* Skip the prefix '<' */
            if ('<' == directive[0]) {
                directive++;
                directive_len--;
            }

            /* Skip the postfix '>' */
            if ('>' == args[args_len-1]) {
                args_len--;
            }

            subtree = mpxs_Apache2__Directive_as_hash(aTHX_ tree->first_child);
            hash_insert(aTHX_ hash, directive, directive_len,
                        args, args_len, subtree);
        }
        else {
            hash_insert(aTHX_ hash, directive, directive_len,
                        args, args_len, (SV *)NULL);
        }

        tree = tree->next;
    }

    return newRV_noinc((SV *)hash);
}

MP_STATIC XS(MPXS_Apache2__Directive_lookup)
{
    dXSARGS;

    if (items < 2 || items > 3) {
            Perl_croak(aTHX_
                       "Usage: Apache2::Directive::lookup(self, key, [args])");
    }

    mpxs_PPCODE({
        Apache2__Directive tree;
        char *value;
        const char *directive;
        const char *args;
        int args_len;
        int directive_len;

        char *key = (char *)SvPV_nolen(ST(1));
        int scalar_context = (G_SCALAR == GIMME_V);

            if (SvROK(ST(0)) && sv_derived_from(ST(0), "Apache2::Directive")) {
                IV tmp = SvIV((SV*)SvRV(ST(0)));
                tree = INT2PTR(Apache2__Directive,tmp);
            }
            else {
                tree = ap_conftree;
            }

            if (items < 3) {
                value = NULL;
            }
            else {
                value = (char *)SvPV_nolen(ST(2));
            }

        while (tree) {
            directive = tree->directive;
            directive_len = strlen(directive);

            /* Remove starting '<' for container directives */
            if (directive[0] == '<') {
                directive++;
                directive_len--;
            }

            if (0 == strncasecmp(directive, key, directive_len)) {

                if (value) {
                    args = tree->args;
                    args_len = strlen(args);

                    /* Skip the postfix '>' */
                    if ('>' == args[args_len-1]) {
                        args_len--;
                    }

                }

                if ( (!value) || (0 == strncasecmp(args, value, args_len)) ) {
                    if (tree->first_child) {
                        XPUSHs(sv_2mortal(mpxs_Apache2__Directive_as_hash(
                                              aTHX_ tree->first_child)));
                    }
                    else {
                       XPUSHs(sv_2mortal(newSVpv(tree->args, 0)));
                    }

                    if (scalar_context) {
                        break;
                    }
                }
            }

            tree = tree->next ? tree->next : NULL;
        }
    });
}