Diferencias entre las revisiones 1 y 2
Versión 1 con fecha 2007-12-10 16:33:05
Tamaño: 3736
Comentario:
Versión 2 con fecha 2007-12-10 18:03:40
Tamaño: 14105
Comentario:
Los textos eliminados se marcan así. Los textos añadidos se marcan así.
Línea 9: Línea 9:
Dentro de GLib está implementada una serie de tipos de datos que nos hace más fácil, si cabe, el tratamiento de los datos y además tiene la propiedad de mejorar la portabilidad de nuestros programas. Los tipos que se usan en GLib no son muy diferentes de los que usamos en el estándar de C. Así que realmente no nos resultará nada complicado acostumbrarnos a usarlos.
De momento, vamos a comenzar aprendiendo los tipos de GLib que corresponden con los del estándar de C.

=== Tipos de GLib que corresponden con los del estándar C. ===
Dentro de GLib está implementada una serie de tipos de datos facilita, si cabe, el tratamiento de los datos y además tiene la propiedad de mejorar la portabilidad de los programas. Los tipos que se usan en GLib no son muy diferentes de los utilizados en el estándar de C. Por lo cual no resultará complicado acostumbrarse a su utilización.
De momento, comenzaremos aprendiendo los tipos de GLib que corresponden con los del estándar de C.

Tabla 6-1.1. ''Tipos de GLib que corresponden con los del estándar C.''
Línea 21: Línea 21:
Como podemos observar en la tabla, estos tipos no son muy diferentes de los que usamos cuando programamos con los tipos del estándar de C, aunque es altamente recomendable poner un poco de esfuerzo para acostumbrarse a estos tipos si se va a trabajar con la plataforma GNOME. Como se observa en la tabla, estos tipos no difieren en grán medida de los utilizados al programar con los tipos del estándar de C, aunque es altamente recomendable poner un poco de esfuerzo para acostumbrarse a estos tipos si se va a trabajar con la plataforma GNOME.
Línea 24: Línea 24:
=== Tipos de GLib que facilitan el uso de tipos del estándar C. === Tabla 6-1.2. ''Tipos de GLib que facilitan el uso de tipos del estándar C.''
Línea 33: Línea 33:
=== Tipos de GLib que aseguran el tamaño del dato entre plataformas. === Tabla 6-1.3. ''Tipos de GLib que aseguran el tamaño del dato entre plataformas.''
Línea 44: Línea 44:
=== Tipos de GLib nuevos que no están en el estándar de C. === Tabla 6-1.4. ''Tipos de GLib nuevos que no están en el estándar de C.''
Línea 50: Línea 50:
Quizás, nuestro lector más novato haya saltado de su asiento al ver algunos tipos como gint8, gint16, gsize, etc. Para aliviarle esta posible preocupación, es interesante denotar que cuando se programa con GLib, los tipos más usados son los char, int, double, etc., aunque ahora usará los mismos, pero con una ge delante del tipo. Quizás, nuestro lector más novato haya saltado de su asiento al ver algunos tipos como gint8, gint16, gsize, etc. Para aliviarle esta posible preocupación, es interesante denotar que cuando se programa con GLib, los tipos más usados son los char, int, double, etc., aunque ahora usará los mismos, pero con una "g" delante del tipo.

== Mensajes de salida. ==

=== Mensajes de salida. ===

GLib implementa una serie de funciones para el envío de mensajes al exterior del programa. Al igual que en el ANSI C se dispone de la mítica función printf (), GLib posee una función que se asemeja en todo menos en el nombre; esta función es g_print () y su prototipo de función sería:

{{{
void g_print (const gchar *format, ...);
}}}

Como se puede ver, el uso de g_print () es idéntico al de printf (). Es una función que recibe como parámetros un formato y las variables que se usan dentro de las características del formato, igual que printf ().

Ejemplo 6-2.1. Mensajes de salida.

   
 

Además, GLib provee de un método para dar formato todos los mensajes de salida que se emitan con la función g_print (), por lo que se podría decir que, cada vez que mandamos un mensaje con formato a g_print (), éste debe salir por pantalla con algún tipo de mensaje adicional. Esto es debido a la función g_print_set_handler (), que tiene como parámetros un puntero a función. Esta función, que es representada por el puntero a función, será definida por el programador y, con ella, se podrá decir qué mensaje adicional queremos que se vea. Para entenderlo mejor, se mostrará a continuación la sinopsis de esta función, así como un ejemplo.

