Hi Jim, > The only non-POSIX behaviour is that it fetches the yesexpr and noexpr from > a compiled PO file rather than from the system's nl_langinfo function.
Here is a proposed compromise that enables the strict POSIX behaviour when POSIXLY_CORRECT is set. This is a POSIX compliance fix for the programs 'cp', 'mv', 'rm', 'find', 'xargs'. This is a continuation of the approach presented last year in http://lists.gnu.org/archive/html/bug-gnulib/2007-08/msg00075.html Paul replied with the argument of POSIX compliance here: http://lists.gnu.org/archive/html/bug-gnulib/2007-08/msg00093.html I think testing POSIXLY_CORRECT should address this argument. Tested with the 'yesno' module. (Why does the 'rpmatch' module not have a unit test? Why does it not even have an include file that declares the rpmatch function?) Jim, Eric, is this ok to apply? 2007-08-18 Bruno Haible <[EMAIL PROTECTED]> * 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. *** lib/rpmatch.c.orig 2008-04-28 01:25:41.000000000 +0200 --- lib/rpmatch.c 2008-04-28 01:02:12.000000000 +0200 *************** *** 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 Foundation, Inc. This program is free software: you can redistribute it and/or modify --- 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 Foundation, Inc. This program is free software: you can redistribute it and/or modify *************** *** 19,59 **** #include <config.h> #include <stddef.h> #include <stdlib.h> #if ENABLE_NLS # include <sys/types.h> # include <limits.h> # include <regex.h> # include "gettext.h" # define _(msgid) gettext (msgid) static int ! try (const char *response, const char *pattern, const int match, ! const int nomatch, const char **lastp, regex_t *re) { ! if (pattern != *lastp) { /* The pattern has changed. */ ! if (*lastp) { /* Free the old compiled pattern. */ regfree (re); *lastp = NULL; } /* Compile the pattern and cache it for future runs. */ ! if (regcomp (re, pattern, REG_EXTENDED) != 0) return -1; ! *lastp = pattern; } /* See if the regular expression matches RESPONSE. */ ! return regexec (re, response, 0, NULL, 0) == 0 ? match : nomatch; } #endif int rpmatch (const char *response) { --- 19,126 ---- #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, char **lastp, regex_t *re) { ! if (*lastp == NULL || strcmp (pattern, *lastp) != 0) { + char *safe_pattern; + /* The pattern has changed. */ ! 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, safe_pattern, REG_EXTENDED) != 0) return -1; ! *lastp = safe_pattern; } /* See if the regular expression matches RESPONSE. */ ! 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,84 **** /* 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 const char *yesexpr, *noexpr; ! static regex_t yesre, nore; int result; ! return ((result = try (response, _("^[yY]"), 1, 0, ! &yesexpr, &yesre)) ! ? result ! : try (response, _("^[nN]"), 0, -1, &noexpr, &nore)); #else /* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */ return (*response == 'y' || *response == 'Y' ? 1 --- 128,171 ---- /* Match against one of the response patterns, compiling the pattern first if necessary. */ /* We cache the response patterns and compiled regexps here. */ ! 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; ! /* 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 *** m4/rpmatch.m4.orig 2008-04-28 01:25:41.000000000 +0200 --- m4/rpmatch.m4 2008-04-28 00:24:03.000000000 +0200 *************** *** 1,5 **** ! # rpmatch.m4 serial 5 ! dnl Copyright (C) 2002, 2003 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. --- 1,5 ---- ! # 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,16 **** ]) # Prerequisites of lib/rpmatch.c. ! AC_DEFUN([gl_PREREQ_RPMATCH], [:]) --- 13,27 ---- ]) # Prerequisites of lib/rpmatch.c. ! 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 ! ]) *** modules/rpmatch.orig 2008-04-28 01:25:41.000000000 +0200 --- modules/rpmatch 2008-04-27 23:49:28.000000000 +0200 *************** *** 6,11 **** --- 6,12 ---- m4/rpmatch.m4 Depends-on: + stdbool gettext-h regex
