Heirloom Toolchest tr: erro (s) tentando excluir o complemento de um conjunto contendo um caractere multibyte?

6

Estou tentando usar o comando tr do Hierkoom Toolchest para superar uma limitação atual da implementação de coreutils , de modo a ser capaz de "bombear" (com as opções -dc ) caracteres multibyte de um gerador "aleatório" ( / dev / urandom ) para o terminal. É digno de nota o fato de isso ter sido compilado a partir da fonte em Archbang depois de ter deixado de fazê-lo usando o AUR versão (s) .

Para simplificar isso, vamos escolher um caractere (☠) e descobrir seu valor octal porque é assim que ele deve ser expresso para toolchest tr :

echo '☠' | hexdump -b            # -b for octal
0000000 342 230 240 012                                                
0000004
echo -e '423040'        # uses the "0nnn" format, make sure it prints
☠

Existe uma diferença em como o valor octal é expresso em Bash com o echo construído ( 0nnn ) comparado com o toolchest tr here ( nnn ):

The character '\' followed by 1, 2 or 3 octal digits stands for the character whose byte code is given by those digits. Multibyte characters can be specified as a sequence of octal bytes.

Vamos tentar. A opção -dc simplesmente exclui o complemento de SET1. Você especifica um único conjunto e qualquer coisa da entrada padrão que não contém um elemento do conjunto é descartada:

echo '012345' | /usr/5bin/tr -dc '456'   #sanity check
45                                       #all good

Agora, estes:

echo -e '423040' | /usr/5bin/tr -dc '200'
echo -e '☠' | /usr/5bin/tr -dc '☠'

que deve imprimir um (1) ☠ ou, finalmente, os seguintes caracteres (muito mais) produzem o mesmo erro:

/usr/5bin/tr -dc '200' < /dev/urandom

*** Error in '/usr/5bin/tr': double free or corruption (!prev): 0x0000000000d24420 ***

Na verdade, toda vez que a entrada e SET1 contêm o caractere escolhido, o erro aparece com -dc . O comportamento também é o mesmo em todas as versões do SysV 3rd, 4th, Posix, Posix2001 ou ucb (BSD) do comando fornecido no toolchest. Às vezes, como no caso de tr -dc '1' < /dev/urandom , recebo um segfault propriamente dito ou algumas poucas linhas de saída seguidas com isto:

Error in '/usr/5bin/tr': realloc(): invalid pointer: 0x00007f93ee284010 ***
======= Backtrace: =========
/usr/lib/libc.so.6(+0x73f8e)[0x7f93ee338f8e]
/usr/lib/libc.so.6(+0x7988e)[0x7f93ee33e88e]
/usr/lib/libc.so.6(realloc+0x1c8)[0x7f93ee342918]
/usr/5bin/tr[0x401a74]
/usr/5bin/tr[0x400e93]
/usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7f93ee2e5000]
/usr/5bin/tr[0x400f63]
======= Memory map: ========
00400000-00403000 r-xp 00000000 08:21 1579535                            /usr/5bin/tr
00602000-00603000 rw-p 00002000 08:21 1579535                            /usr/5bin/tr
0067a000-006bc000 rw-p 00000000 00:00 0                                  [heap]
7f93edc6e000-7f93edc84000 r-xp 00000000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93edc84000-7f93ede83000 ---p 00016000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93ede83000-7f93ede84000 rw-p 00015000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93ede84000-7f93ee2c5000 rw-p 00000000 00:00 0 
7f93ee2c5000-7f93ee469000 r-xp 00000000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee469000-7f93ee669000 ---p 001a4000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee669000-7f93ee66d000 r--p 001a4000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee66d000-7f93ee66f000 rw-p 001a8000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee66f000-7f93ee673000 rw-p 00000000 00:00 0 
7f93ee673000-7f93ee694000 r-xp 00000000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee6eb000-7f93ee874000 r--p 00000000 08:21 1448356                    /usr/lib/locale/locale-archive
7f93ee874000-7f93ee877000 rw-p 00000000 00:00 0 
7f93ee891000-7f93ee893000 rw-p 00000000 00:00 0 
7f93ee893000-7f93ee894000 r--p 00020000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee894000-7f93ee895000 rw-p 00021000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee895000-7f93ee896000 rw-p 00000000 00:00 0 
7fffed79c000-7fffed7bd000 rw-p 00000000 00:00 0                          [stack]
7fffed7e9000-7fffed7eb000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

