(root)/
tar-1.35/
gnu/
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-2023 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 <stddef.h>
      26  
      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)
      38  
      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)
      46  {
      47    const char *translated_pattern;
      48  
      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.  */
      60  
      61    /* If the user wants strict POSIX compliance, use nl_langinfo.  */
      62    if (posixly_correct)
      63      {
      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;
      68     }
      69  
      70    /* Look in the gnulib message catalog.  */
      71    translated_pattern = _(english_pattern);
      72    if (translated_pattern == english_pattern)
      73      {
      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;
      82      }
      83    return translated_pattern;
      84  }
      85  # else
      86  #  define localized_pattern(english_pattern,nl_index,posixly_correct) \
      87       _(english_pattern)
      88  # endif
      89  
      90  static int
      91  try (const char *response, const char *pattern, char **lastp, regex_t *re)
      92  {
      93    if (*lastp == NULL || strcmp (pattern, *lastp) != 0)
      94      {
      95        char *safe_pattern;
      96  
      97        /* The pattern has changed.  */
      98        if (*lastp != NULL)
      99          {
     100            /* Free the old compiled pattern.  */
     101            regfree (re);
     102            free (*lastp);
     103            *lastp = NULL;
     104          }
     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)
     112          {
     113            free (safe_pattern);
     114            return -1;
     115          }
     116        *lastp = safe_pattern;
     117      }
     118  
     119    /* See if the regular expression matches RESPONSE.  */
     120    return regexec (re, response, 0, NULL, 0) == 0;
     121  }
     122  #endif
     123  
     124  
     125  int
     126  rpmatch (const char *response)
     127  {
     128  #if ENABLE_NLS
     129    /* Match against one of the response patterns, compiling the pattern
     130       first if necessary.  */
     131  
     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;
     135  
     136  # if HAVE_LANGINFO_YESEXPR
     137    bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
     138  # endif
     139  
     140    const char *yesexpr, *noexpr;
     141    int result;
     142  
     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;
     155  
     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;
     168  
     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
     175  }