TD 1 : Entrées-Sorties - Bibliothèque C

Introduction      FAQ   TD 1   TD 2   TD 3   TD 4   TD 5   TD 6   TD 7   TD 8

Récupérez l'archive forum.tar.gz

Comment mettre à jour son projet ?

La version actuelle de forum.tar.gz est celle du mardi 17 janvier 2007, 10h50.

La plupart des modifications que nous faisons sont dans les fichiers libforum*.a. Pour bénéficier de ces modifications, vous devez simplement extraire l'archive dans un autre répertoire, et déplacer les fichiers dans votre propre répertoire, avant de recompiler votre projet:

mkdir tmp_forum
cd tmp_forum
tar -zvxf ~/forum.tar.gz       # extraire l'archive dans un repertoire temporaire
mv libforum*.a ~/mon_forum/    # deplacer les librairies dans votre projet 
cd ..
rm -rf tmp_forum               # effacer le repertoire temporaire

cd ~/mon_forum/
make                           # recompiler

Pour des raisons peu claires, le programme ./client s'exécute difficilement dans les terminaux par défaut (Kconsole). Nous vous conseillons donc de lancer un autre type de terminal pour tester le client:

xterm & 

Dernières modifications:

Cette archive contient un certain nombre de fichiers sources (*.c et *.h), un Makefile (spécifiant les dépendances entre les fichiers) et deux librairies déjà compilées (libforum*.a). Le but des TD est d'écrire des fonctions remplaçant progressivement celles contenues dans ces librairies.

Comment compiler et tester le programme ?

Pour compiler le projet sur vos machines, vous devez d'abord extraire le contenu de l'archive:

tar -zxvf forum.tar.gz

A chaque fois que vous voulez compiler, il suffit de faire (dans forum/):

make

qui utilise le fichier Makefile pour compiler dans le bon ordre tous les fichiers:

gcc -Wall -g   -c -o common.o common.c
gcc -Wall -g   -c -o client.o client.c
gcc -Wall -g -o client common.o client.o libforumclient.a
gcc -Wall -g   -c -o server.o server.c
gcc -Wall -g -o server common.o server.o libforumserver.a

On pourra tester ensuite en lançant le serveur:

./server

puis le client intéractif:

./client

Finalement, utiliser la touche q pour terminer le client, et pour le serveur, la commande:

killall server

1.  Verrou persistant dans un fichier (program_lock et program_unlock)

Le client et le serveur font appel aux fonctions program_lock() et program_unlock(), prototypées et documentées dans common.h, et définies dans common.c. La première vérifie que le programme qui vient d'être lancé (client ou serveur) n'est pas déjà en train de s'exécuter, en testant la non-existence d'un certain fichier. La deuxième est exécutée à la terminaison du programme, pour supprimer ce fichier, et permettre la prochaine exécution du programme.

Implémentez ces fonctions dans common.c, à l'aide des fonctions de bibliothèque fopen(), fclose(), getenv() et unlink().

program_lock.c et program_unlock.c

2.  Lecture d'une ligne dans un fichier (read_line)

La fonction read_line() est prototypée dans common.h et définie dans common.c :

extern int read_line (FILE *fp, char *line, int maxlen);
Elle est utilisée par le client et le serveur pour lire une chaîne de caractéres dans un fichier. L'argument fp désigne ce fichier et line est un tampon d'au moins maxlen octets. La lecture s'arrête dès lors que l'un des trois cas suivants est rencontré :
  • le caractère \n (retour à la ligne) est lu ;
  • on arrive en fin de fichier (détecté via EOF ou feof()) ;
  • maxlen-1 caractères ont été lus.
La chaîne est complétée par un caractère \0, et la fonction retourne le nombre de caractères effectivement lus.

Cette fonction est actuellement implantée libforumclient.a pour permettre de tester le système complet. Vous devez implanter votre propre fonction read_line dans common.c, en supprimant les commentaires qui l'entourent.

read_line.c

3.  Écriture du fichier de configuration du client (write_rc)

Le fichier ~/.forum, appelé fichier de configuration du client, sert à mémoriser les articles déjà lus lors des exécutions précédentes du client par un utilisateur donné.

Le client maintient une liste doublement chaînée cyclique d'intervalles, définie de la manière suivante :

struct interval { 
  int beg; 
  int end; 
  struct interval *next; 
  struct interval *prev; 
}; 
Chaque intervalle désigne des numéros consécutifs d'articles déjà lus. Le champ prev de la tête et le champ next de la queue pointent respectivement sur la queue et la tête de la liste. La liste est supposée triée en permanence et les intervalles disjoints. L'intervalle [-1;-1] correspond à l'intervalle vide.

La fonction write_rc() est prototypée dans client.h et définie dans client.c :

extern void write_rc (char *filename, struct interval* intervals);
L'argument filename est le nom du fichier dans lequel écrire les intervalles d'articles lus et l'argument intervals est la liste d'intervalles.

Une fonction write_rc est déjà fournie dans libforumclient.a pour cette tâche. Ré-implantez cette fonction, en respectant le format décrit par l'exemple suivant : la liste formée de l'article 17 (intervalle 17 à 17) puis de l'intervalle 41 à 43 puis de l'intervalle 2000 à 2007 sera écrite sous la forme :

 17 41-43 2000-2007 
Le nombre et la nature des espaces (blanc, tabulation ou retour à la ligne) n'importe pas entre les intervalles (tant qu'il y en a au moins 1), mais les espaces sont interdits entre les numéros d'articles et les tirets.

write_rc.c

4.  Lecture du fichier de configuration du client (read_rc)

La fonction read_rc() est prototypée dans client.h et définie dans client.c :

extern struct interval* read_rc (char *filename);
L'argument filename est le nom du fichier depuis lequel les intervalles d'articles sont lus ; la fonction retourne une liste d'intervalles. La fonction doit également vérifier que la liste ainsi construite ne comporte que des intervalles disjoints et que ceux-ci sont triés. Si ce n'est pas le cas, ou que le format n'est pas respecté (voir question précédente), la fonction appelle exit_with_error en indiquant que le fichier de configuration est corrompu.

Une fonction read_rc est déjà fournie dans libforumclient.a pour cette tâche. Ré-implantez cette fonction. Vous aurez intérêt à utiliser les fonctions isdigit() et isspace et à construire l'automate fini correspondant à l'analyse lexicale du fichier de configuration.

read_rc.c

5.  Création du répertoire du forum (safe_mkdir)

Le serveur stocke l'ensemble des articles dans un répertoire de votre machine (/tmp/forum si vous n'utilisez pas l'argument -d). Si ce répertoire n'existe pas, le serveur le crée au démarrage.

Pour cela, le serveur appelle la fonction safe_mkdir() :

extern void safe_mkdir(char *directory);

Elle est définie dans le fichier server.c. Cette fonction ne gère pas certains cas, en particulier la possibilité que l'un des répertoires pères n'existent pas non plus (testez avec ./server -d /tmp/dir1/dir2/forum).

Modifiez la fonction safe_mkdir() pour supporter cette possibilité, en créant au besoin les répertoires pères.

Nouvelle fonction à utiliser: mkdir, et optionnellement l'appel système access pour simplifier le test d'existence d'un fichier (d'un répertoire en l'occurence).