É tudo isso sugerindo um erro de compilação da minha parte ou não estou usando isso corretamente?

Com o patch enviado , temos:

echo -e '423040' | /home/me/bin/trsc -dc '200'
echo -e '☠' | /home/me/bin/trsc -dc '☠'
☠

Como deveríamos !! Mas:

/home/me/bin/trsc -dc '200' < /dev/urandom

ainda permanece um mistério já que o personagem escolhido não está na saída ...

    
por Community 30.05.2014 / 11:27

1 resposta

7

Eu já vi isso antes. Um inseto. Experimente:

--- tr.c        6 Sep 2005 23:04:11 -0000       1.10
+++ tr.c        30 May 2014 09:46:33 -0000
@@ -291,7 +291,6 @@
                if(c<ccnt) code[c] = d;
                if(d<ccnt && sflag) squeez[d] = 1;
        }
-       free(vect);
        while((d = next(&string2)) != NIL) {
                if(sflag) squeez[d] = 1;
                if(string2.max==NIL && (string2.p==NULL || *string2.p==0))

(que foi de uma rápida olhada alguns meses atrás, enquanto esse patch vai te dar certo, eu não posso garantir que está certo. Aplique com patch -l ).

Agora observe também que /dev/urandom fornece um fluxo de bytes . Em UTF-8, nem todas as sequências de bytes são mapeadas para caracteres válidos. Por exemplo, 0x41 0x81 0x41 não é válido porque 0x81 é >= 0x80, portanto, só pode ocorrer em uma sequência de 2 ou mais sobre 0x80 bytes.

Um byte inválido, porque não está no conjunto de caracteres que é o complemento de ☠, não será excluído por tr .

Melhor seria provavelmente:

recode ucs-2..u8 < /dev/urandom | tr -cd ☠

ucs-2 sendo os caracteres U + 0000 a U + FFFF codificados em 2 bytes por caractere, /dev/urandom se parece mais com um fluxo de caracteres ucs-2. (no entanto, estamos perdendo os caracteres U + 10000 para U + 10FFFF).

Mas isso ainda inclui o intervalo de pares substitutos D800..DFFF qual mbrtowc(3) vai sufocar (pelo menos com a minha versão da libc).

Esses pontos de código são reservados para o propósito de codificação UTF-16. O d800dc00, por exemplo, é a codificação UTF-16BE do U + 10000, mas não há U + D800 ou U + DC00. A codificação UTF-8 não faz sentido como um caractere (mesmo se adjacente).

Então você precisa excluí-los primeiro:

perl -ne 'BEGIN{$/=;binmode STDOUT,":utf8"}
          $c = unpack("n",$_); if ($c < 0xd800 || $c > 0xdfff) {
            no warnings "utf8"; print chr($c)
          }' < /dev/urandom | tr -cd ☠

Se o objetivo for obter um fluxo de caracteres Unicode aleatórios codificados em UTF-8, é melhor obter um ponto de código aleatório no intervalo permitido (0.xx7ff, 0xf000..0x10ffff) e convertê-lo em UTF. -8. Se você quiser basear em /dev/urandom , você pode usar 3 bytes (24 bits) para cada ponto de código:

perl -ne 'BEGIN{$/=;binmode STDOUT,":utf8"}
          $c = unpack("N","
--- tr.c        6 Sep 2005 23:04:11 -0000       1.10
+++ tr.c        30 May 2014 09:46:33 -0000
@@ -291,7 +291,6 @@
                if(c<ccnt) code[c] = d;
                if(d<ccnt && sflag) squeez[d] = 1;
        }
-       free(vect);
        while((d = next(&string2)) != NIL) {
                if(sflag) squeez[d] = 1;
                if(string2.max==NIL && (string2.p==NULL || *string2.p==0))
$_") * 0x10F800 >> 24; $c+=0x800 if $c >= 0xd800; do {no warnings "utf8"; print chr($c)}' < /dev/urandom | tr -cd ☠
    
por 30.05.2014 / 11:47

Tags