|
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, ¯o->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 |
};
|