Blame modules/core/mod_macro.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
Packit 90a5c9
#include "apr.h"
Packit 90a5c9
#include "apr_strings.h"
Packit 90a5c9
#include "apr_hash.h"
Packit 90a5c9
Packit 90a5c9
/************************************************ COMPILE TIME DEBUG CONTROL */
Packit 90a5c9
/*
Packit 90a5c9
   debug:
Packit 90a5c9
   #define MOD_MACRO_DEBUG 1
Packit 90a5c9
Packit 90a5c9
   gdb:
Packit 90a5c9
   run -f ./test/conf/test??.conf
Packit 90a5c9
*/
Packit 90a5c9
/* #define MOD_MACRO_DEBUG 1 */
Packit 90a5c9
#undef MOD_MACRO_DEBUG
Packit 90a5c9
Packit 90a5c9
#if defined(debug)
Packit 90a5c9
#undef debug
Packit 90a5c9
#endif /* debug */
Packit 90a5c9
Packit 90a5c9
#if defined(MOD_MACRO_DEBUG)
Packit 90a5c9
#define debug(stmt) stmt
Packit 90a5c9
#else
Packit 90a5c9
#define debug(stmt)
Packit 90a5c9
#endif /* MOD_MACRO_DEBUG */
Packit 90a5c9
Packit 90a5c9
/******************************************************** MODULE DECLARATION */
Packit 90a5c9
Packit 90a5c9
module AP_MODULE_DECLARE_DATA macro_module;
Packit 90a5c9
Packit 90a5c9
/********************************************************** MACRO MANAGEMENT */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  this is a macro: name, arguments, contents, location.
Packit 90a5c9
*/
Packit 90a5c9
typedef struct
Packit 90a5c9
{
Packit 90a5c9
    char *name;                    /* lower case name of the macro */
Packit 90a5c9
    apr_array_header_t *arguments; /* of char*, macro parameter names */
Packit 90a5c9
    apr_array_header_t *contents;  /* of char*, macro body */
Packit 90a5c9
    char *location;                /* of macro definition, for error messages */
Packit 90a5c9
} ap_macro_t;
Packit 90a5c9
Packit 90a5c9
/* configuration tokens.
Packit 90a5c9
 */
Packit 90a5c9
#define BEGIN_MACRO "
Packit 90a5c9
#define END_MACRO   "</Macro>"
Packit 90a5c9
#define USE_MACRO   "Use"
Packit 90a5c9
#define UNDEF_MACRO "UndefMacro"
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  Macros are kept globally...
Packit 90a5c9
  They are not per-server or per-directory entities.
Packit 90a5c9
Packit 90a5c9
  note: they are in a temp_pool, and there is a lazy initialization.
Packit 90a5c9
        ap_macros is reset to NULL in pre_config hook to not depend
Packit 90a5c9
        on static vs dynamic configuration.
Packit 90a5c9
Packit 90a5c9
  hash type: (char *) name -> (ap_macro_t *) macro
Packit 90a5c9
*/
Packit 90a5c9
static apr_hash_t *ap_macros = NULL;
Packit 90a5c9
Packit 90a5c9
/*************************************************************** PARSE UTILS */
Packit 90a5c9
Packit 90a5c9
#define empty_string_p(p) (!(p) || *(p) == '\0')
Packit 90a5c9
#define trim(line) while (*(line) == ' ' || *(line) == '\t') (line)++
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  return configuration-parsed arguments from line as an array.
Packit 90a5c9
  the line is expected not to contain any '\n'?
