(root)/
findutils-4.9.0/
gl/
lib/
rpmatch.c
       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-2022 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  }