lunes, 3 de septiembre de 2007

Reimplementando BIFs en REXX

En REXX es frecuente referirse oficiosamente a toda función que forma parte del núcleo del lenguaje como BIF: Built-In Function. El estándar ANSI de 1996 define una serie de funciones dentro de esta categoría como por ejemplo abbrev, words, date o address. En general las implementaciones del lenguaje tienen todas las BIFs definidas en el estándar, añadiendo aportaciones propias o tal vez careciendo de alguna concreta.

Lo peculiar del caso es que REXX permite que el programador defina las funciones internas de su programa (aquellas que se incluyen dentro del mismo fichero) utilizando los mismos nombres que alguna BIF y aun así seguir utilizando la BIF cuyo nombre hemos utilizado.


/* words.rex */

parse pull frase
say words(frase)

exit


Este sencillo ejemplo se limita a contar el número de palabras que tiene una cadena que introduce el usuario utilizando la BIF words. La línea con la sentencia parse pull frase se limita a meter la entrada del usuario en la variable frase. Y la siguiente línea nos dice la salida de la función words aplicada sobre la variable frase, es decir, muestra en pantalla el número de palabras.

Si el programador introduce una función en el código como en el siguiente caso:


/* words.rex */

parse pull frase
say words(frase)

exit

words: procedure
return 'No me apetece contar palabras'


El resultado es que en lugar de ejecutarse la BIF words, se ejecuta nuestra función. Es decir, que sea cual sea la entrada del usuario, el programa siempre imprimirá que no le apetece contar palabras.

A pesar de tener ocupado el nombre de la BIF words, podemos seguir accediendo a ella llamandola por su nombre en mayúsculas entrecomillado (por comillas simples). Así, en el siguiente ejemplo, se imprime siempre que no le apetece contar palabras e inmediatamente después el número de palabras tal como lo cuenta la BIF words:


/* words.rex */

parse pull frase
say words(frase)
say 'WORDS'(frase)

exit

words: procedure
return 'No me apetece contar palabras'


Evidentemente podemos ejecutar la BIF desde dentro de nuestra función:


/* words.rex */

parse pull frase
say words(frase)

exit

words: procedure
parse arg cadena
return 'WORDS'(cadena)


Esto nos permite cierto número de posibilidades como cambiar el comportamiento de las BIFs de nuestro intérprete. Esto puede ser interesante, por ejemplo, cuando un intérprete no implementa una BIF como estamos acostumbrados a utilizar. Imaginemos que un programador acostumbrado a las características de la BIF date para hacer aritmética de fechas propias del estándar y presentes en Object REXX y Regina desea que su código funcione en REXX clásico en OS/2 (que carece de esa característica). Si hace una función date que supla esas características ausentes en ese intérprete, solamente tiene que añadir esa función en la versión de su programa para OS/2 y no tendrá que tocar ninguna línea más de código.

Lo interesante es que podemos reimplementar muchas de las BIFs orientadas a manejo de cadenas solamente utilizando bucles condicionales y la instrucción PARSE. En el caso que nos ocupa, podríamos hacer:


words: procedure
parse arg string
n = 0
do while string <> ''
parse var string . string
n = n + 1
end
return n


parse coge el argumento de la función que hemos escrito y lo introduce en la variable string, mientras la variable string no esté vacía parse coge la primera palabra de la variable string y la descarta (.) metiendo el resto en string. Cada vez que descarta una palabra, aumenta el contador, y cuando las ha descartado todas, nos devuelve el valor de ese contador, es decir, del número de palabras. (Podríamos habernos ahorrado un par de líneas, pero he preferido dejarlo así por claridad.)

Como ejercicio para principiantes, el intentar reimplementar BIFs sin utilizar BIFs es algo, creo yo, muy educativo. Permite apreciar el valor de la instrucción parse y familiarizarse con las BIFs orientadas a cadenas.

Solamente podemos "redefinir" las BIFs a través de funciones internas de nuestro programa. Las funciones externas no nos valdrán y tampoco aquellas que estén en el macroespacio (si la implementación lo tiene). Pero cuando la BIF no está definida en nuestro intérprete (como ocurre con countstr en REXX clásico para OS/2), entonces podemos implementarla como una función externa y cargarla en el macroespacio...

Pero, como decía Jack Lemmon en Irma la dulce: Eso es otra historia.

domingo, 2 de septiembre de 2007

