summary refs log tree commit diff
path: root/locale/programs/ld-collate.c
diff options
context:
space:
mode:
authorCarlos O'Donell <carlos@redhat.com>2021-07-29 22:45:39 -0400
committerCarlos O'Donell <carlos@redhat.com>2021-09-06 11:06:45 -0400
commitf5117c6504888fab5423282a4607c552b90fd3f9 (patch)
tree2961802e4e55a5669f9b224923955871524a33fd /locale/programs/ld-collate.c
parent1d9f99ce1b3788d1897cb53a76d57e973111b8fe (diff)
downloadglibc-f5117c6504888fab5423282a4607c552b90fd3f9.tar.gz
glibc-f5117c6504888fab5423282a4607c552b90fd3f9.tar.xz
glibc-f5117c6504888fab5423282a4607c552b90fd3f9.zip
Add 'codepoint_collation' support for LC_COLLATE.
Support a new directive 'codepoint_collation' in the LC_COLLATE
section of a locale source file. This new directive causes all
collation rules to be dropped and instead STRCMP (strcmp or
wcscmp) is used for collation of the input character set. This
is required to allow for a C.UTF-8 that contains zero collation
rules (minimal size) and sorts using code point sorting.

To date the only implementation of a locale with zero collation
rules is the C/POSIX locale. The C/POSIX locale provides
identity tables for _NL_COLLATE_COLLSEQMB and
_NL_COLLATE_COLLSEQWC that map to ASCII even though it has zero
rules. This has lead to existing fnmatch, regexec, and regcomp
implementations that require these tables. It is not correct
to use these tables when nrules == 0, but the conservative fix
is to provide these tables when nrules == 0. This assures that
existing static applications using a new C.UTF-8 locale with
'codepoint_collation' at least have functional range expressions
with ASCII e.g. [0-9] or [a-z]. Such static applications would
not have the fixes to fnmatch, regexec and regcomp that avoid
the use of the tables when nrules == 0. Future fixes to fnmatch,
regexec, and regcomp would allow range expressions to use the
full set of code points for such ranges.

Tested on x86_64 and i686 without regression.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
Diffstat (limited to 'locale/programs/ld-collate.c')
-rw-r--r--locale/programs/ld-collate.c36
1 files changed, 33 insertions, 3 deletions
diff --git a/locale/programs/ld-collate.c b/locale/programs/ld-collate.c
index f4a8f34e46..06a5203334 100644
--- a/locale/programs/ld-collate.c
+++ b/locale/programs/ld-collate.c
@@ -23,6 +23,7 @@
 #include <wchar.h>
 #include <stdint.h>
 #include <sys/param.h>
+#include <array_length.h>
 
 #include "localedef.h"
 #include "charmap.h"
@@ -194,6 +195,9 @@ struct name_list
 /* The real definition of the struct for the LC_COLLATE locale.  */
 struct locale_collate_t
 {
+  /* Does the locale use code points to compare the encoding?  */
+  bool codepoint_collation;
+
   int col_weight_max;
   int cur_weight_max;
 
@@ -1509,6 +1513,7 @@ collate_startup (struct linereader *ldfile, struct localedef_t *locale,
 	  obstack_init (&collate->mempool);
 
 	  collate->col_weight_max = -1;
+	  collate->codepoint_collation = false;
 	}
       else
 	/* Reuse the copy_locale's data structures.  */
@@ -1567,6 +1572,10 @@ collate_finish (struct localedef_t *locale, const struct charmap_t *charmap)
       return;
     }
 
+  /* No data required.  */
+  if (collate->codepoint_collation)
+    return;
+
   /* If this assertion is hit change the type in `element_t'.  */
   assert (nrules <= sizeof (runp->used_in_level) * 8);
 
@@ -2091,6 +2100,10 @@ add_to_tablewc (uint32_t ch, struct element_t *runp)
     }
 }
 
+/* Include the C locale identity tables for _NL_COLLATE_COLLSEQMB and
+   _NL_COLLATE_COLLSEQWC.  */
+#include "C-collate-seq.c"
+
 void
 collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
 		const char *output_path)
@@ -2114,7 +2127,7 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
   add_locale_uint32 (&file, nrules);
 
   /* If we have no LC_COLLATE data emit only the number of rules as zero.  */
-  if (collate == NULL)
+  if (collate == NULL || collate->codepoint_collation)
     {
       size_t idx;
       for (idx = 1; idx < nelems; idx++)
@@ -2122,6 +2135,17 @@ collate_output (struct localedef_t *locale, const struct charmap_t *charmap,
 	  /* The words have to be handled specially.  */
 	  if (idx == _NL_ITEM_INDEX (_NL_COLLATE_SYMB_HASH_SIZEMB))
 	    add_locale_uint32 (&file, 0);
+	  else if (idx == _NL_ITEM_INDEX (_NL_COLLATE_CODESET)
+		   && collate != NULL)
+	    /* A valid LC_COLLATE must have a code set name.  */
+	    add_locale_string (&file, charmap->code_set_name);
+	  else if (idx == _NL_ITEM_INDEX (_NL_COLLATE_COLLSEQMB)
+		   && collate != NULL)
+	    add_locale_raw_data (&file, collseqmb, sizeof (collseqmb));
+	  else if (idx == _NL_ITEM_INDEX (_NL_COLLATE_COLLSEQWC)
+		   && collate != NULL)
+	    add_locale_uint32_array (&file, collseqwc,
+				     array_length (collseqwc));
 	  else
 	    add_locale_empty (&file);
 	}
@@ -2671,6 +2695,10 @@ collate_read (struct linereader *ldfile, struct localedef_t *result,
 
       switch (nowtok)
 	{
+	case tok_codepoint_collation:
+	  collate->codepoint_collation = true;
+	  break;
+
 	case tok_copy:
 	  /* Allow copying other locales.  */
 	  now = lr_token (ldfile, charmap, result, NULL, verbose);
@@ -3741,9 +3769,11 @@ error while adding equivalent collating symbol"));
 	  /* Next we assume `LC_COLLATE'.  */
 	  if (!ignore_content)
 	    {
-	      if (state == 0 && copy_locale == NULL)
+	      if (state == 0
+		  && copy_locale == NULL
+		  && !collate->codepoint_collation)
 		/* We must either see a copy statement or have
-		   ordering values.  */
+		   ordering values, or codepoint_collation.  */
 		lr_error (ldfile,
 			  _("%s: empty category description not allowed"),
 			  "LC_COLLATE");