/* 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 . */ #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); int wl, al; if (!cp) cp = ep; wl = (int)(cp - value); type = decode_warn_name (value, wl); if (cp == ep) action = w_warn; else { /* There's a warning action: decode it. */ ++cp; al = (int)(ep - cp); action = decode_warn_action (cp, al); } if (type == wt_max) { if (!flocp) ONS (fatal, NILF, _("unknown warning '%.*s'"), wl, value); ONS (error, flocp, _("unknown warning '%.*s': ignored"), wl, value); } else if (action == w_unset) { if (!flocp) ONS (fatal, NILF, _("unknown warning action '%.*s'"), al, cp); ONS (error, flocp, _("unknown warning action '%.*s': ignored"), al, cp); } else 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 (); }