mplayer en OS/2 y eComStation

Aún recuerdo cómo hace años el proyecto mplayer puso en su lista negra a WarpVision, que por aquel entonces era practicamente el único reproductor de vídeo para OS/2 con cierta funcionalidad, porque había utilizado código fuente de mplayer (código bajo la licencia GPL) y se trataba de un programa comercial.

Tras el paso de los años el proyecto WarpVision cambió de tutela y pasó a Netlabs que liberó su código fuente. Y lo que hace años era un prometedor reproductor multimedia para una plataforma de software virtualmente abandonada, ahora sigue siendo exactamente eso: un proyecto prometedor.

Y precisamente el hecho de que las funcionalidades del programa y su estabilidad hayan mejorado tan poco significativamente resulta enormemente frustrante (como el mismo Adrian Gschwend, responsable de Netlabs, comentaba no hace muchos meses ante la decisión de despedir al desarrollador del programa). WarpVision no sigue un esquema de numerado de versiones claro y resulta muy difícil decidir qué versión utilizar porque hay características que dejan de funcionar en una versión nueva que ha corregido algún problema que tenía la versión anterior.

En el transcurso de estos años el panorama ha cambiado en OS/2 de un modo curioso; toda una serie de personas individuales y comunidades han aportado una serie de herramientas de desarrollo que mantienen vivo a un sistema operativo que lleva muerto ya más de media década: Knut St. Osmundsen es el responsable de las versiones del compilador GCC y la biblioteca libc más actuales existentes para la plataforma (con características nuevas de las que carecen los equivalentes anteriores: EMX); Doodle ha portado SDL y Cairo; Dmitry A. Kuminov ha portado QT3; la comunidad OpenWatcom ha sacado recientemente la versión 1.7 de su compilador de C, C++ y Fortran; varias personas mantienen todos los programas de la familia de productos Mozilla al día...

En estas circunstancias en las que el software libre da una inyección de vitalidad al sistema, varias personas, probablemente insatisfechas con WarpVision, han mirado hacia afuera para ver qué podían traer hacia el sistema. Ese bendito australiano llamado Paul Smedley portó ffplay (el reproductor basado en ffmpeg) y mplayer utilizando SDL con resultados discretos. El coreano KO Myung-Hun, basandose probablemente en el trabajo de Paul, ha hecho sus propias versiones de estos programas eliminando la dependencia de la biblioteca SDL y activando aceleración 2D a través de WarpOverlay y en el caso de mplayer ha creado una verdadera joya.

Mi máquina es muy modesta en recursos: un AMD K6-2 a 450 MHz, una tarjeta gráfica de tan solo 8 megas, una RAM de unos 500 megas... y en muchos casos no conseguía reproducir la mayor parte de los vídeos (aun en mis restantes sistemas operativos). Por poner un ejemplo: no conseguía ver los capítulos de Naruto Shipuuden de ningún modo, no importaba el sistema operativo o el reproductor que utilizase o lo que jugase con las opciones de reproducción.

Hasta el port de mplayer de KO Myung-Hun.

Hasta ahora ha resultado extraordinariamente estable, me ha permitido ver casi todos los vídeos que he probado (hasta ahora solamente se le resisten los FLV bajados de YouTube y los WMV), carga y sincroniza correctamente ficheros de subtítulos externos, reproduce DVDs sin problemas... y si un vídeo me da problemas con su resolución completa, con la opción "-lavdopts lowres=1:fast:skiploopfilter=all" puedo verlo en baja resolución sin inconvenientes (salvo, claro está, la resolución misma).

Le faltan pequeñas cosas como reproducir VCD, ocultar el puntero de ratón en pantalla completa, desactivar el salvapantallas mientras está reproduciendo, que el ejecutable y la ventana de reproducción tenga un icono (es sorprendente lo que me importa una nimiedad como esa, teniendo en cuenta lo poco que suele importarme la estética)... Tal vez una interfaz gráfica sería deseable y dado que el trabajo de Myung-Hun incluye un modo esclavo que es necesario para hacer un front-end (con el que he intentado jugar sin éxito alguno) ya existe todo lo necesario para ello (de hecho, el front-end multiplataforma SMPlayer, basado en QT es un buen candidato a port).

Pero mientras llegan esas cosas que no son vitales, ¡qué irónico es que OS/2 se haya convertido en el sistema que he de usar para ver vídeos!