From 100abaa82df32a2fb1cea16638cc4853bdc3ffc0 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sun, 22 Mar 2026 19:15:35 +0100 Subject: [PATCH] Conditional Config: add support for <, >, <= and >= in @if $SOMETHING ... And also don't require double quotes on the right hand side. So you now use something like: @if $MAXCONNECTIONS >= 1024 --- doc/RELEASE-NOTES.md | 3 ++ include/struct.h | 2 + src/conf_preprocessor.c | 93 +++++++++++++++++++++++++++++++---------- 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index e80973af0..45ed97f96 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -17,6 +17,9 @@ This is work in progress and may not always be a stable version. * New `@if file-exists("filename")` to check if a file exists. Paths are relative to the conf directory, or absolute if starting with `/`. + * Variable comparisons now support `>`, `>=`, `<`, `<=` in addition + to `==` and `!=`. Uses natural ordering, so version strings and + numbers compare correctly. Example: `@if $MAXCONNECTIONS >= 1024`. ### Changes: * [GeoIP](https://www.unrealircd.org/docs/GeoIP): diff --git a/include/struct.h b/include/struct.h index b7ef79c6f..0936ede8e 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1650,6 +1650,7 @@ struct AuthConfig { */ typedef enum ConfigIfCondition { IF_DEFINED=1, IF_VALUE=2, IF_MODULE_LOADED=3, IF_MODULE_EXISTS=4, IF_MINIMUM_VERSION=5, IF_FILE_EXISTS=6} ConfigIfCondition; +typedef enum CompareOp { COMPARE_EQ=0, COMPARE_NE=1, COMPARE_GT=2, COMPARE_GE=3, COMPARE_LT=4, COMPARE_LE=5 } CompareOp; struct ConditionalConfig { @@ -1657,6 +1658,7 @@ struct ConditionalConfig int priority; /**< Preprocessor level. Starts with 1, then 2, 3, .. */ ConfigIfCondition condition; /**< See ConfigIfCondition, one of: IF_* */ int negative; /**< For ! conditions */ + CompareOp compare_op; /**< Only for IF_VALUE */ char *name; /**< Name of the variable or module */ char *opt; /**< Only for IF_VALUE */ }; diff --git a/src/conf_preprocessor.c b/src/conf_preprocessor.c index 7dc81ed43..093e0b8be 100644 --- a/src/conf_preprocessor.c +++ b/src/conf_preprocessor.c @@ -94,6 +94,10 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, /* Currently we support: * $XYZ == "something" * $XYZ != "something" + * $XYZ > "something" + * $XYZ >= "something" + * $XYZ < "something" + * $XYZ <= "something" * module-loaded("something") * !module-loaded("something") * module-exists("something") @@ -127,9 +131,15 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, /* Otherwise it should be a $VARIABLE comparison */ { char *name, *name_terminate, *name2; + CompareOp compare_op; + int op_len; // Should be one of: // $XYZ == "something" // $XYZ != "something" + // $XYZ >= "something" + // $XYZ <= "something" + // $XYZ > "something" + // $XYZ < "something" // Anything else is an error. if (*p != '$') { @@ -137,10 +147,16 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, filename, linenumber, statement); return PREPROCESSOR_ERROR; } + if (negative) + { + config_error("%s:%i: @if: the '!' prefix cannot be used with variable comparisons, use != instead: %s", + filename, linenumber, statement); + return PREPROCESSOR_ERROR; + } p++; /* variable name starts now */ name = p; - read_until(&p, " \t=!"); + read_until(&p, " \t=!<>"); if (!*p) { config_error("%s:%i: invalid if statement (termination error): %s", @@ -151,40 +167,65 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, skip_whitespace(&p); if (!strncmp(p, "==", 2)) { - negative = 0; + compare_op = COMPARE_EQ; + op_len = 2; } else if (!strncmp(p, "!=", 2)) { - negative = 1; + compare_op = COMPARE_NE; + op_len = 2; + } else + if (!strncmp(p, ">=", 2)) + { + compare_op = COMPARE_GE; + op_len = 2; + } else + if (!strncmp(p, "<=", 2)) + { + compare_op = COMPARE_LE; + op_len = 2; + } else + if (*p == '>') + { + compare_op = COMPARE_GT; + op_len = 1; + } else + if (*p == '<') + { + compare_op = COMPARE_LT; + op_len = 1; } else { *name_terminate = '\0'; - config_error("%s:%i: @if: expected == or != after '%s'", + config_error("%s:%i: @if: expected comparison operator (==, !=, >, >=, <, <=) after '%s'", filename, linenumber, name); return PREPROCESSOR_ERROR; } - p += 2; + p += op_len; *name_terminate = '\0'; skip_whitespace(&p); - if (*p != '"') + if (*p == '"') { - config_error("%s:%i: @if: expected double quotes, missing \" perhaps?", - filename, linenumber); - return PREPROCESSOR_ERROR; - } - p++; - name2 = p; - read_until(&p, "\""); - if (!*p) + p++; + name2 = p; + read_until(&p, "\""); + if (!*p) + { + config_error("%s:%i: invalid @if statement, missing \" at end perhaps?", + filename, linenumber); + return PREPROCESSOR_ERROR; + } + *p = '\0'; + } else { - config_error("%s:%i: invalid @if statement, missing \" at end perhaps?", - filename, linenumber); - return PREPROCESSOR_ERROR; + name2 = p; + read_until(&p, " \t"); + if (*p) + *p = '\0'; } - *p = '\0'; cc = safe_alloc(sizeof(ConditionalConfig)); cc->condition = IF_VALUE; - cc->negative = negative; + cc->compare_op = compare_op; safe_strdup(cc->name, name); safe_strdup(cc->opt, name2); *cc_out = cc; @@ -327,6 +368,7 @@ void preprocessor_cc_duplicate_list(ConditionalConfig *r, ConditionalConfig **ou cc->priority = r->priority; cc->condition = r->condition; cc->negative = r->negative; + cc->compare_op = r->compare_op; AddListItem(cc, *out); } } @@ -400,9 +442,18 @@ int preprocessor_resolve_if(ConditionalConfig *cc, PreprocessorPhase phase) if (cc->condition == IF_VALUE) { NameValuePrioList *d = find_config_define(cc->name); - if (d && !strcasecmp(d->value, cc->opt)) + if (d) { - result = 1; + int cmp = strnatcasecmp(d->value, cc->opt); + switch (cc->compare_op) + { + case COMPARE_EQ: result = (cmp == 0); break; + case COMPARE_NE: result = (cmp != 0); break; + case COMPARE_GT: result = (cmp > 0); break; + case COMPARE_GE: result = (cmp >= 0); break; + case COMPARE_LT: result = (cmp < 0); break; + case COMPARE_LE: result = (cmp <= 0); break; + } } } else {