(root)/
glibc-2.38/
locale/
programs/
ld-identification.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 <langinfo.h>
      22  #include <stdlib.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  /* The real definition of the struct for the LC_IDENTIFICATION locale.  */
      35  struct locale_identification_t
      36  {
      37    const char *title;
      38    const char *source;
      39    const char *address;
      40    const char *contact;
      41    const char *email;
      42    const char *tel;
      43    const char *fax;
      44    const char *language;
      45    const char *territory;
      46    const char *audience;
      47    const char *application;
      48    const char *abbreviation;
      49    const char *revision;
      50    const char *date;
      51    const char *category[__LC_LAST];
      52  };
      53  
      54  
      55  static const char *category_name[__LC_LAST] =
      56  {
      57    [LC_CTYPE] = "LC_CTYPE",
      58    [LC_NUMERIC] = "LC_NUMERIC",
      59    [LC_TIME] = "LC_TIME",
      60    [LC_COLLATE] = "LC_COLLATE",
      61    [LC_MONETARY] = "LC_MONETARY",
      62    [LC_MESSAGES] = "LC_MESSAGES",
      63    [LC_ALL] = "LC_ALL",
      64    [LC_PAPER] = "LC_PAPER",
      65    [LC_NAME] = "LC_NAME",
      66    [LC_ADDRESS] = "LC_ADDRESS",
      67    [LC_TELEPHONE] = "LC_TELEPHONE",
      68    [LC_MEASUREMENT] = "LC_MEASUREMENT",
      69    [LC_IDENTIFICATION] = "LC_IDENTIFICATION"
      70  };
      71  
      72  
      73  static void
      74  identification_startup (struct linereader *lr, struct localedef_t *locale,
      75  			int ignore_content)
      76  {
      77    if (!ignore_content)
      78      {
      79        locale->categories[LC_IDENTIFICATION].identification =
      80  	(struct locale_identification_t *)
      81  	xcalloc (1, sizeof (struct locale_identification_t));
      82  
      83        locale->categories[LC_IDENTIFICATION].identification->category[LC_ALL] =
      84  	"";
      85      }
      86  
      87    if (lr != NULL)
      88      {
      89        lr->translate_strings = 1;
      90        lr->return_widestr = 0;
      91      }
      92  }
      93  
      94  
      95  void
      96  identification_finish (struct localedef_t *locale,
      97  		       const struct charmap_t *charmap)
      98  {
      99    struct locale_identification_t *identification
     100      = locale->categories[LC_IDENTIFICATION].identification;
     101    int nothing = 0;
     102    size_t num;
     103  
     104    /* Now resolve copying and also handle completely missing definitions.  */
     105    if (identification == NULL)
     106      {
     107        /* First see whether we were supposed to copy.  If yes, find the
     108  	 actual definition.  */
     109        if (locale->copy_name[LC_IDENTIFICATION] != NULL)
     110  	{
     111  	  /* Find the copying locale.  This has to happen transitively since
     112  	     the locale we are copying from might also copying another one.  */
     113  	  struct localedef_t *from = locale;
     114  
     115  	  do
     116  	    from = find_locale (LC_IDENTIFICATION,
     117  				from->copy_name[LC_IDENTIFICATION],
     118  				from->repertoire_name, charmap);
     119  	  while (from->categories[LC_IDENTIFICATION].identification == NULL
     120  		 && from->copy_name[LC_IDENTIFICATION] != NULL);
     121  
     122  	  identification = locale->categories[LC_IDENTIFICATION].identification
     123  	    = from->categories[LC_IDENTIFICATION].identification;
     124  	}
     125  
     126        /* If there is still no definition issue an warning and create an
     127  	 empty one.  */
     128        if (identification == NULL)
     129  	{
     130  	  record_warning (_("\
     131  No definition for %s category found"), "LC_IDENTIFICATION");
     132  	  identification_startup (NULL, locale, 0);
     133  	  identification
     134  	    = locale->categories[LC_IDENTIFICATION].identification;
     135  	  nothing = 1;
     136  	}
     137      }
     138  
     139  #define TEST_ELEM(cat) \
     140    if (identification->cat == NULL)					      \
     141      {									      \
     142        if (verbose && ! nothing)						      \
     143  	record_warning (_("%s: field `%s' not defined"), "LC_IDENTIFICATION", \
     144  			#cat);						      \
     145        identification->cat = "";						      \
     146      }
     147  
     148    TEST_ELEM (title);
     149    TEST_ELEM (source);
     150    TEST_ELEM (address);
     151    TEST_ELEM (contact);
     152    TEST_ELEM (email);
     153    TEST_ELEM (tel);
     154    TEST_ELEM (fax);
     155    TEST_ELEM (language);
     156    TEST_ELEM (territory);
     157    TEST_ELEM (audience);
     158    TEST_ELEM (application);
     159    TEST_ELEM (abbreviation);
     160    TEST_ELEM (revision);
     161    TEST_ELEM (date);
     162  
     163    for (num = 0; num < __LC_LAST; ++num)
     164      {
     165        /* We don't accept/parse this category, so skip it early.  */
     166        if (num == LC_ALL)
     167  	continue;
     168  
     169        if (identification->category[num] == NULL)
     170  	{
     171  	  if (verbose && ! nothing)
     172  	    record_warning (_("\
     173  %s: no identification for category `%s'"), "LC_IDENTIFICATION",
     174  			    category_name[num]);
     175  	  identification->category[num] = "";
     176  	}
     177        else
     178  	{
     179  	  /* Only list the standards we care about.  This is based on the
     180  	     ISO 30112 WD10 [2014] standard which supersedes all previous
     181  	     revisions of the ISO 14652 standard.  */
     182  	  static const char * const standards[] =
     183  	    {
     184  	      "posix:1993",
     185  	      "i18n:2004",
     186  	      "i18n:2012",
     187  	    };
     188  	  size_t i;
     189  	  bool matched = false;
     190  
     191  	  for (i = 0; i < sizeof (standards) / sizeof (standards[0]); ++i)
     192  	    if (strcmp (identification->category[num], standards[i]) == 0)
     193  	      matched = true;
     194  
     195  	  if (matched != true)
     196  	    record_error (0, 0, _("\
     197  %s: unknown standard `%s' for category `%s'"),
     198  			  "LC_IDENTIFICATION",
     199  			  identification->category[num],
     200  			  category_name[num]);
     201  	}
     202      }
     203  }
     204  
     205  
     206  void
     207  identification_output (struct localedef_t *locale,
     208  		       const struct charmap_t *charmap,
     209  		       const char *output_path)
     210  {
     211    struct locale_identification_t *identification
     212      = locale->categories[LC_IDENTIFICATION].identification;
     213    struct locale_file file;
     214    size_t num;
     215  
     216    init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION));
     217    add_locale_string (&file, identification->title);
     218    add_locale_string (&file, identification->source);
     219    add_locale_string (&file, identification->address);
     220    add_locale_string (&file, identification->contact);
     221    add_locale_string (&file, identification->email);
     222    add_locale_string (&file, identification->tel);
     223    add_locale_string (&file, identification->fax);
     224    add_locale_string (&file, identification->language);
     225    add_locale_string (&file, identification->territory);
     226    add_locale_string (&file, identification->audience);
     227    add_locale_string (&file, identification->application);
     228    add_locale_string (&file, identification->abbreviation);
     229    add_locale_string (&file, identification->revision);
     230    add_locale_string (&file, identification->date);
     231    start_locale_structure (&file);
     232    for (num = 0; num < __LC_LAST; ++num)
     233      if (num != LC_ALL)
     234        add_locale_string (&file, identification->category[num]);
     235    end_locale_structure (&file);
     236    add_locale_string (&file, charmap->code_set_name);
     237    write_locale_data (output_path, LC_IDENTIFICATION, "LC_IDENTIFICATION",
     238  		     &file);
     239  }
     240  
     241  
     242  /* The parser for the LC_IDENTIFICATION section of the locale definition.  */
     243  void
     244  identification_read (struct linereader *ldfile, struct localedef_t *result,
     245  	       const struct charmap_t *charmap, const char *repertoire_name,
     246  	       int ignore_content)
     247  {
     248    struct locale_identification_t *identification;
     249    struct token *now;
     250    struct token *arg;
     251    struct token *cattok;
     252    int category;
     253    enum token_t nowtok;
     254  
     255    /* The rest of the line containing `LC_IDENTIFICATION' must be free.  */
     256    lr_ignore_rest (ldfile, 1);
     257  
     258    do
     259      {
     260        now = lr_token (ldfile, charmap, result, NULL, verbose);
     261        nowtok = now->tok;
     262      }
     263    while (nowtok == tok_eol);
     264  
     265    /* If we see `copy' now we are almost done.  */
     266    if (nowtok == tok_copy)
     267      {
     268        handle_copy (ldfile, charmap, repertoire_name, result,
     269  		   tok_lc_identification, LC_IDENTIFICATION,
     270  		   "LC_IDENTIFICATION", ignore_content);
     271        return;
     272      }
     273  
     274    /* Prepare the data structures.  */
     275    identification_startup (ldfile, result, ignore_content);
     276    identification = result->categories[LC_IDENTIFICATION].identification;
     277  
     278    while (1)
     279      {
     280        /* Of course we don't proceed beyond the end of file.  */
     281        if (nowtok == tok_eof)
     282  	break;
     283  
     284        /* Ignore empty lines.  */
     285        if (nowtok == tok_eol)
     286  	{
     287  	  now = lr_token (ldfile, charmap, result, NULL, verbose);
     288  	  nowtok = now->tok;
     289  	  continue;
     290  	}
     291  
     292        switch (nowtok)
     293  	{
     294  #define STR_ELEM(cat) \
     295  	case tok_##cat:							      \
     296  	  /* Ignore the rest of the line if we don't need the input of	      \
     297  	     this line.  */						      \
     298  	  if (ignore_content)						      \
     299  	    {								      \
     300  	      lr_ignore_rest (ldfile, 0);				      \
     301  	      break;							      \
     302  	    }								      \
     303  									      \
     304  	  arg = lr_token (ldfile, charmap, result, NULL, verbose);	      \
     305  	  if (arg->tok != tok_string)					      \
     306  	    goto err_label;						      \
     307  	  if (identification->cat != NULL)				      \
     308  	    lr_error (ldfile, _("\
     309  %s: field `%s' declared more than once"), "LC_IDENTIFICATION", #cat);	      \
     310  	  else if (!ignore_content && arg->val.str.startmb == NULL)	      \
     311  	    {								      \
     312  	      lr_error (ldfile, _("\
     313  %s: unknown character in field `%s'"), "LC_IDENTIFICATION", #cat);	      \
     314  	      identification->cat = "";					      \
     315  	    }								      \
     316  	  else if (!ignore_content)					      \
     317  	    identification->cat = arg->val.str.startmb;			      \
     318  	  break
     319  
     320  	  STR_ELEM (title);
     321  	  STR_ELEM (source);
     322  	  STR_ELEM (address);
     323  	  STR_ELEM (contact);
     324  	  STR_ELEM (email);
     325  	  STR_ELEM (tel);
     326  	  STR_ELEM (fax);
     327  	  STR_ELEM (language);
     328  	  STR_ELEM (territory);
     329  	  STR_ELEM (audience);
     330  	  STR_ELEM (application);
     331  	  STR_ELEM (abbreviation);
     332  	  STR_ELEM (revision);
     333  	  STR_ELEM (date);
     334  
     335  	case tok_category:
     336  	  /* Ignore the rest of the line if we don't need the input of
     337  	     this line.  */
     338  	  if (ignore_content)
     339  	    {
     340  	      lr_ignore_rest (ldfile, 0);
     341  	      break;
     342  	    }
     343  
     344  	  /* We expect two operands.  */
     345  	  arg = lr_token (ldfile, charmap, result, NULL, verbose);
     346  	  if (arg->tok != tok_string && arg->tok != tok_ident)
     347  	    goto err_label;
     348  	  /* Next is a semicolon.  */
     349  	  cattok = lr_token (ldfile, charmap, result, NULL, verbose);
     350  	  if (cattok->tok != tok_semicolon)
     351  	    goto err_label;
     352  	  /* Now a LC_xxx identifier.  */
     353  	  cattok = lr_token (ldfile, charmap, result, NULL, verbose);
     354  	  switch (cattok->tok)
     355  	    {
     356  #define CATEGORY(lname, uname) \
     357  	    case tok_lc_##lname:					      \
     358  	      category = LC_##uname;					      \
     359  	      break
     360  
     361  	      CATEGORY (identification, IDENTIFICATION);
     362  	      CATEGORY (ctype, CTYPE);
     363  	      CATEGORY (collate, COLLATE);
     364  	      CATEGORY (time, TIME);
     365  	      CATEGORY (numeric, NUMERIC);
     366  	      CATEGORY (monetary, MONETARY);
     367  	      CATEGORY (messages, MESSAGES);
     368  	      CATEGORY (paper, PAPER);
     369  	      CATEGORY (name, NAME);
     370  	      CATEGORY (address, ADDRESS);
     371  	      CATEGORY (telephone, TELEPHONE);
     372  	      CATEGORY (measurement, MEASUREMENT);
     373  
     374  	    default:
     375  	      goto err_label;
     376  	    }
     377  	  if (identification->category[category] != NULL)
     378  	    {
     379  	      lr_error (ldfile, _("\
     380  %s: duplicate category version definition"), "LC_IDENTIFICATION");
     381  	      free (arg->val.str.startmb);
     382  	    }
     383  	  else
     384  	    identification->category[category] = arg->val.str.startmb;
     385  	  break;
     386  
     387  	case tok_end:
     388  	  /* Next we assume `LC_IDENTIFICATION'.  */
     389  	  arg = lr_token (ldfile, charmap, result, NULL, verbose);
     390  	  if (arg->tok == tok_eof)
     391  	    break;
     392  	  if (arg->tok == tok_eol)
     393  	    lr_error (ldfile, _("%s: incomplete `END' line"),
     394  		      "LC_IDENTIFICATION");
     395  	  else if (arg->tok != tok_lc_identification)
     396  	    lr_error (ldfile, _("\
     397  %1$s: definition does not end with `END %1$s'"), "LC_IDENTIFICATION");
     398  	  lr_ignore_rest (ldfile, arg->tok == tok_lc_identification);
     399  	  return;
     400  
     401  	default:
     402  	err_label:
     403  	  SYNTAX_ERROR (_("%s: syntax error"), "LC_IDENTIFICATION");
     404  	}
     405  
     406        /* Prepare for the next round.  */
     407        now = lr_token (ldfile, charmap, result, NULL, verbose);
     408        nowtok = now->tok;
     409      }
     410  
     411    /* When we come here we reached the end of the file.  */
     412    lr_error (ldfile, _("%s: premature end of file"), "LC_IDENTIFICATION");
     413  }