Este es un informe sobre el trabajo realizado para una materia optativa llamada Software Libre/Código Abierto de la Universidad Nacional del Centro de la Provincia de Buenos Aires.
Ver la presentación con las pautas del enunciado.
1. 1
Software Libre / Código Abierto
Trabajo Final
22 de julio de 2008
Alumno: Martín Ignacio Pacheco
Lib.: 245214
2. 2
Cuestionario
1. Las libertades que deben garantizarse sobre el software para que este sea
considerado como software libre según Richard Stallman, fundador del
movimiento Software Libre, son:
Libertad 0: La libertad de ejecutar o usar un programa por cualquier
persona y sea cual sea el propósito (gubernamental, privado, militar,
etc.).
Libertad 1: La libertad de estudiar el funcionamiento del programa y
adaptarlo a nuestras necesidades. El acceso al código fuente es
necesario para llevar a cabo esta libertad.
Libertad 2: La libertad de copiar el programa para ayudar a cualquier
persona de la comunidad que lo necesite. La idea es la de distribuirlo
bajo algún tipo de licencia de software libre que tienda al bien
común.
Vale aclarar aquí, que cuando se hace referencia al bien común, no
significa el bien de la mayoría sino el bien de Todos.
Libertad 3: La libertad de modificar el programa y publicar estas
mejoras de modo tal que se beneficie a toda la comunidad.
Aquí la necesidad del acceso al código fuente también es
indispensable.
2. El movimiento del código abierto y el movimiento del software libre
comparten muchas cosas pero son filosóficamente distintos.
El movimiento código abierto es mucho más nuevo ya que apareció en
escena en el año1998, mientras que el movimiento del software libre surgió
a mediados de los 80.
Por empezar el término código abierto rompe con la ambigüedad que
provoca el término software libre (del ingles free software), ya que la palabra
free puede entenderse como gratis o libre. Software libre opera bajo el lema
“Free as in speech, not as free in beer” (Libre como en expresión, no como
en cerveza gratis).
El software libre, por lo general, si tiene un propietario y tiene licencia,
digamos que no es de dominio público. Esto se debe a que, por ejemplo,
alguien podría ser capaz de tomar un paquete de software libre, cambiarlo un
poco y convertirlo en un paquete de software propietario. Lo cual significa
que los usuarios que usen software libre no tendrían la libertad de cooperar y
compartir. Para evitar esto se usa una técnica llamada copy-left.
La idea del copy-left es que se le da la vuelta al copy-right. Es decir, que el
copy-left tiene un copy-right en donde los autores dan permiso a los usuarios
de distribuir copias, modificarlo para ampliarlo, etc, pero cuando lo
distribuyan deben ser bajos algunos términos, ni mas ni menos, para que
cuando este sea distribuido la libertad (o las libertades) viajen con el
software. De esta manera se convierte en un derecho inherente el cooperar
con gente y formar una comunidad.
La diferencia principal entre estos movimientos, es que el movimiento
código abierto, cree que tanto el software libre como el software no libre
3. 3
deben coexistir mientras que el movimiento de software libre cree que todo
el software debe ser libre.
A mi entender código abierto tiende a darle más importancia o relevancia a
los benéficos prácticos de compartir código fuente, digamos que es más
pragmático a la hora de presentar los beneficios que trae el software libre y
no se plantea motivos éticos haciendo caso omiso al factor de la libertad;
mientras que el software libre se maneja con conceptos más rígidos e
idealistas haciendo énfasis en los aspectos éticos del software dónde el
concepto de libertad es mucho mas profundo.
El movimiento código abierto podría verse como una metodología de
desarrollo, mientras que el software libre es la filosofía política que lo
motiva.
Creo que de cierta manera el movimiento código abierto ayuda o colabora de
otra manera al movimiento del software libre puesto que raramente se suelen
encontrar entre si en el proceso de colaboración. De manera tal que el
movimiento código abierto ha ido por otro camino distinto pero sin perder el
objetivo al cual apunta el movimiento del software libre.
3. Antes de mencionar las ventajas y desventajas del desarrollo de software
comercial clásico con respecto al desarrollo del software libre quiero
introducir una diferencia entre los dos tipos de desarrollo; ya que me parece
que es menester mencionarla.
Lo hago usando el clásico ejemplo de una receta de cocina que permite
preparar un determinado plato de comida.
Primero hay que tener en cuenta que para preparar un plato de comida es
necesario tener o saber la receta ya que sin ella no podríamos de ninguna
manera prepararlo o en todo caso nos saldría cualquier cosa menos el plato
en cuestión.
En este caso nuestra receta seria como el código fuente en la que las
instrucciones de la misma nos permiten confeccionar el plato.
Ahora bien, sin la receta sabemos que podemos degustar el plato, en el caso
de que lo haya preparado un tercero, pero si deseamos agregarle algún
condimento o modificarlo para crear otro tipo de plato, ya sea a gusto
personal o por una cuestión comercial, no sabemos si este va en contra de
algunos de los elementos que componen el plato original, por lo que haría
falta tener la receta para estar seguros de no estropear el plato.
En este sentido el código fuente en el desarrollo del software juega un rol
fundamental en como entendemos las libertades del mismo, pero
principalmente porque se logra mejor calidad por ende mayor seguridad,
mayor eficiencia; además el software evoluciona mas rápido y por sobre todo
se contribuye a nivel social, puesto que se tiende a un cooperativismo y nos
alejamos mas del competitivismo puesto que este primero nos ayuda a
mejorar a nivel humano.
Las ventajas que puedo mencionar son:
- Un costo bajo para adquirir un producto y el libre uso que se
puede hacer del mismo. Por ejemplo cualquier persona con una
maquina y una conexión a internet puede hacer uso de este tipo de
software, mucho más en el ámbito universitario sin sentirse
frustrado por el precio del software propietario.
Es cierto que el Software Libre supone que el usuario no es una
persona que carece de todo tipo de conocimientos
4. 4
computacionales, esto tampoco implica que se oriente a un usuario
totalmente entendido en el tema sino que hay tener en cuenta que
es una camino difícil en el cual incursionar, pero como
contrapartida se logra un gran potencial en muchos aspectos sobre
todo por la Libertad 1.
De esta manera podemos usar código de otras aplicaciones para
desarrollar una nueva sin necesidad de volver a realizar lo que
hacen esas aplicaciones. De manera el desarrollo de aplicaciones
es más eficiente.
- Tiene como principal objetivo compartir la información,
trabajando de manera cooperativa lo que potencia la innovación
tecnológica del software.
El Software Libre define que el conocimiento es de la humanidad,
es por ello que los usuarios juegan un rol muy importante en el
desarrollo.
- Se requieren menores requisitos de hardware y las soluciones de
software son mucho más duraderas. Por ejemplo hay servidores
que usan Linux sin interfaz grafica lo que reduce bastante los
requisitos de hardware. Es sabido que hoy por hoy para algún
programa nuevo, hablando de la mayoría del software propietario,
hay que tener ciertos requisitos de hardware considerables. Con
soluciones de Software Libre al existir usuarios con diferentes
intereses permite que estas soluciones tengan mas soporte en
general, más flexible.
- La corrección de errores tiene un gran dinamismo puesto que en
los proyectos colaboran muchas personas a través de la red de
forma tal que permite que un error sea reportado rápidamente y
que todos lo sepan.
- Se permite de manera muy flexible adaptar un programa a las
necesidades del usuario ya que se dispone del código fuente,
mientras que en el software propietario para adaptarlo dependemos
exclusivamente del dueño del programa.
- Teniendo en cuenta el punto anterior, es más factible que el
soporte que se brinda en una aplicación libre sea más eficiente que
en el caso del software propietario, puesto que en este caso el
profesional que hace de soporte dependa del dueño de la
aplicación.
- A nivel técnico el software evoluciona mas rápido. Los periodos
de detección de fallos de seguridad y la corrección de los mismos
es mucho mas rápida (de 2 a 5 días, en software privativo de 6
meses a 2 años).
- También hay una cuestión de seguridad de datos públicos que se
manipulan, puesto que con software libre puedo ver como se
manejan los mismos mientras que con el software propietario no.
Aquí quiero señalar que desde mi punto de vista el Gobierno o sea
el Estado en su totalidad debería usar herramientas públicas y
5. 5
libres para procesar todos los datos puesto que estos datos son
públicos y donde el uso de software propietario me oculta el
control computacional que se realizan sobre los mismos.
Las desventajas que puedo mencionar son:
- Se requiere gran coordinación para llevar a adelante desarrollos
grandes, por ejemplo es necesario tener un orden o estándar ala
hora de programar con tabulaciones, etc. Esto pasa porque las
personas que trabajan o contribuyen no están en un mismo lugar
físico, como si ocurre en casi todas las empresas de software
propietario.
- Algo que mencione antes, la curva de aprendizaje es mayor,
generalmente se tarda mas en aprender a usar software libre, que
uno propietario, por ejemplo Windows. Por lo que los
desarrolladores deben ser personas bien preparadas con
herramientas libres.
- Se puede crear confusión debido a la gran diversidad de
distribuciones que hay y también con respecto a las licencias, etc.
- No se tiene una garantía proveniente de un autor.
- La mayoría de las configuraciones del hardware no es intuitiva. Se
necesitan algunos conocimientos previos para llevar a cabo
algunas configuraciones. Hoy por hoy en la red existe material con
el cual se pueden profundizar estos temas.
4. Lo primero que uno nota en estos dos tipos de productos es que el software
propietario requiere de un serial o clave del producto. Por ejemplo en el caso
de Windows Vista además de requerir un serial para instalarlo, pide otra
clave para activar el producto después de la instalación, caso que no me
ocurrió nunca con productos del software libre.
También es necesario aceptar una licencia del producto, de manera que si
uno no esta de acuerdo con algo de lo que allí se dice directamente no puede
usar el producto.
Otra diferencia son los costos al cual se venden productos propietarios siendo
estos notablemente mayores.
Por lo general el software propietario es más intuitivo a la hora de usarlo por
primera vez. En cambio en el software libre el choque frente al uso de una
herramienta es un poquito más fuerte. Un ejemplo es el manejador Grub para
el entorno grafico KDE, puesto que para alguien recién iniciado puede llegar
a ocasionar problemas si lo usa sin conocimientos previos.
Otro aspecto es que los productos propietarios en general o en su mayoría
traen o simulan de alguna manera un entorno gráfico con el cual uno
interactúa. Esto mismo hace que el usuario no sepa como ocurren realmente
las cosas en el corazón de la aplicación.
En los productos de software libre uno si puede ver como es el entre telón del
programa, puesto que siempre vienen con documentación sobre el su
funcionamiento además del propio código fuente, por supuesto. A la hora de
6. 6
usar este tipo de productos, si uno se propone estudiar el producto puede
llevarlo y adaptarlo a un uso específico que se le quiere dar logrando de esta
manera una adaptación eficiente. Esto mismo con un producto propietario no
se puede hacer ya que el mismo producto me impone sus límites de alcance.
El soporte técnico que brindan en general los productos propietarios es
ineficiente o carecen de personal profesional. Lo peor de esto es que uno
adquiere el producto a un costo relativamente alto, mientras que con
productos libres el costo es menor y uno se puede comunicar con el
desarrollador o contactar gente de la comunidad para buscar ayuda y en su
mayoría son personas entendidas en el producto.
5. Mostrándole que el camino natural para aprender informática es utilizar
software libre, pero ¿porque?
Explicándole la historia de los movimientos de software libre y código
abierto, como nacieron y cuales son las bases en las cuales fueron
construidos. Principalmente recalcando que con este horizonte se tiende al
bien común, mostrando las diferencias frente al producto propietario y por
sobre todo que nos encaminamos hacia un conocimiento universal para todos
puesto que una de las libertades que debe gozar cualquier ser humano es la
del conocimiento.
También, destacando como con los productos libres se fomenta el método
científico y de esta manera se ve que la historia de la informática no pasa por
determinados nombres de empresas.
Mostrando que con los productos de software libre / código abierto se puede
aprender los conceptos fundamentales que sirven como base en el uso de las
herramientas informáticas. Un claro ejemplo es que en vez de enseñar a
realizar paginas web mediante algún entorno, por ejemplo DreamWeaver o
FontPage, primero es necesario aprender el lenguaje HTML y que con un
simple editor de textos rustico como pico o ed puedo hacer una pagina web,
obviamente no seria súper moderna pero si podemos hacer una.
Que con equipos de segunda mano se pueden correr aplicaciones libres.
Que se dispone de una enorme colección de productos que pueden se usados
legalmente sin ningún tipo de erogación monetaria o muy pequeña en
relación a los productos propietarios y como día a día esta colección crece
rápidamente.
Como en virtud del conocimiento funcional y no meramente operativo se
pueden adaptar rápidamente a programas privativos.
Que las herramientas públicas se pueden adaptar tanto como se desea a las
necesidades de cada uno, de poder combinarlas y mejorarlas.
Y por sobre todo que si se es coherente con la filosofía del software libre el
espíritu de investigación y cooperación deben estar siempre presentes.
6. En el caso de una persona del ámbito informático, también le explicaría la
base filosófica que sustenta tanto el movimiento software libre y el
movimiento código abierto.
Esto principalmente para que el individuo sepa el marco dentro del cual uno
esta inmerso cuando se hace uso de productos de este tipo.
Luego denotando las ventajas y desventajas antes mencionadas que se tienen
en el desarrollo de software libre.
En cuanto a la seguridad, que en los productos de software libre/código
abierto, las fallas de seguridad y cuestiones técnicas de los productos son
publicadas, haciendo que todos sepan de los errores lo cual permite prevenir
sabiendo hasta que se corrijan (corto tiempo en comparación con productos
7. 7
propietarios) ya que los productos propietarios basan la seguridad mediante
ocultamiento, o sea que no publican las fallas hasta que no publiquen la
solución de las mismas (llamado parche). Lo cual hace que en caso de que
uno mismo no sepa de tales errores de un determinado producto correr altos
riesgos usándolo.
Y lo más importante, que al usar estos productos hay que tener siempre
presente los ideales y que se esta contribuyendo socialmente a un
cooperativismo ya que con este se logra mejor calidad de productos que en
un marco competitivo.
8. 8
Práctico
Primero instale cvs descargándolo de la red. Esta parte práctica la realice en mi
computadora logeado como usuario común (mpacheco). En cada punto de esta
práctica voy exponiendo los comandos que uso y su correspondiente explicación de
lo que estoy haciendo.
1. Creo el directorio donde estará el repositorio del proyecto data2html.
mpacheco@CqPresarioF700:~$ mkdir data2html
Defino la variable de entorno (CVSROOT) para indicar el repositorio que se
usara.
mpacheco@CqPresarioF700:~$ export CVSROOT=~/data2html
Luego inicializo el cvs, este me creara la carpeta CVSROOT dentro de
data2html que contiene los archivos necesarios para tratarla como un
repositorio.
mpacheco@CqPresarioF700:~$ cvs init
Creo dentro del repositorio, data2html, la carpeta que contendrá el primer
módulo, en este caso la llame modulo_1. Aun no podemos trabajar con el.
mpacheco@CqPresarioF700:~$ mkdir ~/data2html/modulo_1
También cree otro directorio, dentro de mi home, en donde contener la copia
del trabajo.
mpacheco@CqPresarioF700:~$ mkdir copiasData2html
Ahora me posiciono dentro del directorio creado anteriormente
(copiasData2html) y hago un checkout, esto importa modulo_1 como módulo
de cvs dentro del directorio copiasData2html.
mpacheco@CqPresarioF700:~/copiasData2html$ cvs checkout modulo_1
El directorio modulo_1 volverá a ser creado y contendrá un subdirectorio de
control llamado CVS en donde se encuentra la información acerca del
modulo correspondiente dentro del repositorio.
A partir de ahora no será necesario usar la variable CVSROOT al trabajar
con este módulo de trabajo, ya que la información necesaria de conexión al
repositorio esta en el archivo CVS/Root, en el directorio de trabajo
(~/copiasData2html/modulo_1).
mpacheco@CqPresarioF700:~$ more /copiasData2html/modulo_1/CVS/Root
/home/mpacheco/data2html
Ahora una vez creado el script data2html, en el directorio
/home/mpacheco/script le asignamos permiso de ejecución.
mpacheco@CqPresarioF700:~$ ls -l script/
total 4
-rw-r--r-- 1 mpacheco mpacheco 139 2008-07-15 11:50 data2html
mpacheco@CqPresarioF700:~$ chmod 744 script/data2html
9. 9
mpacheco@CqPresarioF700:~$ ls -l script/
total 4
-rwxr--r-- 1 mpacheco mpacheco 139 2008-07-15 11:50 data2html
Entramos a nuestra copia de data2html y al directorio donde esta el
moudlo_1.
mpacheco@CqPresarioF700:~$ cd copiasData2html/modulo_1
Ahora movemos el script en cuestión a la capeta modulo_1 de nuestra copia.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ mv script/data2html.
Ahora agregamos el archivo al repositorio.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs add data2html
Luego hacemos el commit de los cambios al modulo_1.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs commit -m "Se
ingresa al repositorio data2html"
2. Los archivos usados como ejemplos del uso de srcipt son:
chessplayers
Jugadores de Ajedrez Profesional
Apellido:Nombre:Nacionalidad:ELO
Fischer:Robert:USA:2800
Kasparov:Gary:RUS:2802
Karpov:Anatoly:RUS:2754
Polgar:Judit:POL:2607
Bianchi:Albino:ARG:1546
articulos.txt
Articulos
Nombre:Unidades:Precio
Mermelada:5:3.70
Gaseosa:3:2.00
Vino:6:8.50
Fideo:12:3.00
10. 10
alumnos.dat
Listado de alumnos
Apellido:Nombre:Dni
Perez:Jose:283949320
Fernandez:Martin:30123456
Gonzalez:Juan:23434403
Ferreira:Pablo:23456789
Raymond:Eric:10234432
Parcker:Peter:14890254
Las salidas correspondientes con la ejecución del comando:
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ ./data2html
articulos.txt chessplayersalumnos.dat
'articulos.txt' formateado
'chessplayers' formateado
'alumnos.dat' formateado
El código html de cada uno de ellos respectivamente es:
chessplayers.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>Jugadores de Ajedrez Profesional</title>
</head>
<body>
<h1>Jugadores de Ajedrez Profesional</h1>
<center><table border=1>
<tr><td>Apellido</td><td>Nombre</td><td>Nacionalidad</td><td>ELO</td></tr>
<tr><td>Fischer</td><td>Robert</td><td>USA</td><td>2800</td></tr>
<tr><td>Kasparov</td><td>Gary</td><td>RUS</td><td>2802</td></tr>
<tr><td>Karpov</td><td>Anatoly</td><td>RUS</td><td>2754</td></tr>
<tr><td>Polgar</td><td>Judit</td><td>POL</td><td>2607</td></tr>
<tr><td>Bianchi</td><td>Albino</td><td>ARG</td><td>1546</td></tr>
</table></center>
11. 11
</body>
</html>
articulos.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>Articulos</title>
</head>
<body>
<h1>Articulos</h1>
<center><table border=1>
<tr><td>Nombre</td><td>Unidades</td><td>Precio</td></tr>
<tr><td>Mermelada</td><td>5</td><td>3.70</td></tr>
<tr><td>Gaseosa</td><td>3</td><td>2.00</td></tr>
<tr><td>Vino</td><td>6</td><td>8.50</td></tr>
<tr><td>Fideo</td><td>12</td><td>3.00</td></tr>
</table></center>
</body>
</html>
alumnos.dat.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://
www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>Listado de alumnos</title>
</head>
<body>
<h1>Listado de alumnos</h1>
<center><table border=1>
<tr><td>Apellido</td><td>Nombre</td><td>Dni</td></tr>
<tr><td>Perez</td><td>Jose</td><td>283949320</td></tr>
<tr><td>Fernandez</td><td>Martin</td><td>30123456</td></tr>
<tr><td>Gonzalez</td><td>Juan</td><td>23434403</td></tr>
<tr><td>Ferreira</td><td>Pablo</td><td>23456789</td></tr>
12. 12
<tr><td>Raymond</td><td>Eric</td><td>10234432</td></tr>
<tr><td>Parcker</td><td>Peter</td><td>14890254</td></tr>
</table></center>
</body>
</html>
En el caso de no pasarle ningún archivo por parámetro la salida
correspondiente seria:
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ ./data2html
Título de la tabla > Lista de cosas
Título de las columnas 'col1:...:colN' > Nombre:Utilidad:Precio:Unidades
Filas 'dato1:...:datoN', 'q|Q' para finalizar
Fila 1 > Alicate:Electricidad:$15.75:18
Fila 2 > Destornillador:Electricidad:$9.80:23
Fila 3 > Pro:Diversión:Gratis:1000
Fila 4 > q
'file.tmp' formateado
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<title>Lista de cosas</title>
</head>
<body>
<h1>Lista de cosas</h1>
<center><table border=1>
<tr><td>Nombre</td><td>Utilidad</td><td>Precio</td><td>Unidades</td></tr>
<tr><td>Alicate</td><td>Electricidad</td><td>$15.75</td><td>18</td></tr>
<tr><td>Destornillador</td><td>Electricidad</td><td>$9.80</td><td>23</td></tr>
<tr><td>Pro</td><td>Diversión</td><td>Gratis</td><td>1000</td></tr>
</table></center>
</body>
</html>
El script esta comentado explicando lo que se hace en cada paso y porque el
uso de algunos comandos.
13. 13
data2html
#!/bin/bash
#
# trabajo.lyx, v 1.4 2007/03/14 21:32:34 hcurti Exp
#
# Este script trasnforma texto delimitado en texto con formato html.
# Existen dos formas de pasarle argumentos.
# Sin argumentos, se usara la entrada y salida estandar. Caso tratado con la
función
# tratamiento_a.
# Con ficheros como argumentos, sera tratado con la función tratammiento_b.
#
# Funcion que verifica que un fichero tenga terminación .txt en su nombre.
function verificar_txt {
sin_txtx=$(echo ${arg%.txt})
if [ "$1" = "$sin_txt" ]; then
return 1
fi
return 0
}
# Función que hace la conversión propiamente dicha.
function convertir {
# Se crea el archivo con la extensión indicada.
verificar_txt $1
ok_txt=$?
if [ $ok_txt = 0 ]; then
arg=$1
file_dest=$(echo ${arg%.txt}).html
else
file_dest=$1.html
fi
echo > $file_dest
# Se insertan las lineas con formato en el archivo file_dest.
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">' > $file_dest
echo "<html>" >> $file_dest
echo " <head>" >> $file_dest
echo " <title>$(head -1 $1)</title>" >> $file_dest
14. 14
echo " </head>" >> $file_dest
echo " <body>" >> $file_dest
echo " <h1>$(head -1 $1)</h1>" >> $file_dest
echo " <center><table border=1>" >> $file_dest
# Se insertaran las filas de la tabla formateada, iterando por cada una
de ellas
# Obtengo el numero de filas del archivo en cuestion, descartando la fila
que tiene el título.
num_filas=$(wc -l $1 | cut -d " " -f 1)
# Le resto uno, la fila de título, para iterar solo por las filas
restantes.
let "num_filas= $num_filas - 1"
# Calculo el núemro de columnas que tengo en la tabla contando la
cantidad de palabras que tengo en el archivo y lo divido por la cantidad
de filas
# ya que el numero de palabras por fila es proporcional.
num_cols=$(tail -${num_filas} $1 | tr ":" " " | wc -w) # Se usa 'tr ":" "
"' para que 'wc -w' cuente las plabras de cada fila, ya que con los : la
cuenta como una palabra entera.
let "num_cols= $num_cols / $num_filas"
# Se recorren las lineas del archivo menos la primera que pertenece a la
del título.
for line in $(tail -${num_filas} $1)
do
fila="<tr>"
col=1
# Se recorren las columnas de cada linea haciendo la fila
formateada.
while [ $col -le $num_cols ]; do
fila=" $fila<td>$(echo $line |cut -d: -f $col)</td>"
let "col=$col + 1"
done
fila="$fila</tr>"
# Se coloca la fila formatead en el archivo .html.
echo "$fila" >> $file_dest
done
echo " </table></center>" >> $file_dest
echo " </body>" >> $file_dest
echo "</html>" >> $file_dest
# Se retorna el exito del utltimo comando de la función.
return 0
}
15. 15
# Función que verifica errores y ejecuta la conversión.
function procesar {
# -r verifica que el fichero exista y tenga permiso de lectura.
if [ -r $1 ]; then
convertir $1
# Tomamos el exito o fracaso de la función anterior y mostramos si
se pudo o no procesar el argumento.
convertido_ok=$?
if [ $convertido_ok = 0 ]; then
echo "'$1' formateado"
fi
else
echo ":El fichero '$1' no es válido"
# Retorna el exito de la ultima sentencia de la función, si se usa
exit 1(para el caso de fallo) en lugar de return 1,
# en la función tratamiento_b se sale de la iteración sin
verificar el resto de los archivos en caso de haber otros
# despues del que fallo.
return 1
fi
}
# Función que trata el caso de no tener argumentos.
# Se piden los datos por la entrada estandar, luego se van colocando en un
archivo temporal, este se manda a la función procesar,
# y luego se muestra el archivo temporal por la salida standar. Por ultimo se
borran los archivos temporales.
function tratamiento_a {
echo -n "Título de la tabla > "
read titulo
echo $titulo > file.tmp
echo -n "Título de las columnas 'col1:...:colN' > "
read columnas
echo $columnas >> file.tmp
echo "Filas 'dato1:...:datoN', 'q|Q' para finalizar"
dato="continue"
declare -i f=1
while [ $dato != "q" ] && [ $dato != "Q" ]; do
echo -n "Fila ${f} > "
read dato
16. 16
if [ $dato != "q" ] && [ $dato != "Q" ]; then
echo $dato >> file.tmp
f=$f+1
fi
done
procesar file.tmp
cat file.tmp.html
rm file.tmp
rm file.tmp.html
}
# Función que trata el caso de archivos pasados como argumento iterando por cada
uno de ellos.
function tratamiento_b {
for arg in "$@"
do
procesar $arg
done
}
# Verifica si tenemos argumentos.
if [ $# -eq 0 ]; then
tratamiento_a
else
tratamiento_b $@
fi
3. Realizamos el commit del script usando tag v_1_0. Este TAG permite
obtener versiones del modulo independiente de los números de revisión de
los archivos.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs commit -m "Se
hace el commit de data2html en v_1_0" data2html
/home/mpacheco/data2html/modulo_1/data2html,v <-- data2html
new revision: 1.2; previous revision: 1.1
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs tag v_1_0
cvs tag: Tagging .
T data2html
4. Para el tratamiento de la opciones en los parámetros del script uso el
comando getopt. Se puede usar también el comando getopts, que es una
evolución de getopt, pero no hace el tratamiento para parámetros largos.
17. 17
En esta versión del script se corrigió un error, que era que cuando mostraba
la salida del código html por la salida estándar también se mostraba que el
archivo temporal file.tmp era procesado.
También se le amplio la capacidad de poder leer en el archivo los campos de
las columnas que tienen varias palabras. Lo que se hizo fue salvar los
espacios traduciéndolos en '_'. Luego cuando se inserta el código html estos
vuelven a ser espacios literales.
En cuanto a lo que concierne este ejercicio lo que no pude hacer fue que me
tomase el símbolo '?' como parámetro, osea que para la ayuda solo quedo '-h'
y '--help'.
Una cosa a tener en cuenta que fue aclarado en la ayuda que brinda el script,
es que cuando se le pasan los datos a los parámetros (por ejemlo '--title
Titulo_de_la_pagina') estos no pueden tener la cadena null, ya que se usa
para setear algunas variables, ni tampoco usar espacios. En cuanto a esto
último, el símbolo '_' queda traducido en el código html como un espacio
literal.
Las pruebas realizadas son sobre los archivos mencionados en el punto 2.
También lo probamos con otro en donde se usa como delimitador el carácter
4 y se usan varias palabras en algunos campos. El archivo es el
siguiente:
delimitados.txt
Personal
Nombres4Apellidos4Cargo
Juan Jose Antonio4Ramon Ortega4Gerente
Guillermo Martin4Tela Perez4Vicepresidente
Martin Ignacio4Pacheco Bonzkewitzz4Ayudante
Roberto4Zippo4Secretario
Filipo4Andolini4Secretario
Juan Jose4Strachi Cuneo4Abogado
Lamberto4Cardinale4Acesor
Luigui4Buffon4Acesor
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$./data2html -H -t
Empresa_local -d 4casas --css plantilla.css delimitados.txt
'delimitados.txt' formateado
En archivo de salida seria:
delimitados.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
18. 18
<html>
<head>
<META http-equiv=Content-Style-Type content=text/css>
<LINK href=otaplantilla.css type=text/css rel=stylesheet>
<title>Empresa local</title>
</head>
<body>
Personal
<center><table border=1>
<tr><td>Nombres</td><td>Apellidos</td><td>Cargo</td></tr>
<tr><td>Juan Jose Antonio</td><td>Ramon Ortega</td><td>Gerente</td></tr>
<tr><td>Guillermo Martin</td><td>Tela Perez</td><td>Vicepresidente</td></tr>
<tr><td>Martin Ignacio</td><td>Pacheco Bonzkewitzz</td><td>Ayudante</td></tr>
<tr><td>Roberto</td><td>Zippo</td><td>Secretario</td></tr>
<tr><td>Filipo</td><td>Andolini</td><td>Secretario</td></tr>
<tr><td>Juan Jose</td><td>Strachi Cuneo</td><td>Abogado</td></tr>
<tr><td>Lamberto</td><td>Cardinale</td><td>Acesor</td></tr>
<tr><td>Luigui</td><td>Buffon</td><td>Acesor</td></tr>
</table></center>
</body>
</html>
Como se puede observar se le paso como delimitador el string 4casas del cual
se tomo el primer carácter.
También se agregaron las líneas para una otraplantilla.css.
Como título de la página quedo el que le enviamos por parámetro pero sin el
'_'. Por último no se colocaron las etiquetas que corresponden a h1.
Un uso del script no usando un archivo(s) como entrada es el siguiente:
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ ./data2html -Ht
Titulo_de_la_pagina --css otaplantilla.css -d 02de
Título de la tabla > Titulo de la tabla
Título de las columnas 'col1:...:colN' > Tipo 10Tipo 20Tipo 3
19. 19
Filas 'dato1(delimitador)...(delimitador)datoN', 'q|Q' para finalizar, por
defecto delimitador es ':'
Fila 1 > Dato 10Dato 20Dato 3
Fila 2 > Dato 20Dato 50Dato 43 y Dato 23
Fila 3 > q
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/ TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<META http-equiv=Content-Style-Type content=text/css>
<LINK href=otaplantilla.css type=text/css rel=stylesheet>
<title>Titulo de la pagina</title>
</head>
<body>
Titulo de la tabla
<center><table border=1>
<tr><td>Tipo 1</td><td>Tipo 2</td><td>Tipo 3</td></tr>
<tr><td>Dato 1</td><td>Dato 2</td><td>Dato 3</td></tr>
<tr><td>Dato 2</td><td>Dato 5</td><td>Dato 43 y Dato 23</td></tr>
</table></center>
</body>
</html>
Como se puede observar en el código html se pueden poner espacios en las
celdas de la tabla.
Se uso como delimitador el ingresado por parámetro, en este caso el cero.
También se agrego el link aotrasplanilla.css y además se imprime el titulo de
la tabla sin la etiqueta h1.
Ahora se muestra este mismo ejemplo pero con el parámetro -e que imprime
solo la tabla.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ ./data2html -eHt
Titulo_de_la_pagina --css otaplantilla.css -d 02de
Título de la tabla > Titulo de la tabla
Título de las columnas 'col1:...:colN' > Tipo 10Tipo 20Tipo 3
20. 20
Filas 'dato1(delimitador)...(delimitador)datoN', 'q|Q' para finalizar, por
defecto delimitador es ':'
Fila 1 > Dato 10Dato 20Dato 3
Fila 2 > Dato 20Dato 50Dato 43 y Dato 23
Fila 3 > q
<table border=1>
<tr><td>Tipo 1</td><td>Tipo 2</td><td>Tipo 3</td></tr>
<tr><td>Dato 1</td><td>Dato 2</td><td>Dato 3</td></tr>
<tr><td>Dato 2</td><td>Dato 5</td><td>Dato 43 y Dato 23</td></tr>
</table>
El script quedo de la siguiente foma:
data2html
#!/bin/bash
#
# trabajo.lyx, v 1.4 2007/03/14 21:32:34 hcurti Exp
#
# Este script trasnforma texto delimitado en texto con formato html respetando
los siguientes elementos:
#<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
# "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
#<html>
# <head>
# <title>Titulo de la pagina</title>
# </head>
# <body>
# <h1>Titulo de la tabla</h1>
# <center><table border=1>
# <tr><th>Tipo_1</th><th>Tipo...</th><th>Tipo_N</th></tr>
# <tr><td>Dato_1.1</td><td>Dato...</td><td>Dato_1.N</td></tr>
# <tr><td>......</td><td>.......</td><td>......</td></tr>
# <tr><td>Dato_M.1</td><td>Dato...</td><td>Dato_M.N</td></tr>
21. 21
# </table></center>
# </body>
#</html>
#
# Ver la ayuda de la función help_ para ver la funcionalidades que se brindan.
#
# Funcion que muestra la ayuda por la salida estandar.
function help_ {
echo "Software Libre / Código Abierto - Facultad de Cs. Exactas - U.N.I.C.E.N."
echo "data2html, versión 2.0"
echo "Transforma un ARCHIVO(s) de texto delimitado, o la entrada estándar, en
texto con formato html."
echo "Modo de empleo: data2html [OPCION]... [FICHERO]..."
echo "Sin ARCHIVO(s) se lee por la entrada estandar y se muestra el resultado
por salida estandar."
echo "Con ARCHIVO(s) se generra un ARCHIVO.html."
echo ""
echo "-c, --css [PLANTILLA.CSS] Añade un link a PLANTILLA.CSS, no verifica la
existencia la misma."
echo " En campo PLANTILLA.CSS es obligartorio."
echo "-d, --delimiter [DELIM] Busca los campos separados en la tabla con
DELIM. Se toma el primer carácter de DELIM."
echo " El campo DELIM es obligatorio."
echo "-e, --embedded Se omite el codigo html fuera de <table>
</table>."
echo "-t, --title [TITULO] Utiliza como título de la pagina el campo TITULO
en lugar del que se encuentra en el archivo o el"
echo " ingresado por la entrada estandar. El campo
TITULO es obligatorio. Para espacios usar '_', luego se eliminan."
echo "-H, -noh1 No imprime el título de la tabla con h1."
echo "-h, --help Muestra esta ayuda y finaliza."
echo "-u, --usage Muestra las sinopsis aceptadas y finaliza."
echo "-v, --version Informa la versión y finaliza."
22. 22
echo ""
echo "La palbra 'null' para los datos de los parametros 'PLANTILLA.CSS' y
'TITULO' esta reservada."
echo ""
echo "Comunicar errores a <mpacheco@alumnos.exa.unicen.edu.ar>"
echo "Escrito por Martín I. Pacheco"
exit 0
}
# Funcion que muestra las sinopsis aceptadas.
function usage_ {
echo "data2html [OPCION]... [FICHERO]..."
echo "data2html [OPCION]..."
exit 0
}
# Funcion que muestra la versión.
function version_ {
echo "Version 2.0"
exit 0
}
# Funcion que verifica que un fichero tenga terminación .txt en su nombre.
function verificar_txt {
sin_txt=$(echo ${arg%.txt})
if [ "$1" = "$sin_txt" ] ; then
return 1
fi
return 0
}
# Función que hace la conversión propiamente dicha.
function convertir {
# Recibe como argumentos: archivo, plantilla.css, delimitador, flag embedded, el
titulo y el flag de sin h1.
23. 23
# Se crea el archivo con la extensión indicada.
verificar_txt $1
ok_txt=$?
if [ $ok_txt = 0 ] ; then
arg=$1
file_dest=$(echo ${arg%.txt}).html
else
file_dest=$1.html
fi
# Se mira el flag de embedded.
if [ $4 = "null" ] ; then
# Se insertan las lineas con formato en el archivo file_dest.
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">' > $file_dest
echo "<html>" >> $file_dest
echo " <head>" >> $file_dest
# Se agrega una linea de plantilla css si ha sido pasada como parámetro.
if [ $2 != "null" ] ; then
echo " <META http-equiv="Content-Style-Type" content="text/css">"
>> $file_dest
echo " <LINK href="$2" type="text/css" rel="stylesheet">" >>
$file_dest
fi
# Se cambia el titulo de pagina si hay un titulo pasado como parámetro.
if [ $5 != "null" ] ; then
echo " <title>$(echo $5 | tr "_" " ")</title>" >> $file_dest
else
echo " <title>$(head -1 $1)</title>" >> $file_dest
fi
echo " </head>" >> $file_dest
echo " <body>" >> $file_dest
# Se coloca el titulo sin <h1> observndo el flag.
24. 24
if [ $6 != "null" ] ; then
echo " $(head -1 $1)" >> $file_dest
else
echo " <h1>$(head -1 $1)</h1>" >> $file_dest
fi
echo -n " <center>" >> $file_dest
echo "<table border=1>" >> $file_dest
else
echo "<table border=1>" > $file_dest
fi
# Se insertaran las filas de la tabla formateada, iterando por cada una
de ellas
# Obtengo el numero de filas del archivo en cuestion, descartando la fila
que tiene el título.
num_filas=$(wc -l $1 | cut -d " " -f 1)
# Le resto uno, la fila de título, para iterar solo por las filas
restantes.
let "num_filas= $num_filas - 1"
# Calculo el núemro de columnas que tengo en la tabla contando la
cantidad de palabras que tengo en el archivo y lo divido por la
cantidad de filas
# ya que el numero de palabras por fila es proporcional.
# Se ve si hay un delimitador. Antes de traducir el delimitador para
contar las palabras se salvan los espacios con '_'.
if [ $3 != ":" ] ; then
num_cols=$(tail -${num_filas} $1 | tr " " "_" | tr "$3" " " | wc
-w)
else
num_cols=$(tail -${num_filas} $1 | tr " " "_" | tr ":" " " | wc -
w) # Se usa 'tr ":" " "' para que 'wc -w' cuente las plabras de
cada fila, ya que con los : la cuenta como una palabra
entera.
fi
let "num_cols= $num_cols / $num_filas"
25. 25
# Se recorren las lineas del archivo menos la primera que pertenece a la
del título.
for line in $(tail -${num_filas} $1 | tr " " "_")
do
fila="<tr>"
col=1
# Se recorren las columnas de cada linea haciendo la fila
formateada.
while [ $col -le $num_cols ]; do
# Se ve si hay un delimitador.
if [ $3 != ":" ] ; then
fila=" $fila<td>$(echo $line | tr "_" " " | cut -
d$3 -f $col)</td>"
else
fila=" $fila<td>$(echo $line | tr "_" " " | cut -d:
-f $col)</td>"
fi
let "col=$col + 1"
done
fila="$(echo $fila)</tr>"
# Se coloca la fila formatead en el archivo .html.
echo "$fila" >> $file_dest
done
# Se mira el flag de embedded.
if [ $4 = "null" ] ; then
echo " </table></center>" >> $file_dest
echo " </body>" >> $file_dest
echo "</html>" >> $file_dest
else
echo " </table>" >> $file_dest
fi
26. 26
# Se retorna el exito del utltimo comando de la función.
return 0
}
# Función que verifica errores y ejecuta la conversión.
function procesar {
# Recibe como argumentos: archivo, la plantilla css, delimitador, flag embedded,
el titulo y el flag de sin h1.
# -r verifica que el fichero exista y tenga permiso de lectura.
if [ -r $1 ] ; then
convertir $1 $2 $3 $4 $5 $6
# Tomamos el exito o fracaso de la función anterior y mostramos si se
pudo o no procesar el argumento.
convertido_ok=$?
if [ $convertido_ok = 0 ] && [ $1 != "file.tmp" ]; then
echo "'$1' formateado"
fi
else
echo ":El fichero '$1' no es válido"
# Retorna el exito de la ultima sentencia de la función, si se usa exit
1(para el caso de fallo) en lugar de return 1,
# en la función tratamiento_b se sale de la iteración sin verificar el
resto de los archivos en caso de haber otros
# despues del que fallo.
return 1
fi
}
# Función que trata el caso de no tener argumentos.
# Se piden los datos por la entrada estandar, luego se van colocando en un
archivo temporal, este se manda a la función procesar,
# y luego se muestra el archivo temporal por la salida standar. Por ultimo se
borran los archivos temporales.
function tratamiento_a {
27. 27
echo -n "Título de la tabla > "
read -n 256 titulo
echo $titulo > file.tmp
echo -n "Título de las columnas 'col1:...:colN' > "
read -n 256 columnas
echo $columnas >> file.tmp
echo "Filas 'dato1(delimitador)...(delimitador)datoN', 'q|Q' para finalizar, por
defecto delimitador es ':'"
dato_comp="continue" # Se utiliza dato_comp porque sino daba error al hacer las
comparaciones de $dato,donde esta tiene espacios.
declare -i f=1
while [ $dato_comp != "q" ] && [ $dato_comp != "Q" ] ; do
echo -n "Fila ${f} > "
read -n 256 dato
dato_comp=$(echo $dato | tr " " "_");
if [ $dato_comp != "q" ] && [ $dato_comp != "Q" ] ; then
echo $dato >> file.tmp
f=$f+1
fi
done
procesar file.tmp $1 $2 $3 $4 $5
echo ""
cat file.tmp.html
rm file.tmp
rm file.tmp.html
}
# Función que trata el caso de archivos pasados como argumento iterando por cada
uno de ellos.
function tratamiento_b {
# Se guardan los parametros de formato para lluego iterar sobre los ficheros de
entrada ya que se hace un corrimiento para iterar sobre los archivos.
file_css=$1
28. 28
delimiter=$2
embedded=$3
title=$4
noh1=$5
shift 5
for arg in "$@"
do
procesar $arg $file_css $delimiter $embedded $title $noh1
done
}
# Se setean las variables, donde se almacenan los argumentos o se setean los
flag, con null.
file_css="null"
delimiter=":" # Se setea con el delimitador por defecto.
embedded="null"
title="null"
noh1="null"
temp=`getopt -o c:d:et:Hhuv --long
css:,delimiter:,embedded,title:,noh1,help,usage,version
-n 'example.bash' -- "$@"`
if [ $? != 0 ] ; then
echo "Pruebe 'data2html --help' para mas información." >&2
exit 1
fi
eval set -- "$temp"
while true
do
case "$1" in
-c|--css) file_css=$2 ; if [ $2 = "null" ] ; then echo "null es palabra
reservada." ; echo "Pruebe 'data2html --help' para mas información." >&2; exit
1 ; fi ; shift 2 ;;
-d|--delimiter) delimiter=$(printf "%c" $2) ; shift 2 ;;
29. 29
-e|--embedded) embedded="true" ; shift ;;
-t|--title) title=$2 ; if [ $2 = "null" ] ; then echo "null es
palabra reservada." ; echo "Pruebe 'data2html --help' para mas
información." >&2; exit 1 ; fi ; shift 2 ;;
-H|--noh1) noh1="true" ; shift ;;
-h|--help) help_ ; shift ;;
-u|--usage) usage_ ; shift ;;
-v|--version) version_ ; shift ;;
--) shift ; break ;;
*) exit 1 ;;
esac
done
if [ $# -eq 0 ] ; then
tratamiento_a $file_css $delimiter $embedded $title $noh1 # Cuando no
tenemos archivos como entrada.
else
tratamiento_b $file_css $delimiter $embedded $title $noh1 $@ # Cuando
tenemos archivos como entrada.
fi
5. El makefile tiene tres objetivos, el primero de ellos instala el script en el
directorio Data2html dentro del home del usuario; el segundo hace la
desinstalación o sea se elimina este directorio con todo su contenido.
Con el tercer objetivo, clean, se borran los respaldos generados por los
editores de texto, o sea aquellos archivos que terminen con el símbolo '~'
dentro del directorio donde se instala el script.
Makefile
.PHONY= clean
SCRIPTS_TARGET= data2html
SCRIPTS_TARGETDIR= ~/Data2html
install : $(SCRIPTS_TARGET)
[ -d $(SCRIPTS_TARGETDIR) ] || mkdir $(SCRIPTS_TARGETDIR)
cp $< $(SCRIPTS_TARGETDIR)
uninstall :
rm -rf $(SCRIPTS_TARGETDIR)
30. 30
clean :
rm $(SCRIPTS_TARGETDIR)/*~
6. Se realiza el commit del script y del archivo Makefile y se marcan con el tag
v_2_0.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs add Makefile
cvs add: scheduling file `Makefile' for addition
cvs add: use `cvs commit' to add this file permanently
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs commit -m "Se
ingresa el archivo Makefile" Makefile
/home/mpacheco/data2html/modulo_1/Makefile,v <-- Makefile
initial revision: 1.1
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs commit -m "Se
hace el commit dedata2html en v_2_0" data2html
/home/mpacheco/data2html/modulo_1/data2html,v <-- data2html
new revision: 1.3; previous revision: 1.2
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs tag v_2_0
data2html
T data2html
Luego me equivoque y probé cvs tag pero sin ponerle el archivo y lo que
hizo fue ponerle también el tag v_2_0 al archivo Makefile.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs tag v_2_0
cvs tag: Tagging .
T Makefile
Esto me vino bien, para probar otras cosas. Lo que hice fue removerlo del
repositorio.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs remove Makefile
cvs remove: file `Makefile' still in working directory
cvs remove: 1 file exists; remove it first
Acá se puede ver que primero hay que borrarlo del directorio donde se hizo
el checkpoint o sea donde trabaje.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ rm Makefile
Ahora si lo boro del repositorio.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs remove Makefile
cvs remove: scheduling `Makefile' for removal
cvs remove: use `cvs commit' to remove this file permanently
Ahora hacemos el commit para aplicar los cambios.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs commit -m "Se
elimino Makefile porque quedo marcado con tag V_2_0"
cvs commit: Examining .
31. 31
/home/mpacheco/data2html/modulo_1/Makefile,v <-- Makefile
new revision: delete; previous revision: 1.1
Ahora hice de nuevo el Makefile de un respaldo que tenia y lo agregue
nuevamente al repositorio.
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs add Makefile
cvs add: Re-adding file `Makefile' after dead revision 1.2.
cvs add: use `cvs commit' to add this file permanently
mpacheco@CqPresarioF700:~/copiasData2html/modulo_1$ cvs commit -m "Se
ingresa de nuevo el archive Makefile" Makefile
/home/mpacheco/data2html/modulo_1/Makefile,v <-- Makefile
new revision: 1.3; previous revision: 1.2