Bob et Alice et Robert sont dans le même groupe de TD. Bob veut montrer son
programme (composé de plusieurs fichiers de grosses taille) à Alice sans que
Robert (qui peut deviner les noms des fichiers) puisse le lire.
Comment peut-il faire simplement en créant des fichiers et des répertoires
avec des droits d'accès bien choisis (Bob peut chuchoter une information
à Alice sans que Bob l'entende)?
Donner la liste des commandes, dans l'ordre, exécutées par Bob avant
d'envoyer de chuchoter un secret à Alice.
(On pourra utiliser les commandes shell
mkdir,
chmod,
cp.)
En fait Bob a fait cd pour se placer dans son répertoire de login ~bob,
puis il a fait une fausse manipulation: il a exécuté
la commande chmod -x . alors qu'il voulait
faire chmod og-x . et s'affole lorsqu'il
fait ls -ld et voit le résultat. Essayant de réparer, il fait
chmod og+x . mais il panique à la vue du résultat.
Sauriez-vous lui expliquer ce qui s'est passé et l'aider le à réparer?
Écrire des commandes mon_true et mon_false comparables aux
commandes true et false du système (tester man true et man false)
qui ne font rien et dont les
valeurs de retour sont respectivement toujours 0 et toujours 1.
Pour préciser la valeur de retour, on utilisera la fonction exit
du module Pervasives. Ce module est ouvert par défaut, il n'est
donc pas nécessaire de préfixer cette fonction par le nom du module,
ni d'ouvrir le module (open Pervasives).
Pour afficher la valeur de retour d'une commande qui vient d'être
exécutée par l'interpréteur de commandes on utilisera la commande
echo $?. (Testez vos programmes!)
Écrire une commande bonjour qui affiche la chaîne de caractères
"Bonjour monde !"
suivie d'un retour à la ligne sur la sortie standard et qui a toujours pour
valeur de retour 0. Pour réaliser l'affichage d'une chaîne de
caractères suivie d'un retour à la ligne, on utilisera la fonction
print_endline du module Pervasives.
Écrire une commande mon_echo qui affiche les chaînes de caractères
passées en argument sur la sortie standard séparées par un espace (pas
d'espace à la fin !) et a pour valeur de retour 0. Les arguments de
la ligne de commande sont placés dans le tableau argv du
module Sys dont l'élément d'indice i est récupéré par Sys.argv.(i).
La longueur d'un tableau est retournée par la fonction Array.length.
L'affichage d'une chaîne de caractères sans retour à la ligne se fait par la
fonction print_string du module Pervasives.
Écrire une commande mon_printenv qui affiche des informations sur
les variables d'environnement et qui a le même comportement que la
commande printenv du système (man):
Lorsque la commande est appelée sans argument, elle affiche
l'ensemble des variables d'environnement et retourne 0 lorsque
tout s'est bien passé. Pour cela,
on utilisera la fonction Unix.environment de type unit -> string array
qui retourne l'ensemble des variables d'environnement sous la forme
d'un tableau et la fonction Array.iter de type
('a -> unit) -> 'a array -> unit qui permet d'itérer une fonction passée
en premier argument
sur les éléments d'un tableau passé en second argument.
Lorsque la commande est appelée avec au moins un argument, elle
affiche (sur des lignes séparées) les valeurs des variables
d'environnement dont les noms sont passés en arguments. Si un nom
ne correspond pas à une variable d'environnement valide, la commande n'affiche rien pour cet argument, mais essaye
d'afficher les valeurs des arguments suivants. Si au moins un des
arguments n'est pas le nom d'une variable d'environnement valide la
commande retourne 1, sinon elle retourne 0. Pour récupérer la
valeur d'une variable d'environnement en fonction de son nom, on
utilisera la fonction Sys.getenv. Cette dernière lève une
exception Not_found lorsque le nom passé en argument ne correspond
pas à une variable d'environnement valide.
On a utilisé ici la fonction Unix.handle_unix_error de type
('a -> 'b) -> 'a -> 'b pour récupérer et afficher les éventuelles formes
de l'exception
Unix_error levées dans le module Unix lors des appels système.
La commande Unix.getcwd n'est pas toujours directement un appel système mais
simplement une fonction écrite en C (qui elle-même fait des appels
système). On se propose donc de réécrire la fonction getcwd en OCaml.
Cette fonction doit pouvoir être utilisée comme fonction de librairie : elle
ne doit donc pas modifier les variables globales du programme ni créer une
fuite de mémoire. Cette fonction remonte récursivement dans la hiérarchie
jusqu'à la racine du système en recherchant à chaque étape le répertoire
courant « . » dans le répertoire supérieur « .. ». Décrire le schéma
général du programme.
Écrire une fonction equal_node de type stats -> stats -> bool
qui teste si deux noeuds de la hiérarchie de fichiers sont
identiques. Deux noeuds sont identiques si et seulement si leurs
numéros de noeuds et leurs numéros de partition sont égaux.
Écrire une commande try_finally qui prend quatre arguments f, x,
finally et y et qui effectue le calcul f x, puis, avant de retourner
le résultat, exécute finally y, y compris lorsque le résultat est une
exception.
Écrire une fonction dir_find qui prend en arguments un prédicat fstring -> bool
et un nom de répertoire et recherche dans celui-ci le nom d'un fichier
qui satisfait le prédicat f. Si la fonction trouve le fichier elle
retourne son nom, sinon elle lève l'exception Not_found. Pour écrire
cette fonction, on utilisera les fonctions Unix.opendir,
Unix.readdir et Unix.closedir.
Pour ne pas créer une fuite de mémoire, on fera bien attention à
refermer le répertoire ouvert, y compris lorsqu'une exception est
levée, avant de rendre le résultat ou de relever une exception.
Écrire une fonction mon_getcwd qui se comporte comme Unix.getcwd.
L'algorithme manipule des chemins et des noeuds. Les informations sur les noeuds, indispensables à la
comparaison de noeuds, sont obtenues par l'appel système
Unix.lstat qui prend en argument un chemin (on
n'utilise pas Unix.stat ici car on recherche un chemin directe issu
de la racine qui ne traverse pas de liens symboliques). Pour être
portable, on utilisera les fonctions concat, current_dir_name (.) et parent_dir_name
(..) du module Filename pour manipuler les chemins.
On évitera l'utilisation de chdir qui affecterait alors le reste du
programme.
Terminer le programme. On n'oubliera pas d'appeler
handle_unix_error pour reporter les messages d'erreurs éventuels
qui peuvent se produire pendant le parcours de la hiérarchie.
On désire écrire une commande mon_find qui permet d'effectuer diverses
recherches dans le système de fichiers. On se limite ici à un sous-ensemble
des possibilités de la commande find du système.
1)
Donner des exemples de requêtes avec la
commande find d'Unix (man find) permettant d'exécuter les opérations
suivantes:
Afficher tous les chemins (fichiers et répertoires) accessibles depuis le
répertoire courant;
Afficher tous les chemins accessibles depuis le
répertoire courant qui correspondent à des répertoires et qui ont une
profondeur inférieure à 2 dans l'arborescence.
3) Écrire une seconde version mon_find1 qui prend en argument une
profondeur p et affiche la liste des chemins accessibles depuis le
répertoire courant avec une profondeur inférieure à p.
4) Implanter les comportements équivalents aux options :
-maxdepth ;
-type ;
-atime ;
-follow ;
-regexp.
Pour réaliser cette commande, on pourra utiliser le module Arg pour
récupérer les arguments de la ligne de commande et le module Str (dont la
librairie str.cma est à charger explicitement pour compiler)
pour manipuler les expressions régulières.