{{{
GPrintFunc g_print_set_handler (GPrintFunc funcion);
}}}

Esta sería la sinopsis de la función g_pint_set_handlet (), que recibiría como parámetro un puntero a función. Esta función tendría este aspecto

{{{
void (* GPrintFunc) (const gchar * cadena );
}}}

Éste sería el aspecto que ha de tener la función que se le pasará como parámetro a g_print_set_handler. El siguiente ejemplo ha sido concebido con la intención de facilitar su comprensión.

Ejemplo 6-2.2. Personalización de mensajes de salida.

   
 

GLib también nos provee de una función para comunicar mensajes de salida por la salida de errores estándar (stderr). Para el envío de mensajes tenemos la función g_printerr que funciona exactamente igual que g_print. Existe también una función como g_set_print_handler, llamada g_set_printerr_handler que funciona igual que la anterior.

Se puede ver, en conclusión, que el formateo personalizado de los mensajes de salida en GLib es bastante simple.

=== Funciones de depuración. ===

La depuración de un programa siempre es un trasiego por el cual todo programador, ya sea principiante o avanzado, ha de pasar. Y para hacer la vida más fácil al programador de GNOME, GLib nos ofrece una serie de funciones que harán más sencilla la depuración del código.

Estas funciones son útiles para comprobar que se cumple una serie de condiciones lógicas delimitadas por el programador. En caso de no ser cumplida, GLib emitirá un mensaje por consola, explicando que la condición que el programador ha puesto no se ha cumplido e indicando el archivo, la línea y la función en las que se ha roto la condición.

{{{
#define g_assert ( expresion );
}}}

La primera función de este tipo que vamos a ver es g_assert. Esta función recibe como parámetro un enunciado lógico y comprueba su validez. Por poner un ejemplo de su uso, podríamos exponer el caso en el que a una función se le pase un puntero. Y nosotros podríamos poner g_assert (ptr != NULL) para certificar que ese caso no se dé nunca. De hecho, si se diese ese caso, la función g_assert comprobaría que esa expresión no es válida y mandaría interrumpir la ejecución del programa. Para tener más claro este concepto, obsérvese la ejecución del siguiente ejemplo:

Ejemplo 6-2.3. Depuración de programas con g_assert.

   
 

Si se compila este programa, en la salida del mismo, se podrá observar que cuando entra por segunda vez en la función escribe_linea, cesa la ejecución del programa. Además, por la consola saldrá este mensaje o uno parecido.

{{{
 (process:457): ** ERROR **: file
   ejemplo.c: line 9 (escribe_linea):
 assertion failed: (cadena !=
   NULL)
 aborting...
}}}

El mensaje da información del proceso donde se rompió la condición y muestra por pantalla información del archivo, de la línea y de la función en las que se viola la condición por la cual g_assert ha parado la ejecución. También indica qué expresión se ha incumplido, porque se puede dar el caso de tener varios g_assert en una misma función.

Quizá un método tan drástico como parar la ejecución del programa no sea lo mejor. Es probable que sólo se busque algo que termine la ejecución de la función y avise que una condición no ha sido cumplida. Para estos casos existen varias macros que facilitarán mucho la vida de los programadores. Estas macros son:

{{{
#define g_return_if_fail ()( expresion );

#define g_return_val_if_fail ()( expresion , val );
}}}

Las dos macros son similares en su funcionamiento, ya que comprueban si la expresión lógica que se les pasa como primer parámetro es verdadera o falsa. En el caso de ser falsa, aparecerá por consola un mensaje explicando que esa condición no ha sido cumplida. Esto es muy parecido a lo que hacía g_assert (); la diferencia estriba en que estas dos funciones no finalizan la ejecución del programa, sino que simplemente hacen un retorno dentro de la propia función y, en consecuencia, el resto del programa seguirá ejecutándose.

Como se dijo antes, estas macros son similares. La diferencia es que la segunda, g_return_val_if_fail, hace que la función devuelva el valor que se le pasa como segundo parámetro.

Obviamente, g_return_if_fail se usará en funciones que no devuelvan ningún valor y g_return_val_if_fail en funciones que devuelvan algún valor. Para entender mejor estas funciones obsérvese el siguiente ejemplo.

