1  /* Test of the gettext functions.
       2     Copyright (C) 2000-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <libintl.h>
      20  #include <locale.h>
      21  #include <mcheck.h>
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <error.h>
      26  #include <errno.h>
      27  
      28  
      29  const struct
      30  {
      31    const char *msgid;
      32    const char *msgstr;
      33  } msgs[] =
      34  {
      35  #define INPUT(Str) { Str,
      36  #define OUTPUT(Str) Str },
      37  #include TESTSTRS_H
      38  };
      39  
      40  const char *catname[] =
      41  {
      42    [LC_MESSAGES] = "LC_MESSAGES",
      43    [LC_TIME] = "LC_TIME",
      44    [LC_NUMERIC] = "LC_NUMERIC"
      45  };
      46  
      47  
      48  static int positive_gettext_test (void);
      49  static int negative_gettext_test (void);
      50  static int positive_dgettext_test (const char *domain);
      51  static int positive_dcgettext_test (const char *domain, int category);
      52  static int negative_dcgettext_test (const char *domain, int category);
      53  
      54  
      55  #define check_setlocale(cat, name) do {					      \
      56      if (setlocale (cat, name) == NULL)					      \
      57        {									      \
      58  	printf ("%s:%u: setlocale (%s, \"%s\"): %m\n",			      \
      59  		__FILE__, __LINE__, #cat, name);			      \
      60  	result = 1;							      \
      61        }									      \
      62    } while (0)
      63  
      64  int
      65  main (int argc, char *argv[])
      66  {
      67    int result = 0;
      68  
      69    /* For debugging.  */
      70    mtrace ();
      71  
      72    /* This is the place where the .mo files are placed.  */
      73    if (argc > 1)
      74      {
      75        bindtextdomain ("existing-domain", argv[1]);
      76        bindtextdomain ("existing-time-domain", argv[1]);
      77        bindtextdomain ("non-existing-domain", argv[1]);
      78      }
      79  
      80    /* The locale the catalog is created for is "existing-category".  Now
      81       set the various variables in question to this value and run the
      82       test.  */
      83    setenv ("LANGUAGE", "existing-locale", 1);
      84    setenv ("LC_ALL", "non-existing-locale", 1);
      85    setenv ("LC_MESSAGES", "non-existing-locale", 1);
      86    setenv ("LC_CTYPE", "non-existing-locale", 1);
      87    setenv ("LANG", "non-existing-locale", 1);
      88    check_setlocale (LC_CTYPE, "de_DE.UTF-8");
      89    check_setlocale (LC_MESSAGES, "de_DE.UTF-8");
      90    unsetenv ("OUTPUT_CHARSET");
      91    /* This is the name of the existing domain with a catalog for the
      92       LC_MESSAGES category.  */
      93    textdomain ("existing-domain");
      94    puts ("test `gettext' with LANGUAGE set");
      95    if (positive_gettext_test () != 0)
      96      {
      97        puts ("FAILED");
      98        result = 1;
      99      }
     100    /* This is the name of a non-existing domain with a catalog for the
     101       LC_MESSAGES category.  We leave this value set for the `dgettext'
     102       and `dcgettext' tests.  */
     103    textdomain ("non-existing-domain");
     104    puts ("test `gettext' with LANGUAGE set");
     105    if (negative_gettext_test () != 0)
     106      {
     107        puts ("FAILED");
     108        result = 1;
     109      }
     110    puts ("test `dgettext' with LANGUAGE set");
     111    if (positive_dgettext_test ("existing-domain") != 0)
     112      {
     113        puts ("FAILED");
     114        result = 1;
     115      }
     116  
     117    /* Now the same tests with LC_ALL deciding.  */
     118    unsetenv ("LANGUAGE");
     119    setenv ("LC_ALL", "existing-locale", 1);
     120    check_setlocale (LC_ALL, "");
     121    puts ("test `gettext' with LC_ALL set");
     122    /* This is the name of the existing domain with a catalog for the
     123       LC_MESSAGES category.  */
     124    textdomain ("existing-domain");
     125    if (positive_gettext_test () != 0)
     126      {
     127        puts ("FAILED");
     128        result = 1;
     129      }
     130    /* This is the name of a non-existing domain with a catalog for the
     131       LC_MESSAGES category.  We leave this value set for the `dgettext'
     132       and `dcgettext' tests.  */
     133    textdomain ("non-existing-domain");
     134    puts ("test `gettext' with LC_ALL deciding");
     135    if (negative_gettext_test () != 0)
     136      {
     137        puts ("FAILED");
     138        result = 1;
     139      }
     140    puts ("test `dgettext' with LC_ALL deciding");
     141    if (positive_dgettext_test ("existing-domain") != 0)
     142      {
     143        puts ("FAILED");
     144        result = 1;
     145      }
     146  
     147    /* Now the same tests with LC_MESSAGES deciding.  */
     148    unsetenv ("LC_ALL");
     149    setenv ("LC_MESSAGES", "existing-locale", 1);
     150    check_setlocale (LC_MESSAGES, "");
     151    setenv ("LC_TIME", "existing-locale", 1);
     152    check_setlocale (LC_TIME, "");
     153    setenv ("LC_NUMERIC", "non-existing-locale", 1);
     154    char *what = setlocale (LC_NUMERIC, "");
     155    if (what != NULL)
     156      {
     157        printf ("setlocale succeeded (%s), expected failure\n", what);
     158        result = 1;
     159      }
     160  
     161    puts ("test `gettext' with LC_MESSAGES set");
     162    /* This is the name of the existing domain with a catalog for the
     163       LC_MESSAGES category.  */
     164    textdomain ("existing-domain");
     165    if (positive_gettext_test () != 0)
     166      {
     167        puts ("FAILED");
     168        result = 1;
     169      }
     170    /* This is the name of a non-existing domain with a catalog for the
     171       LC_MESSAGES category.  We leave this value set for the `dgettext'
     172       and `dcgettext' tests.  */
     173    textdomain ("non-existing-domain");
     174    puts ("test `gettext' with LC_MESSAGES deciding");
     175    if (negative_gettext_test () != 0)
     176      {
     177        puts ("FAILED");
     178        result = 1;
     179      }
     180    puts ("test `dgettext' with LC_MESSAGES deciding");
     181    if (positive_dgettext_test ("existing-domain") != 0)
     182      {
     183        puts ("FAILED");
     184        result = 1;
     185      }
     186    puts ("test `dcgettext' with category == LC_MESSAGES");
     187    if (positive_dcgettext_test ("existing-domain", LC_MESSAGES) != 0)
     188      {
     189        puts ("FAILED");
     190        result = 1;
     191      }
     192    /* Try a different category.  For this we also switch the domain.  */
     193    puts ("test `dcgettext' with LANGUAGE == LC_TIME");
     194    if (positive_dcgettext_test ("existing-time-domain", LC_TIME) != 0)
     195      {
     196        puts ("FAILED");
     197        result = 1;
     198      }
     199    /* This time use a category for which there is no catalog.  */
     200    puts ("test `dcgettext' with LANGUAGE == LC_NUMERIC");
     201    if (negative_dcgettext_test ("existing-domain", LC_NUMERIC) != 0)
     202      {
     203        puts ("FAILED");
     204        result = 1;
     205      }
     206  
     207    /* Now the same tests with LANG deciding.  */
     208    unsetenv ("LC_MESSAGES");
     209    unsetenv ("LC_CTYPE");
     210    unsetenv ("LC_TIME");
     211    unsetenv ("LC_NUMERIC");
     212    setenv ("LANG", "existing-locale", 1);
     213    check_setlocale (LC_ALL, "");
     214    /* This is the name of the existing domain with a catalog for the
     215       LC_MESSAGES category.  */
     216    textdomain ("existing-domain");
     217    puts ("test `gettext' with LANG set");
     218    if (positive_gettext_test () != 0)
     219      {
     220        puts ("FAILED");
     221        result = 1;
     222      }
     223    /* This is the name of a non-existing domain with a catalog for the
     224       LC_MESSAGES category.  We leave this value set for the `dgettext'
     225       and `dcgettext' tests.  */
     226    textdomain ("non-existing-domain");
     227    puts ("test `gettext' with LANG set");
     228    if (negative_gettext_test () != 0)
     229      {
     230        puts ("FAILED");
     231        result = 1;
     232      }
     233    puts ("test `dgettext' with LANG set");
     234    if (positive_dgettext_test ("existing-domain") != 0)
     235      {
     236        puts ("FAILED");
     237        result = 1;
     238      }
     239  
     240    check_setlocale (LC_ALL, "C");
     241  
     242    return result;
     243  }
     244  
     245  
     246  static int
     247  positive_gettext_test (void)
     248  {
     249    size_t cnt;
     250    int result = 0;
     251  
     252    for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
     253      {
     254        const char *found = gettext (msgs[cnt].msgid);
     255  
     256        if (found == NULL
     257  	  || (msgs[cnt].msgstr[0] != '\0'
     258  	      && strcmp (found, msgs[cnt].msgstr) != 0))
     259  	{
     260  	  /* Oops, shouldn't happen.  */
     261  	  printf ("\
     262    gettext (\"%s\") failed, returned \"%s\", expected \"%s\"\n",
     263  		  msgs[cnt].msgid, found, msgs[cnt].msgstr);
     264  	  result = 1;
     265  	}
     266      }
     267  
     268    return result;
     269  }
     270  
     271  
     272  static int
     273  negative_gettext_test (void)
     274  {
     275    size_t cnt;
     276    int result = 0;
     277  
     278    for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
     279      {
     280        const char *found = gettext (msgs[cnt].msgid);
     281  
     282        if (found != msgs[cnt].msgid)
     283  	{
     284  	  /* Oops, shouldn't happen.  */
     285  	  printf ("  gettext (\"%s\") failed\n", msgs[cnt].msgid);
     286  	  result = 1;
     287  	}
     288      }
     289  
     290    return result;
     291  }
     292  
     293  
     294  static int
     295  positive_dgettext_test (const char *domain)
     296  {
     297    size_t cnt;
     298    int result = 0;
     299  
     300    for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
     301      {
     302        const char *found = dgettext (domain, msgs[cnt].msgid);
     303  
     304        if (found == NULL
     305  	  || (msgs[cnt].msgstr[0] != '\0'
     306  	      && strcmp (found, msgs[cnt].msgstr) != 0))
     307  	{
     308  	  /* Oops, shouldn't happen.  */
     309  	  printf ("\
     310    dgettext (\"%s\", \"%s\") failed, returned \"%s\", expected \"%s\"\n",
     311  		  domain, msgs[cnt].msgid, found, msgs[cnt].msgstr);
     312  	  result = 1;
     313  	}
     314      }
     315  
     316    return result;
     317  }
     318  
     319  
     320  static int
     321  positive_dcgettext_test (const char *domain, int category)
     322  {
     323    size_t cnt;
     324    int result = 0;
     325  
     326    for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
     327      {
     328        const char *found = dcgettext (domain, msgs[cnt].msgid, category);
     329  
     330        if (found == NULL
     331  	  || (msgs[cnt].msgstr[0] != '\0'
     332  	      && strcmp (found, msgs[cnt].msgstr) != 0))
     333  	{
     334  	  /* Oops, shouldn't happen.  */
     335  	  printf ("\
     336    dcgettext (\"%s\", \"%s\", %s) failed, returned \"%s\", expected \"%s\"\n",
     337  		  domain, msgs[cnt].msgid, catname[category], found,
     338  		  msgs[cnt].msgstr);
     339  	  result = 1;
     340  	}
     341      }
     342  
     343    return result;
     344  }
     345  
     346  
     347  static int
     348  negative_dcgettext_test (const char *domain, int category)
     349  {
     350    size_t cnt;
     351    int result = 0;
     352  
     353    for (cnt = 0; cnt < sizeof (msgs) / sizeof (msgs[0]); ++cnt)
     354      {
     355        const char *found = dcgettext (domain, msgs[cnt].msgid, category);
     356  
     357        if (found != msgs[cnt].msgid)
     358  	{
     359  	  /* Oops, shouldn't happen.  */
     360  	  printf ("  dcgettext (\"%s\", \"%s\", %s) failed\n",
     361  		  domain, msgs[cnt].msgid, catname[category]);
     362  	  result = 1;
     363  	}
     364      }
     365  
     366    return result;
     367  }