Comando para recuperar la lista de caracteres en una clase de caracteres dada en el entorno local actual

18

Lo que podría ser una manera de recuperar una lista de todos los personajes de una clase de caracteres dada (como blank, alpha, digit...) en la localización actual.

Por ejemplo,

LC_ALL=en_GB.UTF-8 that-command blank

idealmente, en mi sistema Debian, mostraría algo como:

      09 U+0009 HORIZONTAL TAB
      20 U+0020 SPACE
e1 9a 80 U+1680 OGHAM SPACE MARK
e1 a0 8e U+180E MONGOLIAN VOWEL SEPARATOR
e2 80 80 U+2000 EN QUAD
e2 80 81 U+2001 EM QUAD
e2 80 82 U+2002 EN SPACE
e2 80 83 U+2003 EM SPACE
e2 80 84 U+2004 THREE-PER-EM SPACE
e2 80 85 U+2005 FOUR-PER-EM SPACE
e2 80 86 U+2006 SIX-PER-EM SPACE
e2 80 88 U+2008 PUNCTUATION SPACE
e2 80 89 U+2009 THIN SPACE
e2 80 8a U+200A HAIR SPACE
e2 81 9f U+205F MEDIUM MATHEMATICAL SPACE
e3 80 80 U+3000 IDEOGRAPHIC SPACE

Y en la configuración regional C podría mostrar algo como:

09 U+0009 HORIZONTAL TAB
20 U+0020 SPACE

Es decir, la representación del carácter en la configuración regional en términos de matrices de bytes (como UTF-8 en el primer ejemplo y un solo byte en el segundo), el punto de código de caracteres Unicode equivalente y una descripción.

Contexto

(editar) Ahora que la vulnerabilidad ha sido parcheada y revelada por mucho tiempo, puedo agregar un poco de contexto.

Hice esa pregunta en el momento en que estaba investigando CVE 2014-0475 . glibctenía un error en el sentido de que permitía al usuario usar configuraciones regionales como las LC_ALL=../../../../tmp/evil-localeque se resuelven en relación con la ruta de búsqueda de configuración regional del sistema estándar y, por lo tanto, permite usar cualquier archivo como definición de configuración regional.

Podría crear un entorno local malicioso, por ejemplo, con un solo byte por juego de caracteres donde la mayoría de los caracteres excepto s, hy algunos otros se consideraron espacios en blanco y eso se bashejecutaría shal analizar un /etc/bash.bashrcarchivo Debian típico (y eso podría usarse para obtener acceso de shell en un gitel servidor de alojamiento, por ejemplo, bashse utiliza como el shell de inicio de sesión del usuario del gitservidor y que el sshservidor acepta LC_*/ LANGvariables y que el atacante puede cargar archivos al servidor).

Ahora, si alguna vez encontré una LC_CTYPE(definición de configuración regional compilada) /tmp/evil, ¿cómo podría descubrir que era una canalla y de qué manera?

Por lo tanto, mi objetivo es descompilar esas definiciones de configuración regional y, si no, al menos saber qué caracteres (junto con su codificación) están en una clase de caracteres dada.

Así que con eso en mente:

  • Las soluciones que miran los archivos de origen para la configuración regional (las definiciones de configuración regional como las /usr/share/i18n/localede Debian) no son útiles en mi caso.
  • Las propiedades de los caracteres Unicode son irrelevantes. Solo me importa lo que dice la localidad. En un sistema Debian, incluso entre dos configuraciones regionales del sistema UTF-8, y mucho menos las falsas, la lista de caracteres en una clase puede ser diferente.
  • Las herramientas como recode, pythono perlque realizan la conversión de byte / multi-byte a / desde caracteres no pueden usarse, ya que pueden (y en la práctica lo hacen) realizar la conversión de una manera diferente a la configuración regional.
Stéphane Chazelas
fuente
Para la mayoría de las configuraciones regionales, en última instancia, proviene del material LC_CTYPE en (con glibc) /usr/share/i18n/locales/i18n... que, por supuesto, proviene en gran medida de la base de datos de caracteres Unicode. Por supuesto, sería bueno tener un comando
derobert
@derobert, sí, mientras locale(al menos la de GNU) recupera muchas de las informaciones almacenadas en muchas de las categorías, cosas que no son las más importantes en LC_CTYPE y LC_COLLATE. Me pregunto si hay una API oculta para recuperar esa información o descompilar la información del entorno local.
Stéphane Chazelas
Sí, puedes analizar esa información. Finalmente, finalmente pude terminar mi edición. Probablemente ya haya instalado varios comandos, al menos lo hice, y ni siquiera los conocía. Espero que ayude. Específicamente recodey uconvpuede darle lo que usted dice que está buscando. Posiblemente incluso justo luity odsupongo ...
mikeserv
¡Eso es muy bueno! Eso significa que no necesitas perlnada, creo.
mikeserv
Parece que básicamente puedo extraer mi juego de caracteres LC_CTYPEcon od -A n -t c <LC_CTYPE | tsortProbablemente ya lo hayas probado, pero nunca antes había oído hablar de eso y estaba leyendo infoy me recordó esto, y parece funcionar. También hay, ptxpero creo que es menos relevante. De todos modos, si no lo ha intentado y decide hacerlo, advertencia justa, requiere un poco de paciencia. lehman.cuny.edu/cgi-bin/man-cgi?tsort+1
mikeserv

