Heirloom Toolchest tr: error (s) tratando de eliminar el complemento de un set que contiene un carácter multibyte?

Intento usar el command tr de Heirloom Toolchest para superar una limitación actual de la implementación de coreutils , para poder "bombear" (con las opciones -dc ) caracteres multibyte de un generador "aleatorio" ( / dev / urandom ) a la terminal. Es de destacar el hecho de que esto ha sido comstackdo desde la fuente en Archbang después de no haberlo hecho usando las versiones de AUR.

Para simplificar esto, vamos a elegir un personaje (☠) y descubrir su valor octal porque así es como debe expressse para el tr herramientas:

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

Hay una diferencia en cómo se expresa el valor octal en Bash con el echo incorporado ( 0nnn ) comparado con el toolchest tr aquí ( nnn ):

El carácter '\' seguido de 1, 2 o 3 dígitos octales representa el carácter cuyo código de bytes está dado por esos dígitos. Los caracteres multibyte se pueden especificar como una secuencia de octal bytes.

Vamos a intentarlo. La opción -dc simplemente elimina el complemento de SET1. Usted especifica un único set y cualquier elemento de la input estándar que no contenga un elemento del set se descarta:

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

Ahora estos:

 echo -e '\0342\0230\0240' | /usr/5bin/tr -dc '\342\230\240' echo -e '☠' | /usr/5bin/tr -dc '☠' 

que ambos deberían imprimir uno (1) ☠, o, en última instancia, los siguientes (muchos más caracteres) producen el mismo error:

 /usr/5bin/tr -dc '\342\230\240' < /dev/urandom *** Error in `/usr/5bin/tr': double free or corruption (!prev): 0x0000000000d24420 *** 

En realidad, cada vez que la input y SET1 contienen el carácter elegido, aparece el error con -dc . El comportamiento también es el mismo en las versiones SysV 3rd, 4th, Posix, Posix2001 o ucb (BSD) del command proporcionado en el Toolchest. A veces, como en el caso de tr -dc '1' < /dev/urandom , obtengo una segfault propia o algunas pocas líneas de salida seguidas con esto:

 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] 

¿Eso es todo lo que sugiere un error de compilation de mi parte o no lo estoy usando correctamente?


Con el parche aportado tenemos:

 echo -e '\0342\0230\0240' | /home/me/bin/trsc -dc '\342\230\240' echo -e '☠' | /home/me/bin/trsc -dc '☠' ☠ 

Como deberíamos !! Pero:

 /home/me/bin/trsc -dc '\342\230\240' < /dev/urandom 

sigue siendo un misterio ya que el personaje elegido no está en la salida …

Lo he visto antes Un insecto. Tratar:

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

(Eso fue por una mirada rápida hace unos meses, mientras que este parche te pondrá en marcha, no puedo garantizar que sea correcto. Aplicar con patch -l ).

Ahora también tenga en count que /dev/urandom proporciona una secuencia de bytes . En UTF-8, no todas las secuencias de bytes se asignan a caracteres válidos. Por ejemplo, 0x41 0x81 0x41 no es válido porque 0x81 es >= 0x80, por lo que solo puede ocurrir en una secuencia de 2 o más en bytes de 0x80.

Un byte no válido, porque no está en el set de caracteres que es el complemento de ☠, no será borrado por tr .

Mejor sería probablemente:

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

ucs-2 son los caracteres U + 0000 a U + FFFF codificados en 2 bytes por carácter, /dev/urandom parece más a una secuencia de caracteres ucs-2. (Sin embargo, nos faltan los caracteres U + 10000 a U + 10FFFF).

Pero eso aún includeía el range de par suplente D800..DFFF que mbrtowc(3) se bloqueará (al less con mi versión de libc).

Esos puntos de código están reservados para el propósito de la encoding UTF-16. d800dc00, por ejemplo, es la encoding UTF-16BE de U + 10000, pero no hay un carácter U + D800 o U + DC00. La encoding UTF-8 de esos tampoco tiene sentido como personaje (incluso si es adyacente).

Entonces necesitarías excluirlos primero:

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

Si el objective es get una secuencia de caracteres Unicode aleatorios codificados en UTF-8, lo mejor sería get un punto de código aleatorio en el range permitido (0..0xd7ff, 0xf000..0x10ffff) y convertirlo a UTF-8. Si desea basarlo en /dev/urandom , puede usar 3 bytes (24 bits) para cada punto de código:

 perl -ne 'BEGIN{$/=\3;binmode STDOUT,":utf8"} $c = unpack("N","\0$_") * 0x10F800 >> 24; $c+=0x800 if $c >= 0xd800; do {no warnings "utf8"; print chr($c)}' < /dev/urandom | tr -cd ☠