exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / rpmatch.c
blobac16fbf0cab84d97310fc181ef6c39db53336b76
1 /* Determine whether string value is affirmation or negative response
2 according to current locale's data.
4 Copyright (C) 1996, 1998, 2000, 2002-2003, 2006-2024 Free Software
5 Foundation, Inc.
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.
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.
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/>. */
20 #include <config.h>
22 /* Specification. */
23 #include <stdlib.h>
25 #include <stddef.h>
27 #if ENABLE_NLS
28 # include <sys/types.h>
29 # include <limits.h>
30 # include <string.h>
31 # if HAVE_LANGINFO_YESEXPR
32 # include <langinfo.h>
33 # endif
34 # include <regex.h>
35 # include "gettext.h"
36 # define _(msgid) gettext (msgid)
37 # define N_(msgid) gettext_noop (msgid)
39 # if HAVE_LANGINFO_YESEXPR
40 /* Return the localized regular expression pattern corresponding to
41 ENGLISH_PATTERN. NL_INDEX can be used with nl_langinfo.
42 The resulting string may only be used until the next nl_langinfo call. */
43 static const char *
44 localized_pattern (const char *english_pattern, nl_item nl_index,
45 bool posixly_correct)
47 const char *translated_pattern;
49 /* We prefer to get the patterns from a PO file. It would be possible to
50 always use nl_langinfo (YESEXPR) instead of _("^[yY]"), and
51 nl_langinfo (NOEXPR) instead of _("^[nN]"), if we could assume that the
52 system's locale support is good. But this is not the case e.g. on Cygwin.
53 The localizations of gnulib.pot are of better quality in general.
54 Also, if we use locale info from non-free systems that don't have a
55 'localedef' command, we deprive the users of the freedom to localize
56 this pattern for their preferred language.
57 But some programs, such as 'cp', 'mv', 'rm', 'find', 'xargs', are
58 specified by POSIX to use nl_langinfo (YESEXPR). We implement this
59 behaviour if POSIXLY_CORRECT is set, for the sake of these programs. */
61 /* If the user wants strict POSIX compliance, use nl_langinfo. */
62 if (posixly_correct)
64 translated_pattern = nl_langinfo (nl_index);
65 /* Check against a broken system return value. */
66 if (translated_pattern != NULL && translated_pattern[0] != '\0')
67 return translated_pattern;
70 /* Look in the gnulib message catalog. */
71 translated_pattern = _(english_pattern);
72 if (translated_pattern == english_pattern)
74 /* The gnulib message catalog provides no translation.
75 Try the system's message catalog. */
76 translated_pattern = nl_langinfo (nl_index);
77 /* Check against a broken system return value. */
78 if (translated_pattern != NULL && translated_pattern[0] != '\0')
79 return translated_pattern;
80 /* Fall back to English. */
81 translated_pattern = english_pattern;
83 return translated_pattern;
85 # else
86 # define localized_pattern(english_pattern,nl_index,posixly_correct) \
87 _(english_pattern)
88 # endif
90 static int
91 try (const char *response, const char *pattern, char **lastp, regex_t *re)
93 if (*lastp == NULL || strcmp (pattern, *lastp) != 0)
95 char *safe_pattern;
97 /* The pattern has changed. */
98 if (*lastp != NULL)
100 /* Free the old compiled pattern. */
101 regfree (re);
102 free (*lastp);
103 *lastp = NULL;
105 /* Put the PATTERN into safe memory before calling regcomp.
106 (regcomp may call nl_langinfo, overwriting PATTERN's storage. */
107 safe_pattern = strdup (pattern);
108 if (safe_pattern == NULL)
109 return -1;
110 /* Compile the pattern and cache it for future runs. */
111 if (regcomp (re, safe_pattern, REG_EXTENDED) != 0)
113 free (safe_pattern);
114 return -1;
116 *lastp = safe_pattern;
119 /* See if the regular expression matches RESPONSE. */
120 return regexec (re, response, 0, NULL, 0) == 0;
122 #endif
126 rpmatch (const char *response)
128 #if ENABLE_NLS
129 /* Match against one of the response patterns, compiling the pattern
130 first if necessary. */
132 /* We cache the response patterns and compiled regexps here. */
133 static char *last_yesexpr, *last_noexpr;
134 static regex_t cached_yesre, cached_nore;
136 # if HAVE_LANGINFO_YESEXPR
137 bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
138 # endif
140 const char *yesexpr, *noexpr;
141 int result;
143 /* TRANSLATORS: A regular expression testing for an affirmative answer
144 (english: "yes"). Testing the first character may be sufficient.
145 Take care to consider upper and lower case.
146 To enquire the regular expression that your system uses for this
147 purpose, you can use the command
148 locale -k LC_MESSAGES | grep '^yesexpr=' */
149 yesexpr = localized_pattern (N_("^[yY]"), YESEXPR, posixly_correct);
150 result = try (response, yesexpr, &last_yesexpr, &cached_yesre);
151 if (result < 0)
152 return -1;
153 if (result)
154 return 1;
156 /* TRANSLATORS: A regular expression testing for a negative answer
157 (english: "no"). Testing the first character may be sufficient.
158 Take care to consider upper and lower case.
159 To enquire the regular expression that your system uses for this
160 purpose, you can use the command
161 locale -k LC_MESSAGES | grep '^noexpr=' */
162 noexpr = localized_pattern (N_("^[nN]"), NOEXPR, posixly_correct);
163 result = try (response, noexpr, &last_noexpr, &cached_nore);
164 if (result < 0)
165 return -1;
166 if (result)
167 return 0;
169 return -1;
170 #else
171 /* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */
172 return (*response == 'y' || *response == 'Y' ? 1
173 : *response == 'n' || *response == 'N' ? 0 : -1);
174 #endif