summaryrefslogtreecommitdiff
path: root/lib/rpmatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rpmatch.c')
-rw-r--r--lib/rpmatch.c151
1 files changed, 124 insertions, 27 deletions
diff --git a/lib/rpmatch.c b/lib/rpmatch.c
index e5f79f8..3c9ce54 100644
--- a/lib/rpmatch.c
+++ b/lib/rpmatch.c
@@ -1,13 +1,13 @@
/* Determine whether string value is affirmation or negative response
according to current locale's data.
- Copyright (C) 1996, 1998, 2000, 2002, 2003, 2006 Free Software
+ Copyright (C) 1996, 1998, 2000, 2002-2003, 2006-2016 Free Software
Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ 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; either version 2, or (at your option)
- any later version.
+ the Free Software Foundation; either version 3 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
@@ -15,42 +15,110 @@
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, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
-#include <stddef.h>
+/* Specification. */
#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+
#if ENABLE_NLS
# include <sys/types.h>
# include <limits.h>
+# include <string.h>
+# if HAVE_LANGINFO_YESEXPR
+# include <langinfo.h>
+# endif
# include <regex.h>
# include "gettext.h"
# define _(msgid) gettext (msgid)
+# define N_(msgid) gettext_noop (msgid)
+
+# if HAVE_LANGINFO_YESEXPR
+/* Return the localized regular expression pattern corresponding to
+ ENGLISH_PATTERN. NL_INDEX can be used with nl_langinfo.
+ The resulting string may only be used until the next nl_langinfo call. */
+static const char *
+localized_pattern (const char *english_pattern, nl_item nl_index,
+ bool posixly_correct)
+{
+ const char *translated_pattern;
+
+ /* We prefer to get the patterns from a PO file. It would be possible to
+ always use nl_langinfo (YESEXPR) instead of _("^[yY]"), and
+ nl_langinfo (NOEXPR) instead of _("^[nN]"), if we could assume that the
+ system's locale support is good. But this is not the case e.g. on Cygwin.
+ The localizations of gnulib.pot are of better quality in general.
+ Also, if we use locale info from non-free systems that don't have a
+ 'localedef' command, we deprive the users of the freedom to localize
+ this pattern for their preferred language.
+ But some programs, such as 'cp', 'mv', 'rm', 'find', 'xargs', are
+ specified by POSIX to use nl_langinfo (YESEXPR). We implement this
+ behaviour if POSIXLY_CORRECT is set, for the sake of these programs. */
+
+ /* If the user wants strict POSIX compliance, use nl_langinfo. */
+ if (posixly_correct)
+ {
+ translated_pattern = nl_langinfo (nl_index);
+ /* Check against a broken system return value. */
+ if (translated_pattern != NULL && translated_pattern[0] != '\0')
+ return translated_pattern;
+ }
+
+ /* Look in the gnulib message catalog. */
+ translated_pattern = _(english_pattern);
+ if (translated_pattern == english_pattern)
+ {
+ /* The gnulib message catalog provides no translation.
+ Try the system's message catalog. */
+ translated_pattern = nl_langinfo (nl_index);
+ /* Check against a broken system return value. */
+ if (translated_pattern != NULL && translated_pattern[0] != '\0')
+ return translated_pattern;
+ /* Fall back to English. */
+ translated_pattern = english_pattern;
+ }
+ return translated_pattern;
+}
+# else
+# define localized_pattern(english_pattern,nl_index,posixly_correct) \
+ _(english_pattern)
+# endif
static int
-try (const char *response, const char *pattern, const int match,
- const int nomatch, const char **lastp, regex_t *re)
+try (const char *response, const char *pattern, char **lastp, regex_t *re)
{
- if (pattern != *lastp)
+ if (*lastp == NULL || strcmp (pattern, *lastp) != 0)
{
+ char *safe_pattern;
+
/* The pattern has changed. */
- if (*lastp)
- {
- /* Free the old compiled pattern. */
- regfree (re);
- *lastp = NULL;
- }
+ if (*lastp != NULL)
+ {
+ /* Free the old compiled pattern. */
+ regfree (re);
+ free (*lastp);
+ *lastp = NULL;
+ }
+ /* Put the PATTERN into safe memory before calling regcomp.
+ (regcomp may call nl_langinfo, overwriting PATTERN's storage. */
+ safe_pattern = strdup (pattern);
+ if (safe_pattern == NULL)
+ return -1;
/* Compile the pattern and cache it for future runs. */
- if (regcomp (re, pattern, REG_EXTENDED) != 0)
- return -1;
- *lastp = pattern;
+ if (regcomp (re, safe_pattern, REG_EXTENDED) != 0)
+ {
+ free (safe_pattern);
+ return -1;
+ }
+ *lastp = safe_pattern;
}
/* See if the regular expression matches RESPONSE. */
- return regexec (re, response, 0, NULL, 0) == 0 ? match : nomatch;
+ return regexec (re, response, 0, NULL, 0) == 0;
}
#endif
@@ -63,17 +131,46 @@ rpmatch (const char *response)
first if necessary. */
/* We cache the response patterns and compiled regexps here. */
- static const char *yesexpr, *noexpr;
- static regex_t yesre, nore;
+ static char *last_yesexpr, *last_noexpr;
+ static regex_t cached_yesre, cached_nore;
+
+# if HAVE_LANGINFO_YESEXPR
+ bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
+# endif
+
+ const char *yesexpr, *noexpr;
int result;
- return ((result = try (response, _("^[yY]"), 1, 0,
- &yesexpr, &yesre))
- ? result
- : try (response, _("^[nN]"), 0, -1, &noexpr, &nore));
+ /* TRANSLATORS: A regular expression testing for an affirmative answer
+ (english: "yes"). Testing the first character may be sufficient.
+ Take care to consider upper and lower case.
+ To enquire the regular expression that your system uses for this
+ purpose, you can use the command
+ locale -k LC_MESSAGES | grep '^yesexpr=' */
+ yesexpr = localized_pattern (N_("^[yY]"), YESEXPR, posixly_correct);
+ result = try (response, yesexpr, &last_yesexpr, &cached_yesre);
+ if (result < 0)
+ return -1;
+ if (result)
+ return 1;
+
+ /* TRANSLATORS: A regular expression testing for a negative answer
+ (english: "no"). Testing the first character may be sufficient.
+ Take care to consider upper and lower case.
+ To enquire the regular expression that your system uses for this
+ purpose, you can use the command
+ locale -k LC_MESSAGES | grep '^noexpr=' */
+ noexpr = localized_pattern (N_("^[nN]"), NOEXPR, posixly_correct);
+ result = try (response, noexpr, &last_noexpr, &cached_nore);
+ if (result < 0)
+ return -1;
+ if (result)
+ return 0;
+
+ return -1;
#else
/* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */
return (*response == 'y' || *response == 'Y' ? 1
- : *response == 'n' || *response == 'N' ? 0 : -1);
+ : *response == 'n' || *response == 'N' ? 0 : -1);
#endif
}