Comando para recuperar a lista de caracteres em uma determinada classe de caracteres no código do idioma atual

18

O que poderia ser uma maneira de recuperar uma lista de todos os caracteres em uma determinada classe de personagem (como blank, alpha, digit...) no local atual.

Por exemplo,

LC_ALL=en_GB.UTF-8 that-command blank

idealmente, no meu sistema Debian, exibiria 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

E na localidade C poderia exibir algo como:

09 U+0009 HORIZONTAL TAB
20 U+0020 SPACE

Ou seja, a representação do caractere no código do idioma em termos de matrizes de bytes (como UTF-8 no primeiro exemplo e byte único no segundo), o ponto de código do caractere Unicode equivalente e uma descrição.

Contexto

(editar) Agora que a vulnerabilidade foi corrigida e divulgada há muito tempo, posso adicionar um pouco de contexto.

Fiz essa pergunta no momento em que estava investigando o CVE 2014-0475 . glibctinha um bug, pois permitia que o usuário usasse códigos de LC_ALL=../../../../tmp/evil-localeidioma assim resolvidos em relação ao caminho de pesquisa de código de idioma padrão do sistema e, portanto, permitia usar qualquer arquivo como definição de código de idioma.

Eu poderia criar uma localidade não autorizada, por exemplo, com um único byte por conjunto de caracteres de caracteres, onde a maioria dos caracteres s, exceto halguns, eram considerados espaços em branco e isso seria bashexecutado shao analisar um /etc/bash.bashrcarquivo Debian típico (e que poderia ser usado para obter acesso ao shell em um gito servidor de hospedagem, por exemplo, fornecido bashé usado como o shell de login do gitusuário do servidor e que o sshservidor aceita LC_*/ LANGvariáveis ​​e que o invasor pode fazer upload de arquivos para o servidor).

Agora, se eu encontrasse uma LC_CTYPE(definição compilada de localidade) em /tmp/evil, como descobriria que era desonesta e de que maneira.

Portanto, meu objetivo é descompilar essas definições de localidade e, se não, pelo menos saber qual caractere (junto com sua codificação) está em uma determinada classe de caracteres.

Então, com isso em mente:

  • Soluções que analisam os arquivos de origem para o código do idioma (as definições de código do idioma como as /usr/share/i18n/localedo Debian) não são úteis no meu caso.
  • As propriedades de caracteres Unicode são irrelevantes. Eu só me importo com o que o local diz. Em um sistema Debian, mesmo entre dois locais do sistema UTF-8, sem falar nos invasores, a lista de caracteres em uma classe pode ser diferente.
  • Ferramentas como recode, pythonou perlque faça o byte / multi-byte de / para a conversão de caracteres não podem ser usados como eles podem (e na prática não) fazer a conversão de uma forma diferente da localidade.
Stéphane Chazelas
fonte
Para a maioria das localidades, em última análise, vem do material LC_CTYPE em (com glibc) /usr/share/i18n/locales/i18n... o qual, é claro, vem em grande parte do banco de dados de caracteres Unicode. Claro, seria bom ter um comando
derobert
@derobert, sim, enquanto locale(pelo menos a GNU) recupera muitas das informações armazenadas em muitas das categorias, coisas que não são as mais importantes em LC_CTYPE e LC_COLLATE. Gostaria de saber se existe uma API oculta para recuperar essas informações ou descompilar as informações de localidade.
Stéphane Chazelas
Sim - você pode analisar essas informações - eu finalmente resolvi encerrar minha edição. Existem vários comandos que você provavelmente já instalou - pelo menos eu fiz e nem sabia sobre eles. Espero que ajude. Especificamente recodee uconvpode dar a você o que você diz estar procurando. Possivelmente até mesmo luite odeu acho ...
mikeserv
Isso é muito bom! Isso significa que você não precisa perl, eu acho.
mikeserv
Eu pareço ser capaz de extrair basicamente a minha charset partir LC_CTYPEcom apenas od -A n -t c <LC_CTYPE | tsortProvavelmente você já tentou fazê-lo já, mas eu nunca tinha ouvido falar dele antes e eu estava lendo infoe lembrou-me deste - e parece trabalho. Há também, ptxmas acho que é menos relevante. De qualquer forma, se você ainda não experimentou e decide fazê-lo - aviso justo -, é preciso um pouco de paciência. Você
usar o seguinte

