The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NOMBRE

perlhacktut - Tutorial de creación de un parche sencillo de código C

DESCRIPCIÓN

Este documento describe la creación de un parche sencillo.

Si todavía no ha leído perlhack, es lo primero que debe hacer. También debería leer perlsource.

Por último, consulte perlhacktips.

EJEMPLO DE PARCHE SENCILLO

Vamos a crear un parche sencillo de principio a fin.

Para ello, nos ocuparemos de implementar algo que propuso Larry: si el primer formato activo en una instrucción pack es U (por ejemplo, pack "U3C8", @algo), entonces la cadena resultante debería tratarse como codificada en UTF-8.

Si trabaja con un clon del repositorio git de Perl, tendrá que crear una rama para incorporar sus cambios. Esto facilitará la creación de parches. Encontrará más información en perlgit.

Escribir el parche

¿Cómo nos preparamos para corregir esto? Primero tenemos que buscar el código en cuestión. La función pack se usa en tiempo de ejecución, por lo que estará en uno de los archivos pp. Como sospechábamos, pp_pack está en pp.c. Puesto que vamos a modificar este archivo, antes creamos una copia y le asignamos el nombre pp.c~.

[Bueno, estaba en pp.c en el momento de redactar este tutorial. Ahora se ha escindido de pp_unpack en su propio archivo, pp_pack.c]

Echemos un vistazo a pp_pack: tenemos un patrón en pat y un bucle que recorre el patrón y pasa de uno en uno los caracteres de formato a datum_type. Luego, para cada carácter de formato posible, se consumen los demás argumentos del patrón (un ancho de campo, un asterisco, etc.) y se convierte el siguiente fragmento de entrada al formato especificado, agregándolo al SV de salida, cat.

¿Cómo sabemos si la U es el primer formato en pat? Si tenemos un puntero al comienzo de pat y vemos una U, podemos comprobar si aún estamos en el principio de la cadena. Aquí es donde se establece pat:

    STRLEN fromlen;
    register char *pat = SvPVx(*++MARK, fromlen);
    register char *patend = pat + fromlen;
    register I32 len;
    I32 datumtype;
    SV *fromstr;

Tendremos ahí otro puntero de cadena:

    STRLEN fromlen;
    register char *pat = SvPVx(*++MARK, fromlen);
    register char *patend = pat + fromlen;
 +  char *patcopy;
    register I32 len;
    I32 datumtype;
    SV *fromstr;

Justo antes de empezar el bucle, se establece patcopy como comienzo de pat:

    items = SP - MARK;
    MARK++;
    sv_setpvn(cat, "", 0);
 +  patcopy = pat;
    while (pat < patend) {

Ahora bien, si vemos una U al principio de la cadena, activamos la marca UTF8 para el SV de salida, cat:

 +  if (datumtype == 'U' && pat==patcopy+1)
 +      SvUTF8_on(cat);
    if (datumtype == '#') {
        while (pat < patend && *pat != '\n')
            pat++;

Recuerde que tiene que ser patcopy+1, ya que el primer carácter de la cadena es la U consumida por datumtype.

Vaya, nos olvidamos de una cosa: ¿qué pasa si hay espacios al principio del patrón? pack(" U*", @algo) tendrá U como primer carácter activo, pero no es el primer carácter del patrón. En este caso, tenemos que incrementar patcopy junto con pat cuando veamos espacios:

    if (isSPACE(datumtype))
        continue;

debe convertirse en

    if (isSPACE(datumtype)) {
        patcopy++;
        continue;
    }

Muy bien. Ya hemos corregido el código C. Ahora tenemos que hacer dos cosas más para terminar de preparar el parche: hemos cambiado el comportamiento de Perl, por lo que debemos documentar el cambio. También tenemos que proporcionar pruebas de regresión adicionales para asegurarnos de que nuestro parche funciona y no crea ningún error en otro lugar.

Comprobar el parche

Las pruebas de regresión para cada operador se encuentran en t/op/, así que hacemos una copia de t/op/pack.t en t/op/pack.t~. Ahora podemos agregar nuestras pruebas al final. En primer lugar, comprobaremos que U crea realmente cadenas Unicode.

t/op/pack.t tiene una función ok() adecuada, pero si no la tuviera, podríamos usar la de t/test.pl.

 require './test.pl';
 plan( tests => 159 );

así que en vez de esto:

 print 'not ' unless "1.20.300.4000" eq sprintf "%vd",
                                               pack("U*",1,20,300,4000);
 print "ok $test\n"; $test++;

podemos escribir una prueba más apropiada (en Test::More encontrará una descripción completa de is() y otras funciones para realizar pruebas):

 is( "1.20.300.4000", sprintf "%vd", pack("U*",1,20,300,4000),
                                       "U* produce Unicode" );

Ahora comprobamos que la comprobación de espacio inicial es correcta:

 is( "1.20.300.4000", sprintf "%vd", pack("  U*",1,20,300,4000),
                                     "  con espacios al comienzo" );

Por último, vamos a comprobar que no creamos cadenas Unicode si U no es el primer formato activo:

 isnt( v1.20.300.4000, sprintf "%vd", pack("C0U*",1,20,300,4000),
                                       "U* no aparece al principio, luego no es Unicode" );

No hay que olvidarse de actualizar el número de pruebas especificado al principio del código, para que el programa de ejecución automática de pruebas no se confunda. Será algo así:

 print "1..156\n";

o bien:

 plan( tests => 156 );

Ahora compilamos Perl y ejecutamos la serie de pruebas. ¡Pruebas nuevas superadas! ¡Viva!

Documentar el parche

Por último, falta la documentación. Para acabar el trabajo no queda más remedio que ocuparse del papeleo, así que vamos a describir el cambio que acabamos de hacer. El lugar correspondiente es pod/perlfunc.pod. Una vez más, hacemos antes una copia de seguridad y después insertamos el texto siguiente en la descripción de pack:

 =item *

 Si el patrón comienza con una C<U>, la cadena resultante se tratará
 como caracteres Unicode codificados en UTF-8. Puede forzar la aplicación
 de una codificación UTF-8 en una cadena con C<U0> al principio, y los bytes
 que siguen se interpretarán como caracteres Unicode. Si no desea que ocurra
 esto, puede comenzar el patrón con C<C0> (o cualquier otra cosa) para evitar
 que Perl fuerce la codificación en UTF-8 de la cadena, y luego continuar con
 C<U*> en algún lugar del patrón.

Enviar

Vea perlhack para obtener más información sobre cómo enviar este parche.

AUTOR

Actualmente la lista de correo perl5-porters se encarga de actualizar este documento redactado originalmente por Nathan Torkington.

TRADUCTORES

  • Joaquín Ferrero (Tech Lead)

  • Enrique Nell (Language Lead)