diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index 539cb2c7e..e9d2b52fb 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -31,6 +31,8 @@ This is work in progress and may not always be a stable version. * `@define` can now use value-returning functions, e.g., `@define $ADMIN_EMAIL environment("ADMIN_EMAIL")` to set a variable from an OS environment variable. + * New `@error "message"` and `@warning "message"` directives. + Eg for `if !environment("ADMIN")` `@error "ADMIN env var not defined"`. ### Changes: * [GeoIP](https://www.unrealircd.org/docs/GeoIP): diff --git a/include/h.h b/include/h.h index cfa4d3832..1aaad2189 100644 --- a/include/h.h +++ b/include/h.h @@ -80,6 +80,7 @@ extern PreprocessorItem parse_preprocessor_item(char *start, char *end, const ch extern void preprocessor_cc_duplicate_list(ConditionalConfig *r, ConditionalConfig **out); extern void preprocessor_cc_free_level(ConditionalConfig **cc_list, int level); extern void preprocessor_cc_free_list(ConditionalConfig *cc); +extern int preprocessor_resolve_if(ConditionalConfig *cc, PreprocessorPhase phase); extern void preprocessor_resolve_conditionals_ce(ConfigEntry **ce_list, PreprocessorPhase phase); extern void preprocessor_resolve_conditionals_all(PreprocessorPhase phase); extern void init_config_defines(void); diff --git a/include/struct.h b/include/struct.h index e69916192..d1f29b73d 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1611,7 +1611,9 @@ typedef enum PreprocessorItem { PREPROCESSOR_DEFINE = 1, PREPROCESSOR_IF = 2, PREPROCESSOR_ENDIF = 3, - PREPROCESSOR_ELSE = 4 + PREPROCESSOR_ELSE = 4, + PREPROCESSOR_USER_ERROR = 5, + PREPROCESSOR_USER_WARNING = 6 } PreprocessorItem; typedef enum PreprocessorPhase { diff --git a/src/conf.c b/src/conf.c index 6184846e7..7a2b6e054 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1286,6 +1286,19 @@ ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsig break; } cc = NULL; + /* If we are inside a false @if block, skip directives + * that have side effects (@define, @error, @warning). + * We must still process @if/@else/@endif for nesting. + */ + if (cc_list && + !preprocessor_resolve_if(cc_list, PREPROCESSOR_PHASE_INITIAL) && + strncmp(start, "@if ", 4) && + strncmp(start, "@else", 5) && + strncmp(start, "@endif", 6)) + { + linenumber++; + break; + } n = parse_preprocessor_item(start, ptr, filename, linenumber, &cc); linenumber++; if (n == PREPROCESSOR_IF) @@ -1338,7 +1351,12 @@ ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsig { errors++; goto breakout; + } else + if (n == PREPROCESSOR_USER_ERROR) + { + errors++; } + /* PREPROCESSOR_USER_WARNING: nothing to do, message already printed */ if (!*ptr) goto breakout; /* special case, since we don't want the for loop to ptr++ */ diff --git a/src/conf_preprocessor.c b/src/conf_preprocessor.c index 88822c1ec..d8be9c119 100644 --- a/src/conf_preprocessor.c +++ b/src/conf_preprocessor.c @@ -476,6 +476,38 @@ PreprocessorItem evaluate_preprocessor_define(char *statement, const char *file return PREPROCESSOR_DEFINE; } +/** Evaluate an @error or @warning directive. + * @param statement Text after "@error " or "@warning " + * @param is_error 1 for @error, 0 for @warning + * @param filename Config filename (for error messages) + * @param linenumber Line number (for error messages) + * @returns PREPROCESSOR_USER_ERROR or PREPROCESSOR_USER_WARNING + */ +static PreprocessorItem evaluate_preprocessor_error_or_warning(char *statement, + int is_error, const char *filename, int linenumber) +{ + char *p = statement; + + skip_whitespace(&p); + if (*p == '"') + { + p++; + char *msg = p; + read_until(&p, "\""); + if (*p) + *p = '\0'; + p = msg; + } + + if (is_error) + { + config_error("%s:%i: %s", filename, linenumber, p); + return PREPROCESSOR_USER_ERROR; + } + config_warn("%s:%i: %s", filename, linenumber, p); + return PREPROCESSOR_USER_WARNING; +} + PreprocessorItem parse_preprocessor_item(char *start, char *end, const char *filename, int linenumber, ConditionalConfig **cc) { char buf[512]; @@ -496,6 +528,10 @@ PreprocessorItem parse_preprocessor_item(char *start, char *end, const char *fi return PREPROCESSOR_ELSE; else if (!strncmp(buf, "@endif", 6)) return PREPROCESSOR_ENDIF; + else if (!strncmp(buf, "@error ", 7)) + return evaluate_preprocessor_error_or_warning(buf+7, 1, filename, linenumber); + else if (!strncmp(buf, "@warning ", 9)) + return evaluate_preprocessor_error_or_warning(buf+9, 0, filename, linenumber); config_error("%s:%i: Unknown preprocessor directive: %s", filename, linenumber, buf); return PREPROCESSOR_ERROR; /* ??? */