]> git.cworth.org Git - tar/blobdiff - lib/rpmatch.c
Imported Upstream version 1.21
[tar] / lib / rpmatch.c
index 826bc4c5e5244970bbbae96c273dabd2030bd5a9..217e1710a2b1891ec11d45247a668acf9ddf47e8 100644 (file)
@@ -1,7 +1,7 @@
 /* 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-2008 Free Software
    Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
 
 #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)
+      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)
+      if (regcomp (re, safe_pattern, REG_EXTENDED) != 0)
        return -1;
-      *lastp = pattern;
+      *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
 
@@ -62,14 +128,43 @@ 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