sábado, 25 de enero de 2014

extract-txt-bin-portions.pl: programa el Perl para encontrar cadenas UTF-8 en archivos binarios sin formato

# Lee un archivo mixto UTF-8 - no UTF-8
# output:
# Repeticiones de las líneas de texto:
# "bin: " byte-inicial-secuencia-no-UTF-8 byte-final-secuencia-no-UTF-8
# "txt: " byte-inicial-secuencia-UTF-8 byte-final-secuencia-UTF-8
# por orden de aparición de las secuencias (orden normal de los bytes dentro del archivo)

open my $fh, "<:raw", @ARGV[0] or die "cannot open < @ARGV[0]: $!";
my $i = 0;
my $ini_ciclo_bin = 1;

my $length = read $fh, my $byte, 1;
$i += $length;
# print "a ".unpack("B*", $byte) . "\n";

my $ini_bin;
my $fin_bin;
my $ini_txt;
my $fin_txt;


while($length) {
# ciclo bin
while($length and not unpack("B*", $byte) =~ /^(0|110|1110|11110|111110|1111110).*/) {
$length = read $fh, $byte, 1;
$i += $length;
# print "b ".unpack("B*", $byte) . "\n";
}

# ciclo txt
# inicialización secuencia txt
$ini_txt = $i;
$fin_txt = 0; # no def
my $s_val = 1;   # boolean TRUE, secuencia txt válida (secuencia de caracteres)
while($length and unpack("B*", $byte) =~ /^(0|110|1110|11110|111110|1111110).*/ and $s_val) {
# inicialización 1 caracter
my $l = index(unpack("B*", $byte),'0');
if($l eq 0) {
$l = 1;
}
my $ini_sec_bytes = $i;

# lectura 1 caracter
while($length and $i-$ini_sec_bytes+1 lt $l and $s_val) {
$length = read $fh, $byte, 1;
$i += $length;
# print "c ".unpack("B*", $byte) . "\n";
if(not unpack("B*", $byte) =~ /^10.*/) {
$s_val = 0;   # boolean FALSE
}
}

if($s_val and $i-$ini_sec_bytes+1 eq $l) {
$fin_txt = $i;   # def
$length = read $fh, $byte, 1;
$i += $length;
# print "d ".unpack("B*", $byte) . "\n";
}
}

# delimitar secuencias
if($fin_txt) {   # secuencia txt no vacía
if($ini_txt ne $ini_ciclo_bin) {   # secuencia binaria no vacía
$ini_bin = $ini_ciclo_bin;
$fin_bin = $ini_txt - 1;
print "bin: $ini_bin $fin_bin\n";
}
print "txt: $ini_txt $fin_txt\n";
$ini_ciclo_bin = $fin_txt + 1;   # para la vuelta entrante
}   # si no las condiciones continúan igual para reentrar el ciclo-bin
}
# delimitación final
if($i) {   # archivo no vacío
if($fin_txt and $fin_txt ne $i) {   # secuencia txt no vacía ($fin_txt) and quedaron ($i-$fin_txt) bytes bin al final del archivo
$ini_bin = $fin_txt + 1;
$fin_bin = $i;
print "bin: $ini_bin $fin_bin\n";
}
elsif($fin_txt eq 0) {   # último ciclo txt vacío ==> todo el ciclo corresponde a datos bin
$ini_bin = $ini_ciclo_bin;
$fin_bin = $i;
print "bin: $ini_bin $fin_bin\n";
}
# si no si ($fin_txt eq $i) el archivo termina justo en una secuencia txt, último OUTPUT emitido en ciclo txt
}

close $fh;

jueves, 9 de enero de 2014

Notas sobre dd

Interpretación de la pág. man dd:

Extracto: "Si la opción bs=bytes se dio, y  no  se  especificó  una  conversión aparte  de  sync,  noerror,  o  notrunc,  escribe  la cantidad de datos leídos (que podría ser menor de la pedida) en  un  bloque  de  salida separado."

Interpretación de "escribe la cantidad de datos leídos": significa que bs=x ==es equiv. a== ibs=x y obs=x, en las circunstancias descriptas.
Interpretación de "bloque  de  salida separado": Así como "sed" tiene un "pattern space" y "otro buffer auxiliar" en donde se escribe el input y se puede manipular antes de que salga el output, algo similar es la forma de trabajo de "dd" con su "bloque de salida" ("separado" = nuevo / a parte de cualquier bloque de salida existente).

Interpretación de la pág. man dd:
Extracto: "...poner bs no es equivalente a poner ibs y obs a su mismo valor, al menos si no se ha especificado una conversión aparte de sync, noerror y notrunc"

Corrección: "...poner bs *SÍ* es equivalente a poner ibs y obs a su mismo valor, al menos si no se ha especificado una conversión aparte de sync, noerror y notrunc"