Respostas:

7

POSSÍVEL SOLUÇÃO FINAL

Então, eu peguei todas as informações abaixo e criei isso:

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 :

Eu uso odcomo o filtro final acima como preferência e porque sei que não vou trabalhar com caracteres de vários bytes, com os quais ele não lidará corretamente. recode u2..dumpambos irão gerar uma saída mais semelhante à especificada na pergunta e manipularão caracteres largos corretamente.

RESULTADO

        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 DO PROGRAMADOR

Como demonstro abaixo, recodevocê fornecerá seu mapa de caracteres completo. De acordo com o manual, ele faz isso de acordo com o valor atual da DEFAULT_CHARSETvariável de ambiente ou, na sua falta, opera exatamente como você especifica:

Quando um nome de conjunto de caracteres é omitido ou deixado em branco, o valor da DEFAULT_CHARSETvariável no ambiente é usado. Se essa variável não estiver definida, a recodebiblioteca usará a codificação do código do idioma atual. Em sistemas compatíveis com POSIX , isso depende do primeiro valor não vazio entre as variáveis ​​de ambiente LC_ALL, LC_CTYPE, LANGe pode ser determinado através do comandolocale charmap.

Também digno de nota recodeé que é uma API :

O programa chamado recodeé apenas uma aplicação de sua biblioteca de recodificação. A biblioteca de recodificação está disponível separadamente para outros programas em C. Uma boa maneira de adquirir alguma familiaridade com a biblioteca de recodificação é familiarizando-se com o recodepróprio programa.

Para usar a biblioteca de recodificação após a instalação, um programa C precisa ter uma linha:

#include <recode.h>

Para comparação internacionalmente amigável de strings Os padrões POSIXe Cdefinem a strcoll()função:

A strcoll()função deve comparar a string apontada por s1com a string apontada por s2, ambas interpretadas conforme apropriado para a categoria LC_COLLATE da localidade atual.

A strcoll()função não deve alterar a configuração de errno se for bem-sucedida.

Como nenhum valor de retorno é reservado para indicar um erro, um aplicativo que deseje verificar se há situações de erro deve definir errno como 0, depois ligar strcoll()e verificar errno.

Aqui está um exemplo localizado separadamente de seu uso:

#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);
}

Com relação às POSIXclasses de caracteres, você já observou que usou a CAPI para encontrá-las. Para caracteres e classes unicode, você pode usar charset recode's dump-with-names para obter a saída desejada. De seu manual novamente :

Por exemplo, o comando recode l2..full < inputimplica uma conversão necessária do Latin-2 para o UCS-2, pois o dump-with-names é conectado apenas a partir do UCS-2. Nesses casos, recodenão exibe os códigos Latin-2 originais no dump, apenas os valores UCS-2 correspondentes . Para dar um exemplo mais simples, o comando

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

produz a seguinte saída:

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)

O comentário descritivo é fornecido em inglês e ASCII; no entanto, se a descrição em inglês não estiver disponível, mas houver uma em francês, a descrição em francês será fornecida usando o Latin-1. No entanto, se a variável de ambiente LANGUAGEou LANGcomeçar com as letras fr , a preferência de listagem será em francês quando as duas descrições estiverem disponíveis.

Usando uma sintaxe semelhante à acima combinada com seu conjunto de dados de teste incluído, posso obter meu próprio mapa de caracteres com:

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

RESULTADO

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)

Mas para caracteres comuns, recodeaparentemente não é necessário. Isso deve fornecer chars nomeados para tudo no conjunto de 128 bytes:

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

RESULTADO

 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

Obviamente, apenas 128 bytes são representados, mas isso ocorre porque meu local, utf-8 charmaps ou não, usa o conjunto de caracteres ASCII e nada mais. Então é tudo que eu recebo. Se eu o executasse sem luitfiltrá-lo, odo reverteria e imprimiria o mesmo mapa novamente até\0400.

Existem dois grandes problemas com o método acima. Primeiro, há a ordem de intercalação do sistema - para localidades não ASCII, os valores de mordida para os conjuntos de caracteres não são simplesmente in sequêncios, o que, como eu acho, é provavelmente o núcleo do problema que você está tentando resolver.