Packit 90a5c9
*/
Packit 90a5c9
static apr_array_header_t *get_arguments(apr_pool_t * pool, const char *line)
Packit 90a5c9
{
Packit 90a5c9
    apr_array_header_t *args = apr_array_make(pool, 1, sizeof(char *));
Packit 90a5c9
Packit 90a5c9
    trim(line);
Packit 90a5c9
    while (*line) {
Packit 90a5c9
        char *arg = ap_getword_conf(pool, &line);
Packit 90a5c9
        char **new = apr_array_push(args);
Packit 90a5c9
        *new = arg;
Packit 90a5c9
        trim(line);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return args;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  warn if anything non blank appears, but ignore comments...
Packit 90a5c9
*/
Packit 90a5c9
static void warn_if_non_blank(const char * what,
Packit 90a5c9
                              char * ptr,
Packit 90a5c9
                              ap_configfile_t * cfg)
Packit 90a5c9
{
Packit 90a5c9
    char * p;
Packit 90a5c9
    for (p=ptr; *p; p++) {
Packit 90a5c9
        if (*p == '#')
Packit 90a5c9
            break;
Packit 90a5c9
        if (*p != ' ' && *p != '\t') {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02989)
Packit 90a5c9
                         "%s on line %d of %s: %s",
Packit 90a5c9
                         what, cfg->line_number, cfg->name, ptr);
Packit 90a5c9
            break;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  get read lines as an array till end_token.
Packit 90a5c9
  counts nesting for begin_token/end_token.
Packit 90a5c9
  it assumes a line-per-line configuration (thru getline).
Packit 90a5c9
  this function could be exported.
Packit 90a5c9
  begin_token may be NULL.
Packit 90a5c9
*/
Packit 90a5c9
static char *get_lines_till_end_token(apr_pool_t * pool,
Packit 90a5c9
                                      ap_configfile_t * config_file,
Packit 90a5c9
                                      const char *end_token,
Packit 90a5c9
                                      const char *begin_token,
Packit 90a5c9
                                      const char *where,
Packit 90a5c9
                                      apr_array_header_t ** plines)
Packit 90a5c9
{
Packit 90a5c9
    apr_array_header_t *lines = apr_array_make(pool, 1, sizeof(char *));
Packit 90a5c9
    char line[MAX_STRING_LEN];  /* sorry, but this is expected by getline:-( */
Packit 90a5c9
    int macro_nesting = 1, any_nesting = 1;
Packit 90a5c9
    int line_number_start = config_file->line_number;
Packit 90a5c9
Packit 90a5c9
    while (!ap_cfg_getline(line, MAX_STRING_LEN, config_file)) {
Packit 90a5c9
        char *ptr = line;
Packit 90a5c9
        char *first, **new;
Packit 90a5c9
        /* skip comments */
Packit 90a5c9
        if (*line == '#')
Packit 90a5c9
            continue;
Packit 90a5c9
        first = ap_getword_conf_nc(pool, &ptr);
Packit 90a5c9
        if (first) {
Packit 90a5c9
            /* detect nesting... */
Packit 90a5c9
            if (!strncmp(first, "</", 2)) {
Packit 90a5c9
                any_nesting--;
Packit 90a5c9
                if (any_nesting < 0) {
Packit 90a5c9
                    ap_log_error(APLOG_MARK, APLOG_WARNING,
Packit 90a5c9
                                 0, NULL, APLOGNO(02793)
Packit 90a5c9
                                 "bad (negative) nesting on line %d of %s",
Packit 90a5c9
                                 config_file->line_number - line_number_start,
Packit 90a5c9
                                 where);
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else if (!strncmp(first, "<", 1)) {
Packit 90a5c9
                any_nesting++;
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            if (!strcasecmp(first, end_token)) {
Packit 90a5c9
                /* check for proper closing */
Packit 90a5c9
                char * endp = (char *) ap_strrchr_c(line, '>');
Packit 90a5c9
Packit 90a5c9
                /* this cannot happen if end_token contains '>' */
Packit 90a5c9
                if (endp == NULL) {
Packit 90a5c9
                  return "end directive missing closing '>'";
Packit 90a5c9
                }
Packit 90a5c9
Packit 90a5c9
                warn_if_non_blank(
Packit 90a5c9
                    APLOGNO(02794) "non blank chars found after directive closing",
Packit 90a5c9
                    endp+1, config_file);
Packit 90a5c9
Packit 90a5c9
                macro_nesting--;
Packit 90a5c9
                if (!macro_nesting) {
Packit 90a5c9
                    if (any_nesting) {
Packit 90a5c9
                        ap_log_error(APLOG_MARK,
Packit 90a5c9
                                     APLOG_WARNING, 0, NULL, APLOGNO(02795)
Packit 90a5c9
                                     "bad cumulated nesting (%+d) in %s",
Packit 90a5c9
                                     any_nesting, where);
Packit 90a5c9
                    }
Packit 90a5c9
                    *plines = lines;
Packit 90a5c9
                    return NULL;
Packit 90a5c9
                }
Packit 90a5c9
            }
Packit 90a5c9
            else if (begin_token && !strcasecmp(first, begin_token)) {
Packit 90a5c9
                macro_nesting++;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        new = apr_array_push(lines);
Packit 90a5c9
        *new = apr_psprintf(pool, "%s" APR_EOL_STR, line); /* put EOL back? */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return apr_psprintf(pool, "expected token not found: %s", end_token);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* the @* arguments are double-quote escaped when substituted */
Packit 90a5c9
#define ESCAPE_ARG '@'
Packit 90a5c9
Packit 90a5c9
/* other $* and %* arguments are simply replaced without escaping */
Packit 90a5c9
#define ARG_PREFIX "$%@"
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  characters allowed in an argument?
Packit 90a5c9
  not used yet, because that would trigger some backward compatibility.
Packit 90a5c9
*/
Packit 90a5c9
#define ARG_CONTENT              \
Packit 90a5c9
    "abcdefghijklmnopqrstuvwxyz"   \
Packit 90a5c9
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"   \
Packit 90a5c9
    "0123456789_" ARG_PREFIX
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  returns whether it looks like an argument, i.e. prefixed by ARG_PREFIX.
Packit 90a5c9
*/
Packit 90a5c9
static int looks_like_an_argument(const char *word)
Packit 90a5c9
{
Packit 90a5c9
    return ap_strchr(ARG_PREFIX, *word) != 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  generates an error on macro with two arguments of the same name.
Packit 90a5c9
  generates an error if a macro argument name is empty.
Packit 90a5c9
  generates a warning if arguments name prefixes conflict.
Packit 90a5c9
  generates a warning if the first char of an argument is not in ARG_PREFIX
Packit 90a5c9
*/
Packit 90a5c9
static const char *check_macro_arguments(apr_pool_t * pool,
Packit 90a5c9
                                         const ap_macro_t * macro)
Packit 90a5c9
{
Packit 90a5c9
    char **tab = (char **) macro->arguments->elts;
Packit 90a5c9
    int nelts = macro->arguments->nelts;
Packit 90a5c9
    int i;
Packit 90a5c9
Packit 90a5c9
    for (i = 0; i < nelts; i++) {
Packit 90a5c9
        size_t ltabi = strlen(tab[i]);
Packit 90a5c9
        int j;
Packit 90a5c9
Packit 90a5c9
        if (ltabi == 0) {
Packit 90a5c9
            return apr_psprintf(pool,
Packit 90a5c9
                                "macro '%s' (%s): empty argument #%d name",
Packit 90a5c9
                                macro->name, macro->location, i + 1);
Packit 90a5c9
        }
Packit 90a5c9
        else if (!looks_like_an_argument(tab[i])) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02796)
Packit 90a5c9
                         "macro '%s' (%s) "
Packit 90a5c9
                         "argument name '%s' (#%d) without expected prefix, "
Packit 90a5c9
                         "better prefix argument names with one of '%s'.",
Packit 90a5c9
                         macro->name, macro->location,
Packit 90a5c9
                         tab[i], i + 1, ARG_PREFIX);
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        for (j = i + 1; j < nelts; j++) {
Packit 90a5c9
            size_t ltabj = strlen(tab[j]);
Packit 90a5c9
Packit 90a5c9
            /* must not use the same argument name twice */
Packit 90a5c9
            if (!strcmp(tab[i], tab[j])) {
Packit 90a5c9
                return apr_psprintf(pool,
Packit 90a5c9
                                    "argument name conflict in macro '%s' (%s): "
Packit 90a5c9
                                    "argument '%s': #%d and #%d, "
Packit 90a5c9
                                    "change argument names!",
Packit 90a5c9
                                    macro->name, macro->location,
Packit 90a5c9
                                    tab[i], i + 1, j + 1);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            /* warn about common prefix, but only if non empty names */
Packit 90a5c9
            if (ltabi && ltabj &&
Packit 90a5c9
                !strncmp(tab[i], tab[j], ltabi < ltabj ? ltabi : ltabj)) {
Packit 90a5c9
                ap_log_error(APLOG_MARK, APLOG_WARNING,
Packit 90a5c9
                             0, NULL, APLOGNO(02797)
Packit 90a5c9
                             "macro '%s' (%s): "
Packit 90a5c9
                             "argument name prefix conflict (%s #%d and %s #%d), "
Packit 90a5c9
                             "be careful about your macro definition!",
Packit 90a5c9
                             macro->name, macro->location,
Packit 90a5c9
                             tab[i], i + 1, tab[j], j + 1);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  warn about empty strings in array. could be legitimate.
Packit 90a5c9
*/
Packit 90a5c9
static void check_macro_use_arguments(const char *where,
Packit 90a5c9
                                      const apr_array_header_t * array)
Packit 90a5c9
{
Packit 90a5c9
    char **tab = (char **) array->elts;
Packit 90a5c9
    int i;
Packit 90a5c9
    for (i = 0; i < array->nelts; i++) {
Packit 90a5c9
        if (empty_string_p(tab[i])) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02798)
Packit 90a5c9
                         "%s: empty argument #%d", where, i + 1);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/******************************************************** SUBSTITUTION UTILS */
Packit 90a5c9
Packit 90a5c9
/* could be switched to '\'' */
Packit 90a5c9
#define DELIM '"'
Packit 90a5c9
#define ESCAPE '\\'
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  returns the number of needed escapes for the string
Packit 90a5c9
*/
Packit 90a5c9
static int number_of_escapes(const char delim, const char *str)
Packit 90a5c9
{
Packit 90a5c9
    int nesc = 0;
Packit 90a5c9
    const char *s = str;
Packit 90a5c9
    while (*s) {
Packit 90a5c9
        if (*s == ESCAPE || *s == delim)
Packit 90a5c9
            nesc++;
Packit 90a5c9
        s++;
Packit 90a5c9
    }
Packit 90a5c9
    debug(fprintf(stderr, "escapes: %d ---%s---\n", nesc, str));
Packit 90a5c9
    return nesc;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  replace name by replacement at the beginning of buf of bufsize.
Packit 90a5c9
  returns an error message or NULL.
Packit 90a5c9
  C is not really a nice language for processing strings.
Packit 90a5c9
*/
Packit 90a5c9
static char *substitute(char *buf,
Packit 90a5c9
                        const int bufsize,
Packit 90a5c9
                        const char *name,
Packit 90a5c9
                        const char *replacement, const int do_esc)
Packit 90a5c9
{
Packit 90a5c9
    int lbuf = strlen(buf),
Packit 90a5c9
        lname = strlen(name),
Packit 90a5c9
        lrepl = strlen(replacement),
Packit 90a5c9
        lsubs = lrepl +
Packit 90a5c9
        (do_esc ? (2 + number_of_escapes(DELIM, replacement)) : 0),
Packit 90a5c9
        shift = lsubs - lname, size = lbuf + shift, i, j;
Packit 90a5c9
Packit 90a5c9
    /* buf must starts with name */
Packit 90a5c9
    ap_assert(!strncmp(buf, name, lname));
Packit 90a5c9
Packit 90a5c9
    /* hmmm??? */
Packit 90a5c9
    if (!strcmp(name, replacement))
Packit 90a5c9
        return NULL;
Packit 90a5c9
Packit 90a5c9
    debug(fprintf(stderr,
Packit 90a5c9
                  "substitute(%s,%s,%s,%d,sh=%d,lbuf=%d,lrepl=%d,lsubs=%d)\n",
Packit 90a5c9
                  buf, name, replacement, do_esc, shift, lbuf, lrepl, lsubs));
Packit 90a5c9
Packit 90a5c9
    if (size >= bufsize) {
Packit 90a5c9
        /* could/should I reallocate? */
Packit 90a5c9
        return "cannot substitute, buffer size too small";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* cannot use strcpy as strings may overlap */
Packit 90a5c9
    if (shift != 0) {
Packit 90a5c9
        memmove(buf + lname + shift, buf + lname, lbuf - lname + 1);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* insert the replacement with escapes */
Packit 90a5c9
    j = 0;
Packit 90a5c9
    if (do_esc)
Packit 90a5c9
        buf[j++] = DELIM;
Packit 90a5c9
    for (i = 0; i < lrepl; i++, j++) {
Packit 90a5c9
        if (do_esc && (replacement[i] == DELIM || replacement[i] == ESCAPE))
Packit 90a5c9
            buf[j++] = ESCAPE;
Packit 90a5c9
        buf[j] = replacement[i];
Packit 90a5c9
    }
Packit 90a5c9
    if (do_esc)
Packit 90a5c9
        buf[j++] = DELIM;
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  find first occurrence of args in buf.
Packit 90a5c9
  in case of conflict, the LONGEST argument is kept. (could be the FIRST?).
Packit 90a5c9
  returns the pointer and the whichone found, or NULL.
Packit 90a5c9
*/
Packit 90a5c9
static char *next_substitution(const char *buf,
Packit 90a5c9
                               const apr_array_header_t * args, int *whichone)
Packit 90a5c9
{
Packit 90a5c9
    char *chosen = NULL, **tab = (char **) args->elts;
Packit 90a5c9
    size_t lchosen = 0;
Packit 90a5c9
    int i;
Packit 90a5c9
Packit 90a5c9
    for (i = 0; i < args->nelts; i++) {
Packit 90a5c9
        char *found = ap_strstr((char *) buf, tab[i]);
Packit 90a5c9
        size_t lfound = strlen(tab[i]);
Packit 90a5c9
        if (found && (!chosen || found < chosen ||
Packit 90a5c9
                      (found == chosen && lchosen < lfound))) {
Packit 90a5c9
            chosen = found;
Packit 90a5c9
            lchosen = lfound;
Packit 90a5c9
            *whichone = i;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return chosen;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  substitute macro arguments by replacements in buf of bufsize.
Packit 90a5c9
  returns an error message or NULL.
Packit 90a5c9
  if used is defined, returns the used macro arguments.
Packit 90a5c9
*/
Packit 90a5c9
static const char *substitute_macro_args(
Packit 90a5c9
    char *buf,
Packit 90a5c9
    int bufsize,
Packit 90a5c9
    const ap_macro_t * macro,
Packit 90a5c9
    const apr_array_header_t * replacements,
Packit 90a5c9
    apr_array_header_t * used)
Packit 90a5c9
{
Packit 90a5c9
    char *ptr = buf,
Packit 90a5c9
        **atab = (char **) macro->arguments->elts,
Packit 90a5c9
        **rtab = (char **) replacements->elts;
Packit 90a5c9
    int whichone = -1;
Packit 90a5c9
Packit 90a5c9
    if (used) {
Packit 90a5c9
        ap_assert(used->nalloc >= replacements->nelts);
Packit 90a5c9
    }
Packit 90a5c9
    debug(fprintf(stderr, "1# %s", buf));
Packit 90a5c9
Packit 90a5c9
    while ((ptr = next_substitution(ptr, macro->arguments, &whichone))) {
Packit 90a5c9
        const char *errmsg = substitute(ptr, buf - ptr + bufsize,
Packit 90a5c9
                                        atab[whichone], rtab[whichone],
Packit 90a5c9
                                        atab[whichone][0] == ESCAPE_ARG);
Packit 90a5c9
        if (errmsg) {
Packit 90a5c9
            return errmsg;
Packit 90a5c9
        }
Packit 90a5c9
        ptr += strlen(rtab[whichone]);
Packit 90a5c9
        if (used) {
Packit 90a5c9
            used->elts[whichone] = 1;
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    debug(fprintf(stderr, "2# %s", buf));
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  perform substitutions in a macro contents and
Packit 90a5c9
  return the result as a newly allocated array, if result is defined.
Packit 90a5c9
  may also return an error message.
Packit 90a5c9
  passes used down to substitute_macro_args.
Packit 90a5c9
*/
Packit 90a5c9
static const char *process_content(apr_pool_t * pool,
Packit 90a5c9
                                   const ap_macro_t * macro,
Packit 90a5c9
                                   const apr_array_header_t * replacements,
Packit 90a5c9
                                   apr_array_header_t * used,
Packit 90a5c9
                                   apr_array_header_t ** result)
Packit 90a5c9
{
Packit 90a5c9
    apr_array_header_t *contents = macro->contents;
Packit 90a5c9
    char line[MAX_STRING_LEN];
Packit 90a5c9
    int i;
Packit 90a5c9
Packit 90a5c9
    if (result) {
Packit 90a5c9
        *result = apr_array_make(pool, contents->nelts, sizeof(char *));
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* for each line of the macro body */
Packit 90a5c9
    for (i = 0; i < contents->nelts; i++) {
Packit 90a5c9
        const char *errmsg;
Packit 90a5c9
        /* copy the line and substitute macro parameters */
Packit 90a5c9
        strncpy(line, ((char **) contents->elts)[i], MAX_STRING_LEN - 1);
Packit 90a5c9
        errmsg = substitute_macro_args(line, MAX_STRING_LEN,
Packit 90a5c9
                                       macro, replacements, used);
Packit 90a5c9
        if (errmsg) {
Packit 90a5c9
            return apr_psprintf(pool,
Packit 90a5c9
                               "while processing line %d of macro '%s' (%s) %s",
Packit 90a5c9
                                i + 1, macro->name, macro->location, errmsg);
Packit 90a5c9
        }
Packit 90a5c9
        /* append substituted line to result array */
Packit 90a5c9
        if (result) {
Packit 90a5c9
            char **new = apr_array_push(*result);
Packit 90a5c9
            *new = apr_pstrdup(pool, line);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  warn if some macro arguments are not used.
Packit 90a5c9
*/
Packit 90a5c9
static const char *check_macro_contents(apr_pool_t * pool,
Packit 90a5c9
                                        const ap_macro_t * macro)
Packit 90a5c9
{
Packit 90a5c9
    int nelts = macro->arguments->nelts;
Packit 90a5c9
    char **names = (char **) macro->arguments->elts;
Packit 90a5c9
    apr_array_header_t *used;
Packit 90a5c9
    int i;
Packit 90a5c9
    const char *errmsg;
Packit 90a5c9
Packit 90a5c9
    if (macro->contents->nelts == 0) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02799)
Packit 90a5c9
                     "macro '%s' (%s): empty contents!",
Packit 90a5c9
                     macro->name, macro->location);
Packit 90a5c9
        return NULL;            /* no need to further warnings... */
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    used = apr_array_make(pool, nelts, sizeof(char));
Packit 90a5c9
Packit 90a5c9
    for (i = 0; i < nelts; i++) {
Packit 90a5c9
        used->elts[i] = 0;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    errmsg = process_content(pool, macro, macro->arguments, used, NULL);
Packit 90a5c9
Packit 90a5c9
    if (errmsg) {
Packit 90a5c9
        return errmsg;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    for (i = 0; i < nelts; i++) {
Packit 90a5c9
        if (!used->elts[i]) {
Packit 90a5c9
            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02800)
Packit 90a5c9
                         "macro '%s' (%s): argument '%s' (#%d) never used",
Packit 90a5c9
                         macro->name, macro->location, names[i], i + 1);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/************************************************** MACRO PSEUDO CONFIG FILE */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  The expanded content of the macro is to be parsed as a ap_configfile_t.
Packit 90a5c9
  This is used to have some kind of old fashionned C object oriented inherited
Packit 90a5c9
  data structure for configs.
Packit 90a5c9
Packit 90a5c9
  The following struct stores the contents.
Packit 90a5c9
Packit 90a5c9
  This structure holds pointers (next, upper) to the current "file" which was
Packit 90a5c9
  being processed and is interrupted by the macro expansion. At the end
Packit 90a5c9
  of processing the macro, the initial data structure will be put back
Packit 90a5c9
  in place (see function next_one) and the reading will go on from there.
Packit 90a5c9
Packit 90a5c9
  If macros are used within macros, there may be a cascade of such temporary
Packit 90a5c9
  arrays used to insert the expanded macro contents before resuming the real
Packit 90a5c9
  file processing.
Packit 90a5c9
Packit 90a5c9
  There is some hopus-pocus to deal with line_number when transiting from
Packit 90a5c9
  one config to the other.
Packit 90a5c9
*/
Packit 90a5c9
typedef struct
Packit 90a5c9
{
Packit 90a5c9
    int index;                    /* current element */
Packit 90a5c9
    int char_index;               /* current char in element */
Packit 90a5c9
    int length;                   /* cached length of the current line */
Packit 90a5c9
    apr_array_header_t *contents; /* array of char * */
Packit 90a5c9
    ap_configfile_t *next;        /* next config once this one is processed */
Packit 90a5c9
    ap_configfile_t **upper;      /* hack: where to update it if needed */
Packit 90a5c9
} array_contents_t;
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  Get next config if any.
Packit 90a5c9
  this may be called several times if there are continuations.
Packit 90a5c9
*/
Packit 90a5c9
static int next_one(array_contents_t * ml)
Packit 90a5c9
{
Packit 90a5c9
    if (ml->next) {
Packit 90a5c9
        ap_assert(ml->upper);
Packit 90a5c9
        *(ml->upper) = ml->next;
Packit 90a5c9
        return 1;
Packit 90a5c9
    }
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  returns next char if possible
Packit 90a5c9
  this may involve switching to enclosing config.
Packit 90a5c9
*/
Packit 90a5c9
static apr_status_t array_getch(char *ch, void *param)
Packit 90a5c9
{
Packit 90a5c9
    array_contents_t *ml = (array_contents_t *) param;
Packit 90a5c9
    char **tab = (char **) ml->contents->elts;
Packit 90a5c9
Packit 90a5c9
    while (ml->char_index >= ml->length) {
Packit 90a5c9
        if (ml->index >= ml->contents->nelts) {
Packit 90a5c9
            /* maybe update */
Packit 90a5c9
            if (ml->next && ml->next->getch && next_one(ml)) {
Packit 90a5c9
                apr_status_t rc = ml->next->getch(ch, ml->next->param);
Packit 90a5c9
                if (*ch==LF)
Packit 90a5c9
                    ml->next->line_number++;
Packit 90a5c9
                return rc;
Packit 90a5c9
            }
Packit 90a5c9
            return APR_EOF;
Packit 90a5c9
        }
Packit 90a5c9
        ml->index++;
Packit 90a5c9
        ml->char_index = 0;
Packit 90a5c9
        ml->length = ml->index >= ml->contents->nelts ?
Packit 90a5c9
            0 : strlen(tab[ml->index]);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    *ch = tab[ml->index][ml->char_index++];
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  returns a buf a la fgets.
Packit 90a5c9
  no more than a line at a time, otherwise the parsing is too much ahead...
Packit 90a5c9
  NULL at EOF.
Packit 90a5c9
*/
Packit 90a5c9
static apr_status_t array_getstr(void *buf, size_t bufsize, void *param)
Packit 90a5c9
{
Packit 90a5c9
    array_contents_t *ml = (array_contents_t *) param;
Packit 90a5c9
    char *buffer = (char *) buf;
Packit 90a5c9
    char next = '\0';
Packit 90a5c9
    size_t i = 0;
Packit 90a5c9
    apr_status_t rc = APR_SUCCESS;
Packit 90a5c9
Packit 90a5c9
    /* read chars from stream, stop on newline */
Packit 90a5c9
    while (i < bufsize - 1 && next != LF &&
Packit 90a5c9
           ((rc = array_getch(&next, param)) == APR_SUCCESS)) {
Packit 90a5c9
        buffer[i++] = next;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (rc == APR_EOF) {
Packit 90a5c9
        /* maybe update to next, possibly a recursion */
Packit 90a5c9
        if (next_one(ml)) {
Packit 90a5c9
            ap_assert(ml->next->getstr);
Packit 90a5c9
            /* keep next line count in sync! the caller will update
Packit 90a5c9
               the current line_number, we need to forward to the next */
Packit 90a5c9
            ml->next->line_number++;
Packit 90a5c9
            return ml->next->getstr(buf, bufsize, ml->next->param);
Packit 90a5c9
        }
Packit 90a5c9
        /* else that is really all we can do */
Packit 90a5c9
        return APR_EOF;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    buffer[i] = '\0';
Packit 90a5c9
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  close the array stream?
Packit 90a5c9
*/
Packit 90a5c9
static apr_status_t array_close(void *param)
Packit 90a5c9
{
Packit 90a5c9
    array_contents_t *ml = (array_contents_t *) param;
Packit 90a5c9
    /* move index at end of stream... */
Packit 90a5c9
    ml->index = ml->contents->nelts;
Packit 90a5c9
    ml->char_index = ml->length;
Packit 90a5c9
    return APR_SUCCESS;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  create an array config stream insertion "object".
Packit 90a5c9
  could be exported.
Packit 90a5c9
*/
Packit 90a5c9
static ap_configfile_t *make_array_config(apr_pool_t * pool,
Packit 90a5c9
                                          apr_array_header_t * contents,
Packit 90a5c9
                                          const char *where,
Packit 90a5c9
                                          ap_configfile_t * cfg,
Packit 90a5c9
                                          ap_configfile_t ** upper)
Packit 90a5c9
{
Packit 90a5c9
    array_contents_t *ls =
Packit 90a5c9
        (array_contents_t *) apr_palloc(pool, sizeof(array_contents_t));
Packit 90a5c9
    ap_assert(ls!=NULL);
Packit 90a5c9
Packit 90a5c9
    ls->index = 0;
Packit 90a5c9
    ls->char_index = 0;
Packit 90a5c9
    ls->contents = contents;
Packit 90a5c9
    ls->length = ls->contents->nelts < 1 ?
Packit 90a5c9
        0 : strlen(((char **) ls->contents->elts)[0]);
Packit 90a5c9
    ls->next = cfg;
Packit 90a5c9
    ls->upper = upper;
Packit 90a5c9
Packit 90a5c9
    return ap_pcfg_open_custom(pool, where, (void *) ls,
Packit 90a5c9
                               array_getch, array_getstr, array_close);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
/********************************************************** KEYWORD HANDLING */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  handles: <Macro macroname arg1 arg2 ...> any trash there is ignored...
Packit 90a5c9
*/
Packit 90a5c9
static const char *macro_section(cmd_parms * cmd,
Packit 90a5c9
                                 void *dummy, const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    apr_pool_t *pool;
Packit 90a5c9
    char *endp, *name, *where;
Packit 90a5c9
    const char *errmsg;
Packit 90a5c9
    ap_macro_t *macro;
Packit 90a5c9
Packit 90a5c9
    debug(fprintf(stderr, "macro_section: arg='%s'\n", arg));
Packit 90a5c9
Packit 90a5c9
    /* lazy initialization */
Packit 90a5c9
    if (ap_macros == NULL) {
Packit 90a5c9
        pool = cmd->pool;
Packit 90a5c9
        ap_macros = apr_hash_make(pool);
Packit 90a5c9
        ap_assert(ap_macros != NULL);
Packit 90a5c9
        apr_pool_cleanup_register(pool, &ap_macros,
Packit 90a5c9
                                  ap_pool_cleanup_set_null,
Packit 90a5c9
                                  apr_pool_cleanup_null);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        pool = apr_hash_pool_get(ap_macros);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    endp = (char *) ap_strrchr_c(arg, '>');
Packit 90a5c9
Packit 90a5c9
    if (endp == NULL) {
Packit 90a5c9
        return BEGIN_MACRO "> directive missing closing '>'";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (endp == arg) {
Packit 90a5c9
        return BEGIN_MACRO " macro definition: empty name";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    warn_if_non_blank(APLOGNO(02801) "non blank chars found after "
Packit 90a5c9
                      BEGIN_MACRO " closing '>'",
Packit 90a5c9
                      endp+1, cmd->config_file);
Packit 90a5c9
Packit 90a5c9
    /* coldly drop '>[^>]*$' out */
Packit 90a5c9
    *endp = '\0';
Packit 90a5c9
Packit 90a5c9
    /* get lowercase macro name */
Packit 90a5c9
    name = ap_getword_conf(pool, &arg;;
Packit 90a5c9
    if (empty_string_p(name)) {
Packit 90a5c9
        return BEGIN_MACRO " macro definition: name not found";
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    ap_str_tolower(name);
Packit 90a5c9
    macro = apr_hash_get(ap_macros, name, APR_HASH_KEY_STRING);
Packit 90a5c9
Packit 90a5c9
    if (macro != NULL) {
Packit 90a5c9
        /* already defined: warn about the redefinition */
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02802)
Packit 90a5c9
                     "macro '%s' multiply defined: "
Packit 90a5c9
                     "%s, redefined on line %d of \"%s\"",
Packit 90a5c9
                     macro->name, macro->location,
Packit 90a5c9
                     cmd->config_file->line_number, cmd->config_file->name);
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        /* allocate a new macro */
Packit 90a5c9
        macro = (ap_macro_t *) apr_palloc(pool, sizeof(ap_macro_t));
Packit 90a5c9
        macro->name = name;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    debug(fprintf(stderr, "macro_section: name=%s\n", name));
Packit 90a5c9
Packit 90a5c9
    /* get macro arguments */
Packit 90a5c9
    macro->location = apr_psprintf(pool,
Packit 90a5c9
                                   "defined on line %d of \"%s\"",
Packit 90a5c9
                                   cmd->config_file->line_number,
Packit 90a5c9
                                   cmd->config_file->name);
Packit 90a5c9
    debug(fprintf(stderr, "macro_section: location=%s\n", macro->location));
Packit 90a5c9
Packit 90a5c9
    where =
Packit 90a5c9
        apr_psprintf(pool, "macro '%s' (%s)", macro->name, macro->location);
Packit 90a5c9
Packit 90a5c9
    if (looks_like_an_argument(name)) {
Packit 90a5c9
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02803)
Packit 90a5c9
                     "%s better prefix a macro name with any of '%s'",
Packit 90a5c9
                     where, ARG_PREFIX);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* get macro parameters */
Packit 90a5c9
    macro->arguments = get_arguments(pool, arg);
Packit 90a5c9
Packit 90a5c9
    errmsg = check_macro_arguments(cmd->temp_pool, macro);
Packit 90a5c9
Packit 90a5c9
    if (errmsg) {
Packit 90a5c9
        return errmsg;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    errmsg = get_lines_till_end_token(pool, cmd->config_file,
Packit 90a5c9
                                      END_MACRO, BEGIN_MACRO,
Packit 90a5c9
                                      where, &macro->contents);
Packit 90a5c9
Packit 90a5c9
    if (errmsg) {
Packit 90a5c9
        return apr_psprintf(cmd->temp_pool,
Packit 90a5c9
                            "%s" APR_EOL_STR "\tcontents error: %s",
Packit 90a5c9
                            where, errmsg);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    errmsg = check_macro_contents(cmd->temp_pool, macro);
Packit 90a5c9
Packit 90a5c9
    if (errmsg) {
Packit 90a5c9
        return apr_psprintf(cmd->temp_pool,
Packit 90a5c9
                            "%s" APR_EOL_STR "\tcontents checking error: %s",
Packit 90a5c9
                            where, errmsg);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* store the new macro */
Packit 90a5c9
    apr_hash_set(ap_macros, name, APR_HASH_KEY_STRING, macro);
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  handles: Use name value1 value2 ...
Packit 90a5c9
*/
Packit 90a5c9
static const char *use_macro(cmd_parms * cmd, void *dummy, const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    char *name, *recursion, *where;
Packit 90a5c9
    const char *errmsg;
Packit 90a5c9
    ap_macro_t *macro;
Packit 90a5c9
    apr_array_header_t *replacements;
Packit 90a5c9
    apr_array_header_t *contents;
Packit 90a5c9
Packit 90a5c9
    debug(fprintf(stderr, "use_macro -%s-\n", arg));
Packit 90a5c9
Packit 90a5c9
    /* must be initialized, or no macros has been defined */
Packit 90a5c9
    if (ap_macros == NULL) {
Packit 90a5c9
        return "no macro defined before " USE_MACRO;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* get lowercase macro name */
Packit 90a5c9
    name = ap_getword_conf(cmd->temp_pool, &arg;;
Packit 90a5c9
    ap_str_tolower(name);
Packit 90a5c9
Packit 90a5c9
    if (empty_string_p(name)) {
Packit 90a5c9
        return "no macro name specified with " USE_MACRO;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* get macro definition */
Packit 90a5c9
    macro = apr_hash_get(ap_macros, name, APR_HASH_KEY_STRING);
Packit 90a5c9
Packit 90a5c9
    if (!macro) {
Packit 90a5c9
        return apr_psprintf(cmd->temp_pool, "macro '%s' undefined", name);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* recursion is detected here by looking at the config file name,
Packit 90a5c9
     * which may already contains "macro 'foo'". Ok, it looks like a hack,
Packit 90a5c9
     * but otherwise it is uneasy to keep this data available somewhere...
Packit 90a5c9
     * the name has just the needed visibility and liveness.
Packit 90a5c9
     */
Packit 90a5c9
    recursion =
Packit 90a5c9
        apr_pstrcat(cmd->temp_pool, "macro '", macro->name, "'", NULL);
Packit 90a5c9
Packit 90a5c9
    if (ap_strstr((char *) cmd->config_file->name, recursion)) {
Packit 90a5c9
        return apr_psprintf(cmd->temp_pool,
Packit 90a5c9
                            "recursive use of macro '%s' is invalid",
Packit 90a5c9
                            macro->name);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* get macro arguments */
Packit 90a5c9
    replacements = get_arguments(cmd->temp_pool, arg);
Packit 90a5c9
Packit 90a5c9
    if (macro->arguments->nelts != replacements->nelts) {
Packit 90a5c9
        return apr_psprintf(cmd->temp_pool,
Packit 90a5c9
                            "macro '%s' (%s) used "
Packit 90a5c9
                            "with %d arguments instead of %d",
Packit 90a5c9
                            macro->name, macro->location,
Packit 90a5c9
                            replacements->nelts, macro->arguments->nelts);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    where = apr_psprintf(cmd->temp_pool,
Packit 90a5c9
                         "macro '%s' (%s) used on line %d of \"%s\"",
Packit 90a5c9
                         macro->name, macro->location,
Packit 90a5c9
                         cmd->config_file->line_number,
Packit 90a5c9
                         cmd->config_file->name);
Packit 90a5c9
Packit 90a5c9
    check_macro_use_arguments(where, replacements);
Packit 90a5c9
Packit 90a5c9
    errmsg = process_content(cmd->temp_pool, macro, replacements,
Packit 90a5c9
                             NULL, &contents);
Packit 90a5c9
Packit 90a5c9
    if (errmsg) {
Packit 90a5c9
        return apr_psprintf(cmd->temp_pool,
Packit 90a5c9
                            "%s error while substituting: %s",
Packit 90a5c9
                            where, errmsg);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* the current "config file" is replaced by a string array...
Packit 90a5c9
       at the end of processing the array, the initial config file
Packit 90a5c9
       will be returned there (see next_one) so as to go on. */
Packit 90a5c9
    cmd->config_file = make_array_config(cmd->temp_pool, contents, where,
Packit 90a5c9
                                         cmd->config_file, &cmd->config_file);
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static const char *undef_macro(cmd_parms * cmd, void *dummy, const char *arg)
Packit 90a5c9
{
Packit 90a5c9
    char *name;
Packit 90a5c9
    ap_macro_t *macro;
Packit 90a5c9
Packit 90a5c9
    /* must be initialized, or no macros has been defined */
Packit 90a5c9
    if (ap_macros == NULL) {
Packit 90a5c9
        return "no macro defined before " UNDEF_MACRO;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (empty_string_p(arg)) {
Packit 90a5c9
        return "no macro name specified with " UNDEF_MACRO;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* check that the macro is defined */
Packit 90a5c9
    name = apr_pstrdup(cmd->temp_pool, arg);
Packit 90a5c9
    ap_str_tolower(name);
Packit 90a5c9
    macro = apr_hash_get(ap_macros, name, APR_HASH_KEY_STRING);
Packit 90a5c9
    if (macro == NULL) {
Packit 90a5c9
        /* could be a warning? */
Packit 90a5c9
        return apr_psprintf(cmd->temp_pool,
Packit 90a5c9
                            "cannot remove undefined macro '%s'", name);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* free macro: cannot do that */
Packit 90a5c9
    /* remove macro from hash table */
Packit 90a5c9
    apr_hash_set(ap_macros, name, APR_HASH_KEY_STRING, NULL);
Packit 90a5c9
Packit 90a5c9
    return NULL;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/************************************************************* EXPORT MODULE */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  macro module commands.
Packit 90a5c9
  configuration file macro stuff
Packit 90a5c9
  they are processed immediately when found, hence the EXEC_ON_READ.
Packit 90a5c9
*/
Packit 90a5c9
static const command_rec macro_cmds[] = {
Packit 90a5c9
    AP_INIT_RAW_ARGS(BEGIN_MACRO, macro_section, NULL, EXEC_ON_READ | OR_ALL,
Packit 90a5c9
                     "Beginning of a macro definition section."),
Packit 90a5c9
    AP_INIT_RAW_ARGS(USE_MACRO, use_macro, NULL, EXEC_ON_READ | OR_ALL,
Packit 90a5c9
                     "Use of a macro."),
Packit 90a5c9
    AP_INIT_TAKE1(UNDEF_MACRO, undef_macro, NULL, EXEC_ON_READ | OR_ALL,
Packit 90a5c9
                  "Remove a macro definition."),
Packit 90a5c9
Packit 90a5c9
    {NULL}
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
  Module hooks are request-oriented thus it does not suit configuration
Packit 90a5c9
  file utils a lot. I haven't found any clean hook to apply something
Packit 90a5c9
  before then after configuration file processing. Also what about
Packit 90a5c9
  .htaccess files?
Packit 90a5c9
Packit 90a5c9
  Thus I think that server/util.c or server/config.c
Packit 90a5c9
  would be a better place for this stuff.
Packit 90a5c9
*/
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(macro) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,    /* common stuff */
Packit 90a5c9
        NULL,                   /* create per-directory config */
Packit 90a5c9
        NULL,                   /* merge per-directory config structures */
Packit 90a5c9
        NULL,                   /* create per-server config structure */
Packit 90a5c9
        NULL,                   /* merge per-server config structures */
Packit 90a5c9
        macro_cmds,             /* configuration commands */
Packit 90a5c9
        NULL                    /* register hooks */
Packit 90a5c9
};