diff options
Diffstat (limited to 'src/warning.c')
-rw-r--r-- | src/warning.c | 235 |
1 files changed, 235 insertions, 0 deletions
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 (); +} |