1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "http_config.h"
22 #include "apr_strings.h"
25 /************************************************ COMPILE TIME DEBUG CONTROL */
28 #define MOD_MACRO_DEBUG 1
31 run -f ./test/conf/test??.conf
33 /* #define MOD_MACRO_DEBUG 1 */
34 #undef MOD_MACRO_DEBUG
40 #if defined(MOD_MACRO_DEBUG)
41 #define debug(stmt) stmt
44 #endif /* MOD_MACRO_DEBUG */
46 /******************************************************** MODULE DECLARATION */
48 module AP_MODULE_DECLARE_DATA macro_module;
50 /********************************************************** MACRO MANAGEMENT */
53 this is a macro: name, arguments, contents, location.
57 char *name; /* lower case name of the macro */
58 apr_array_header_t *arguments; /* of char*, macro parameter names */
59 apr_array_header_t *contents; /* of char*, macro body */
60 char *location; /* of macro definition, for error messages */
63 /* configuration tokens.
65 #define BEGIN_MACRO "<Macro"
66 #define END_MACRO "</Macro>"
67 #define USE_MACRO "Use"
68 #define UNDEF_MACRO "UndefMacro"
71 Macros are kept globally...
72 They are not per-server or per-directory entities.
74 note: they are in a temp_pool, and there is a lazy initialization.
75 ap_macros is reset to NULL in pre_config hook to not depend
76 on static vs dynamic configuration.
78 hash type: (char *) name -> (ap_macro_t *) macro
80 static apr_hash_t *ap_macros = NULL;
82 /*************************************************************** PARSE UTILS */
84 #define empty_string_p(p) (!(p) || *(p) == '\0')
85 #define trim(line) while (*(line) == ' ' || *(line) == '\t') (line)++
88 return configuration-parsed arguments from line as an array.
89 the line is expected not to contain any '\n'?
91 static apr_array_header_t *get_arguments(apr_pool_t * pool, const char *line)
93 apr_array_header_t *args = apr_array_make(pool, 1, sizeof(char *));
97 char *arg = ap_getword_conf(pool, &line);
98 char **new = apr_array_push(args);
107 warn if anything non blank appears, but ignore comments...
109 static void warn_if_non_blank(const char * what,
111 ap_configfile_t * cfg)
114 for (p=ptr; *p; p++) {
117 if (*p != ' ' && *p != '\t') {
118 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02989)
119 "%s on line %d of %s: %s",
120 what, cfg->line_number, cfg->name, ptr);
127 get read lines as an array till end_token.
128 counts nesting for begin_token/end_token.
129 it assumes a line-per-line configuration (thru getline).
130 this function could be exported.
131 begin_token may be NULL.
133 static char *get_lines_till_end_token(apr_pool_t * pool,
134 ap_configfile_t * config_file,
135 const char *end_token,
136 const char *begin_token,
138 apr_array_header_t ** plines)
140 apr_array_header_t *lines = apr_array_make(pool, 1, sizeof(char *));
141 char line[MAX_STRING_LEN]; /* sorry, but this is expected by getline:-( */
142 int macro_nesting = 1, any_nesting = 1;
143 int line_number_start = config_file->line_number;
145 while (!ap_cfg_getline(line, MAX_STRING_LEN, config_file)) {
151 first = ap_getword_conf_nc(pool, &ptr);
153 /* detect nesting... */
154 if (!strncmp(first, "</", 2)) {
156 if (any_nesting < 0) {
157 ap_log_error(APLOG_MARK, APLOG_WARNING,
158 0, NULL, APLOGNO(02793)
159 "bad (negative) nesting on line %d of %s",
160 config_file->line_number - line_number_start,
164 else if (!strncmp(first, "<", 1)) {
168 if (!strcasecmp(first, end_token)) {
169 /* check for proper closing */
170 char * endp = (char *) ap_strrchr_c(line, '>');
172 /* this cannot happen if end_token contains '>' */
174 return "end directive missing closing '>'";
178 APLOGNO(02794) "non blank chars found after directive closing",
179 endp+1, config_file);
182 if (!macro_nesting) {
184 ap_log_error(APLOG_MARK,
185 APLOG_WARNING, 0, NULL, APLOGNO(02795)
186 "bad cumulated nesting (%+d) in %s",
193 else if (begin_token && !strcasecmp(first, begin_token)) {
197 new = apr_array_push(lines);
198 *new = apr_psprintf(pool, "%s" APR_EOL_STR, line); /* put EOL back? */
201 return apr_psprintf(pool, "expected token not found: %s", end_token);
204 /* the @* arguments are double-quote escaped when substituted */
205 #define ESCAPE_ARG '@'
207 /* other $* and %* arguments are simply replaced without escaping */
208 #define ARG_PREFIX "$%@"
211 characters allowed in an argument?
212 not used yet, because that would trigger some backward compatibility.
214 #define ARG_CONTENT \
215 "abcdefghijklmnopqrstuvwxyz" \
216 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
217 "0123456789_" ARG_PREFIX
220 returns whether it looks like an argument, i.e. prefixed by ARG_PREFIX.
222 static int looks_like_an_argument(const char *word)
224 return ap_strchr(ARG_PREFIX, *word) != 0;
228 generates an error on macro with two arguments of the same name.
229 generates an error if a macro argument name is empty.
230 generates a warning if arguments name prefixes conflict.
231 generates a warning if the first char of an argument is not in ARG_PREFIX
233 static const char *check_macro_arguments(apr_pool_t * pool,
234 const ap_macro_t * macro)
236 char **tab = (char **) macro->arguments->elts;
237 int nelts = macro->arguments->nelts;
240 for (i = 0; i < nelts; i++) {
241 size_t ltabi = strlen(tab[i]);
245 return apr_psprintf(pool,
246 "macro '%s' (%s): empty argument #%d name",
247 macro->name, macro->location, i + 1);
249 else if (!looks_like_an_argument(tab[i])) {
250 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02796)
252 "argument name '%s' (#%d) without expected prefix, "
253 "better prefix argument names with one of '%s'.",
254 macro->name, macro->location,
255 tab[i], i + 1, ARG_PREFIX);
258 for (j = i + 1; j < nelts; j++) {
259 size_t ltabj = strlen(tab[j]);
261 /* must not use the same argument name twice */
262 if (!strcmp(tab[i], tab[j])) {
263 return apr_psprintf(pool,
264 "argument name conflict in macro '%s' (%s): "
265 "argument '%s': #%d and #%d, "
266 "change argument names!",
267 macro->name, macro->location,
268 tab[i], i + 1, j + 1);
271 /* warn about common prefix, but only if non empty names */
272 if (ltabi && ltabj &&
273 !strncmp(tab[i], tab[j], ltabi < ltabj ? ltabi : ltabj)) {
274 ap_log_error(APLOG_MARK, APLOG_WARNING,
275 0, NULL, APLOGNO(02797)
277 "argument name prefix conflict (%s #%d and %s #%d), "
278 "be careful about your macro definition!",
279 macro->name, macro->location,
280 tab[i], i + 1, tab[j], j + 1);
289 warn about empty strings in array. could be legitimate.
291 static void check_macro_use_arguments(const char *where,
292 const apr_array_header_t * array)
294 char **tab = (char **) array->elts;
296 for (i = 0; i < array->nelts; i++) {
297 if (empty_string_p(tab[i])) {
298 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02798)
299 "%s: empty argument #%d", where, i + 1);
304 /******************************************************** SUBSTITUTION UTILS */
306 /* could be switched to '\'' */
311 returns the number of needed escapes for the string
313 static int number_of_escapes(const char delim, const char *str)
318 if (*s == ESCAPE || *s == delim)
322 debug(fprintf(stderr, "escapes: %d ---%s---\n", nesc, str));
327 replace name by replacement at the beginning of buf of bufsize.
328 returns an error message or NULL.
329 C is not really a nice language for processing strings.
331 static char *substitute(char *buf,
334 const char *replacement, const int do_esc)
336 int lbuf = strlen(buf),
337 lname = strlen(name),
338 lrepl = strlen(replacement),
340 (do_esc ? (2 + number_of_escapes(DELIM, replacement)) : 0),
341 shift = lsubs - lname, size = lbuf + shift, i, j;
343 /* buf must starts with name */
344 ap_assert(!strncmp(buf, name, lname));
347 if (!strcmp(name, replacement))
350 debug(fprintf(stderr,
351 "substitute(%s,%s,%s,%d,sh=%d,lbuf=%d,lrepl=%d,lsubs=%d)\n",
352 buf, name, replacement, do_esc, shift, lbuf, lrepl, lsubs));
354 if (size >= bufsize) {
355 /* could/should I reallocate? */
356 return "cannot substitute, buffer size too small";
359 /* cannot use strcpy as strings may overlap */
361 memmove(buf + lname + shift, buf + lname, lbuf - lname + 1);
364 /* insert the replacement with escapes */
368 for (i = 0; i < lrepl; i++, j++) {
369 if (do_esc && (replacement[i] == DELIM || replacement[i] == ESCAPE))
371 buf[j] = replacement[i];
380 find first occurence of args in buf.
381 in case of conflict, the LONGEST argument is kept. (could be the FIRST?).
382 returns the pointer and the whichone found, or NULL.
384 static char *next_substitution(const char *buf,
385 const apr_array_header_t * args, int *whichone)
387 char *chosen = NULL, **tab = (char **) args->elts;
391 for (i = 0; i < args->nelts; i++) {
392 char *found = ap_strstr((char *) buf, tab[i]);
393 size_t lfound = strlen(tab[i]);
394 if (found && (!chosen || found < chosen ||
395 (found == chosen && lchosen < lfound))) {
406 substitute macro arguments by replacements in buf of bufsize.
407 returns an error message or NULL.
408 if used is defined, returns the used macro arguments.
410 static const char *substitute_macro_args(
413 const ap_macro_t * macro,
414 const apr_array_header_t * replacements,
415 apr_array_header_t * used)
418 **atab = (char **) macro->arguments->elts,
419 **rtab = (char **) replacements->elts;
423 ap_assert(used->nalloc >= replacements->nelts);
425 debug(fprintf(stderr, "1# %s", buf));
427 while ((ptr = next_substitution(ptr, macro->arguments, &whichone))) {
428 const char *errmsg = substitute(ptr, buf - ptr + bufsize,
429 atab[whichone], rtab[whichone],
430 atab[whichone][0] == ESCAPE_ARG);
434 ptr += strlen(rtab[whichone]);
436 used->elts[whichone] = 1;
439 debug(fprintf(stderr, "2# %s", buf));
445 perform substitutions in a macro contents and
446 return the result as a newly allocated array, if result is defined.
447 may also return an error message.
448 passes used down to substitute_macro_args.
450 static const char *process_content(apr_pool_t * pool,
451 const ap_macro_t * macro,
452 const apr_array_header_t * replacements,
453 apr_array_header_t * used,
454 apr_array_header_t ** result)
456 apr_array_header_t *contents = macro->contents;
457 char line[MAX_STRING_LEN];
461 *result = apr_array_make(pool, contents->nelts, sizeof(char *));
464 /* for each line of the macro body */
465 for (i = 0; i < contents->nelts; i++) {
467 /* copy the line and subtitute macro parameters */
468 strncpy(line, ((char **) contents->elts)[i], MAX_STRING_LEN - 1);
469 errmsg = substitute_macro_args(line, MAX_STRING_LEN,
470 macro, replacements, used);
472 return apr_psprintf(pool,
473 "while processing line %d of macro '%s' (%s) %s",
474 i + 1, macro->name, macro->location, errmsg);
476 /* append substituted line to result array */
478 char **new = apr_array_push(*result);
479 *new = apr_pstrdup(pool, line);
487 warn if some macro arguments are not used.
489 static const char *check_macro_contents(apr_pool_t * pool,
490 const ap_macro_t * macro)
492 int nelts = macro->arguments->nelts;
493 char **names = (char **) macro->arguments->elts;
494 apr_array_header_t *used;
498 if (macro->contents->nelts == 0) {
499 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02799)
500 "macro '%s' (%s): empty contents!",
501 macro->name, macro->location);
502 return NULL; /* no need to further warnings... */
505 used = apr_array_make(pool, nelts, sizeof(char));
507 for (i = 0; i < nelts; i++) {
511 errmsg = process_content(pool, macro, macro->arguments, used, NULL);
517 for (i = 0; i < nelts; i++) {
518 if (!used->elts[i]) {
519 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02800)
520 "macro '%s' (%s): argument '%s' (#%d) never used",
521 macro->name, macro->location, names[i], i + 1);
529 /************************************************** MACRO PSEUDO CONFIG FILE */
532 The expanded content of the macro is to be parsed as a ap_configfile_t.
533 This is used to have some kind of old fashionned C object oriented inherited
534 data structure for configs.
536 The following struct stores the contents.
538 This structure holds pointers (next, upper) to the current "file" which was
539 being processed and is interrupted by the macro expansion. At the end
540 of processing the macro, the initial data structure will be put back
541 in place (see function next_one) and the reading will go on from there.
543 If macros are used within macros, there may be a cascade of such temporary
544 arrays used to insert the expanded macro contents before resuming the real
547 There is some hopus-pocus to deal with line_number when transiting from
548 one config to the other.
552 int index; /* current element */
553 int char_index; /* current char in element */
554 int length; /* cached length of the current line */
555 apr_array_header_t *contents; /* array of char * */
556 ap_configfile_t *next; /* next config once this one is processed */
557 ap_configfile_t **upper; /* hack: where to update it if needed */
561 Get next config if any.
562 this may be called several times if there are continuations.
564 static int next_one(array_contents_t * ml)
567 ap_assert(ml->upper);
568 *(ml->upper) = ml->next;
575 returns next char if possible
576 this may involve switching to enclosing config.
578 static apr_status_t array_getch(char *ch, void *param)
580 array_contents_t *ml = (array_contents_t *) param;
581 char **tab = (char **) ml->contents->elts;
583 while (ml->char_index >= ml->length) {
584 if (ml->index >= ml->contents->nelts) {
586 if (ml->next && ml->next->getch && next_one(ml)) {
587 apr_status_t rc = ml->next->getch(ch, ml->next->param);
589 ml->next->line_number++;
596 ml->length = ml->index >= ml->contents->nelts ?
597 0 : strlen(tab[ml->index]);
600 *ch = tab[ml->index][ml->char_index++];
605 returns a buf a la fgets.
606 no more than a line at a time, otherwise the parsing is too much ahead...
609 static apr_status_t array_getstr(void *buf, size_t bufsize, void *param)
611 array_contents_t *ml = (array_contents_t *) param;
612 char *buffer = (char *) buf;
615 apr_status_t rc = APR_SUCCESS;
617 /* read chars from stream, stop on newline */
618 while (i < bufsize - 1 && next != LF &&
619 ((rc = array_getch(&next, param)) == APR_SUCCESS)) {
624 /* maybe update to next, possibly a recursion */
626 ap_assert(ml->next->getstr);
627 /* keep next line count in sync! the caller will update
628 the current line_number, we need to forward to the next */
629 ml->next->line_number++;
630 return ml->next->getstr(buf, bufsize, ml->next->param);
632 /* else that is really all we can do */
642 close the array stream?
644 static apr_status_t array_close(void *param)
646 array_contents_t *ml = (array_contents_t *) param;
647 /* move index at end of stream... */
648 ml->index = ml->contents->nelts;
649 ml->char_index = ml->length;
654 create an array config stream insertion "object".
657 static ap_configfile_t *make_array_config(apr_pool_t * pool,
658 apr_array_header_t * contents,
660 ap_configfile_t * cfg,
661 ap_configfile_t ** upper)
663 array_contents_t *ls =
664 (array_contents_t *) apr_palloc(pool, sizeof(array_contents_t));
669 ls->contents = contents;
670 ls->length = ls->contents->nelts < 1 ?
671 0 : strlen(((char **) ls->contents->elts)[0]);
675 return ap_pcfg_open_custom(pool, where, (void *) ls,
676 array_getch, array_getstr, array_close);
680 /********************************************************** KEYWORD HANDLING */
683 handles: <Macro macroname arg1 arg2 ...> any trash there is ignored...
685 static const char *macro_section(cmd_parms * cmd,
686 void *dummy, const char *arg)
689 char *endp, *name, *where;
693 debug(fprintf(stderr, "macro_section: arg='%s'\n", arg));
695 /* lazy initialization */
696 if (ap_macros == NULL)
697 ap_macros = apr_hash_make(cmd->temp_pool);
698 ap_assert(ap_macros != NULL);
700 pool = apr_hash_pool_get(ap_macros);
702 endp = (char *) ap_strrchr_c(arg, '>');
705 return BEGIN_MACRO "> directive missing closing '>'";
709 return BEGIN_MACRO " macro definition: empty name";
712 warn_if_non_blank(APLOGNO(02801) "non blank chars found after "
713 BEGIN_MACRO " closing '>'",
714 endp+1, cmd->config_file);
716 /* coldly drop '>[^>]*$' out */
719 /* get lowercase macro name */
720 name = ap_getword_conf(pool, &arg);
721 if (empty_string_p(name)) {
722 return BEGIN_MACRO " macro definition: name not found";
725 ap_str_tolower(name);
726 macro = apr_hash_get(ap_macros, name, APR_HASH_KEY_STRING);
729 /* already defined: warn about the redefinition */
730 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02802)
731 "macro '%s' multiply defined: "
732 "%s, redefined on line %d of \"%s\"",
733 macro->name, macro->location,
734 cmd->config_file->line_number, cmd->config_file->name);
737 /* allocate a new macro */
738 macro = (ap_macro_t *) apr_palloc(pool, sizeof(ap_macro_t));
742 debug(fprintf(stderr, "macro_section: name=%s\n", name));
744 /* get macro arguments */
745 macro->location = apr_psprintf(pool,
746 "defined on line %d of \"%s\"",
747 cmd->config_file->line_number,
748 cmd->config_file->name);
749 debug(fprintf(stderr, "macro_section: location=%s\n", macro->location));
752 apr_psprintf(pool, "macro '%s' (%s)", macro->name, macro->location);
754 if (looks_like_an_argument(name)) {
755 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(02803)
756 "%s better prefix a macro name with any of '%s'",
760 /* get macro parameters */
761 macro->arguments = get_arguments(pool, arg);
763 errmsg = check_macro_arguments(cmd->temp_pool, macro);
769 errmsg = get_lines_till_end_token(pool, cmd->config_file,
770 END_MACRO, BEGIN_MACRO,
771 where, ¯o->contents);
774 return apr_psprintf(cmd->temp_pool,
775 "%s" APR_EOL_STR "\tcontents error: %s",
779 errmsg = check_macro_contents(cmd->temp_pool, macro);
782 return apr_psprintf(cmd->temp_pool,
783 "%s" APR_EOL_STR "\tcontents checking error: %s",
787 /* store the new macro */
788 apr_hash_set(ap_macros, name, APR_HASH_KEY_STRING, macro);
794 handles: Use name value1 value2 ...
796 static const char *use_macro(cmd_parms * cmd, void *dummy, const char *arg)
798 char *name, *recursion, *where;
801 apr_array_header_t *replacements;
802 apr_array_header_t *contents;
804 debug(fprintf(stderr, "use_macro -%s-\n", arg));
806 /* must be initialized, or no macros has been defined */
807 if (ap_macros == NULL) {
808 return "no macro defined before " USE_MACRO;
811 /* get lowercase macro name */
812 name = ap_getword_conf(cmd->temp_pool, &arg);
813 ap_str_tolower(name);
815 if (empty_string_p(name)) {
816 return "no macro name specified with " USE_MACRO;
819 /* get macro definition */
820 macro = apr_hash_get(ap_macros, name, APR_HASH_KEY_STRING);
823 return apr_psprintf(cmd->temp_pool, "macro '%s' undefined", name);
826 /* recursion is detected here by looking at the config file name,
827 * which may already contains "macro 'foo'". Ok, it looks like a hack,
828 * but otherwise it is uneasy to keep this data available somewhere...
829 * the name has just the needed visibility and liveness.
832 apr_pstrcat(cmd->temp_pool, "macro '", macro->name, "'", NULL);
834 if (ap_strstr((char *) cmd->config_file->name, recursion)) {
835 return apr_psprintf(cmd->temp_pool,
836 "recursive use of macro '%s' is invalid",
840 /* get macro arguments */
841 replacements = get_arguments(cmd->temp_pool, arg);
843 if (macro->arguments->nelts != replacements->nelts) {
844 return apr_psprintf(cmd->temp_pool,
845 "macro '%s' (%s) used "
846 "with %d arguments instead of %d",
847 macro->name, macro->location,
848 replacements->nelts, macro->arguments->nelts);
851 where = apr_psprintf(cmd->temp_pool,
852 "macro '%s' (%s) used on line %d of \"%s\"",
853 macro->name, macro->location,
854 cmd->config_file->line_number,
855 cmd->config_file->name);
857 check_macro_use_arguments(where, replacements);
859 errmsg = process_content(cmd->temp_pool, macro, replacements,
863 return apr_psprintf(cmd->temp_pool,
864 "%s error while substituting: %s",
868 /* the current "config file" is replaced by a string array...
869 at the end of processing the array, the initial config file
870 will be returned there (see next_one) so as to go on. */
871 cmd->config_file = make_array_config(cmd->temp_pool, contents, where,
872 cmd->config_file, &cmd->config_file);
877 static const char *undef_macro(cmd_parms * cmd, void *dummy, const char *arg)
882 /* must be initialized, or no macros has been defined */
883 if (ap_macros == NULL) {
884 return "no macro defined before " UNDEF_MACRO;
887 if (empty_string_p(arg)) {
888 return "no macro name specified with " UNDEF_MACRO;
891 /* check that the macro is defined */
892 name = apr_pstrdup(cmd->temp_pool, arg);
893 ap_str_tolower(name);
894 macro = apr_hash_get(ap_macros, name, APR_HASH_KEY_STRING);
896 /* could be a warning? */
897 return apr_psprintf(cmd->temp_pool,
898 "cannot remove undefined macro '%s'", name);
901 /* free macro: cannot do that */
902 /* remove macro from hash table */
903 apr_hash_set(ap_macros, name, APR_HASH_KEY_STRING, NULL);
908 static int macro_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
914 /************************************************************* EXPORT MODULE */
917 macro module commands.
918 configuration file macro stuff
919 they are processed immediatly when found, hence the EXEC_ON_READ.
921 static const command_rec macro_cmds[] = {
922 AP_INIT_RAW_ARGS(BEGIN_MACRO, macro_section, NULL, EXEC_ON_READ | OR_ALL,
923 "Beginning of a macro definition section."),
924 AP_INIT_RAW_ARGS(USE_MACRO, use_macro, NULL, EXEC_ON_READ | OR_ALL,
926 AP_INIT_TAKE1(UNDEF_MACRO, undef_macro, NULL, EXEC_ON_READ | OR_ALL,
927 "Remove a macro definition."),
932 static void macro_hooks(apr_pool_t *p)
934 ap_hook_pre_config(macro_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
938 Module hooks are request-oriented thus it does not suit configuration
939 file utils a lot. I haven't found any clean hook to apply something
940 before then after configuration file processing. Also what about
943 Thus I think that server/util.c or server/config.c
944 would be a better place for this stuff.
947 AP_DECLARE_MODULE(macro) = {
948 STANDARD20_MODULE_STUFF, /* common stuff */
949 NULL, /* create per-directory config */
950 NULL, /* merge per-directory config structures */
951 NULL, /* create per-server config structure */
952 NULL, /* merge per-server config structures */
953 macro_cmds, /* configuration commands */
954 macro_hooks /* register hooks */