Bem, a tr's manpágina do GNU afirma que expandirá as [:upper:] [:lower:]classes em ordem - mas isso não é muito.

Eu imagino que alguma solução pesada possa ser implementada, sortmas isso seria uma ferramenta bastante difícil para uma API de programação de back-end.

recodefará isso corretamente, mas você não parecia muito apaixonado pelo programa outro dia. Talvez as edições de hoje tenham uma luz mais amigável ou talvez não.

O GNU também oferece a gettextbiblioteca de funções e parece capaz de resolver esse problema pelo menos no LC_MESSAGEScontexto:

- Função: char * bind_textdomain_codeset( const char *domainname, const char *codeset)

A bind_textdomain_codesetfunção pode ser usada para especificar o conjunto de caracteres de saída para catálogos de mensagens para o domínio domainname . O argumento do conjunto de códigos deve ser um nome de conjunto de códigos válido que possa ser usado para a função iconv_open ou um ponteiro nulo.

Se o parâmetro codeset for o ponteiro nulo, bind_textdomain_codeset retornará o conjunto de códigos atualmente selecionado para o domínio com o nome domainname . Retorna NULL se nenhum conjunto de códigos ainda foi selecionado.

A bind_textdomain_codesetfunção pode ser usada várias vezes. Se usada várias vezes com o mesmo argumento domainname, a chamada posterior substitui as configurações feitas pela anterior.

A bind_textdomain_codesetfunção retorna um ponteiro para uma seqüência de caracteres que contém o nome do conjunto de códigos selecionado. A sequência é alocada internamente na função e não deve ser alterada pelo usuário. Se o sistema ficou fora do núcleo durante a execução de bind_textdomain_codeset, o valor de retorno é NULL e a variável global errno é configurada de acordo.

Você também pode usar categorias de caracteres Unicode nativas , que são independentes do idioma e renunciam completamente às classes POSIX, ou talvez chamar o primeiro para fornecer informações suficientes para definir o último.

Além de complicações, o Unicode também traz novas possibilidades. Uma é que cada caractere Unicode pertence a uma determinada categoria. Você pode combinar um único caractere pertencente à categoria "letra" com \p{L}. Você pode combinar um único caractere não pertencente a essa categoria \P{L}.

Novamente, "caractere" realmente significa "ponto de código Unicode". \p{L}corresponde a um único ponto de código na categoria "letra". Se a sua sequência de entrada estiver à codificada como U+0061 U+0300, ela corresponderá asem o acento. Se a entrada estiver àcodificada como U+00E0, ela combina àcom o acento. O motivo é que o código aponta U+0061 (a)e U+00E0 (à)está na categoria "letra", enquanto U+0300está na categoria "marca".

Agora você deve entender por que \P{M}\p{M}*+o equivalente a \X. \P{M}corresponde a um ponto de código que não é uma marca combinada, enquanto \p{M}*+ corresponde a zero ou mais pontos de código que estão combinando marcas. Para corresponder a uma letra incluindo quaisquer sinais diacríticos, use \p{L}\p{M}*+. Esse último regex sempre corresponderá à, independentemente de como é codificado. O quantificador possessivo garante que o retorno não \P{M}\p{M}*+corresponda a uma não marca sem as marcas combinadas a seguir, o que \X nunca faria.

O mesmo site que forneceu as informações acima também discute Tcla implementação de regex compatível com POSIX, que pode ser outra maneira de atingir seu objetivo.

E, por último, entre as soluções, sugerirei que você possa interrogar o LC_COLLATEarquivo em si para obter o mapa de caracteres completo e em ordem do sistema. Isso pode não parecer fácil, mas obtive algum sucesso com o seguinte, depois de compilá-lo localedefcomo demonstrado abaixo:

<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

É, reconhecidamente, atualmente falho, mas espero que demonstre a possibilidade pelo menos.

NO PRIMEIRO BLUSH

strings $_/en_GB

#OUTPUT

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

Realmente não parecia muito, mas então comecei a perceber copycomandos em toda a lista. O arquivo acima parece copyem "en_US", por exemplo, e outro realmente grande que parece que todos compartilham até certo ponto iso_14651_t1_common.

É bem grande:

strings $_ | wc -c

#OUTPUT
431545

Aqui está a introdução para /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>;

...

Você pode fazer grepisso, é claro, mas você pode apenas:

recode -lf gb

