diff options
author | Paul Smith <psmith@gnu.org> | 2023-03-18 17:24:45 -0400 |
---|---|---|
committer | Paul Smith <psmith@gnu.org> | 2023-04-02 10:02:18 -0400 |
commit | a0d1e76d604df5bd006fd5ed6a69963d3c6b4d7a (patch) | |
tree | 02d739d8168207be28535f359b0a6583f231f047 /src | |
parent | 03ecd94488b85adc38746ec3e7c2a297a522598e (diff) | |
download | make-git-a0d1e76d604df5bd006fd5ed6a69963d3c6b4d7a.tar.gz |
Add support for .WARNINGS special variable
Create a new special variable, .WARNINGS, to allow per-makefile
control over warnings. The command line settings will override
this.
Move the handling of warning flags to a new file: src/warning.c.
Allow the decode to work with generic strings, and call it from
decode_switches().
* Makefile.am: Add new file src/warning.c.
* build_w32.bat: Ditto.
* builddos.bat: Ditto.
* po/POTFILES.in: Ditto.
* src/makeint.h: #define for the .WARNINGS variable name.
* src/warning.h: Add declarations for methods moved from main.c.
Rename the enum warning_state to warning_action.
* src/warning.c: New file. Move all warning encode/decode here
from main.c.
* src/main.c: Move methods into warning.c and call those methods
instead.
(main): Set .WARNINGS as a special variable.
* src/job.c (construct_command_argv): Rename to warning_action.
* src/read.c (tilde_expand): Ditto.
* src/variable.c (set_special_var): Update warnings when the
.WARNINGS special variable is set.
* tests/scripts/options/warn: Check invalid warning options.
* tests/scripts/variables/WARNINGS: Add tests for the .WARNINGS
special variable.
Diffstat (limited to 'src')
-rw-r--r-- | src/expand.c | 1 | ||||
-rw-r--r-- | src/job.c | 2 | ||||
-rw-r--r-- | src/main.c | 166 | ||||
-rw-r--r-- | src/makeint.h | 5 | ||||
-rw-r--r-- | src/misc.c | 3 | ||||
-rw-r--r-- | src/output.c | 25 | ||||
-rw-r--r-- | src/read.c | 2 | ||||
-rw-r--r-- | src/variable.c | 41 | ||||
-rw-r--r-- | src/warning.c | 235 | ||||
-rw-r--r-- | src/warning.h | 50 |
10 files changed, 338 insertions, 192 deletions
diff --git a/src/expand.c b/src/expand.c index 533e7dfa..a1efa831 100644 --- a/src/expand.c +++ b/src/expand.c @@ -24,6 +24,7 @@ this program. If not, see <https://www.gnu.org/licenses/>. */ #include "job.h" #include "variable.h" #include "rule.h" +#include "warning.h" /* Initially, any errors reported when expanding strings will be reported against the file where the error appears. */ @@ -3635,7 +3635,7 @@ construct_command_argv (char *line, char **restp, struct file *file, { struct variable *var; /* Turn off undefined variables warning while we expand HOME. */ - enum warning_state save = warn_get (wt_undefined_var); + enum warning_action save = warn_get (wt_undefined_var); warn_set (wt_undefined_var, w_ignore); shell = allocated_expand_variable_for_file (STRING_SIZE_TUPLE ("SHELL"), file); @@ -273,18 +273,6 @@ static struct stringlist *eval_strings = 0; static int print_usage_flag = 0; -/* The default state of warnings. */ - -enum warning_state default_warnings[wt_max]; - -/* Current state of warnings. */ - -enum warning_state warnings[wt_max]; - -/* Global warning settings. */ - -enum warning_state warn_global; - /* Command line warning flags. */ static struct stringlist *warn_flags = 0; @@ -663,15 +651,6 @@ initialize_global_hash_tables (void) hash_init_function_table (); } -static void -initialize_warnings () -{ - /* All warnings must have a default. */ - default_warnings[wt_invalid_var] = w_warn; - default_warnings[wt_invalid_ref] = w_warn; - default_warnings[wt_undefined_var] = w_ignore; -} - /* This character map locate stop chars when parsing GNU makefiles. Each element is true if we should stop parsing on that character. */ @@ -886,101 +865,6 @@ decode_debug_flags (void) debug_flag = 0; } -static const char *w_state_map[w_error+1] = {NULL, "ignore", "warn", "error"}; -static const char *w_name_map[wt_max] = {"invalid-var", - "invalid-ref", - "undefined-var"}; - -#define encode_warn_state(_b,_s) variable_buffer_output (_b, w_state_map[_s], strlen (w_state_map[_s])) -#define encode_warn_name(_b,_t) variable_buffer_output (_b, w_name_map[_t], strlen (w_name_map[_t])) - -static enum warning_state -decode_warn_state (const char *state, size_t length) -{ - for (enum warning_state st = w_ignore; st <= w_error; ++st) - { - size_t len = strlen (w_state_map[st]); - if (length == len && strncasecmp (state, w_state_map[st], length) == 0) - return st; - } - - return w_unset; -} - -static enum warning_type -decode_warn_name (const char *name, size_t length) -{ - for (enum warning_type wt = wt_invalid_var; wt < wt_max; ++wt) - { - size_t len = strlen (w_name_map[wt]); - if (length == len && strncasecmp (name, w_name_map[wt], length) == 0) - return wt; - } - - return wt_max; -} - -static void -decode_warn_flags () -{ - const char **pp; - - /* */ - if (warn_undefined_variables_flag) - { - warn_set (wt_undefined_var, w_warn); - warn_undefined_variables_flag = 0; - } - - if (warn_flags) - for (pp=warn_flags->list; *pp; ++pp) - { - const char *p = *pp; - - while (*p != '\0') - { - enum warning_state state; - /* See if the value is comma-separated. */ - const char *ep = strchr (p, ','); - if (!ep) - ep = p + strlen (p); - - /* If the value is just a state set it globally. */ - state = decode_warn_state (p, ep - p); - if (state != w_unset) - warn_global = state; - else - { - enum warning_type type; - const char *cp = memchr (p, ':', ep - p); - if (!cp) - cp = ep; - type = decode_warn_name (p, cp - p); - if (type == wt_max) - ONS (fatal, NILF, - _("unknown warning '%.*s'"), (int)(cp - p), p); - - /* If there's a warning state, decode it. */ - if (cp == ep) - state = w_warn; - else - { - ++cp; - state = decode_warn_state (cp, ep - cp); - if (state == w_unset) - ONS (fatal, NILF, - _("unknown warning state '%.*s'"), (int)(ep - cp), cp); - } - warn_set (type, state); - } - - p = ep; - while (*p == ',') - ++p; - } - } -} - static void decode_output_sync_flags (void) { @@ -1323,7 +1207,7 @@ main (int argc, char **argv, char **envp) initialize_stopchar_map (); - initialize_warnings (); + warn_init (); #ifdef SET_STACK_SIZE /* Get rid of any avoidable limit on stack size. */ @@ -1554,6 +1438,7 @@ main (int argc, char **argv, char **envp) define_variable_cname (".VARIABLES", "", o_default, 0)->special = 1; /* define_variable_cname (".TARGETS", "", o_default, 0)->special = 1; */ define_variable_cname (".RECIPEPREFIX", "", o_default, 0)->special = 1; + define_variable_cname (WARNINGS_NAME, "", o_default, 0)->special = 1; define_variable_cname (".SHELLFLAGS", "-c", o_default, 0); define_variable_cname (".LOADED", "", o_default, 0); @@ -3013,7 +2898,7 @@ main (int argc, char **argv, char **envp) /* If we detected some clock skew, generate one last warning */ if (clock_skew_detected) O (error, NILF, - _("warning: Clock skew detected. Your build may be incomplete.")); + _("warning: Clock skew detected. Your build may be incomplete.")); /* Exit. */ die (makefile_status); @@ -3443,9 +3328,19 @@ decode_switches (int argc, const char **argv, enum variable_origin origin) /* If there are any options that need to be decoded do it now. */ decode_debug_flags (); - decode_warn_flags (); decode_output_sync_flags (); + /* Support old-style option. */ + if (warn_undefined_variables_flag) + { + decode_warn_actions ("undefined-var", NULL); + warn_undefined_variables_flag = 0; + } + + if (warn_flags) + for (const char **pp = warn_flags->list; *pp; ++pp) + decode_warn_actions (*pp, NULL); + /* Perform any special switch handling. */ run_silent = silent_flag; } @@ -3655,38 +3550,7 @@ define_makeflags (int makefile) case filename: case strlist: if (cs->c == WARN_OPT) - { - enum warning_type wt; - char sp = '='; - - /* See if any warning options are set. */ - for (wt = 0; wt < wt_max; ++wt) - if (warnings[wt] != w_unset) - break; - if (wt == wt_max && warn_global == w_unset) - break; - - /* Something is set so construct a --warn option. */ - fp = variable_buffer_output(fp, STRING_SIZE_TUPLE (" --warn")); - if (wt == wt_max && warn_global == w_warn) - break; - - if (warn_global > w_unset) - { - fp = variable_buffer_output (fp, &sp, 1); - sp = ','; - fp = encode_warn_state (fp, warn_global); - } - for (wt = 0; wt < wt_max; ++wt) - if (warnings[wt] > w_unset) - { - fp = variable_buffer_output (fp, &sp, 1); - sp = ','; - fp = encode_warn_name (fp, wt); - if (warnings[wt] != w_warn) - fp = encode_warn_state (variable_buffer_output(fp, ":", 1), warnings[wt]); - } - } + fp = encode_warn_flag (fp); else { struct stringlist *sl = *(struct stringlist **) cs->value_ptr; diff --git a/src/makeint.h b/src/makeint.h index e130e761..f34ec361 100644 --- a/src/makeint.h +++ b/src/makeint.h @@ -553,6 +553,8 @@ void error (const floc *flocp, size_t length, const char *fmt, ...) ATTRIBUTE ((__format__ (__printf__, 3, 4))); void fatal (const floc *flocp, size_t length, const char *fmt, ...) ATTRIBUTE ((noreturn, __format__ (__printf__, 3, 4))); +char *format (const char *prefix, size_t length, const char *fmt, ...) + ATTRIBUTE ((__format__ (__printf__, 3, 4))); void out_of_memory (void) NORETURN; /* When adding macros to this list be sure to update the value of @@ -760,6 +762,9 @@ extern int batch_mode_shell; #define RECIPEPREFIX_DEFAULT '\t' extern char cmd_prefix; +/* Setting warning actions. */ +#define WARNINGS_NAME ".WARNINGS" + extern unsigned int no_intermediates; #if HAVE_MKFIFO @@ -437,6 +437,7 @@ find_next_token (const char **ptr, size_t *lengthptr) return (char *)p; } + /* Write a BUFFER of size LEN to file descriptor FD. Retry short writes from EINTR. Return LEN, or -1 on error. */ ssize_t @@ -611,7 +612,7 @@ get_tmpdir () unsigned int found = 0; for (tp = tlist; *tp; ++tp) - if ((tmpdir = getenv (*tp)) && *tmpdir != '\0') + if ((tmpdir = getenv (*tp)) != NULL && *tmpdir != '\0') { struct stat st; int r; diff --git a/src/output.c b/src/output.c index eddabe03..2cf76072 100644 --- a/src/output.c +++ b/src/output.c @@ -475,8 +475,8 @@ error (const floc *flocp, size_t len, const char *fmt, ...) void fatal (const floc *flocp, size_t len, const char *fmt, ...) { - va_list args; const char *stop = _(". Stop.\n"); + va_list args; char *start; char *p; @@ -505,6 +505,29 @@ fatal (const floc *flocp, size_t len, const char *fmt, ...) die (MAKE_FAILURE); } +/* Format a message and return a pointer to an internal buffer. */ + +char * +format (const char *prefix, size_t len, const char *fmt, ...) +{ + va_list args; + size_t plen = prefix ? strlen (prefix) : 0; + char *start; + char *p; + + len += strlen (fmt) + plen + 1; + start = p = get_buffer (len); + + if (plen) + p = mempcpy (p, prefix, plen); + + va_start (args, fmt); + vsprintf (p, fmt, args); + va_end (args); + + return start; +} + /* Print an error message from errno. */ void @@ -3068,7 +3068,7 @@ tilde_expand (const char *name) { /* Turn off undefined variables warning while we expand HOME. */ - enum warning_state save = warn_get (wt_undefined_var); + enum warning_action save = warn_get (wt_undefined_var); warn_set (wt_undefined_var, w_ignore); home_dir = allocated_expand_variable (STRING_SIZE_TUPLE ("HOME")); diff --git a/src/variable.c b/src/variable.c index d2cfcc94..bfaef6c1 100644 --- a/src/variable.c +++ b/src/variable.c @@ -198,10 +198,8 @@ check_valid_name (const floc* flocp, const char *name, size_t length) if (cp == end) return; - if (warn_error (wt_invalid_var)) - ONS (fatal, flocp, _("invalid variable name '%.*s'"), (int)length, name); - - ONS (error, flocp, _("invalid variable name '%.*s'"), (int)length, name); + warning (wt_invalid_var, flocp, + ONS (format, 0, _("invalid variable name '%.*s'"), (int)length, name)); } void @@ -491,12 +489,8 @@ check_variable_reference (const char *name, size_t length) if (cp == end) return; - if (warn_error (wt_invalid_ref)) - ONS (fatal, *expanding_var, - _("invalid variable reference '%.*s'"), (int)length, name); - - ONS (error, *expanding_var, - _("invalid variable reference '%.*s'"), (int)length, name); + warning (wt_invalid_ref, *expanding_var, + ONS (format, 0, _("invalid variable reference '%.*s'"), (int)length, name)); } /* Lookup a variable whose name is a string starting at NAME @@ -1335,11 +1329,18 @@ set_special_var (struct variable *var, enum variable_origin origin) reset_makeflags (origin); else if (streq (var->name, RECIPEPREFIX_NAME)) + /* The user is resetting the command introduction prefix. This has to + happen immediately, so that subsequent rules are interpreted + properly. */ + cmd_prefix = var->value[0]=='\0' ? RECIPEPREFIX_DEFAULT : var->value[0]; + + else if (streq (var->name, WARNINGS_NAME)) { - /* The user is resetting the command introduction prefix. This has to - happen immediately, so that subsequent rules are interpreted - properly. */ - cmd_prefix = var->value[0]=='\0' ? RECIPEPREFIX_DEFAULT : var->value[0]; + /* It's weird but for .WARNINGS to make sense we need to expand them + when they are set, even if it's a recursive variable. */ + char *actions = allocated_expand_variable (STRING_SIZE_TUPLE (WARNINGS_NAME)); + decode_warn_actions (actions, &var->fileinfo); + free (actions); } return var; @@ -1499,7 +1500,7 @@ do_variable_definition (const floc *flocp, const char *varname, { char *s; if (streq (varname, MAKEFLAGS_NAME) - && (s = strstr (v->value, " -- "))) + && (s = strstr (v->value, " -- ")) != NULL) /* We found a separator in MAKEFLAGS. Ignore variable assignments: set_special_var() will reconstruct things. */ cp = mempcpy (cp, v->value, s - v->value); @@ -1914,6 +1915,7 @@ static const struct defined_vars defined_vars[] = { { STRING_SIZE_TUPLE ("-*-eval-flags-*-") }, { STRING_SIZE_TUPLE ("VPATH") }, { STRING_SIZE_TUPLE ("GPATH") }, + { STRING_SIZE_TUPLE (WARNINGS_NAME) }, { NULL, 0 } }; @@ -1927,12 +1929,9 @@ warn_undefined (const char *name, size_t len) if (dp->len == len && memcmp (dp->name, name, len) == 0) return; - if (warn_error (wt_undefined_var)) - fatal (reading_file, len, _("reference to undefined variable '%.*s'"), - (int)len, name); - else - error (reading_file, len, _("reference to undefined variable '%.*s'"), - (int)len, name); + warning (wt_undefined_var, reading_file, + ONS (format, 0, _("reference to undefined variable '%.*s'"), + (int)len, name)); } } diff --git a/src/warning.c b/src/warning.c new file mode 100644 index 00000000..c295fdc7 --- /dev/null +++ b/src/warning.c @@ -0,0 +1,235 @@ +/* Control warning output in GNU Make. +Copyright (C) 2023 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <https://www.gnu.org/licenses/>. */ + +#include "makeint.h" +#include "warning.h" +#include "variable.h" + +/* Current action for each warning. */ +enum warning_action warnings[wt_max]; + +/* The default behavior of warnings. */ +static struct warning_data warn_default; + +/* Warning settings from the .WARNING variable. */ +static struct warning_data warn_variable; + +/* Warning settings from the command line. */ +static struct warning_data warn_flag; + +static const char *w_action_map[w_error+1] = {NULL, "ignore", "warn", "error"}; +static const char *w_name_map[wt_max] = { + "invalid-var", + "invalid-ref", + "undefined-var" + }; + +#define encode_warn_action(_b,_s) \ + variable_buffer_output (_b, w_action_map[_s], strlen (w_action_map[_s])) +#define encode_warn_name(_b,_t) \ + variable_buffer_output (_b, w_name_map[_t], strlen (w_name_map[_t])) + +static void set_warnings () +{ + /* Called whenever any warnings could change; resets the current actions. */ + for (enum warning_type wt = 0; wt < wt_max; ++wt) + warnings[wt] = + warn_flag.actions[wt] != w_unset ? warn_flag.actions[wt] + : warn_flag.global != w_unset ? warn_flag.global + : warn_variable.actions[wt] != w_unset ? warn_variable.actions[wt] + : warn_variable.global != w_unset ? warn_variable.global + : warn_default.actions[wt]; +} + +void +warn_init () +{ + memset (&warn_default, '\0', sizeof (warn_default)); + memset (&warn_variable, '\0', sizeof (warn_variable)); + memset (&warn_flag, '\0', sizeof (warn_flag)); + + /* All warnings must have a default. */ + warn_default.global = w_warn; + warn_default.actions[wt_invalid_var] = w_warn; + warn_default.actions[wt_invalid_ref] = w_warn; + warn_default.actions[wt_undefined_var] = w_ignore; + + set_warnings (); +} + +static void +init_data (struct warning_data *data) +{ + data->global = w_unset; + for (enum warning_type wt = wt_invalid_var; wt < wt_max; ++wt) + data->actions[wt] = w_unset; +} + +static enum warning_action +decode_warn_action (const char *action, size_t length) +{ + for (enum warning_action st = w_ignore; st <= w_error; ++st) + { + size_t len = strlen (w_action_map[st]); + if (length == len && strncasecmp (action, w_action_map[st], length) == 0) + return st; + } + + return w_unset; +} + +static enum warning_type +decode_warn_name (const char *name, size_t length) +{ + for (enum warning_type wt = wt_invalid_var; wt < wt_max; ++wt) + { + size_t len = strlen (w_name_map[wt]); + if (length == len && strncasecmp (name, w_name_map[wt], length) == 0) + return wt; + } + + return wt_max; +} + +void +decode_warn_actions (const char *value, const floc *flocp) +{ + struct warning_data *data = &warn_flag; + + NEXT_TOKEN (value); + + if (flocp) + { + data = &warn_variable; + /* When a variable is set to empty, reset everything. */ + if (*value == '\0') + init_data (data); + } + + while (*value != '\0') + { + enum warning_action action; + + /* Find the end of the next warning definition. */ + const char *ep = value; + while (! STOP_SET (*ep, MAP_BLANK|MAP_COMMA|MAP_NUL)) + ++ep; + + /* If the value is just an action set it globally. */ + action = decode_warn_action (value, ep - value); + if (action != w_unset) + data->global = action; + else + { + enum warning_type type; + const char *cp = memchr (value, ':', ep - value); + if (!cp) + cp = ep; + type = decode_warn_name (value, cp - value); + if (type == wt_max) + { + int l = (int)(cp - value); + if (!flocp) + ONS (fatal, NILF, _("unknown warning '%.*s'"), l, value); + ONS (error, flocp, + _("unknown warning '%.*s': ignored"), l, value); + } + + /* If there's a warning action, decode it. */ + if (cp == ep) + action = w_warn; + else + { + ++cp; + action = decode_warn_action (cp, ep - cp); + if (action == w_unset) + { + int l = (int)(ep - cp); + if (!flocp) + ONS (fatal, NILF, _("unknown warning action '%.*s'"), l, cp); + ONS (error, flocp, + _("unknown warning action '%.*s': ignored"), l, cp); + } + } + data->actions[type] = action; + } + + value = ep; + while (STOP_SET (*value, MAP_BLANK|MAP_COMMA)) + ++value; + } + + set_warnings (); +} + +char * +encode_warn_flag (char *fp) +{ + enum warning_type wt; + char sp = '='; + + /* See if any warning options are set. */ + for (wt = 0; wt < wt_max; ++wt) + if (warn_flag.actions[wt] != w_unset) + break; + if (wt == wt_max && warn_flag.global == w_unset) + return fp; + + /* Something is set so construct a --warn option. */ + fp = variable_buffer_output (fp, STRING_SIZE_TUPLE (" --warn")); + + /* If only a global action set to warn, we're done. */ + if (wt == wt_max && warn_flag.global == w_warn) + return fp; + + /* If a global action is set, add it. */ + if (warn_flag.global > w_unset) + { + fp = variable_buffer_output (fp, &sp, 1); + sp = ','; + fp = encode_warn_action (fp, warn_flag.global); + } + + /* Add any specific actions. */ + if (wt != wt_max) + for (wt = 0; wt < wt_max; ++wt) + { + enum warning_action act = warn_flag.actions[wt]; + if (act > w_unset) + { + fp = variable_buffer_output (fp, &sp, 1); + sp = ','; + fp = encode_warn_name (fp, wt); + if (act != w_warn) + fp = encode_warn_action (variable_buffer_output (fp, ":", 1), act); + } + } + + return fp; +} + +void +warn_get_vardata (struct warning_data *data) +{ + memcpy (data, &warn_variable, sizeof (warn_variable)); +} + +void +warn_set_vardata (const struct warning_data *data) +{ + memcpy (&warn_variable, data, sizeof (warn_variable)); + set_warnings (); +} diff --git a/src/warning.h b/src/warning.h index 78e99893..658af93a 100644 --- a/src/warning.h +++ b/src/warning.h @@ -23,8 +23,8 @@ enum warning_type wt_max }; -/* State of a given warning. */ -enum warning_state +/* Action taken for a given warning. */ +enum warning_action { w_unset = 0, w_ignore, @@ -32,25 +32,24 @@ enum warning_state w_error }; -/* The default state of warnings. */ -extern enum warning_state default_warnings[wt_max]; - -/* Current state of warnings. */ -extern enum warning_state warnings[wt_max]; +struct warning_data + { + enum warning_action global; /* Global setting. */ + enum warning_action actions[wt_max]; /* Action for each warning type. */ + }; -/* Global warning settings. */ -extern enum warning_state warn_global; +/* Actions taken for each warning. */ +extern enum warning_action warnings[wt_max]; -/* Get the current state of a given warning. */ -#define warn_get(_w) (warnings[_w] != w_unset ? warnings[_w] \ - : warn_global != w_unset ? warn_global \ - : default_warnings[_w]) +/* Get the current action for a given warning. */ +#define warn_get(_w) (warnings[_w]) -/* Set the current state of a given warning. Can't use w_unset here. */ -#define warn_set(_w,_f) do{ warnings[_w] = (_f); } while (0) +/* Set the current actin for a given warning. Can't use w_unset here. + This should only be used for temporary resetting of warnings. */ +#define warn_set(_w,_f) do{ warnings[_w] = (_f); }while(0) /* True if we should check for the warning in the first place. */ -#define warn_check(_w) (warn_get (_w) > w_ignore) +#define warn_check(_w) (warn_get (_w) > w_ignore) /* Check if the warning is ignored. */ #define warn_ignored(_w) (warn_get (_w) == w_ignore) @@ -60,3 +59,22 @@ extern enum warning_state warn_global; /* Check if the warning is in "error" mode. */ #define warn_error(_w) (warn_get (_w) == w_error) + +void warn_init (void); +void decode_warn_actions (const char *value, const floc *flocp); +char *encode_warn_flag (char *fp); + +void warn_get_vardata (struct warning_data *data); +void warn_set_vardata (const struct warning_data *data); + +#define warning(_t,_f,_m) \ + do{ \ + if (warn_check (_t)) \ + { \ + char *_a = xstrdup (_m); \ + if (warn_error (_t)) \ + fatal (_f, strlen (_a), "%s", _a); \ + error (_f, strlen (_a), _("warning: %s"), _a); \ + free (_a); \ + } \ + }while(0) |