diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index 45ed97f96..045a64b3b 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -20,6 +20,7 @@ This is work in progress and may not always be a stable version. * Variable comparisons now support `>`, `>=`, `<`, `<=` in addition to `==` and `!=`. Uses natural ordering, so version strings and numbers compare correctly. Example: `@if $MAXCONNECTIONS >= 1024`. + * Support `@else`: `@if ... @else ... @endif`. ### Changes: * [GeoIP](https://www.unrealircd.org/docs/GeoIP): diff --git a/include/struct.h b/include/struct.h index 0936ede8e..2c4830ea6 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1610,7 +1610,8 @@ typedef enum PreprocessorItem { PREPROCESSOR_ERROR = 0, PREPROCESSOR_DEFINE = 1, PREPROCESSOR_IF = 2, - PREPROCESSOR_ENDIF = 3 + PREPROCESSOR_ENDIF = 3, + PREPROCESSOR_ELSE = 4 } PreprocessorItem; typedef enum PreprocessorPhase { @@ -1659,6 +1660,7 @@ struct ConditionalConfig ConfigIfCondition condition; /**< See ConfigIfCondition, one of: IF_* */ int negative; /**< For ! conditions */ CompareOp compare_op; /**< Only for IF_VALUE */ + int had_else; /**< Set to 1 after @else, to prevent duplicate @else */ char *name; /**< Name of the variable or module */ char *opt; /**< Only for IF_VALUE */ }; diff --git a/src/conf.c b/src/conf.c index de6d1bb87..6184846e7 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1294,6 +1294,35 @@ ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsig cc->priority = preprocessor_level; AddListItem(cc, cc_list); } else + if (n == PREPROCESSOR_ELSE) + { + ConditionalConfig *cc_item; + if (preprocessor_level == 0) + { + config_error("%s:%i: @else unexpected. There was no preceding unclosed @if.", + filename, linenumber); + errors++; + } else + { + /* Find the condition at the current nesting level and flip it */ + for (cc_item = cc_list; cc_item; cc_item = cc_item->next) + { + if (cc_item->priority == preprocessor_level) + { + if (cc_item->had_else) + { + config_error("%s:%i: duplicate @else for the same @if block.", + filename, linenumber); + errors++; + goto breakout; + } + cc_item->negative = cc_item->negative ? 0 : 1; + cc_item->had_else = 1; + break; + } + } + } + } else if (n == PREPROCESSOR_ENDIF) { if (preprocessor_level == 0) diff --git a/src/conf_preprocessor.c b/src/conf_preprocessor.c index 093e0b8be..35a33211d 100644 --- a/src/conf_preprocessor.c +++ b/src/conf_preprocessor.c @@ -108,6 +108,7 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, * !file-exists("something") * defined($XYZ) * !defined($XYZ) + * @else is also supported (handled in conf.c, not here). * We do not support && or || or anything else at this time. */ skip_whitespace(&p); @@ -320,6 +321,8 @@ PreprocessorItem parse_preprocessor_item(char *start, char *end, const char *fi return evaluate_preprocessor_define(buf+7, filename, linenumber); else if (!strncmp(buf, "@if ", 4)) return evaluate_preprocessor_if(buf+4, filename, linenumber, cc); + else if (!strncmp(buf, "@else", 5)) + return PREPROCESSOR_ELSE; else if (!strncmp(buf, "@endif", 6)) return PREPROCESSOR_ENDIF; @@ -369,6 +372,7 @@ void preprocessor_cc_duplicate_list(ConditionalConfig *r, ConditionalConfig **ou cc->condition = r->condition; cc->negative = r->negative; cc->compare_op = r->compare_op; + cc->had_else = r->had_else; AddListItem(cc, *out); } }