Representación en lenguaje de conjuntos:
{conversiones suministradas como parám. "conv"} - {conversión: conversión E {sync, noerror, notrunc}}={} => bs=x == ibs=x y obs=x

Interpretación:
Si el usuario no suministra parám conv o suministra conv=Partes del conj. {sync,noerror,notrunc}, entonces => suministrar bs=x *SÍ* equivale funcionalmente a ibs=x y obs=x.

Notas sobre grep

grep puede buscar en archivos mixtos:
- porciones / secuencias de bytes dentro del archivo como "Codificación reconocida / texto" y
- porciones / secuencias de bytes dentro del archivo como "Codificación no reconocida / binario"
Output de grep: "Coincidencia en el archivo binario." en tal caso.



Notas sobre sed y Bash

- Los cuantificadores de expresiones regulares machean modo greedy por default (lo normal). Falta ver si hay un modificador para cambiar a modo non-greedy.

- Las REGEX pasadas a sed desde Bash y sin comillas ("" de Bash), esto es: s/REGEX_sin_comillas/.../, primero pasan por el intérprete de comandos y hay que respetar todas las reglas de escape de Bash.
Ej. 1:
- manual de sed: REGEX=\[ matches [
- desde la línea de comando (sin el auxilio de las comillas "" Bash): \\[ reduce a \[ machea [.
Ej. 2: 
- manual de sed: REGEX= (espacio en blanco) machea (a sí mismo, caracter de espacio en blanco)
- desde la línea de comando: \ (secuencia contrabarra espacio en blanco), reduce a (espacio en blanco) ==> entra dentro de sed ==> machea (a sí mismo, espacio en blanco).
Ej. 3:
- manual de sed: \( abre un capturing group
- desde la línea de comando: \\\( ==> \\ reduce a \, \( reduce a ( ==> entra en sed como \(

Con comillas "" de Bash, el intérprete Bash se comporta distinto:
Recapitulando ej. 1:
- desde la línea de comando (CON el auxilio de las comillas "" Bash): "\[" reduce a \[ machea [.
Recapitulando ej. 2:
- desde la línea de comando: " " (espacio en blanco dentro de comillas "" Bash), reduce a (espacio en blanco) ==> entra dentro de sed ==> machea (a sí mismo, espacio en blanco).

- sed: Para expresar literalmente el caracter / :
1. Escapizarlo (ejemplo invocando sed desde un intérprete que reduce \\ a \): sed -e s/\\//-/g
2. Cambiar el caracter delimitador de 's' (substitution): sed -e s|/|-|g
3. Usar [/] (bara entre corchetes). Explicación: dentro de [ y ] la mayoría de los caracteres especiales sed no necesitan ser escapeados: sed -e s/[/]/-/g. Atención: Bash intenta interpretar los [regex] como una regex corcheteada que denota parte del nombre de un archivo en el path constituido en el comando Bash (expresión-de-path-existente/ antepuesta a la expresión en cuestión, o el directorio actual si no se antepone nada), si no existen tales archivos, lo deja literal como [regex] (en general, echo /[r]aw no actúa igual que echo /[r]oot sobre la interpretación final de [r], en uno es literalmente [r], en el otro es r). En todo caso, 1) [/] es una secuencia que no matcheará contra ningún conjunto de nombres de archivos (en sí el caracter / representa el path raíz), por ende siempre se pasa como el literal [/], y b) en todo caso cualquier caracter es escapeable con \ , como caso particular \[/] reduce a [/].

- sed: varios buscar-reemplazar pueden concatenarse a través de varios -e s... : sed -e s/BUSCAR/REEMPLAZAR/ -e s/BUSCAR/REEMPLAZAR/ -e s...

martes, 17 de diciembre de 2013

Derreferenciamiento en Perl

Interpretación provisoria. No está basada en la teoría, sino en el caso empírico dado.
perl -w o bien use strict deberían quejarse de la mayoría de estos casos.


Código:
@ARY = ( [ qw(vilma palma vampiro) ], [ qw(zapallo zanahoria zapallito) ] );
print "@ARY\n";   # 1) Testigo
print ARY[1];   # 2) La expresión de derreferenciamiento requiere un caracter de tipo bien al principio. El subscript [] no es suficiente para que Perl sepa que nos estamos refiriendo al array ARY.
print "\n";
print "@{ARY[1]}\n";   # 3) Adentro de {}: no rige la regla del caso (2). Afuera de {}: derreferenciamento impropio de un escalar con @.
print "@ARY[1]\n"; # 4) El caracter de tipo tiene mayor precedencia que el subscript de matrices [] (perldsc sobre Perl 5.12.5, "Caveat on precedence"), excepto que la regla (2) tiene mayor prioridad. Mismo mecanismo que el caso (3).
print "@{@ARY[1]}\n"; # 5) Adentro de {}: Si hay un @ bien al principio, entonces la expresión se trata como en el caso (4), si no, es exactamente el caso (3). Afuera de {}: correcto derreferenciamiento de un array con @.
print "@{@{ARY[1]}}\n"; # 6) Expresión explícita del caso (5). Es exactamente el mismo caso.

Output:
1) ARRAY(0x989c818) ARRAY(0x98b7110)
2)
3) ARRAY(0x98b7110)
4) ARRAY(0x98b7110)
5) zapallo zanahoria zapallito
6) zapallo zanahoria zapallito


Código:
@ARY = (58, 90);
print ARY[1];   # 1) ARY: descriptor de archivo (en general nulo => el output no va a ningún lado). [1]: Arrayref anónimo con el elemento 1. Comparar por ejemplo con: print STDOUT"8";
print "\n";
print @{ARY[1]};   # 2) El contexto @{} aplicado a un escalar x, significa el array con el único elemento x.
print "\n";
print @ARY[1]; # El caracter de tipo tiene mayor precedencia que el subscript de matrices [] (perldsc sobre Perl 5.12.5, "Caveat on precedence").
print "\n";

print ( (\@ARY[1]) eq (\$ARY[1]) );   # 4
print "\n";
print ( (\@{ARY[1]}) eq (\@ARY[1]) );   # 5
print "\n";
print ( (\@{ARY[1]}) eq (\$ARY[1]) );   # 6
print "\n";


Output:
1)
2) 90
3) 90

4) 1
5) 1
6) 1