Respuestas:

7

POSIBLE SOLUCIÓN FINAL

Así que tomé toda la información a continuación y llegué a esto:

for class in $(
    locale -v LC_CTYPE | 
    sed 's/combin.*//;s/;/\n/g;q'
) ; do 
    printf "\n\t%s\n\n" $class
    recode u2/test16 -q </dev/null | 
    tr -dc "[:$class:]" | 
    od -A n -t a -t o1z -w12
done

NOTA :

Lo uso odcomo el filtro final anterior por preferencia y porque sé que no trabajaré con caracteres de varios bytes, que no manejará correctamente. recode u2..dumpambos generarán una salida más parecida a la especificada en la pregunta y manejarán los caracteres anchos correctamente.

SALIDA

        upper

   A   B   C   D   E   F   G   H   I   J   K   L
 101 102 103 104 105 106 107 110 111 112 113 114  >ABCDEFGHIJKL<
   M   N   O   P   Q   R   S   T   U   V   W   X
 115 116 117 120 121 122 123 124 125 126 127 130  >MNOPQRSTUVWX<
   Y   Z
 131 132                                          >YZ<

        lower

   a   b   c   d   e   f   g   h   i   j   k   l
 141 142 143 144 145 146 147 150 151 152 153 154  >abcdefghijkl<
   m   n   o   p   q   r   s   t   u   v   w   x
 155 156 157 160 161 162 163 164 165 166 167 170  >mnopqrstuvwx<
   y   z
 171 172                                          >yz<

        alpha

   A   B   C   D   E   F   G   H   I   J   K   L
 101 102 103 104 105 106 107 110 111 112 113 114  >ABCDEFGHIJKL<
   M   N   O   P   Q   R   S   T   U   V   W   X
 115 116 117 120 121 122 123 124 125 126 127 130  >MNOPQRSTUVWX<
   Y   Z   a   b   c   d   e   f   g   h   i   j
 131 132 141 142 143 144 145 146 147 150 151 152  >YZabcdefghij<
   k   l   m   n   o   p   q   r   s   t   u   v
 153 154 155 156 157 160 161 162 163 164 165 166  >klmnopqrstuv<
   w   x   y   z
 167 170 171 172                                  >wxyz<

        digit

   0   1   2   3   4   5   6   7   8   9
 060 061 062 063 064 065 066 067 070 071          >0123456789<

       xdigit                                                                                          

   0   1   2   3   4   5   6   7   8   9   A   B
 060 061 062 063 064 065 066 067 070 071 101 102  >0123456789AB<
   C   D   E   F   a   b   c   d   e   f
 103 104 105 106 141 142 143 144 145 146          >CDEFabcdef<

        space

  ht  nl  vt  ff  cr  sp
 011 012 013 014 015 040                          >..... <

        print

  sp   !   "   #   $   %   &   '   (   )   *   +
 040 041 042 043 044 045 046 047 050 051 052 053  > !"#$%&'()*+<
   ,   -   .   /   0   1   2   3   4   5   6   7
 054 055 056 057 060 061 062 063 064 065 066 067  >,-./01234567<
   8   9   :   ;   <   =   >   ?   @   A   B   C
 070 071 072 073 074 075 076 077 100 101 102 103  >89:;<=>?@ABC<
   D   E   F   G   H   I   J   K   L   M   N   O
 104 105 106 107 110 111 112 113 114 115 116 117  >DEFGHIJKLMNO<
   P   Q   R   S   T   U   V   W   X   Y   Z   [
 120 121 122 123 124 125 126 127 130 131 132 133  >PQRSTUVWXYZ[<
   \   ]   ^   _   `   a   b   c   d   e   f   g
 134 135 136 137 140 141 142 143 144 145 146 147  >\]^_`abcdefg<
   h   i   j   k   l   m   n   o   p   q   r   s
 150 151 152 153 154 155 156 157 160 161 162 163  >hijklmnopqrs<
   t   u   v   w   x   y   z   {   |   }   ~
 164 165 166 167 170 171 172 173 174 175 176      >tuvwxyz{|}~<

        graph

   !   "   #   $   %   &   '   (   )   *   +   ,
 041 042 043 044 045 046 047 050 051 052 053 054  >!"#$%&'()*+,<
   -   .   /   0   1   2   3   4   5   6   7   8
 055 056 057 060 061 062 063 064 065 066 067 070  >-./012345678<
   9   :   ;   <   =   >   ?   @   A   B   C   D
 071 072 073 074 075 076 077 100 101 102 103 104  >9:;<=>?@ABCD<
   E   F   G   H   I   J   K   L   M   N   O   P
 105 106 107 110 111 112 113 114 115 116 117 120  >EFGHIJKLMNOP<
   Q   R   S   T   U   V   W   X   Y   Z   [   \
 121 122 123 124 125 126 127 130 131 132 133 134  >QRSTUVWXYZ[\<
   ]   ^   _   `   a   b   c   d   e   f   g   h
 135 136 137 140 141 142 143 144 145 146 147 150  >]^_`abcdefgh<
   i   j   k   l   m   n   o   p   q   r   s   t
 151 152 153 154 155 156 157 160 161 162 163 164  >ijklmnopqrst<
   u   v   w   x   y   z   {   |   }   ~
 165 166 167 170 171 172 173 174 175 176          >uvwxyz{|}~<

        blank

  ht  sp
 011 040                                          >. <

        cntrl

 nul soh stx etx eot enq ack bel  bs  ht  nl  vt
 000 001 002 003 004 005 006 007 010 011 012 013  >............<
  ff  cr  so  si dle dc1 dc2 dc3 dc4 nak syn etb
 014 015 016 017 020 021 022 023 024 025 026 027  >............<
 can  em sub esc  fs  gs  rs  us del
 030 031 032 033 034 035 036 037 177              >.........<

        punct

   !   "   #   $   %   &   '   (   )   *   +   ,
 041 042 043 044 045 046 047 050 051 052 053 054  >!"#$%&'()*+,<
   -   .   /   :   ;   <   =   >   ?   @   [   \
 055 056 057 072 073 074 075 076 077 100 133 134  >-./:;<=>?@[\<
   ]   ^   _   `   {   |   }   ~
 135 136 137 140 173 174 175 176                  >]^_`{|}~<

        alnum

   0   1   2   3   4   5   6   7   8   9   A   B
 060 061 062 063 064 065 066 067 070 071 101 102  >0123456789AB<
   C   D   E   F   G   H   I   J   K   L   M   N
 103 104 105 106 107 110 111 112 113 114 115 116  >CDEFGHIJKLMN<
   O   P   Q   R   S   T   U   V   W   X   Y   Z
 117 120 121 122 123 124 125 126 127 130 131 132  >OPQRSTUVWXYZ<
   a   b   c   d   e   f   g   h   i   j   k   l
 141 142 143 144 145 146 147 150 151 152 153 154  >abcdefghijkl<
   m   n   o   p   q   r   s   t   u   v   w   x
 155 156 157 160 161 162 163 164 165 166 167 170  >mnopqrstuvwx<
   y   z

API DEL PROGRAMADOR

Como lo demuestro a continuación, recodele proporcionaré su mapa completo de personajes. De acuerdo con su manual, lo hace de acuerdo con el valor actual de la DEFAULT_CHARSETvariable de entorno o, en su defecto, funciona exactamente como usted especifica:

Cuando se omite un nombre de conjunto de caracteres o se deja vacío, DEFAULT_CHARSETse utiliza el valor de la variable en el entorno. Si esta variable no está definida, la recodebiblioteca usa la codificación del entorno local actual. En los sistemas compatibles con POSIX , esto depende del primer valor no vacío entre las variables de entorno LC_ALL, LC_CTYPE, LANGy se puede determinar mediante el comandolocale charmap.

También vale la pena señalar recodeque es una API :

El programa nombrado recodees solo una aplicación de su biblioteca de recodificación. La biblioteca de recodificación está disponible por separado para otros programas en C. Una buena forma de familiarizarse con la biblioteca de recodificación es familiarizarse con el recodeprograma en sí.

Para usar la biblioteca de recodificación una vez que está instalada, un programa en C necesita tener una línea:

#include <recode.h>

Para una comparación de cadenas internacionalmente amigable Los estándares POSIXy Cdefinen la strcoll()función:

La strcoll()función comparará la cadena apuntada por s1con la cadena apuntada por s2, ambas interpretadas como apropiadas para la categoría LC_COLLATE de la localización actual.

La strcoll()función no cambiará la configuración de errno si tiene éxito.

Como no se reserva ningún valor de retorno para indicar un error, una aplicación que desee verificar si hay situaciones de error debe establecer errno en 0, luego llamar strcoll()y luego verificar errno.

Aquí hay un ejemplo de su uso ubicado por separado :

#include <stdio.h>
#include <string.h>

int main ()
{
   char str1[15];
   char str2[15];
   int ret;


   strcpy(str1, "abc");
   strcpy(str2, "ABC");

   ret = strcoll(str1, str2);

   if(ret > 0)
   {
      printf("str1 is less than str2");
   }
   else if(ret < 0) 
   {
      printf("str2 is less than str1");
   }
   else 
   {
      printf("str1 is equal to str2");
   }

   return(0);
}

Con respecto a las POSIXclases de caracteres, ya has notado que usaste la CAPI para encontrarlos. Para caracteres y clases unicode, puede usar el juego de caracteres recode's dump-with-names para obtener el resultado deseado. De su manual nuevamente :

Por ejemplo, el comando recode l2..full < inputimplica una conversión necesaria de Latin-2 a UCS-2, ya que dump-with-names solo está conectado desde UCS-2. En tales casos, recodeno muestra los códigos originales de Latin-2 en el volcado, solo los valores UCS-2 correspondientes . Para dar un ejemplo más simple, el comando

 echo 'Hello, world!' | recode us..dump

produce el siguiente resultado:

UCS2   Mne   Description

0048   H     latin capital letter h 
0065   e     latin small letter e
006C   l     latin small letter l 
006C   l     latin small letter l
006F   o     latin small letter o 
002C   ,     comma 
0020  SP     space 
0077   w     latin small letter w 
006F   o     latin small letter o 
0072   r     latin small letter r 
006C   l     latin small letter l 
0064   d     latin small letter d 
0021   !     exclamation mark 
000A   LF    line feed (lf)

El comentario descriptivo se da en inglés y en ASCII, pero si la descripción en inglés no está disponible pero sí en francés, entonces la descripción en francés se da en su lugar, usando Latin-1. Sin embargo, si la variable de entorno LANGUAGEo LANGcomienza con las letras fr , entonces la preferencia de listado va al francés cuando ambas descripciones están disponibles.

Utilizando una sintaxis similar a la anterior combinada con su conjunto de datos de prueba incluido , puedo obtener mi propio mapa de caracteres con:

recode -q u8/test8..dump </dev/null

SALIDA

UCS2   Mne   Description

0001   SH    start of heading (soh)
0002   SX    start of text (stx)
0003   EX    end of text (etx)    
...
002B   +     plus sign
002C   ,     comma
002D   -     hyphen-minus
...
0043   C     latin capital letter c
0044   D     latin capital letter d
0045   E     latin capital letter e
...
006B   k     latin small letter k
006C   l     latin small letter l
006D   m     latin small letter m
...
007B   (!    left curly bracket
007C   !!    vertical line
007D   !)    right curly bracket
007E   '?    tilde
007F   DT    delete (del)

Pero para los personajes comunes, recodeaparentemente no es necesario. Esto debería darle caracteres con nombre para todo en el conjunto de caracteres de 128 bytes:

printf %b "$(printf \\%04o $(seq 128))" | 
luit -c |
od -A n -t o1z -t a -w12

SALIDA

 001 002 003 004 005 006 007 010 011 012 013 014  >............<
 soh stx etx eot enq ack bel  bs  ht  nl  vt  ff
...
 171 172 173 174 175 176 177                      >yz{|}~.<
   y   z   {   |   }   ~ del

Por supuesto, solo se representan 128 bytes, pero eso se debe a que mi configuración regional, utf-8 charmaps o no, usa el juego de caracteres ASCII y nada más. Entonces eso es todo lo que obtengo. Sin luitembargo, si lo ejecutara sin filtrarlo, odlo rodaría e imprimiría el mismo mapa nuevamente hasta\0400.

Sin embargo, hay dos problemas principales con el método anterior. Primero está el orden de clasificación del sistema: para los entornos locales que no son ASCII, los valores de mordida de los conjuntos de caracteres no son simplemente in sequence, lo que, según creo, es probablemente el núcleo del problema que está tratando de resolver.

Bueno, la tr's manpágina de GNU afirma que expandirá las [:upper:] [:lower:]clases en orden, pero eso no es mucho.

Me imagino que podría implementarse una solución de mano dura, sortpero sería una herramienta bastante difícil de manejar para una API de programación de back-end.

recodehará esto correctamente, pero el otro día no parecías demasiado enamorado del programa. Tal vez las ediciones de hoy arrojen una luz más amigable o tal vez no.

GNU también ofrece la gettextbiblioteca de funciones, y parece ser capaz de abordar este problema al menos para el LC_MESSAGEScontexto:

- Función: char * bind_textdomain_codeset( const char *domainname, const char *codeset)

La bind_textdomain_codesetfunción se puede utilizar para especificar el juego de caracteres de salida para los catálogos de mensajes para el dominio nombre de dominio . El argumento del conjunto de códigos debe ser un nombre de conjunto de códigos válido que pueda usarse para la función iconv_open o un puntero nulo.

Si el parámetro del conjunto de códigos es el puntero nulo, bind_textdomain_codeset devuelve el conjunto de códigos seleccionado actualmente para el dominio con el nombre nombrededominio . Devuelve NULL si todavía no se ha seleccionado un conjunto de códigos .

La bind_textdomain_codesetfunción se puede usar varias veces. Si se usa varias veces con el mismo argumento de nombre de dominio, la llamada posterior anula la configuración realizada por la anterior.

La bind_textdomain_codesetfunción devuelve un puntero a una cadena que contiene el nombre del conjunto de códigos seleccionado. La cadena se asigna internamente en la función y el usuario no debe cambiarla. Si el sistema se salió del núcleo durante la ejecución de bind_textdomain_codeset, el valor de retorno es NULL y la variable global errno se establece en consecuencia.

También puede usar categorías de caracteres Unicode nativas , que son independientes del idioma y renuncian a las clases POSIX por completo, o tal vez recurrir a las primeras para proporcionarle suficiente información para definir las últimas.

Además de las complicaciones, Unicode también trae nuevas posibilidades. Una es que cada personaje Unicode pertenece a una determinada categoría. Puede hacer coincidir un solo carácter perteneciente a la categoría "letra" con \p{L}. Puede hacer coincidir un solo personaje que no pertenece a esa categoría con \P{L}.

Nuevamente, "carácter" realmente significa "punto de código Unicode". \p{L}coincide con un único punto de código en la categoría "letra". Si su cadena de entrada está à codificada como U+0061 U+0300, coincide asin el acento. Si la entrada está àcodificada como U+00E0, coincide àcon el acento. La razón es que tanto el código señala U+0061 (a)como U+00E0 (à)está en la categoría "letra", mientras que U+0300está en la categoría "marca".

Ahora debe entender por qué \P{M}\p{M}*+es el equivalente de \X. \P{M}coincide con un punto de código que no es una marca de combinación, mientras que \p{M}*+ coincide con cero o más puntos de código que son marcas de combinación. Para hacer coincidir una letra con cualquier signo diacrítico, use \p{L}\p{M}*+. Esta última expresión regular siempre coincidirá à, independientemente de cómo esté codificada. El cuantificador posesivo se asegura de que el retroceso no haga \P{M}\p{M}*+coincidir una no marca sin las marcas combinadas que la siguen, lo cual \X nunca funcionaría.

El mismo sitio web que proporcionó la información anterior también analiza Tclla propia implementación de expresiones regulares que cumple con POSIX, que podría ser otra forma de lograr su objetivo.

Y por último, entre las soluciones, sugeriré que puede interrogar el LC_COLLATEarchivo en sí para obtener el mapa de caracteres del sistema completo y en orden . Puede que esto no parezca fácil, pero logré algo de éxito con lo siguiente después de compilarlo localedefcomo se muestra a continuación:

<LC_COLLATE od -j2K -a -w2048 -v  | 
tail -n2 | 
cut -d' ' -f$(seq -s',' 4 2 2048) | 
sed 's/nul\|\\0//g;s/  */ /g;:s;
    s/\([^ ]\{1,3\}\) \1/\1/;ts;
    s/\(\([^ ][^ ]*  *\)\{16\}\)/\1\n/g'

 dc1 dc2 dc3 dc4 nak syn etb can c fs c rs c sp ! "
# $ % & ' ( ) * + , - . / 0 1 2
3 4 5 6 7 8 9 : ; < = > ? @ A B
C D E F G H I J K L M N O P Q R
S T U V W X Y Z [ \ ] ^ _ ` a b
c d e f g h i j k l m n o p q r
s t u v w x y z { | } ~ del soh stx etx
eot enq ack bel c ht c vt cr c si dle dc1 del

Es, sin duda, actualmente defectuoso, pero espero que al menos demuestre la posibilidad.

AL PRIMER RUBOR

strings $_/en_GB

#OUTPUT

int_select "<U0030><U0030>"
...
END LC_TELEPHONE

Realmente no parecía mucho, pero luego comencé a notar copycomandos en toda la lista. El archivo anterior parece estar copyen "en_US", por ejemplo, y otro muy grande que parece que todos comparten hasta cierto punto es iso_14651_t1_common.

Es bastante grande:

strings $_ | wc -c

#OUTPUT
431545

Aquí está la introducción a /usr/share/i18n/locales/POSIX:

# Territory:
# Revision: 1.1
# Date: 1997-03-15
# Application: general
# Users: general
# Repertoiremap: POSIX
# Charset: ISO646:1993
# Distribution and use is free, also for
# commercial purposes.
LC_CTYPE
# The following is the POSIX Locale LC_CTYPE.
# "alpha" is by default "upper" and "lower"
# "alnum" is by definiton "alpha" and "digit"
# "print" is by default "alnum", "punct" and the <U0020> character
# "graph" is by default "alnum" and "punct"
upper   <U0041>;<U0042>;<U0043>;<U0044>;<U0045>;<U0046>;<U0047>;<U0048>;\
        <U0049>;<U004A>;<U004B>;<U004C>;<U004D>;<U004E>;<U004F>;

...

Puede hacerlo, greppor supuesto, pero podría simplemente:

recode -lf gb

En lugar. Obtendrías algo como esto:

Dec  Oct Hex   UCS2  Mne  BS_4730

  0  000  00   0000  NU   null (nul)
  1  001  01   0001  SH   start of heading (soh)
...

... Y MÁS

También hay luitun ptydispositivo de traducción UTF-8 terminal , supongo que actúa como intermediario para XTerms sin soporte UTF-8. Maneja muchos modificadores, como registrar todos los bytes convertidos en un archivo o -ccomo un |pipefiltro simple .

Nunca me di cuenta de que había tanto en esto: las configuraciones regionales y los mapas de caracteres y todo eso. Aparentemente esto es un gran problema, pero supongo que todo sucede detrás de escena. Hay, al menos en mi sistema, un par de cientos de man 3resultados relacionados para búsquedas relacionadas con la configuración regional.

Y también hay:

zcat /usr/share/i18n/charmaps/UTF-8*gz | less

    CHARMAP
<U0000>     /x00         NULL
<U0001>     /x01         START OF HEADING
<U0002>     /x02         START OF TEXT
<U0003>     /x03         END OF TEXT
<U0004>     /x04         END OF TRANSMISSION
<U0005>     /x05         ENQUIRY
...

Eso va a continuar por un muy largo tiempo.

Las Xlibfunciones manejan esto todo el tiempo, luites parte de ese paquete.

Las Tcl_uni...funciones también pueden resultar útiles.

solo un poco de <tab>finalización y manbúsquedas, y he aprendido bastante sobre este tema.

Con localedef: puede compilarlo localesen su I18Ndirectorio. El resultado es funky y no extraordinariamente útil, no como charmapsen absoluto, pero puede obtener el formato sin procesar tal como lo especificó anteriormente como lo hice yo:

mkdir -p dir && cd $_ ; localedef -f UTF-8 -i en_GB ./ 

ls -l
total 1508
drwxr-xr-x 1 mikeserv mikeserv      30 May  6 18:35 LC_MESSAGES
-rw-r--r-- 1 mikeserv mikeserv     146 May  6 18:35 LC_ADDRESS
-rw-r--r-- 1 mikeserv mikeserv 1243766 May  6 18:35 LC_COLLATE
-rw-r--r-- 1 mikeserv mikeserv  256420 May  6 18:35 LC_CTYPE
-rw-r--r-- 1 mikeserv mikeserv     376 May  6 18:35 LC_IDENTIFICATION
-rw-r--r-- 1 mikeserv mikeserv      23 May  6 18:35 LC_MEASUREMENT
-rw-r--r-- 1 mikeserv mikeserv     290 May  6 18:35 LC_MONETARY
-rw-r--r-- 1 mikeserv mikeserv      77 May  6 18:35 LC_NAME
-rw-r--r-- 1 mikeserv mikeserv      54 May  6 18:35 LC_NUMERIC
-rw-r--r-- 1 mikeserv mikeserv      34 May  6 18:35 LC_PAPER
-rw-r--r-- 1 mikeserv mikeserv      56 May  6 18:35 LC_TELEPHONE
-rw-r--r-- 1 mikeserv mikeserv    2470 May  6 18:35 LC_TIME

Luego con odusted puede leerlo: bytes y cadenas:

od -An -a -t u1z -w12 LC_COLLATE | less

 etb dle enq  sp dc3 nul nul nul   T nul nul nul
  23  16   5  32  19   0   0   0  84   0   0   0  >... ....T...<
...

Aunque está muy lejos de ganar un concurso de belleza, esa es una salida utilizable. Y odes tan configurable como quieras que sea, por supuesto.

Supongo que también me olvidé de estos:

    perl -mLocale                                                                                       

 -- Perl module --
Locale::Codes                    Locale::Codes::LangFam           Locale::Codes::Script_Retired
Locale::Codes::Constants         Locale::Codes::LangFam_Codes     Locale::Country
Locale::Codes::Country           Locale::Codes::LangFam_Retired   Locale::Currency
Locale::Codes::Country_Codes     Locale::Codes::LangVar           Locale::Language
Locale::Codes::Country_Retired   Locale::Codes::LangVar_Codes     Locale::Maketext
Locale::Codes::Currency          Locale::Codes::LangVar_Retired   Locale::Maketext::Guts
Locale::Codes::Currency_Codes    Locale::Codes::Language          Locale::Maketext::GutsLoader
Locale::Codes::Currency_Retired  Locale::Codes::Language_Codes    Locale::Maketext::Simple
Locale::Codes::LangExt           Locale::Codes::Language_Retired  Locale::Script
Locale::Codes::LangExt_Codes     Locale::Codes::Script            Locale::gettext
Locale::Codes::LangExt_Retired   Locale::Codes::Script_Codes      locale

Probablemente me olvidé de ellos porque no pude hacer que trabajaran. Supongo que nunca uso Perly no sé cómo cargar un módulo correctamente. Pero las manpáginas se ven muy bien. En cualquier caso, algo me dice que encontrarás que llamar a un módulo de Perl es al menos un poco menos difícil que yo. Y, nuevamente, estos ya estaban en mi computadora, y ni siquiera uso Perl. También hay notablemente algunos I18Nque me desplacé con melancolía sabiendo muy bien que tampoco los haría funcionar.

mikeserv
fuente
1
Esa es toda información muy buena y útil, pero proporciona información sobre los archivos de origen (in i18n) que pueden o no haber sido utilizados para generar la configuración regional que estoy usando actualmente. La información del entorno local probablemente proviene de /usr/lib/locale/locale-archiveo /some/dir/LC_CTYPE, y esa es la parte relevante para mi entorno local que se almacena en los archivos que busco.
Stéphane Chazelas
@StephaneChezales, así que solo extrae tu LC_STUFFarchivo del archivo localedef, también lo hace. Puedo demostrar eso también, supongo. También puede ver eso y prácticamente todo lo demás con stringso con odel resto. Lo hice, de todos modos. Pero, por cierto, charmaps son la configuración regional que está utilizando actualmente, y localedeftambién informará sobre eso. También eso es lo que recodehace también.
mikeserv
Básicamente está diciendo que podemos hacer a mano lo que hacen las bibliotecas del sistema para consultar la información de la clase de caracteres, pero eso necesitará miles de líneas de código para hacerlo de manera confiable y el resultado será específico del sistema. (analizando el entorno de la misma manera que lo hace la biblioteca del sistema (LOCPATH, LANG, LANGUAGE, LC_CTYPE ..., identifica dónde buscar los datos, extraerlos ...). No puedo ver cómo extraer cosas del archivo con localedef sin embargo.
Stéphane Chazelas
@StephaneChazelas - Yo no sugieren lo haces con la mano - Le sugiero que lo hace con una computadora - utilizando los binarios del sistema, tales como od, recode, uconvy el resto. Pero fue mi error, no es localedefque lo extraiga, es recodeeso lo hará. Tienes que echarle un vistazo info recode, y además del recodecomando de tabla que muestro hay casi lo mismo, y creo que manejará las cosas de la misma manera. No solo saca su juego de caracteres de la nada. En cualquier caso, tenía muchas esperanzas para esos perlmódulos. ¿Probaste alguno?
mikeserv
1
Si hay una API para recuperar la lista de caracteres en una clase de caracteres dada en la configuración regional actual, entonces eso es específicamente lo que estoy buscando. Si puede demostrar cómo hacer esto, aceptaré la respuesta. Lo único que se me ocurre (y cómo obtuve el "resultado esperado" en mi pregunta) es usar iswblank(3)para todos los valores de caracteres posibles.
Stéphane Chazelas
1

Al menos en los sistemas GNU, FreeBSD o Solaris, este enfoque de fuerza bruta funciona:

#include <wctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  unsigned long i;
  int need_init;
  wctype_t type;
  FILE* to_perl;

  setlocale(LC_ALL,"");
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <type>\n", (argc?argv[0] : "???"));
    exit(1);
  }
  if (!(type = wctype(argv[1]))) {
    fprintf(stderr, "Invalid type: \"%s\"\n", argv[1]);
    exit(1);
  }

  need_init = wctomb(0, 0);

  to_perl = popen("perl -Mcharnames=full -ane '"
                  "printf \"%17s U+%04X %s\n\", join(\" \", @F[1..$#F]),"
                  "$F[0], charnames::viacode($F[0])'", "w");

#ifdef SUPPORT_ROGUE_LOCALES
  for(i=0; i<=0x7fffffff; i++) {
#else
  for(i=0; i<=0x10ffff; i++) {
    if (i == 0xd800) i = 0xe000; /* skip UTF-16 surrogates */
#endif
    if (iswctype(i, type)) {
      int n;
      unsigned char buf[1024];

      if (need_init) wctomb(0, 0);
      n = wctomb(buf, i);

      if (n > 0) {
        int c;
        fprintf(to_perl, "%lu", i);
        for (c = 0; c < n; c++)
          fprintf(to_perl, " %02X", buf[c]);
        putc('\n', to_perl);
      }
    }
  }
  pclose(to_perl);
  return 0;
}

Si bien según C / POSIX, wchar_tes un tipo opaco que no tiene relación con Unicode y solo se garantiza que cubre todos los caracteres admitidos por la configuración regional del sistema, en la práctica, en la mayoría de los sistemas que admiten Unicode, los valores sí corresponden a los puntos de código Unicode y las definiciones locales están basadas en Unicode.

Unicode está destinado a ser un superconjunto de todos los conjuntos de caracteres conocidos, por lo que recorrer todos los puntos de código válidos en Unicode (0 a 0xD7FF y 0xE000 a 0x10FFFF) debe enumerar al menos todos los caracteres admitidos por un conjunto de caracteres dado.

Aquí, estamos utilizando la API estándar de la configuración regional del sistema para verificar cuáles son de un tipo determinado y convertirlo a su forma codificada en la codificación de la configuración regional. Usamos perly su charnamesmódulo solo para obtener el nombre de un punto de código Unicode dado.

En las configuraciones regionales que usan codificaciones con estado como ISO-2022-JP, nos aseguramos de que el formulario codificado se muestre desde un estado inicial predeterminado.

No he encontrado un sistema que haya instalado entornos locales con una codificación de caracteres con estado, pero al menos en los sistemas GNU, es posible generar algunos para que se pueda hacer un entorno local no autorizado (y al menos las herramientas GNU no funcionan correctamente en esos sistemas). locales). Por ejemplo, con una configuración regional personalizada que utiliza ISO-2022-JP con una ja_JPconfiguración regional normal , obtengo:

$ LOCPATH=$PWD LC_ALL=ja_JP.ISO-2022-JP ~/list-type blank
       09 U+0009 CHARACTER TABULATION
       20 U+0020 SPACE
   1B 24 42 21 21 U+3000 IDEOGRAPHIC SPACE

Comparar con:

$ LC_ALL=ja_JP.eucjp ~/list-type blank
       09 U+0009 CHARACTER TABULATION
       20 U+0020 SPACE
    A1 A1 U+3000 IDEOGRAPHIC SPACE

En ISO-2022-JP, la 1B 24 42secuencia ( \e$B) cambia de ASCII a un estado donde los caracteres se expresan como 2 bytes (7 bits) (aquí 21 21 para ese ESPACIO IDEOGRÁFICO). Mientras que en EUCJP, son los mismos bytes, pero el cambio de estado se realiza al voltear el 8vo bit ( A1 = 21 | 0x80), lo que lo hace más apátrida.

Eso significa que en esas codificaciones con estado, hay varias formas de escribir un carácter dado (por ejemplo, insertando varias de esas secuencias de cambio de estado ), y la secuencia que se muestra con ese código anterior es solo una de ellas (la canónica de una inicial estado predeterminado).

Mientras que para un escenario normal, los personajes no pueden estar fuera 0..0xD7FF, 0xE000..0x10FFFF, para un pícaro local, cualquier carácter en la gama que admite wchar_t puede ser. Por ejemplo, podría crear un entorno local donde U + DCBA o U + 12345678 caracteres (o serían caracteres si se permitieran) son espacios en blanco . Es por eso que querría compilar ese código -D SUPPORT_ROGUE_LOCALESpara cubrirlos, aunque eso significa que lleva mucho más tiempo escanear toda la lista.

No pude usar la solución de @ mikeserv ya que recodeusa sus propias conversiones, ya no se mantiene y solo admite caracteres Unicode hasta 0xFFFF, y GNU tral menos no funciona con caracteres de varios bytes.

No pude usar @ ChrisDown ya pythonque no tiene interfaces para las clases de caracteres POSIX.

Intenté con Perl, pero es falso para puntos de código entre 128 y 255 para entornos locales de varios bytes que no sean UTF-8 y no usa las bibliotecas de conversión del sistema.

Stéphane Chazelas
fuente
Creo que esta es efectivamente la única forma de hacerlo, pero sufre varios problemas, comenzando por el hecho de que utilizó el conocimiento previo para decidir sobre el rango de puntos de código legales. En teoría, al menos, si está utilizando un mapa de caracteres Unicode, las clases de caracteres son independientes del script (según el estándar Unicode, no las configuraciones regionales C), pero las "categorías generales" de Unicode tampoco son las mismas que las clases de caracteres C. Por cierto, los tipos i18n de glibc incluyen dos clases más de personajes: combiningy combining_level3(a saber iswctype(i, wctype("combining")))
rici
@rici, ver edición (y también de la pregunta).
Stéphane Chazelas