Pour ces premiers pas, nous allons nous contenter d’ouvrir
un fichier de type texte, et de lire son contenu dans un buffer que nous afficherons
avec notre sous routine de vidage mémoire. Tout d’abord créez avec nano un petit
fichier avec un texte quelconque et sauvez le avec le nom fic1.txt.
Dans la documentation des appels système linux, nous
trouvons le code 5 pour la fonction OPEN à laquelle il faut fournir le nom du
fichier (avec éventuellement son chemin) et des paramètres pour indiquer si
nous voulons ouvrir le fichier en mode lecture seule (O_RDONLY) en mode
écriture( O_WRONLY) ou lecture écriture (O_RDWR).
Lien vers le source du programme.
Nous allons créer ces valeurs comme constantes et ici je les
ai mis en commentaires pour vous donner les codes correspondants car elles sont
contenues maintenant dans mon fichier des constantes.
Dans la section .data nous ajoutons les libellés d’erreur et
une zone où nous mettons le nom du fichier (fic1.txt) que nous allons lire (En
commentaire j’ai mis aussi le nom du fichier complet pour tester ce cas). Dans
la section .bss, nous déclarons un buffer avec une taille suffisante.
Dans la partie code, nous renseignons les 3 registres avec
l’adresse du nom du fichier, le mode d’ouverture avant d’appeler la
fonction d’ouverture. En retour nous testons le registre r0 pour voir s’il y a
une erreur (vous pouvez le tester en mettant un nom de fichier inexistant dans
le répertoire). S’il n’y a pas d’erreur le registre contient un identifiant le File
Descriptor (FD) qui va remplacer dans les autres appels les codes des consoles
standards (vous savez 0, 1 ou 2)
Car en effet pour la lecture des données, nous utilisons le
même appel système (READ) que pour la saisie au clavier, seul le code du
registre r0 est changé (et le buffer doit être plus grand !!).
Ensuite nous affichons le contenu du buffer pour
vérification et nous terminons le programme par la fermeture du fichier (appel
système CLOSE). Le FD du fichier a du être conservé dans le registre r8 pour éviter
sa perte dans le registre r0 utilisé lors de l’affichage précédent.
Voici le résultat :
Nous voyons que le registre r0 indique le nombre d’octets
lus (en hexa) que le buffer contient bien les caractères avec des lignes se
terminant par des caractères 0A. Le registre r8 contient la valeur 3 donc le FD
du programme. Ce nombre doit correspondre à un indice dans une table gérée par
le système. Il faudrait voir s’il est possible d’accéder à cette table pour
extraire des infos intéressantes sur le fichier : c’est à creuser.
Nous avons lu un fichier au format texte et donc comment
lire d’autres formats ?: et bien pareil car rien en assembleur ne
différencie les types de fichiers, c’est à vous programmeur à définir ce que
représente le fichier et à le traiter de la bonne façon. D’autre part nous
avons déclarer un buffer de 512 caractères ce qui est minuscule si nous voulons
traiter de gros fichiers. Soit nous déclarons des buffers de millions d’octets
(dans la limite de la mémoire disponible) soit nous écrivons une boucle qui lit
le fichier par tranche de 512 octets (ou autres valeurs) et nous traitons
chaque tranche avant de lire l’autre.
Voyons maintenant l’écriture dans un fichier : c’est
tout bête car nous allons utiliser le même appel système que notre sous routine
d’affichage (WRITE) mais en l’appliquant sur un File Descriptor que le système
va créer.
Pour compliquer un petit peu la programmation je vous
propose un programme qui va ouvrir un lire un fichier de commande dont le nom
sera passé dans la ligne de commande. Chaque ligne de ce fichier de la forme
nom=valeur sera décodée et utilisée pour écrire une série de chiffre dans un
fichier de sortie.
Voici le contenu du fichier de commande et qui s’appelle commande.txt (Original non !!) :
Ligne=10
Fichier=resultat.txt
Il est crée avec un éditeur de texte comme nano.
Analysons le programme (lien vers le source du programme) :
Dans la section .data après les habituels libellés d’erreur
nous trouvons la déclaration de tables de pointeurs qui vont nous permettre de
décrire les mots clés autorisés dans le fichier commande. Tout d’abord nous
trouvons une table de pointeurs vers les libellés des mots clé : pour le
premier nous trouvons le libellé fichier à l’adresse cle1 que nous stockons
dans le premier poste. Ensuite nous trouvons la table du type de chaque mot
clé : alpha ou numérique et enfin nous trouvons une table des valeurs de
chaque mot clé. Cette dernière sera alimentée après le décodage du fichier
commande. Si le type d’un mot clé est numérique, le poste contiendra sa valeur
après conversion sinon le poste contiendra l’adresse de la chaine
correspondante.
Ensuite nous trouvons un pointeur du début du tas. Un tas
est une grande zone de mémoire qui va nous servir à stocker les chaines de
caractères les unes à la suite des autres. Bien sûr chaque chaine sera
identifié par un pointeur vers son début. Pour stocker une chaine dans le tas
il nous faut donc toujours connaitre l’adresse de la première position libre
sur le tas.
Dans la section .bss, nous déclarons les buffers et zones de
travail et bien sûr le tas. Ici il est de petite taille mais pour des
programmes qui devront manipuler des quantités de chaines il pourra être
beaucoup plus important. (Cela me fait penser que dans ce programme, je ne vérifie
pas si le stockage d’une chaine dépasse la taille du tas !! à prévoir).
Dans la partie code, nous commençons par récupérer le nombre
de paramètres de la ligne de commande et nous vérifions s’il y a en au moins 2
(rappel le 1er est toujours le nom du programme lancé). Nous
récupérons l’adresse du 2ième que nous mettons dans r5 et nous nous
en servons pour ouvrir le fichier.et récupérer le FD pour lire le contenu et
enfin pour le fermer car nous avons les données lues dans la zone buffer.
Ensuite nous appelons une routine qui va analyser les lignes
lues. Nous allons balayer les caractères un à un pour trouver un mot clé (dont
la fin est signalée par le signe =) en éliminant les blancs éventuels et en
terminant si nous trouvons les caractères 0 ou 0A hexa. Dés que nous avons
trouvé le signe égal, nous continuons l’analyse mais en mettant dans le
registre r5, l’adresse de la zone valeur pour y stockée les caractères jusqu’à
trouver une fin de ligne ou un 0 binaire.
Si la ligne est complète cad si nous avons les zones motcle et valeurs
renseignes nous appelons la sous routine TraitMotCle.
Dans celle-ci, nous allons chercher si le mot cle trouvé est
un mot clé autorisé en balayant la table des mot clé et en effectuant une
comparaison des 2 chaines. Si nous avons égalité , nous cherchons le type
correspondant grâce à l’indice trouvé.
Si le type est numérique nous devons convertir la chaine stockée dans valeur en
une valeur numérique qui sera stockée dans la table des valeurs dans le poste
correspondant à l’indice trouvée. Si le type est alphanumérique, c’est un peu
plus compliqué. En effet il faut stocker la chaine dans le tas.Pour cela nous
récupérons l’adresse du début du tas puis nous recopions la chaine contenue à
l’adresse valeur sur le tas puis nous mettons à jour le pointeur de tas avec la
prochaine position libre.
Donc au final, nous avons analysé chaque ligne du fichier
commande et stocké dans une table chaque valeur associé au mot clé. Vous pouvez
voir qu’il est possible d’ajouter d’autres mots clé sans modifier la
programmation. Il est possible aussi d’améliorer l’analyse pour signaler qu’ un
mot clé indispensable n’a pas été trouvé (ou plutôt prévoir une valeur par
défaut).
Maintenant nous
revenons dans le programme principal et nous savons que dans le 1er
poste de la table des valeurs nous avons l’adresse du nom du fichier de sortie.
Nous créons ce fichier par l’appel de la fonction CREATE (code 8) avec un
masque spécial ficmask1: .octa 0644. Ce code permet d’avoir les droits corrects
pour l’utilisation du fichier (voir la documentation Unix sur les droits des
fichiers ). Cette fonction retourne dans r0 un FD que nous utiliserons pour
écrire dans le fichier par l’appel système WRITE comme nous l’avons utilisé
pour la routine d’affichage.
Mais auparavant, il nous faut créer les données à écrire.
Pour cela nous récupérons la valeur du paramètre ligne (ici égal à 10) et par
une boucle nous générons autant de lignes qui contient le N° de boucle ? Évidement,
il nous faut convertir le compteur de boucle en caractères ascii et en décimal
signé (la même fonction que nous avons utilisé pour afficher un registre). Nous
ajoutons à chaque ligne le caractère de fin de ligne 0A hexa et nous stockons
le tout dans la zone buffer. Pour l’écriture, il nous faut connaitre le nombre
de caractères total et donc nous le comptons dans le registre r2.
En fin nous fermons le fichier par l’appel système CLOSE.
Vous pouvez regarder le résultat en affichant par more le contenu du fichier
resultat.txt. Vous pouvez modifier le fichier commande pour changer le nombre
de lignes à écrire ou le nom du fichier de sortie sans avoir à modifier le
programme.
Cet exemple est à conserver pour des développements futurs
car nous avons vu quelques points qui vont se répéter lors de l’écriture de
programmes.
Exercice : ajouter dans le fichier commande le mot clé
titre=ETAT NO 1 et modifier le programme
pour faire apparaitre ce titre en haut du fichier résultat.
Tester le
programme en éclatant une ligne sur plusieurs lignes par exemple :
Ligne
=
10
Corriger le programme pour qu’il fonctionne correctement.
Ajouter dans le fichier des commandes des commentaires
commençant par * : ceux-ci ne doivent pas perturber les résultats.
Aucun commentaire:
Enregistrer un commentaire