Em vez de. Você obteria algo como isto:

Dec  Oct Hex   UCS2  Mne  BS_4730

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

... E MAIS

Também há luitum ptydispositivo de tradução UTF-8 terminal, acho que funciona como intermediário para XTerms sem suporte a UTF-8. Ele lida com muitas opções - como registrar todos os bytes convertidos em um arquivo ou -ccomo um |pipefiltro simples .

Eu nunca percebi que havia tanto nisso - os locais e os mapas de personagens e tudo isso. Aparentemente, isso é um grande negócio, mas acho que tudo acontece nos bastidores. Existem - pelo menos no meu sistema - algumas centenas de man 3resultados relacionados para pesquisas relacionadas ao código do idioma.

E também há:

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
...

Isso continuará por muito tempo.

As Xlibfunções lidam com isso o tempo todo - luitfaz parte desse pacote.

As Tcl_uni...funções também podem ser úteis.

apenas um pouco de <tab>conclusão e manpesquisas e aprendi bastante sobre esse assunto.

Com localedef- você pode compilar localesno seu I18Ndiretório. A saída é descolada e não é extraordinariamente útil - não é como a de charmapstodo -, mas você pode obter o formato bruto exatamente como especificou acima, como eu fiz:

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

Então odvocê pode ler - bytes e strings:

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...<
...

Embora esteja muito longe de ganhar um concurso de beleza, essa é uma saída utilizável. E odé tão configurável quanto você deseja que seja, é claro.

Acho que também esqueci disso:

    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

Provavelmente me esqueci deles porque não consegui fazê-los funcionar. Eu nunca uso Perle não sei como carregar um módulo corretamente, eu acho. Mas as manpáginas parecem bem legais. De qualquer forma, algo me diz que você encontrará chamar um módulo Perl pelo menos um pouco menos difícil do que eu. E, novamente, eles já estavam no meu computador - e eu nunca uso o Perl. Também há alguns I18Nque eu percorri melancolicamente sabendo muito bem que também não os faria funcionar.

mikeserv
fonte
11
Essas são informações muito agradáveis ​​e úteis, mas que fornecem informações sobre os arquivos de origem (in i18n) que podem ou não ter sido usados ​​para gerar o código do idioma que estou usando atualmente. As informações da localidade provavelmente são provenientes de /usr/lib/locale/locale-archiveou /some/dir/LC_CTYPE, e essa é a parte relevante para a minha localidade armazenada nos arquivos que busco.
Stéphane Chazelas
@StephaneChezales - apenas extraia LC_STUFFo arquivo do arquivo localedef- ele também faz isso. Eu posso demonstrar isso também, eu acho. Você também pode ver isso e praticamente tudo o resto com stringsou odou qualquer outro. Eu fiz de qualquer maneira. Mas pela maneira - o charmaps são o youre local usando atualmente - e localedefapresentará um relatório sobre isso também. Também é o que recodefaz também.
Mikeerv
Você está basicamente dizendo que podemos fazer manualmente o que as bibliotecas do sistema consultam informações da classe de caracteres, mas serão necessárias milhares de linhas de código para fazer isso de maneira confiável e o resultado será específico do sistema. (analisando o ambiente da mesma maneira que a biblioteca do sistema (LOCPATH, LANG, LANGUAGE, LC_CTYPE ..., identifica onde procurar os dados, extrai-os ...). Não consigo ver como extrair itens do arquivo morto. com localedef embora
Stéphane Chazelas
@StephaneChazelas - Eu não sugiro que você fazê-lo com a mão - eu sugiro que você fazê-lo com um computador - usando binários do sistema, tais como od, recode, uconve o resto. Mas foi um erro meu - não é isso localedefque extrai, é recodeessa vontade. Você precisa dar uma olhada info recode- e além do recodecomando table que apareço, existe praticamente a mesma coisa - e ele lidará com as coisas da mesma maneira, eu acho. Ele não apenas tira seu conjunto de caracteres do nada. De qualquer forma, eu tinha grandes esperanças para esses perlmódulos - você experimentou?
mikeserv
11
Se houver uma API para recuperar a lista de caracteres em uma determinada classe de caracteres no código de idioma atual, é exatamente isso que estou procurando. Se você puder demonstrar como fazer isso, aceitarei a resposta. A única coisa em que pude pensar (e como obtive o "resultado esperado" na minha pergunta) é usar iswblank(3)para todos os valores possíveis de caracteres.
Stéphane Chazelas
1

Nos sistemas GNU, FreeBSD ou Solaris, pelo menos, essa abordagem de força 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;
}

