(root)/
glibc-2.38/
locale/
programs/
ld-address.c
       1  /* Copyright (C) 1998-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     This program is free software; you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published
       6     by the Free Software Foundation; version 2 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #ifdef HAVE_CONFIG_H
      18  # include <config.h>
      19  #endif
      20  
      21  #include <byteswap.h>
      22  #include <langinfo.h>
      23  #include <string.h>
      24  #include <stdint.h>
      25  #include <sys/uio.h>
      26  
      27  #include <assert.h>
      28  
      29  #include "localedef.h"
      30  #include "localeinfo.h"
      31  #include "locfile.h"
      32  
      33  
      34  static struct
      35  {
      36    const char ab2[3];
      37    const char ab3[4];
      38    uint32_t num;
      39  } iso3166[] =
      40  {
      41  #define DEFINE_COUNTRY_CODE(Name, Ab2, Ab3, Num) \
      42    { #Ab2, #Ab3, Num },
      43  #include "iso-3166.def"
      44  };
      45  
      46  
      47  static struct
      48  {
      49    const char ab[3];
      50    const char term[4];
      51    const char lib[4];
      52  } iso639[] =
      53  {
      54  #define DEFINE_LANGUAGE_CODE(Name, Ab, Term, Lib) \
      55    { #Ab, #Term, #Lib },
      56  #define DEFINE_LANGUAGE_CODE3(Name, Term, Lib) \
      57    { "", #Term, #Lib },
      58  #define DEFINE_LANGUAGE_CODE2(Name, Term) \
      59    { "", #Term, "" },
      60  #include "iso-639.def"
      61  };
      62  
      63  
      64  /* The real definition of the struct for the LC_ADDRESS locale.  */
      65  struct locale_address_t
      66  {
      67    const char *postal_fmt;
      68    const char *country_name;
      69    const char *country_post;
      70    const char *country_ab2;
      71    const char *country_ab3;
      72    uint32_t country_num;
      73    const char *country_car;
      74    const char *country_isbn;
      75    const char *lang_name;
      76    const char *lang_ab;
      77    const char *lang_term;
      78    const char *lang_lib;
      79  };
      80  
      81  
      82  static void
      83  address_startup (struct linereader *lr, struct localedef_t *locale,
      84  		 int ignore_content)
      85  {
      86    if (!ignore_content)
      87      locale->categories[LC_ADDRESS].address =
      88        (struct locale_address_t *) xcalloc (1,
      89  					   sizeof (struct locale_address_t));
      90  
      91    if (lr != NULL)
      92      {
      93        lr->translate_strings = 1;
      94        lr->return_widestr = 0;
      95      }
      96  }
      97  
      98  
      99  void
     100  address_finish (struct localedef_t *locale, const struct charmap_t *charmap)
     101  {
     102    struct locale_address_t *address = locale->categories[LC_ADDRESS].address;
     103    size_t cnt;
     104    int helper;
     105    int nothing = 0;
     106  
     107    /* Now resolve copying and also handle completely missing definitions.  */
     108    if (address == NULL)
     109      {
     110        /* First see whether we were supposed to copy.  If yes, find the
     111  	 actual definition.  */
     112        if (locale->copy_name[LC_ADDRESS] != NULL)
     113  	{
     114  	  /* Find the copying locale.  This has to happen transitively since
     115  	     the locale we are copying from might also copying another one.  */
     116  	  struct localedef_t *from = locale;
     117  
     118  	  do
     119  	    from = find_locale (LC_ADDRESS, from->copy_name[LC_ADDRESS],
     120  				from->repertoire_name, charmap);
     121  	  while (from->categories[LC_ADDRESS].address == NULL
     122  		 && from->copy_name[LC_ADDRESS] != NULL);
     123  
     124  	  address = locale->categories[LC_ADDRESS].address
     125  	    = from->categories[LC_ADDRESS].address;
     126  	}
     127  
     128        /* If there is still no definition issue an warning and create an
     129  	 empty one.  */
     130        if (address == NULL)
     131  	{
     132  	  record_warning (_("\
     133  No definition for %s category found"), "LC_ADDRESS");
     134  	  address_startup (NULL, locale, 0);
     135  	  address = locale->categories[LC_ADDRESS].address;
     136  	  nothing = 1;
     137  	}
     138      }
     139  
     140    if (address->postal_fmt == NULL)
     141      {
     142        if (! nothing)
     143  	record_error (0, 0, _("%s: field `%s' not defined"),
     144  		      "LC_ADDRESS", "postal_fmt");
     145        /* Use as the default value the value of the i18n locale.  */
     146        address->postal_fmt = "%a%N%f%N%d%N%b%N%s %h %e %r%N%C-%z %T%N%c%N";
     147      }
     148    else
     149      {
     150        /* We must check whether the format string contains only the allowed
     151  	 escape sequences.  Last checked against ISO 30112 WD10 [2014]. */
     152        const char *cp = address->postal_fmt;
     153  
     154        if (*cp == '\0')
     155  	record_error (0, 0, _("%s: field `%s' must not be empty"),
     156  		      "LC_ADDRESS", "postal_fmt");
     157        else
     158  	while (*cp != '\0')
     159  	  {
     160  	    if (*cp == '%')
     161  	      {
     162  		if (*++cp == 'R')
     163  		  /* Romanize-flag.  */
     164  		  ++cp;
     165  		if (strchr ("nafdbshNtreClzTSc%", *cp) == NULL)
     166  		  {
     167  		    record_error (0, 0, _("\
     168  %s: invalid escape `%%%c' sequence in field `%s'"),
     169  				  "LC_ADDRESS", *cp, "postal_fmt");
     170  		    break;
     171  		  }
     172  	      }
     173  	    ++cp;
     174  	  }
     175      }
     176  
     177  #define TEST_ELEM(cat) \
     178    if (address->cat == NULL)						      \
     179      {									      \
     180        if (verbose && ! nothing)						      \
     181  	record_warning (_("%s: field `%s' not defined"), "LC_ADDRESS", #cat); \
     182        address->cat = "";						      \
     183      }
     184  
     185    TEST_ELEM (country_name);
     186    /* XXX Test against list of defined codes.  */
     187    TEST_ELEM (country_post);
     188    /* XXX Test against list of defined codes.  */
     189    TEST_ELEM (country_car);
     190    /* XXX Test against list of defined codes.  */
     191    TEST_ELEM (country_isbn);
     192    TEST_ELEM (lang_name);
     193  
     194    helper = 1;
     195    if (address->lang_term == NULL)
     196      {
     197        if (verbose && ! nothing)
     198  	record_warning (_("%s: field `%s' not defined"), "LC_ADDRESS",
     199  			"lang_term");
     200        address->lang_term = "";
     201        cnt = sizeof (iso639) / sizeof (iso639[0]);
     202      }
     203    else if (address->lang_term[0] == '\0')
     204      {
     205        if (verbose)
     206  	record_warning (_("%s: field `%s' must not be empty"), "LC_ADDRESS",
     207  			"lang_term");
     208        cnt = sizeof (iso639) / sizeof (iso639[0]);
     209      }
     210    else
     211      {
     212        /* Look for this language in the table.  */
     213        for (cnt = 0; cnt < sizeof (iso639) / sizeof (iso639[0]); ++cnt)
     214  	if (strcmp (address->lang_term, iso639[cnt].term) == 0)
     215  	  break;
     216        if (cnt == sizeof (iso639) / sizeof (iso639[0]))
     217  	record_error (0, 0, _("\
     218  %s: terminology language code `%s' not defined"),
     219  		      "LC_ADDRESS", address->lang_term);
     220      }
     221  
     222    if (address->lang_ab == NULL)
     223      {
     224        if ((cnt == sizeof (iso639) / sizeof (iso639[0])
     225  	   || iso639[cnt].ab[0] != '\0')
     226  	  && verbose && ! nothing)
     227  	record_warning (_("%s: field `%s' not defined"), "LC_ADDRESS",
     228  			"lang_ab");
     229        address->lang_ab = "";
     230      }
     231    else if (address->lang_ab[0] == '\0')
     232      {
     233        if ((cnt == sizeof (iso639) / sizeof (iso639[0])
     234  	   || iso639[cnt].ab[0] != '\0')
     235  	  && verbose)
     236  	record_warning (_("%s: field `%s' must not be empty"),
     237  			"LC_ADDRESS", "lang_ab");
     238      }
     239    else if (cnt < sizeof (iso639) / sizeof (iso639[0])
     240  	   && iso639[cnt].ab[0] == '\0')
     241      {
     242        record_error (0, 0, _("%s: field `%s' must not be defined"),
     243  		    "LC_ADDRESS", "lang_ab");
     244  
     245        address->lang_ab = "";
     246      }
     247    else
     248      {
     249        if (cnt == sizeof (iso639) / sizeof (iso639[0]))
     250  	{
     251  	  helper = 2;
     252  	  for (cnt = 0; cnt < sizeof (iso639) / sizeof (iso639[0]); ++cnt)
     253  	    if (strcmp (address->lang_ab, iso639[cnt].ab) == 0)
     254  	      break;
     255  	  if (cnt == sizeof (iso639) / sizeof (iso639[0]))
     256  	    record_error (0, 0, _("\
     257  %s: language abbreviation `%s' not defined"),
     258  			  "LC_ADDRESS", address->lang_ab);
     259  	}
     260        else
     261  	if (strcmp (iso639[cnt].ab, address->lang_ab) != 0
     262  	    && iso639[cnt].ab[0] != '\0')
     263  	  record_error (0, 0, _("\
     264  %s: `%s' value does not match `%s' value"),
     265  			"LC_ADDRESS", "lang_ab", "lang_term");
     266      }
     267  
     268    if (address->lang_lib == NULL)
     269      /* This is no error.  */
     270      address->lang_lib = address->lang_term;
     271    else if (address->lang_lib[0] == '\0')
     272      {
     273        if (verbose)
     274  	record_warning (_("%s: field `%s' must not be empty"),
     275  			"LC_ADDRESS", "lang_lib");
     276      }
     277    else
     278      {
     279        if (cnt == sizeof (iso639) / sizeof (iso639[0]))
     280  	{
     281  	  for (cnt = 0; cnt < sizeof (iso639) / sizeof (iso639[0]); ++cnt)
     282  	    if (strcmp (address->lang_lib, iso639[cnt].lib) == 0)
     283  	      break;
     284  	  if (cnt == sizeof (iso639) / sizeof (iso639[0]))
     285  	    record_error (0, 0, _("\
     286  %s: language abbreviation `%s' not defined"),
     287  			  "LC_ADDRESS", address->lang_lib);
     288  	}
     289        else
     290  	if (strcmp (iso639[cnt].ab, address->lang_ab) != 0)
     291  	  record_error (0, 0, _("\
     292  %s: `%s' value does not match `%s' value"), "LC_ADDRESS", "lang_lib",
     293  			helper == 1 ? "lang_term" : "lang_ab");
     294      }
     295  
     296    if (address->country_num == 0)
     297      {
     298        if (verbose && ! nothing)
     299  	record_warning (_("%s: field `%s' not defined"), "LC_ADDRESS",
     300  			"country_num");
     301        cnt = sizeof (iso3166) / sizeof (iso3166[0]);
     302      }
     303    else
     304      {
     305        for (cnt = 0; cnt < sizeof (iso3166) / sizeof (iso3166[0]); ++cnt)
     306  	if (address->country_num == iso3166[cnt].num)
     307  	  break;
     308  
     309        if (cnt == sizeof (iso3166) / sizeof (iso3166[0]))
     310  	record_error (0, 0, _("\
     311  %s: numeric country code `%d' not valid"),
     312  		      "LC_ADDRESS", address->country_num);
     313      }
     314  
     315    if (address->country_ab2 == NULL)
     316      {
     317        if (verbose && ! nothing)
     318  	record_warning (_("%s: field `%s' not defined"), "LC_ADDRESS",
     319  			"country_ab2");
     320        address->country_ab2 = "  ";
     321      }
     322    else if (cnt != sizeof (iso3166) / sizeof (iso3166[0])
     323  	   && strcmp (address->country_ab2, iso3166[cnt].ab2) != 0)
     324      record_error (0, 0, _("%s: `%s' value does not match `%s' value"),
     325  		  "LC_ADDRESS", "country_ab2", "country_num");
     326  
     327    if (address->country_ab3 == NULL)
     328      {
     329        if (verbose && ! nothing)
     330  	record_warning (_("%s: field `%s' not defined"), "LC_ADDRESS",
     331  			"country_ab3");
     332        address->country_ab3 = "   ";
     333      }
     334    else if (cnt != sizeof (iso3166) / sizeof (iso3166[0])
     335  	   && strcmp (address->country_ab3, iso3166[cnt].ab3) != 0)
     336      record_error (0, 0, _("\
     337  %s: `%s' value does not match `%s' value"),
     338  		  "LC_ADDRESS", "country_ab3", "country_num");
     339  }
     340  
     341  
     342  void
     343  address_output (struct localedef_t *locale, const struct charmap_t *charmap,
     344  		const char *output_path)
     345  {
     346    struct locale_address_t *address = locale->categories[LC_ADDRESS].address;
     347    struct locale_file file;
     348  
     349    init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_ADDRESS));
     350    add_locale_string (&file, address->postal_fmt);
     351    add_locale_string (&file, address->country_name);
     352    add_locale_string (&file, address->country_post);
     353    add_locale_string (&file, address->country_ab2);
     354    add_locale_string (&file, address->country_ab3);
     355    add_locale_string (&file, address->country_car);
     356    add_locale_uint32 (&file, address->country_num);
     357    add_locale_string (&file, address->country_isbn);
     358    add_locale_string (&file, address->lang_name);
     359    add_locale_string (&file, address->lang_ab);
     360    add_locale_string (&file, address->lang_term);
     361    add_locale_string (&file, address->lang_lib);
     362    add_locale_string (&file, charmap->code_set_name);
     363    write_locale_data (output_path, LC_ADDRESS, "LC_ADDRESS", &file);
     364  }
     365  
     366  
     367  /* The parser for the LC_ADDRESS section of the locale definition.  */
     368  void
     369  address_read (struct linereader *ldfile, struct localedef_t *result,
     370  	      const struct charmap_t *charmap, const char *repertoire_name,
     371  	      int ignore_content)
     372  {
     373    struct locale_address_t *address;
     374    struct token *now;
     375    struct token *arg;
     376    enum token_t nowtok;
     377  
     378    /* The rest of the line containing `LC_ADDRESS' must be free.  */
     379    lr_ignore_rest (ldfile, 1);
     380  
     381  
     382    do
     383      {
     384        now = lr_token (ldfile, charmap, result, NULL, verbose);
     385        nowtok = now->tok;
     386      }
     387    while (nowtok == tok_eol);
     388  
     389    /* If we see `copy' now we are almost done.  */
     390    if (nowtok == tok_copy)
     391      {
     392        handle_copy (ldfile, charmap, repertoire_name, result, tok_lc_address,
     393  		   LC_ADDRESS, "LC_ADDRESS", ignore_content);
     394        return;
     395      }
     396  
     397    /* Prepare the data structures.  */
     398    address_startup (ldfile, result, ignore_content);
     399    address = result->categories[LC_ADDRESS].address;
     400  
     401    while (1)
     402      {
     403        /* Of course we don't proceed beyond the end of file.  */
     404        if (nowtok == tok_eof)
     405  	break;
     406  
     407        /* Ignore empty lines.  */
     408        if (nowtok == tok_eol)
     409  	{
     410  	  now = lr_token (ldfile, charmap, result, NULL, verbose);
     411  	  nowtok = now->tok;
     412  	  continue;
     413  	}
     414  
     415        switch (nowtok)
     416  	{
     417  #define STR_ELEM(cat) \
     418  	case tok_##cat:							      \
     419  	  /* Ignore the rest of the line if we don't need the input of	      \
     420  	     this line.  */						      \
     421  	  if (ignore_content)						      \
     422  	    {								      \
     423  	      lr_ignore_rest (ldfile, 0);				      \
     424  	      break;							      \
     425  	    }								      \
     426  									      \
     427  	  arg = lr_token (ldfile, charmap, result, NULL, verbose);	      \
     428  	  if (arg->tok != tok_string)					      \
     429  	    goto err_label;						      \
     430  	  if (address->cat != NULL)					      \
     431  	    lr_error (ldfile, _("\
     432  %s: field `%s' declared more than once"), "LC_ADDRESS", #cat);		      \
     433  	  else if (!ignore_content && arg->val.str.startmb == NULL)	      \
     434  	    {								      \
     435  	      lr_error (ldfile, _("\
     436  %s: unknown character in field `%s'"), "LC_ADDRESS", #cat);		      \
     437  	      address->cat = "";					      \
     438  	    }								      \
     439  	  else if (!ignore_content)					      \
     440  	    address->cat = arg->val.str.startmb;			      \
     441  	  break
     442  
     443  	  STR_ELEM (postal_fmt);
     444  	  STR_ELEM (country_name);
     445  	  STR_ELEM (country_post);
     446  	  STR_ELEM (country_ab2);
     447  	  STR_ELEM (country_ab3);
     448  	  STR_ELEM (country_car);
     449  	  STR_ELEM (lang_name);
     450  	  STR_ELEM (lang_ab);
     451  	  STR_ELEM (lang_term);
     452  	  STR_ELEM (lang_lib);
     453  
     454  #define INT_STR_ELEM(cat) \
     455  	case tok_##cat:							      \
     456  	  /* Ignore the rest of the line if we don't need the input of	      \
     457  	     this line.  */						      \
     458  	  if (ignore_content)						      \
     459  	    {								      \
     460  	      lr_ignore_rest (ldfile, 0);				      \
     461  	      break;							      \
     462  	    }								      \
     463  									      \
     464  	  arg = lr_token (ldfile, charmap, result, NULL, verbose);	      \
     465  	  if (arg->tok != tok_string && arg->tok != tok_number)		      \
     466  	    goto err_label;						      \
     467  	  if (address->cat != NULL)					      \
     468  	    lr_error (ldfile, _("\
     469  %s: field `%s' declared more than once"), "LC_ADDRESS", #cat);		      \
     470  	  else if (!ignore_content && arg->tok == tok_string		      \
     471  		   && arg->val.str.startmb == NULL)			      \
     472  	    {								      \
     473  	      lr_error (ldfile, _("\
     474  %s: unknown character in field `%s'"), "LC_ADDRESS", #cat);		      \
     475  	      address->cat = "";					      \
     476  	    }								      \
     477  	  else if (!ignore_content)					      \
     478  	    {								      \
     479  	      if (arg->tok == tok_string)				      \
     480  		address->cat = arg->val.str.startmb;			      \
     481  	      else							      \
     482  		{							      \
     483  		  char *numbuf = (char *) xmalloc (21);			      \
     484  		  snprintf (numbuf, 21, "%ld", arg->val.num);		      \
     485  		  address->cat = numbuf;				      \
     486  		}							      \
     487  	    }								      \
     488  	  break
     489  
     490  	  INT_STR_ELEM (country_isbn);
     491  
     492  #define INT_ELEM(cat) \
     493  	case tok_##cat:							      \
     494  	  /* Ignore the rest of the line if we don't need the input of	      \
     495  	     this line.  */						      \
     496  	  if (ignore_content)						      \
     497  	    {								      \
     498  	      lr_ignore_rest (ldfile, 0);				      \
     499  	      break;							      \
     500  	    }								      \
     501  									      \
     502  	  arg = lr_token (ldfile, charmap, result, NULL, verbose);	      \
     503  	  if (arg->tok != tok_number)					      \
     504  	    goto err_label;						      \
     505  	  else if (address->cat != 0)					      \
     506  	    lr_error (ldfile, _("\
     507  %s: field `%s' declared more than once"), "LC_ADDRESS", #cat);		      \
     508  	  else if (!ignore_content)					      \
     509  	    address->cat = arg->val.num;				      \
     510  	  break
     511  
     512  	  INT_ELEM (country_num);
     513  
     514  	case tok_end:
     515  	  /* Next we assume `LC_ADDRESS'.  */
     516  	  arg = lr_token (ldfile, charmap, result, NULL, verbose);
     517  	  if (arg->tok == tok_eof)
     518  	    break;
     519  	  if (arg->tok == tok_eol)
     520  	    lr_error (ldfile, _("%s: incomplete `END' line"),
     521  		      "LC_ADDRESS");
     522  	  else if (arg->tok != tok_lc_address)
     523  	    lr_error (ldfile, _("\
     524  %1$s: definition does not end with `END %1$s'"), "LC_ADDRESS");
     525  	  lr_ignore_rest (ldfile, arg->tok == tok_lc_address);
     526  	  return;
     527  
     528  	default:
     529  	err_label:
     530  	  SYNTAX_ERROR (_("%s: syntax error"), "LC_ADDRESS");
     531  	}
     532  
     533        /* Prepare for the next round.  */
     534        now = lr_token (ldfile, charmap, result, NULL, verbose);
     535        nowtok = now->tok;
     536      }
     537  
     538    /* When we come here we reached the end of the file.  */
     539    lr_error (ldfile, _("%s: premature end of file"), "LC_ADDRESS");
     540  }