lunes, 16 de diciembre de 2013

Pruebas manejo de punteros y matrices en C

Código 1:
Compilador: gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), y a parte Visual C++ (ver nota a continuación)
Compilation flags -Wall -g


// matrix operations
#include <stdio.h>

void myPrint(int **table, int x, int y);

int main(int argc, char **argv) {

  int matrix[3][3];

  matrix[0][0] = 2;
  matrix[0][1] = 3;
  matrix[0][2] = 5;

  matrix[1][0] = 1;
  matrix[1][1] = 4;
  matrix[1][2] = 16;

  matrix[2][0] = 9;
  matrix[2][1] = 28;
  matrix[2][2] = 14;

  myPrint(matrix, 2, 1);
  return 0;
}

void myPrint(int **table, int x, int y) {

  printf("%d\n", table[x][y]);
}


Compilación:
warning: passing argument 1 of ‘myPrint’ from incompatible pointer type
note: expected ‘int **’ but argument is of type ‘int (*)[3]’

Ejecución:
Fallo de segmentación





Nota: en Visual C++ la validación de tipos es más estricta y hay que agregar
- myPrint((int **)matrix, 2, 1);   // conciliación de tipos entre parámetros formales y reales
Porque si no da error de compilación, que viene a ser justamente lo mismo que en gcc es warning. 

Código 2:
Compilador Visual C++

// matrix operations
#include <stdio.h>

void myPrint(int (*table)[3], int x, int y);

int main(int argc, char **argv) {

  int matrix[3][3];

  matrix[0][0] = 2;
  matrix[0][1] = 3;
  matrix[0][2] = 5;

  matrix[1][0] = 1;
  matrix[1][1] = 4;
  matrix[1][2] = 16;

  matrix[2][0] = 9;
  matrix[2][1] = 28;
  matrix[2][2] = 14;
  myPrint(matrix, 2, 1);
  return 0;
}

void myPrint(int (*table)[3], int x, int y) {

  printf("%d\n", table[x][y]);
}

Compilación: sin warnings
Ejecución:
28

Código 3:
Compilador: Por separado gcc y Visual C++ (ver nota abajo)
// matrix operations
#include
<stdio.h>
#include <stdlib.h>

void myPrint(int **table, int x, int y);

int main(int argc, char **argv) {

  int *matrix[3];

  matrix[0] = malloc(3*sizeof(int));
  matrix[0][0] = 2;
  matrix[0][1] = 3;
  matrix[0][2] = 5;

  matrix[1] = malloc(3*sizeof(int));
  matrix[1][0] = 1;
  matrix[1][1] = 4;
  matrix[1][2] = 16;

  matrix[2] = malloc(3*sizeof(int));
  matrix[2][0] = 9;
  matrix[2][1] = 28;
  matrix[2][2] = 14;

  myPrint(&matrix, 2, 1);
  return 0;
}


void myPrint(int **table, int x, int y) {

  printf("%d\n", table[x][y]);
}


Compilación: warning: passing argument 1 of ‘myPrint’ from incompatible pointer type
note: expected ‘int **’ but argument is of type ‘int * (*)[3]’

Ejecución:
28



Nota: en Visual C++ para Windows la validación de tipos es más estricta y hace falta las siguientes adaptaciones:

- (int*)malloc(3*sizeof(int)); // explícitamente decir el tipo de puntero con el que se va a usar lo que devuelva malloc.

- myPrint((int **)&matrix, 2, 1); // conciliar tipos entre parámetros formales y actuales Porque si no da error de compilación, que viene a ser justamente lo mismo que en gcc es warning.