Ejemplo 6-2.4. Depuración de programas con g_return_if_fail y g_return_val_if_fail.

   
 

Y, por consiguiente, la salida de este programa sería la siguiente:

{{{
   esto es una cadena
   esto es una cadena

   -- escribe_linea2 ha devuelto TRUE --

   (process:578): ** CRITICAL **: file glib-g_return_fail.c: line 6 (escribe_linea):
   assertion `cadena != NULL' failed

   (process:578): ** CRITICAL **: file glib-g_return_fail.c: line 18 (escribe_linea2):
   assertion `cadena != NULL' failed

   -- escribe_linea2 ha devuelto FALSE --
 
}}}

Como se puede ver, su ejecución también ofrece información sobre el lugar donde no se ha cumplido la expresión como ya lo hacía la función g_assert, salvo que no detiene la ejecución del programa.

Y estas son las funciones para depuración que se pueden encontrar dentro de la librería GLib. Aunque no son las únicas, sí son las más comunes.

=== Funciones de registro. ===

GLib tiene cuatro macros básicas para el tratamiento mensajes, por niveles de importancia. Estas macros permiten al programador realizar una registro del recorrido del programa especificando el nivel de importancia de los sucesos acontecidos dentro del mismo.

Las cuatro macros de las que estamos hablando reciben parámetros de la misma forma que lo hacian ''g_print'' o ''printf''. De este modo, no sólo se tendrá una registro por niveles, sino que, además, se podrá especificar un texto preformateado. De este modo, los mensajes pueden representar por pantalla el contenido de variables o cualquier otra información que nos pudiera ser útil. Las cuatro macros de las que estamos hablando son :

{{{
#define g_message (...);

#define g_warning (...);

#define g_critical (...);

#define g_error (...);
}}}

Como se puede observar, las cuatro macros tienen nombres bastante intuitivos. Para empezar, el lector se habrá dado cuenta, por los nombres de las funciones, de que están ordenadas de menor a mayor importancia, siendo ''g_message'' menos importante que ''g_error''. A continuación se explicará lo que hace cada una de ellas.

'''''g_message''' es una macro que emite un mensaje normal con la información que se le ha pasado como parámetro. La información que emite esta macro no tiene por que estar asociada a un error en el programa. Su principal aplicación es el envío de mensajes con la información que se crea conveniente en el proceso de realización del programa.''

'''''g_warning''' emite un mensaje de aviso con la información que le ha sido pasada como parámetro. Esta información puede servir para ser avisados de situaciones peligrosas que se podrían dar en el programa.''

'''''g_critical''' cumple la misma función que g_warning, sólo que éste además tiene la posibilidad de abortar el programa usando la función g_log_set_always_fatal().''

'''''g_erroremite''' el mensaje que le haya sido pasado como argumento y además aborta irremediablemente la ejecución del programa. Esta macro se usa para obtener información de donde ha sucedido el error irrecuperable con la seguridad de que, a causa de este error, el programa terminara abortando su ejecución de todas maneras.''

Para una mejor comprensión se muestra el siguiente ejemplo. En él se han definido cuatro funciones, cada una de las cuales emite un mensaje diferente. En este ejemplo no se hace que g_critical tenga capacidad de abortar el programa porque si no, no veríamos como funciona g_error.

Ejemplo 6-2.5. ''Uso de mensajes de registro.''

   
 

Aunque este ejemplo no tiene mucha funcionalidad, sirve para ver en acción a las cuatro macros en un mismo programa y da la posibilidad de contemplar en la salida de este programa los mensajes que envía a la consola. Si se ejecuta el ejemplo su salida sería:

