Programación en el entorno GNOME |
---|
Para el tratamiento de ficheros y canales de entrada/salida en general, GLib™ provee un tipo de dato llamado GIOChannel, que permite encapsular ficheros, sockets y pipes. Esta abstracción no sólo posibilita el acceso uniforme a todos estos recursos, sino que también asegura portabilidad e integración al bucle de eventos.
Actualmente sólo hay soporte completo para UNIX™, aunque el tratamiento con ficheros puede hacerse independientemente de la plataforma.
Para crear un GIOChannel, primero se debe crear el descriptor de fichero de UNIX™ por los métodos convencionales (open, socket, etc.) y luego utilizar g_io_channel_unix_new o bien g_io_channel_new_file para acceder a ficheros directamente.
Esta función devuelve el canal creado a partir del descriptor. Dicho parámetro se puede recuperar después utilizando la función g_io_channel_unix_get_fd.
GIOChannel *g_io_channel_new_file( | nombre_fichero, | |
modo, | ||
error) ; |
const gchar * | nombre_fichero; |
const gchar * | modo; |
GError ** | error; |
Devuelve un nuevo canal para acceder al archivo nombre_fichero. El modo tiene la misma forma que el parámetro modo de la función de C estándar fopen. Esto es:
r para lectura.
r+ para lectura y escritura.
w para escritua (Si el fichero no existe, lo crea; en caso contrario lo trunca).
w+ para lectura y ecritura (con el fichero hace lo mismo que el parámetro w
a para escritura al final del fichero (lo crea si no existe).
a+para lectura y escritura al final del fichero (lo crea si no existe).
El parámetro error debe apuntar a una estructura tipo GError previamente creada y es aquí donde se informa de cualquier problema al abrir el fichero. En este último caso la función retorna NULL.
Una vez obtenido el GIOChannel, en general, todas las llamadas a funciones para realizar alguna operación sobre el canal (lectura, escritura, etc.) tomarán como primer parámetro al propio canal y como último un puntero, error, a una estructura GError, donde se informará de cualquier inconveniente ocurrido al intentar la operación. Además, la mayoría de las funciones retornan un código del tipo GIOStatus que puede tomar los siguientes valores:
GIO_STATUS_ERROR: ocurrió un error (los detalles del mismo se encuentran en el parámetro error).
G_IO_STATUS_NORMAL: la operación se realizó con éxito.
G_IO_STATUS_EOF: se alcanzó el fin de fichero.
G_IO_STATUS_AGAIN: el recurso solicitado no está disponible (p.e., se intentó leer datos, pero no estaban en ese momento).
En caso de que el código de retorno sea G_IO_STATUS_ERROR, en la estructura GError se almacena otro código, indicando con mayor detalle la naturaleza del problema. Este nuevo código es de tipo GIOChannelError y puede tomar los siguientes valores:
G_IO_CHANNEL_ERROR_FBIG: el fichero es muy grande.
G_IO_CHANNEL_ERROR_INVAL: argumento inválido.
G_IO_CHANNEL_ERROR_IO: error general de entrada/salida.
G_IO_CHANNEL_ISDIR: el fichero es un directorio, pero se intentó acceder a él como unfichero regular.
G_IO_CHANNEL_ERROR_NOSPC: No hay más espacio disponible para escritura.
G_IO_CHANNEL_ERROR_NXIO: No existe tal dispositivo o dirección.
G_IO_CHANNEL_ERROR_OVERFLOW: el valor proporcionado es mayor de lo que permite su tipo.
G_IO_CHANNEL_ERROR_PIPE: algunos de los extremos del pipe lo desconectó.
G_IO_CHANNEL_ERROR_FAILED: otros errores.
Los primeros valores tienen su correspondencia directa con los valores de la variable errno estándar de C. El último se reserva para otra clase de errores no identificados.
Los canales tienen un tiempo de vida controlado por conteo de referencias. Inicialmente, los canales se crean con una cuenta de 1 y, cuando la misma cae a 0, se destruyen. Para obtener una referencia se utiliza la función g_io_channel_ref y para liberar una referencia g_io_channel_unref. Cabe aclarar que, aunque el objeto GIOChannel se destruya, no necesariamente se cierra el canal. En concreto, si el objeto fue creado a partir de un descriptor de UNIX™, el programador es responsable de cerrarlo (a menos que se utilice la función g_io_channel_set_close_on_unref).
Las funciones mencionadas tienen la siguiente forma:
Para cerrar de un canal se utiliza g_io_channel_shutdown:
GIOStatus
g_io_channel_shutdown( | canal, | |
descarga, | ||
error) ; |
GIOChannel * | canal; |
gboolean | descarga; |
GError ** | error; |
Aquí, el parámetro descarga controla si se escriben los datos que pueda haber en el buffer interno del canal antes de cerrarlo (para forzar una descarga en cualquier momento se puede utilizar g_io_channel_flush).
GLib™ provee una serie de funciones para acceder a un canal abierto de diversas maneras: por caracteres individuales, por bloques de cantidad de bytes fijos y por líneas.
Si el tipo de canal lo permite, la función g_io_channel_seek_position sirve para establecer la siguiente posición donde se leerá o escribirá. Normalmente sólo los ficheros permiten posicionamiento aleatorio.
GIOStatus
g_io_channel_seek_position( | canal, | |
posicion, | ||
tipo_posicion, | ||
error) ; |
GIOChannel * | canal; |
glong | posicion; |
GSeekType | tipo_posicion; |
GError ** | error; |
La semántica de posición (que es un número con signo) queda determinada por el tipo_posicion, que puede tomar los siguientes valores:
G_SEEK_CUR: la nueva posición es relativa a la actual.
G_SEEK_SET: la nueva posición se toma a partir del principio del fichero.
G_SEEK_END: la nueva posición es absoluta respecto al fin de archivo.
Para efectuar lecturas en un canal se utilizan algunas de las siguientes funciones:
GIOStatus
g_io_channel_read_chars( | canal, | |
buffer, | ||
tamaño_buffer, | ||
bytes_leidos, | ||
error) ; |
GIOChannel * | canal; |
gchar * | buffer; |
gsize | tamaño_buffer; |
gsize * | bytes_leidos; |
GError ** | error; |
Lee una cantidad máxima (tamaño_buffer) de bytes del canal como caracteres y los almacena en buffer. Éste debe estar previamente reservado (p.e. con g_malloc). bytes_leidos contiene la cantidad de bytes efectivamente leídos (puede ser menor que la solicitada).
Es importante notar que si el canal está configurado para trabajar con caracteres Unicode™, un carácter puede ocupar más de un byte y, si el buffer no es lo suficientemente grande, puede que no sea posible almacenar ningún carácter. En estas condiciones bytes_leidos es cero, pero no hay indicación de error.
GIOStatus
g_io_channel_read_line( | canal, | |
puntero_cadena, | ||
bytes_leidos, | ||
terminador, | ||
error) ; |
GIOChannel * | canal; |
gchar ** | puntero_cadena; |
gsize * | bytes_leidos; |
gsize * | terminador; |
GError ** | error; |
Lee una línea completa del canal y almacena el puntero a la misma en puntero_cadena>. Una vez finalizada la operación con la línea, es responsabilidad del programador liberar la memoria con g_free. En bytes_leidos se devuelve la cantidad total de bytes leídos (incluyendo caracteres de fin de línea) y en terminador, sólo la cantidad perteneciente a la línea en sí. Tanto bytes_leidos como terminador pueden ser nulos si la información no es de interés.
GIOStatus
g_io_channel_read_line_string( | canal, | |
cadena, | ||
terminador, | ||
error) ; |
GIOChannel * | canal; |
GString * | cadena; |
gsize * | terminador; |
GError ** | error; |
Similar a g_io_channel_read_line, sólo que, para devolver el resultado, utiliza una estructura GString. terminador puede ser nulo.
GIOStatus
g_io_channel_read_to_end( | canal, | |
puntero_cadena, | ||
bytes_leidos, | ||
error) ; |
GIOChannel * | canal; |
gchar ** | puntero_cadena; |
gsize * | bytes_leidos; |
GError ** | error; |
Lee todos los bytes disponibles en el canal hasta encontrar el fin de fichero. En puntero_cadena se devuelve un puntero al espacio de memoria asignado a tal fin que luego debe ser liberado con g_free.
Para la escritura se utiliza la contraparte de g_io_channel_read_chars:
GIOStatus
g_io_channel_write_chars( | canal, | |
buffer, | ||
cantidad_bytes, | ||
bytes_escritos, | ||
error) ; |
GIOChannel * | canal; |
gchar * | buffer; |
gsize | cantidad_bytes; |
gsize * | bytes_escritos; |
GError ** | error; |
Como su propio nombre indica, esta función escribe en el canal cantidad_bytes del buffer. Puede que no lleguen a escribirse todos los bytes que se solicitaron, por lo que la función devuelve la cantidad de bytes efectivamente escritos. Si cantidad_bytes es -1 se considera al buffer como terminado en nulo.
Se aplican las mismas restricciones en cuanto a codificación del canal que para g_io_channel_read_chars.
Uno de los aspectos más interesantes de los GIOChannels es que es posible integrarlos fácilmente al bucle de eventos y así, por ejemplo, hacer que la aplicación reaccione ante la llegada de datos.
Los posibles tipos de eventos que puede generar un canal están dados por el tipo GIOCondition que toma los siguientes valores:
G_IO_IN: hay datos disponibles en el canal.
G_IO_OUT: se pueden escribir datos al canal sin que éste bloquee.
G_IO_PRI: hay datos urgentes para leer.
G_IO_ERR: condición de error.
G_IO_HUP: desconexión; normalmente, se aplica a sockets y pipes. Alguno de los extremos rompió la conexión.
G_IO_NVAL: solicitud no válida (p.e. el descriptor UNIX™ no está abierto).
La forma básica de integrar un objeto generador de eventos al bucle es obtener una estructura GSource. En el caso de los canales, la función para ello es g_io_create_watch:
Esta función devuelve un GSource que luego se registra en algún bucle de eventos con g_source_attach. Sin embargo, por conveniencia, GLib™ provee un par adicional de funciones para integrar directamente un canal al bucle principal: g_io_watch y f_io_add_watch_full.
guint g_io_add_watch( | canal, | |
condición, | ||
función, | ||
datos_usuario) ; |
GIOChannel * | canal; |
GIOCondition | condición; |
GIOFunc | función; |
gpointer | datos_usuario; |
Esta función, que devuelve el identificador de evento, registra el canal para que sea controlado según las condiciones especificadas. En caso de que se cumpla alguna de las condiciones, se llama a la función con los datos_usuario adicionales.
La función especificada en el registro debe tener la siguiente forma:
gboolean (* GIOFunc)( | canal, | |
condición, | ||
datos_usuario) ; |
GIOChannel * | canal; |
GIOCondition | condición; |
gpointer | datos_usuario; |
La condición que la función recibe es aquella que causó el evento en primer lugar. datos_usario es lo que se especificó en el momento del registro.
La otra función, g_io_add_watch_full permite además especificar la prioridad del canal y una función de notificación cuando el canal se desconecta del bucle de eventos.
Los GIOChannels son capaces de codificar o decodificar los caracteres que se escriben o leen del mismo. La codificación que se utiliza internamente es UTF-8, por lo que, eventualmente, se puede transformar de UTF-8 a alguna otra codificación para escribir a un fichero, o viceversa.
Si se necesita que un canal no efectúe transformación alguna a los datos (por ejemplo, para tratamiento de datos binarios) se debe optar por la codificación nula (NULL).
Las funciones para manipular la codificación son g_io_channel_set_encoding y g_io_channel_get_encoding.
GIOStatus g_io_channel_set_encoding( | canal, | |
codificación, | ||
error) ; |
GIOChannel * | canal; |
const gchar * | codificación; |
GError ** | error; |
G_CONST_RETURN gchar *g_io_channel_get_encoding( | canal) ; |
GIOChannel * | canal; |
Por defecto, la codificación externa utilizada para ficheros es UTF-8. Para cambiarla se deben tener en cuenta las siguientes condiciones:
El canal acaba de ser creado.
El canal es para escritura solamente.
El canal es sobre un fichero y el puntero de lectura y escritura acaba de ser recolocado con g_io_channel_seek_position..
La codificación actual es UTF-8 o nula.
Se ha llamado a una función de lectura y ha devuelto G_IO_STATUS_EOFo, en el caso de g_io_channel_read_to_end, G_IO_STATUS_NORMAL.
En UNIX™, el fin de línea se indica con un carácter de linefeed (código ASCII 10), pero esto puede no ser cierto para otras plataformas (p.e. Win32).
Para que las funciones de lectura y escritura por líneas den los resultados esperados para cada plataforma, en caso de ser necesario el acceso a ficheros escritos en otra plataforma, se debe configurar el terminador de línea en el canal. Las funciones para ello son:
void g_io_channel_set_line_term( | canal, | |
terminador, | ||
longitud) ; |
GIOChannel * | canal; |
const gchar * | terminador; |
gint | longitud; |
G_CONST_RETURN gchar *g_io_channel_get_line_term( | canal, | |
longitud) ; |
GIOChannel * | canal; |
gint * | longitud; |
La API de GIOChannel nos permite modificar alguno de los parámetros de funcionamiento interno del canal. Es posible modificar el tamaño del buffer interno del mismo para optimizar el rendimiento de lectura y escritura en casos especiales. Para ello se utilizan las funciones g_io_channel_get_buffer_size y g_io_channel_set_buffer_size.
gsize g_io_channel_get_buffer_size( | canal) ; |
GIOChannel * | canal; |
void g_io_channel_set_buffer_size( | canal, | |
tamaño) ; |
GIOChannel * | canal; |
gsize | tamaño; |
Si en g_io_channel_set_buffer_size se especifica un tamaño 0, GLib™ elegirá un tamaño adecuado al canal.
Hemos visto antes que, utilizando el bucle de eventos, se puede monitorear un canal y producir llamadas a funciones ante determinados eventos. También es posible verificar manualmente si un canal tiene datos listos para la lectura o si se pueden escribir datos en él. Para ello se utiliza la funcióng_io_channel_get_buffer_condition>.
GIOCondition g_io_channel_get_buffer_condition( | canal) ; |
GIOChannel * | canal; |
Esta función puede devolver solamente G_IO_IN y G_IO_OUT y, por supuesto, su significado es exactamente el mismo que se explicó antes.
Por último, con las funciones g_io_channel_set_flags y g_io_channel_get_flags, es posible saber y, en parte, modificar el comportamiento que tiene un canal.
GIOFlags g_io_channel_get_flags( | canal) ; |
GIOChannel * | canal; |
GIOStatus g_io_channel_set_flags( | canal, | |
banderas, | ||
error) ; |
GIOChannel * | canal; |
GIOFlags | banderas; |
GError ** | error; |
Los valores que puede tomar el parámetro banderas son G_IO_FLAG_APPEND y/o G_IO_FLAG_NONBLOCK (los demás valores son de sólo lectura). El valor obtenido en g_io_channel_get_flags es una composición de:
G_IO_FLAG_APPEND: canal en modo de agregado.
G_IO_FLAG_NONBLOCK: canal en modo no bloqueante; esto es, si se intenta, p.e., leer y no hay datos disponibles, se devuelve el control inmediatamente con un estado de G_IO_STATUS_AGAIN.
G_IO_FLAG_IS_READABLE: el canal se puede leer.
G_IO_FLAG_IS_WRITEABLE: se puede escribir en el canal.
G_IO_FLAG_IS_SEEKABLE: se puede mover el puntero cd lectura y/o escritura en el canal con g_io_channel_seek_position.
<< Bucles de ejecución. | Manejo de memoria dinámica. >> |