| /* Functions for recorded errors, warnings, and verbose messages. |
| Copyright (C) 1998-2018 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| This program 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; version 2 of the License, or |
| (at your option) any later version. |
| |
| This program 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 <http://www.gnu.org/licenses/>. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <error.h> |
| #include <errno.h> |
| #include <locale.h> |
| |
| #include "record-status.h" |
| |
| /* Warnings recorded by record_warnings. */ |
| int recorded_warning_count; |
| |
| /* Errors recorded by record_errors. */ |
| int recorded_error_count; |
| |
| /* If not zero suppress warnings and information messages. */ |
| int be_quiet; |
| |
| /* If not zero give a lot more messages. */ |
| int verbose; |
| |
| /* Warnings which can be disabled: */ |
| /* By default we check the character map for ASCII compatibility. */ |
| bool warn_ascii = true; |
| /* By default we check that the international currency symbol matches a |
| known country code. */ |
| bool warn_int_curr_symbol = true; |
| |
| /* Alter the current locale to match the locale configured by the |
| user, and return the previous saved state. */ |
| struct locale_state |
| push_locale (void) |
| { |
| int saved_errno; |
| const char *orig; |
| char *copy = NULL; |
| |
| saved_errno = errno; |
| |
| orig = setlocale (LC_CTYPE, NULL); |
| if (orig == NULL) |
| error (0, 0, "failed to read locale!"); |
| |
| if (setlocale (LC_CTYPE, "") == NULL) |
| error (0, 0, "failed to set locale!"); |
| |
| errno = saved_errno; |
| |
| if (orig != NULL) |
| copy = strdup (orig); |
| |
| /* We will return either a valid locale or NULL if we failed |
| to save the locale. */ |
| return (struct locale_state) { .cur_locale = copy }; |
| } |
| |
| /* Use the saved state to restore the locale. */ |
| void |
| pop_locale (struct locale_state ls) |
| { |
| const char *set = NULL; |
| /* We might have failed to save the locale, so only attempt to |
| restore a validly saved non-NULL locale. */ |
| if (ls.cur_locale != NULL) |
| { |
| set = setlocale (LC_CTYPE, ls.cur_locale); |
| if (set == NULL) |
| error (0, 0, "failed to restore %s locale!", ls.cur_locale); |
| |
| free (ls.cur_locale); |
| } |
| } |
| |
| /* Wrapper to print verbose informative messages. |
| Verbose messages are only printed if --verbose |
| is in effect and --quiet is not. */ |
| void |
| __attribute__ ((__format__ (__printf__, 2, 3), nonnull (1, 2), unused)) |
| record_verbose (FILE *stream, const char *format, ...) |
| { |
| char *str; |
| va_list arg; |
| |
| if (!verbose) |
| return; |
| |
| if (!be_quiet) |
| { |
| struct locale_state ls; |
| int ret; |
| |
| va_start (arg, format); |
| ls = push_locale (); |
| |
| ret = vasprintf (&str, format, arg); |
| if (ret == -1) |
| abort (); |
| |
| pop_locale (ls); |
| va_end (arg); |
| |
| fprintf (stream, "[verbose] %s\n", str); |
| |
| free (str); |
| } |
| } |
| |
| /* Wrapper to print warning messages. We keep track of how |
| many were called because this effects our exit code. |
| Nothing is printed if --quiet is in effect, but warnings |
| are always counted. */ |
| void |
| __attribute__ ((__format__ (__printf__, 1, 2), nonnull (1), unused)) |
| record_warning (const char *format, ...) |
| { |
| char *str; |
| va_list arg; |
| |
| recorded_warning_count++; |
| |
| if (!be_quiet) |
| { |
| struct locale_state ls; |
| int ret; |
| |
| va_start (arg, format); |
| ls = push_locale (); |
| |
| ret = vasprintf (&str, format, arg); |
| if (ret == -1) |
| abort (); |
| |
| pop_locale (ls); |
| va_end (arg); |
| |
| fprintf (stderr, "[warning] %s\n", str); |
| |
| free (str); |
| } |
| } |
| |
| /* Wrapper to print error messages. We keep track of how |
| many were called because this effects our exit code. |
| Nothing is printed if --quiet is in effect, but errors |
| are always counted, and fatal errors always exit the |
| program. */ |
| void |
| __attribute__ ((__format__ (__printf__, 3, 4), nonnull (3), unused)) |
| record_error (int status, int errnum, const char *format, ...) |
| { |
| char *str; |
| va_list arg; |
| |
| recorded_error_count++; |
| |
| /* The existing behaviour is that even if you use --quiet, a fatal |
| error is always printed and terminates the process. */ |
| if (!be_quiet || status != 0) |
| { |
| struct locale_state ls; |
| int ret; |
| |
| va_start (arg, format); |
| ls = push_locale (); |
| |
| ret = vasprintf (&str, format, arg); |
| if (ret == -1) |
| abort (); |
| |
| pop_locale (ls); |
| va_end (arg); |
| |
| error (status, errnum, "[error] %s", str); |
| |
| free (str); |
| } |
| } |
| /* ... likewise for error_at_line. */ |
| void |
| __attribute__ ((__format__ (__printf__, 5, 6), nonnull (3, 5), unused)) |
| record_error_at_line (int status, int errnum, const char *filename, |
| unsigned int linenum, const char *format, ...) |
| { |
| char *str; |
| va_list arg; |
| |
| recorded_error_count++; |
| |
| /* The existing behaviour is that even if you use --quiet, a fatal |
| error is always printed and terminates the process. */ |
| if (!be_quiet || status != 0) |
| { |
| struct locale_state ls; |
| int ret; |
| |
| va_start (arg, format); |
| ls = push_locale (); |
| |
| ret = vasprintf (&str, format, arg); |
| if (ret == -1) |
| abort (); |
| |
| pop_locale (ls); |
| va_end (arg); |
| |
| error_at_line (status, errnum, filename, linenum, "[error] %s", str); |
| |
| free (str); |
| } |
| } |