{{{
   Message: Yo soy solamente un mensaje de aviso


   (process:603): ** WARNING **: Esto es un aviso, algo puede ir mal


   (process:603): ** CRITICAL **: Esto se pone difícil :(


   (process:603): ** ERROR **: 3, 2, 1... acabo de morirme. RIP

   aborting...
   `trap' para punto de parada/seguimiento (core dumped)
 
}}}

Como se puede ver, todas las funciones y macros que se han mostrado en este apartado nos serán especialmente útiles a la hora de realizar un programa y nos posibilitarán una vía estandarizada y muy flexible de emisión de información al exterior. Facilitando la corrección del código y haciendo más facil la tarea del programador.

Glib

TableOfContents()

La biblioteca GLib es una de las más importantes que existen en GNOME. Esta biblioteca es, junto a la biblioteca GTK+, el pilar sobre el que se sustentan todas las aplicaciones.

Tipos de datos de GLib

Dentro de GLib está implementada una serie de tipos de datos facilita, si cabe, el tratamiento de los datos y además tiene la propiedad de mejorar la portabilidad de los programas. Los tipos que se usan en GLib no son muy diferentes de los utilizados en el estándar de C. Por lo cual no resultará complicado acostumbrarse a su utilización. De momento, comenzaremos aprendiendo los tipos de GLib que corresponden con los del estándar de C.

Tabla 6-1.1. Tipos de GLib que corresponden con los del estándar C.

Tipos GLib.

Tipos estándar C.

gchar

char

gint

int

gshort

short

glong

long

gfloat

float

gdouble

double

Como se observa en la tabla, estos tipos no difieren en grán medida de los utilizados al programar con los tipos del estándar de C, aunque es altamente recomendable poner un poco de esfuerzo para acostumbrarse a estos tipos si se va a trabajar con la plataforma GNOME. GLib también posee una serie de tipos que nos hacen más sencillo el tratamiento de datos. Algunos tienen su correspondencia en C, pero nos hacen más sencillo su uso; otros nos sirven para mantener el mismo tamaño de datos de una plataforma a otra y, finalmente, hay otros que no tienen correspondencia con el estándar de C, pero que nos resultarán bastante útiles.

Tabla 6-1.2. Tipos de GLib que facilitan el uso de tipos del estándar C.

Tipos de GLib

Tipos del C estándar.

Definición

gpointer

void *

gpointer es un puntero sin tipo, que luce mejor que escribir void *.

gconstpointer

gconstpointer es un puntero a una constante. Los datos a los que apuntan no deberían ser cambiados. Este tipo suele usarse en los prototipos de funciones para indicar que el valor al que apunta la función no debería ser cambiado en el interior de la función.

guchar

unsigned char

Como se puede observar, estos tipos son idénticos a los tipos de C que carecen de signo, pero con la ventaja de que son más visuales y fáciles de usar.

guint

unsigned int

gushort

unsigned short

gulong

unsigned long

Tabla 6-1.3. Tipos de GLib que aseguran el tamaño del dato entre plataformas.

Tipo

Rango de tamaño del dato.

gint8

-128 a 127

guint8

0 a 255

gint16

-32.768 a 32.767

guint16

0 a 65535

gint32

-2.147.483.648 a 2.147.483.647

guint32

0 a 4.294.967.295

gint64

-9.223.372.036.854.775.808 a 9.223.372.036.854.775.807

guint64

0 a 18.446.744.073.709.551.615

Tabla 6-1.4. Tipos de GLib nuevos que no están en el estándar de C.

Tipos de GLib

Definición

gboolean

Este tipo es booleano y sólo contendrá los valores TRUE o FALSE.

gsize

Es un entero de treinta y dos bits sin signo que sirve para representar tamaños de estructuras de datos.

gssize

Es un entero de 32 bits con signo, que sirve para representar tamaños de estructuras de datos.

Quizás, nuestro lector más novato haya saltado de su asiento al ver algunos tipos como gint8, gint16, gsize, etc. Para aliviarle esta posible preocupación, es interesante denotar que cuando se programa con GLib, los tipos más usados son los char, int, double, etc., aunque ahora usará los mismos, pero con una "g" delante del tipo.

Mensajes de salida.

Mensajes de salida.

GLib implementa una serie de funciones para el envío de mensajes al exterior del programa. Al igual que en el ANSI C se dispone de la mítica función printf (), GLib posee una función que se asemeja en todo menos en el nombre; esta función es g_print () y su prototipo de función sería:

void g_print (const gchar *format, ...);

Como se puede ver, el uso de g_print () es idéntico al de printf (). Es una función que recibe como parámetros un formato y las variables que se usan dentro de las características del formato, igual que printf ().

Ejemplo 6-2.1. Mensajes de salida.

Además, GLib provee de un método para dar formato todos los mensajes de salida que se emitan con la función g_print (), por lo que se podría decir que, cada vez que mandamos un mensaje con formato a g_print (), éste debe salir por pantalla con algún tipo de mensaje adicional. Esto es debido a la función g_print_set_handler (), que tiene como parámetros un puntero a función. Esta función, que es representada por el puntero a función, será definida por el programador y, con ella, se podrá decir qué mensaje adicional queremos que se vea. Para entenderlo mejor, se mostrará a continuación la sinopsis de esta función, así como un ejemplo.

GPrintFunc g_print_set_handler (GPrintFunc funcion);

Esta sería la sinopsis de la función g_pint_set_handlet (), que recibiría como parámetro un puntero a función. Esta función tendría este aspecto

void (* GPrintFunc) (const gchar * cadena );

Éste sería el aspecto que ha de tener la función que se le pasará como parámetro a g_print_set_handler. El siguiente ejemplo ha sido concebido con la intención de facilitar su comprensión.

Ejemplo 6-2.2. Personalización de mensajes de salida.

GLib también nos provee de una función para comunicar mensajes de salida por la salida de errores estándar (stderr). Para el envío de mensajes tenemos la función g_printerr que funciona exactamente igual que g_print. Existe también una función como g_set_print_handler, llamada g_set_printerr_handler que funciona igual que la anterior.

Se puede ver, en conclusión, que el formateo personalizado de los mensajes de salida en GLib es bastante simple.

Funciones de depuración.

La depuración de un programa siempre es un trasiego por el cual todo programador, ya sea principiante o avanzado, ha de pasar. Y para hacer la vida más fácil al programador de GNOME, GLib nos ofrece una serie de funciones que harán más sencilla la depuración del código.

Estas funciones son útiles para comprobar que se cumple una serie de condiciones lógicas delimitadas por el programador. En caso de no ser cumplida, GLib emitirá un mensaje por consola, explicando que la condición que el programador ha puesto no se ha cumplido e indicando el archivo, la línea y la función en las que se ha roto la condición.

#define g_assert ( expresion );

La primera función de este tipo que vamos a ver es g_assert. Esta función recibe como parámetro un enunciado lógico y comprueba su validez. Por poner un ejemplo de su uso, podríamos exponer el caso en el que a una función se le pase un puntero. Y nosotros podríamos poner g_assert (ptr != NULL) para certificar que ese caso no se dé nunca. De hecho, si se diese ese caso, la función g_assert comprobaría que esa expresión no es válida y mandaría interrumpir la ejecución del programa. Para tener más claro este concepto, obsérvese la ejecución del siguiente ejemplo:

Ejemplo 6-2.3. Depuración de programas con g_assert.

Si se compila este programa, en la salida del mismo, se podrá observar que cuando entra por segunda vez en la función escribe_linea, cesa la ejecución del programa. Además, por la consola saldrá este mensaje o uno parecido.

        (process:457): ** ERROR **: file
          ejemplo.c: line 9 (escribe_linea):
        assertion failed: (cadena !=
          NULL)
        aborting...

El mensaje da información del proceso donde se rompió la condición y muestra por pantalla información del archivo, de la línea y de la función en las que se viola la condición por la cual g_assert ha parado la ejecución. También indica qué expresión se ha incumplido, porque se puede dar el caso de tener varios g_assert en una misma función.

Quizá un método tan drástico como parar la ejecución del programa no sea lo mejor. Es probable que sólo se busque algo que termine la ejecución de la función y avise que una condición no ha sido cumplida. Para estos casos existen varias macros que facilitarán mucho la vida de los programadores. Estas macros son:

#define g_return_if_fail ()( expresion );

#define g_return_val_if_fail ()( expresion , val );

Las dos macros son similares en su funcionamiento, ya que comprueban si la expresión lógica que se les pasa como primer parámetro es verdadera o falsa. En el caso de ser falsa, aparecerá por consola un mensaje explicando que esa condición no ha sido cumplida. Esto es muy parecido a lo que hacía g_assert (); la diferencia estriba en que estas dos funciones no finalizan la ejecución del programa, sino que simplemente hacen un retorno dentro de la propia función y, en consecuencia, el resto del programa seguirá ejecutándose.

Como se dijo antes, estas macros son similares. La diferencia es que la segunda, g_return_val_if_fail, hace que la función devuelva el valor que se le pasa como segundo parámetro.

Obviamente, g_return_if_fail se usará en funciones que no devuelvan ningún valor y g_return_val_if_fail en funciones que devuelvan algún valor. Para entender mejor estas funciones obsérvese el siguiente ejemplo.

Ejemplo 6-2.4. Depuración de programas con g_return_if_fail y g_return_val_if_fail.

Y, por consiguiente, la salida de este programa sería la siguiente:

          esto es una cadena
          esto es una cadena

          -- escribe_linea2 ha devuelto TRUE --

          (process:578): ** CRITICAL **: file glib-g_return_fail.c: line 6 (escribe_linea):
          assertion `cadena != NULL' failed

          (process:578): ** CRITICAL **: file glib-g_return_fail.c: line 18 (escribe_linea2):
          assertion `cadena != NULL' failed

          -- escribe_linea2 ha devuelto FALSE --

Como se puede ver, su ejecución también ofrece información sobre el lugar donde no se ha cumplido la expresión como ya lo hacía la función g_assert, salvo que no detiene la ejecución del programa.

Y estas son las funciones para depuración que se pueden encontrar dentro de la librería GLib. Aunque no son las únicas, sí son las más comunes.

Funciones de registro.

GLib tiene cuatro macros básicas para el tratamiento mensajes, por niveles de importancia. Estas macros permiten al programador realizar una registro del recorrido del programa especificando el nivel de importancia de los sucesos acontecidos dentro del mismo.

Las cuatro macros de las que estamos hablando reciben parámetros de la misma forma que lo hacian g_print o printf. De este modo, no sólo se tendrá una registro por niveles, sino que, además, se podrá especificar un texto preformateado. De este modo, los mensajes pueden representar por pantalla el contenido de variables o cualquier otra información que nos pudiera ser útil. Las cuatro macros de las que estamos hablando son :

#define g_message (...);

#define g_warning (...);

#define g_critical (...);

#define g_error (...);

Como se puede observar, las cuatro macros tienen nombres bastante intuitivos. Para empezar, el lector se habrá dado cuenta, por los nombres de las funciones, de que están ordenadas de menor a mayor importancia, siendo g_message menos importante que g_error. A continuación se explicará lo que hace cada una de ellas.

g_message es una macro que emite un mensaje normal con la información que se le ha pasado como parámetro. La información que emite esta macro no tiene por que estar asociada a un error en el programa. Su principal aplicación es el envío de mensajes con la información que se crea conveniente en el proceso de realización del programa.

g_warning emite un mensaje de aviso con la información que le ha sido pasada como parámetro. Esta información puede servir para ser avisados de situaciones peligrosas que se podrían dar en el programa.

g_critical cumple la misma función que g_warning, sólo que éste además tiene la posibilidad de abortar el programa usando la función g_log_set_always_fatal().

g_erroremite el mensaje que le haya sido pasado como argumento y además aborta irremediablemente la ejecución del programa. Esta macro se usa para obtener información de donde ha sucedido el error irrecuperable con la seguridad de que, a causa de este error, el programa terminara abortando su ejecución de todas maneras.

Para una mejor comprensión se muestra el siguiente ejemplo. En él se han definido cuatro funciones, cada una de las cuales emite un mensaje diferente. En este ejemplo no se hace que g_critical tenga capacidad de abortar el programa porque si no, no veríamos como funciona g_error.

Ejemplo 6-2.5. Uso de mensajes de registro.

Aunque este ejemplo no tiene mucha funcionalidad, sirve para ver en acción a las cuatro macros en un mismo programa y da la posibilidad de contemplar en la salida de este programa los mensajes que envía a la consola. Si se ejecuta el ejemplo su salida sería:

          Message: Yo soy solamente un mensaje de aviso


          (process:603): ** WARNING **: Esto es un aviso, algo puede ir mal


          (process:603): ** CRITICAL **: Esto se pone difícil :(


          (process:603): ** ERROR **: 3, 2, 1... acabo de morirme. RIP

          aborting...
          `trap' para punto de parada/seguimiento (core dumped)

Como se puede ver, todas las funciones y macros que se han mostrado en este apartado nos serán especialmente útiles a la hora de realizar un programa y nos posibilitarán una vía estandarizada y muy flexible de emisión de información al exterior. Facilitando la corrección del código y haciendo más facil la tarea del programador.

Documentacion/Desarrollo/Glib (última edición 2008-12-04 08:48:53 efectuada por anónimo)