changeset 10000:8095c13f76df

Change rpmatch to use nl_langinfo when appropriate.
author Bruno Haible <bruno@clisp.org>
date Tue, 29 Apr 2008 02:37:52 +0200
parents 5f559abfabef
children facc928673d7
files ChangeLog lib/rpmatch.c m4/rpmatch.m4 modules/rpmatch
diffstat 4 files changed, 138 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Apr 28 17:08:43 2008 -0600
+++ b/ChangeLog	Tue Apr 29 02:37:52 2008 +0200
@@ -1,3 +1,16 @@
+2008-04-28  Bruno Haible  <bruno@clisp.org>
+
+	Change rpmatch to use nl_langinfo when appropriate.
+	* lib/rpmatch.c: Include stdbool.h, string.h, langinfo.h.
+	(N_): New macro.
+	(localized_pattern): New function/macro.
+	(try): Remove match, nomatch arguments. Copy the pattern into safe
+	memory before caching it.
+	(rpmatch): Use localized_pattern. Add translator comments.
+	* m4/rpmatch.m4 (gl_PREREQ_RPMATCH): Test for nl_langinfo and YESEXPR.
+	Suggested by Eric Blake.
+	* modules/rpmatch (Depends-on): Add stdbool.
+
 2008-04-28  Eric Blake  <ebb9@byu.net>
 
 	Add rawmemchr module, matching glibc.
--- a/lib/rpmatch.c	Mon Apr 28 17:08:43 2008 -0600
+++ b/lib/rpmatch.c	Tue Apr 29 02:37:52 2008 +0200
@@ -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, 2008 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
@@ -19,41 +19,108 @@
 
 #include <config.h>
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdlib.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
 
 
+/* Test a user response to a question.
+   Return 1 if it is affirmative, 0 if it is negative, or -1 if not clear.  */
+
 int
 rpmatch (const char *response)
 {
@@ -61,24 +128,44 @@
   /* Match against one of the response patterns, compiling the pattern
      first if necessary.  */
 
-  /* We get the patterns from a PO file.  It would be possible to 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 used locale info from non-free systems that don't have a
-     'localedef' command, we would deprive the users of the freedom to
-     localize this pattern for their preferred language.  */
+  /* We cache the response patterns and compiled regexps here.  */
+  static char *last_yesexpr, *last_noexpr;
+  static regex_t cached_yesre, cached_nore;
 
-  /* We cache the response patterns and compiled regexps here.  */
-  static const char *yesexpr, *noexpr;
-  static regex_t yesre, 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
--- a/m4/rpmatch.m4	Mon Apr 28 17:08:43 2008 -0600
+++ b/m4/rpmatch.m4	Tue Apr 29 02:37:52 2008 +0200
@@ -1,5 +1,5 @@
-# rpmatch.m4 serial 5
-dnl Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+# rpmatch.m4 serial 6
+dnl Copyright (C) 2002-2003, 2007-2008 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
@@ -13,4 +13,15 @@
 ])
 
 # Prerequisites of lib/rpmatch.c.
-AC_DEFUN([gl_PREREQ_RPMATCH], [:])
+AC_DEFUN([gl_PREREQ_RPMATCH], [
+  AC_CACHE_CHECK([for nl_langinfo and YESEXPR], gl_cv_langinfo_yesexpr,
+    [AC_TRY_LINK([#include <langinfo.h>],
+       [char* cs = nl_langinfo(YESEXPR); return !cs;],
+       [gl_cv_langinfo_yesexpr=yes],
+       [gl_cv_langinfo_yesexpr=no])
+    ])
+  if test $gl_cv_langinfo_yesexpr = yes; then
+    AC_DEFINE([HAVE_LANGINFO_YESEXPR], 1,
+      [Define if you have <langinfo.h> and nl_langinfo(YESEXPR).])
+  fi
+])
--- a/modules/rpmatch	Mon Apr 28 17:08:43 2008 -0600
+++ b/modules/rpmatch	Tue Apr 29 02:37:52 2008 +0200
@@ -6,6 +6,7 @@
 m4/rpmatch.m4
 
 Depends-on:
+stdbool
 gettext-h
 regex