Enquanto por C / POSIX, wchar_té um tipo opaco que não tem relação com Unicode e é garantido apenas para cobrir todos os caracteres suportados pelo código do idioma do sistema, na prática, na maioria dos sistemas que suportam Unicode, os valores correspondem aos pontos de código Unicode e as definições de localidade são baseadas em Unicode.

O Unicode deve ser um superconjunto de todos os conjuntos de caracteres conhecidos; portanto, fazer um loop sobre todos os pontos de código válidos no Unicode (0 a 0xD7FF e 0xE000 a 0x10FFFF) deve listar pelo menos todos os caracteres suportados por um determinado conjunto de caracteres.

Aqui, estamos usando a API padrão do código de idioma do sistema para verificar quais são de um determinado tipo e convertê-lo em seu formato codificado na codificação do código de idioma. Usamos perle seu charnamesmódulo apenas para obter o nome de um determinado ponto de código Unicode.

Em localidades que usam codificações com estado como ISO-2022-JP, garantimos que o formulário codificado seja exibido a partir de um estado inicial padrão.

Eu não encontrei um sistema que tivesse instalado localidades com uma codificação de caracteres com estado, mas pelo menos nos sistemas GNU, é possível gerar alguns para que um local não autorizado possa ser criado (e pelo menos as ferramentas GNU não funcionam corretamente naquelas localidades). Por exemplo, com um código de idioma personalizado que usa ISO-2022-JP com um código de ja_JPidioma normal , recebo:

$ 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

Compare com:

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

No ISO-2022-JP, a 1B 24 42sequência ( \e$B) muda de ASCII para um estado em que os caracteres são expressos como 2 (7 bits) bytes (aqui 21 21 para esse ESPAÇO IDEOGRÁFICO). Enquanto no EUCJP, são os mesmos bytes, mas a troca de estado é feita com o lançamento do oitavo bit ( A1 = 21 | 0x80), o que o torna mais sem estado.

Isso significa que nessas codificações com estado, existem várias maneiras de escrever um determinado caractere (por exemplo, inserindo várias daquelas seqüências de comutação de estado ), e a sequência mostrada pelo código acima é apenas uma delas (a canônica de um inicial). Estado padrão).

Enquanto para uma localidade normal, caracteres não podem estar fora 0..0xD7FF, 0xE000..0x10FFFF, para uma desonestos locale, qualquer caractere no intervalo suportado pelo wchar_t pode ser. Por exemplo, eu poderia criar um local onde caracteres U + DCBA ou U + 12345678 (ou seriam caracteres se permitidos) são espaços em branco . É por isso que você deseja compilar esse código -D SUPPORT_ROGUE_LOCALESpara cobri-lo, embora isso signifique que leva muito mais tempo para verificar a lista inteira.

Eu não poderia usar a solução da @ mikeserv, pois recodeusa suas próprias conversões, não é mais mantida e suporta apenas caracteres Unicode de até 0xFFFF, e o GNU trpelo menos não funciona com caracteres de vários bytes.

Eu não poderia usar o @ ChrisDown's, pois pythonnão possui interfaces para as classes de caracteres POSIX.

Eu tentei o Perl, mas é falso os pontos de código entre 128 e 255 para locais de vários bytes que não sejam o UTF-8 e não usa as bibliotecas de conversão do sistema.

Stéphane Chazelas
fonte
Eu acho que essa é efetivamente a única maneira de fazer isso, mas sofre de vários problemas, começando com o fato de você ter usado conhecimento prévio para decidir sobre a variedade de pontos de código legais. Em teoria, pelo menos, se você estiver usando um mapa de caracteres Unicode, as classes de caracteres são independentes do script (de acordo com o padrão Unicode, não as localidades C), mas as "categorias gerais" Unicode também não são as mesmas que as classes de caracteres C. BTW, ctypes i18n do glibc incluir mais duas classes de personagens: combininge combining_level3(viz. iswctype(i, wctype("combining")))
RICI
@rici, veja editar (e também da pergunta).
Stéphane Chazelas