For cases like HttpProtocolOptions where a new directive is introduced
to multiple active branches simultaneously, it gets awkward to use
<IfVersion> to write conf files which use the new directive but are
compatible across multiple versions.
Triggered by a conversation with a user, but also e.g. see current test
suite t/conf/extra.conf.in which breaks for 2.4 releases older than
2.4.25 with:
<IfVersion >= 2.2.32>
<VirtualHost _default_:http_strict>
DocumentRoot @SERVERROOT@/htdocs/
HttpProtocolOptions Strict Require1.0 RegisteredMethods
Any reason <IfDirective> is a bad idea, so we can do that more cleanly
(... in a couple of decades time)?
Regards, Joe
Index: server/config.c
===================================================================
--- server/config.c (revision 1783943)
+++ server/config.c (working copy)
@@ -2668,6 +2668,16 @@
printf(" %s\n", ap_loaded_modules[n]->name);
}
+AP_DECLARE(int) ap_exists_directive(apr_pool_t *p, const char *name)
+{
+ char *lname = apr_pstrdup(p, name);
+
+ ap_str_tolower(lname);
+
+ return ap_config_hash &&
+ apr_hash_get(ap_config_hash, lname, APR_HASH_KEY_STRING) != NULL;
+}
+
AP_DECLARE(void *) ap_retained_data_get(const char *key)
{
void *retained;
Index: include/http_config.h
===================================================================
--- include/http_config.h (revision 1783943)
+++ include/http_config.h (working copy)
@@ -992,6 +992,12 @@
AP_DECLARE(void) ap_show_directives(void);
/**
+ * Returns non-zero if a configuration directive of the given name has
+ * been registered by a module at the time of calling.
+ */
+AP_DECLARE(int) ap_exists_directive(apr_pool_t *p, const char *name);
+
+/**
* Show the preloaded module names. Used for httpd -l.
*/
AP_DECLARE(void) ap_show_modules(void);
Index: server/core.c
===================================================================
--- server/core.c (revision 1783943)
+++ server/core.c (working copy)
@@ -2894,6 +2894,46 @@
}
}
+
+static const char *start_ifdirective(cmd_parms *cmd, void *dummy, const char
*arg)
+{
+ const char *endp;
+ int defined;
+ int not = 0;
+
+ endp = ap_strrchr_c(arg, '>');
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg = apr_pstrmemdup(cmd->temp_pool, arg, endp - arg);
+
+ if (arg[0] == '!') {
+ not = 1;
+ arg++;
+ }
+
+ if (!arg[0]) {
+ return missing_container_arg(cmd);
+ }
+
+ defined = ap_exists_directive(cmd->temp_pool, arg);
+ if ((!not && defined) || (not && !defined)) {
+ ap_directive_t *parent = NULL;
+ ap_directive_t *current = NULL;
+ const char *retval;
+
+ retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
+ ¤t, &parent, "<IfDirective");
+ *(ap_directive_t **)dummy = current;
+ return retval;
+ }
+ else {
+ *(ap_directive_t **)dummy = NULL;
+ return ap_soak_end_container(cmd, "<IfDirective");
+ }
+}
+
/* httpd.conf commands... beginning with the <VirtualHost> business */
static const char *virtualhost_section(cmd_parms *cmd, void *dummy,
@@ -4509,6 +4549,8 @@
"Container for directives based on existence of command line defines"),
AP_INIT_TAKE1("<IfFile", start_iffile, NULL, EXEC_ON_READ | OR_ALL,
"Container for directives based on existence of files on disk"),
+AP_INIT_TAKE1("<IfDirective", start_ifdirective, NULL, EXEC_ON_READ | OR_ALL,
+ "Container for directives based on existence of named directive"),
AP_INIT_RAW_ARGS("<DirectoryMatch", dirsection, (void*)1, RSRC_CONF,
"Container for directives affecting resources located in the "
"specified directories"),