1 /* Determine whether string value is affirmation or negative response 2 according to current locale's data. 3 4 Copyright (C) 1996, 1998, 2000, 2002-2003, 2006-2021 Free Software 5 Foundation, Inc. 6 7 This program is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 19 20 #include <config.h> 21 22 /* Specification. */ 23 #include <stdlib.h> 24 25 #include <stdbool.h> 26 #include <stddef.h> 27 28 #if ENABLE_NLS 29 # include <sys/types.h> 30 # include <limits.h> 31 # include <string.h> 32 # if HAVE_LANGINFO_YESEXPR 33 # include <langinfo.h> 34 # endif 35 # include <regex.h> 36 # include "gettext.h" 37 # define _(msgid) gettext (msgid) 38 # define N_(msgid) gettext_noop (msgid) 39 40 # if HAVE_LANGINFO_YESEXPR 41 /* Return the localized regular expression pattern corresponding to 42 ENGLISH_PATTERN. NL_INDEX can be used with nl_langinfo. 43 The resulting string may only be used until the next nl_langinfo call. */ 44 static const char * 45 localized_pattern (const char *english_pattern, nl_item nl_index, /* */ 46 bool posixly_correct) 47 { 48 const char *translated_pattern; 49 50 /* We prefer to get the patterns from a PO file. It would be possible to 51 always use nl_langinfo (YESEXPR) instead of _("^[yY]"), and 52 nl_langinfo (NOEXPR) instead of _("^[nN]"), if we could assume that the 53 system's locale support is good. But this is not the case e.g. on Cygwin. 54 The localizations of gnulib.pot are of better quality in general. 55 Also, if we use locale info from non-free systems that don't have a 56 'localedef' command, we deprive the users of the freedom to localize 57 this pattern for their preferred language. 58 But some programs, such as 'cp', 'mv', 'rm', 'find', 'xargs', are 59 specified by POSIX to use nl_langinfo (YESEXPR). We implement this 60 behaviour if POSIXLY_CORRECT is set, for the sake of these programs. */ 61 62 /* If the user wants strict POSIX compliance, use nl_langinfo. */ 63 if (posixly_correct) 64 { 65 translated_pattern = nl_langinfo (nl_index); 66 /* Check against a broken system return value. */ 67 if (translated_pattern != NULL && translated_pattern[0] != '\0') 68 return translated_pattern; 69 } 70 71 /* Look in the gnulib message catalog. */ 72 translated_pattern = _(english_pattern); 73 if (translated_pattern == english_pattern) 74 { 75 /* The gnulib message catalog provides no translation. 76 Try the system's message catalog. */ 77 translated_pattern = nl_langinfo (nl_index); 78 /* Check against a broken system return value. */ 79 if (translated_pattern != NULL && translated_pattern[0] != '\0') 80 return translated_pattern; 81 /* Fall back to English. */ 82 translated_pattern = english_pattern; 83 } 84 return translated_pattern; 85 } 86 # else 87 # define localized_pattern(english_pattern,nl_index,posixly_correct) \ 88 _(english_pattern) 89 # endif 90 91 static int 92 try (const char *response, const char *pattern, char **lastp, regex_t *re) /* */ 93 { 94 if (*lastp == NULL || strcmp (pattern, *lastp) != 0) 95 { 96 char *safe_pattern; 97 98 /* The pattern has changed. */ 99 if (*lastp != NULL) 100 { 101 /* Free the old compiled pattern. */ 102 regfree (re); 103 free (*lastp); 104 *lastp = NULL; 105 } 106 /* Put the PATTERN into safe memory before calling regcomp. 107 (regcomp may call nl_langinfo, overwriting PATTERN's storage. */ 108 safe_pattern = strdup (pattern); 109 if (safe_pattern == NULL) 110 return -1; 111 /* Compile the pattern and cache it for future runs. */ 112 if (regcomp (re, safe_pattern, REG_EXTENDED) != 0) 113 { 114 free (safe_pattern); 115 return -1; 116 } 117 *lastp = safe_pattern; 118 } 119 120 /* See if the regular expression matches RESPONSE. */ 121 return regexec (re, response, 0, NULL, 0) == 0; 122 } 123 #endif 124 125 126 int 127 rpmatch (const char *response) /* */ 128 { 129 #if ENABLE_NLS 130 /* Match against one of the response patterns, compiling the pattern 131 first if necessary. */ 132 133 /* We cache the response patterns and compiled regexps here. */ 134 static char *last_yesexpr, *last_noexpr; 135 static regex_t cached_yesre, cached_nore; 136 137 # if HAVE_LANGINFO_YESEXPR 138 bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL); 139 # endif 140 141 const char *yesexpr, *noexpr; 142 int result; 143 144 /* TRANSLATORS: A regular expression testing for an affirmative answer 145 (english: "yes"). Testing the first character may be sufficient. 146 Take care to consider upper and lower case. 147 To enquire the regular expression that your system uses for this 148 purpose, you can use the command 149 locale -k LC_MESSAGES | grep '^yesexpr=' */ 150 yesexpr = localized_pattern (N_("^[yY]"), YESEXPR, posixly_correct); 151 result = try (response, yesexpr, &last_yesexpr, &cached_yesre); 152 if (result < 0) 153 return -1; 154 if (result) 155 return 1; 156 157 /* TRANSLATORS: A regular expression testing for a negative answer 158 (english: "no"). Testing the first character may be sufficient. 159 Take care to consider upper and lower case. 160 To enquire the regular expression that your system uses for this 161 purpose, you can use the command 162 locale -k LC_MESSAGES | grep '^noexpr=' */ 163 noexpr = localized_pattern (N_("^[nN]"), NOEXPR, posixly_correct); 164 result = try (response, noexpr, &last_noexpr, &cached_nore); 165 if (result < 0) 166 return -1; 167 if (result) 168 return 0; 169 170 return -1; 171 #else 172 /* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */ 173 return (*response == 'y' || *response == 'Y' ? 1 174 : *response == 'n' || *response == 'N' ? 0 : -1); 175 #endif 176 }