next up previous contents index
Next: Recherche dans une liste Up: Premier programmes Previous: Entées / sorties   Contents   Index

Subsections

Plus loin avec les entrées/sorties

Pour le langage Scheme, un port est associé à un fichier connu par son nom.

L'environnement OpenScheme étend la notion de port aux ports virtuels. Parmi les ports virtuels, on trouve les chaînes de caractères, les connexions FTP ou HTTP... De plus, l'environnement propose une puissante fonction de sortie formatée.

Fonctions d'entrée / sorties de bas niveau

Dans la spécification du langage Scheme, les ports ne peuvent être accédés que de manière séquentielle de manière à lire des caractère ou des objets Scheme. OpenScheme permet de lire toutes sorte de données et de le faire en accès direct.

A chaque port est associé un pointeur de fichier. Pour les fichiers en correspondance avec un fichier sur le disque, ce pointeur de fichier peut être consulté et/ou déplacé à loisir, permettant un accès direct à toutes les parties du fichier.

La fonction :

(port-tell [port])

retourne la valeur du pointeur de fichier sous la forme d'un entier. Si le port est omis, le port d'entrée standard est utilisé. La fonction :

(port-seek n [port])

permet de modifier la valeur d'un pointeur de fichier. Si port n'est pas renseigné, le port d'entrée standard est utilisé. La fonction :

(port-length [port])

retourne la valeur maximale d'un pointeur de fichier. Cette valeur correspond en fait à la taille en octets du fichier. Si le port n'est pas indiqué, le port d'entrée standard est utilisé.

La fonction :

(port-read n [port])

lit n octets d'un fichier et retourne la chaîne de caractères formées des octets lus à partir du pointeur de fichier. Si port n'est pas indiqué, le port d'entrée standard est utilisé. Enfin, la fonction :

(port-write str n [port])

écrit n octets de la chaîne de caractères str à la position du pointeur de fichier d'un port. Si port est omis, le port de sortie standard est utilisé.

Caractère spéciaux

Les chaînes de caractères destinées aux sorties peuvent contenir tous les caractères imprimables classiques. OpenScheme reconnaît aussi des caractères spéciaux. Ceci sont préfixés par le caractères d'échappement anti-slash :

Caractère Signification
\n Saut de ligne
\r Début de ligne
\t Tabulation
\b Signal sonore

Par exemple :

Osm> (display "123\r456\t789\n012\n")
456     789
012
  => #unspecified

Sortie formatée

La fonction format permet l'affichage formaté des ses arguments. Sa syntaxe générale est :

(format port pilote arguments...)

Les arguments :

Par exemple :

Osm> (format #f "A test.")
  => A test.#unspecified

Osm> (format #f "A ~a." "test") 
  => "A test."#unspecified

Osm> (define l '(1 2 3))
  => #unspecified

Osm> (set-car! (cdr l) l)
  => #unspecified
     ; a recursive list is built

Osm> (format *current-output-port* "list = ~w\n)
                    (1 ... 3)
  => #unspecified

Osm> (display l)
  => Segmentation fault

Port en mémoire

Les ports virtuels ouverts en mémoire permettent de bénéficier des fonctions standard d'entrée / sortie. Le port en mémoire peut être ouvert en lecture avec :

(open-input-string chaîne)

La fonction retourne un port d'entrée accessible par les fonctions standard :

Osm> (define port (open-input-string "12345"))
  => #unspecified

Osm> (let loop ([i 1])
       (let ([c (read-char port)])
         (if (not (eof-object? c))
          (begin
           (format #t "~a: ~a\n" i c)
           (loop (+ i 1))))))
1: #\1
2: #\2
3: #\3
4: #\4
5: #\5
  => #unspecified

Osm> (close-input-port port)
  => #unspecified

Lorsque tous les caractères ont été lus, les fonctions de lectures retournent la fin de fichier qui peut être testée avec la fonction eof-object?. Le port ouvert se referme avec la fonction standard.

Les fonctions call-with-input-string et with-input-from-string sont aussi disponibles et fonctionnent comme leurs homologues opérant sur les fichiers.

La fonction input-string? retourne #t si son argument est un port d'entrée en mémoire et #f sinon.

Un port en mémoire en sortie est ouvert avec la fonction :

(open-output-string)

Cette fonction retourne un port de sortie sur lequel les fonctions de sorties standard sont utilisables. Lorsque le travail est fini, il est possible de convertir le port en chaîne de caractères avec la fonction get-output-string.

Osm> (define port (open-output-string))
  => #unspecified

Osm> (display "message" port)
  => #unspecified

Osm> (display "- autre message" port)
  => #unspecified

Osm> (get-output-string port)
  => "message - autre message"

Osm> (close-output-port port)
  => #unspecified

Le port ouvert se referme avec la fonction standard.

Les fonctions call-with-output-string et with-output-to-string se comportent comme leurs homologues sur les fichiers.

Répertoires locaux

Les fonctions d'ouverture de port d'OpenScheme opèrent aussi sur les répertoires. Le comportement des fonctions d'entrée / sorties voient leur comportement adapté à ce type de port.

Un répertoire est ouvert simplement en indiquant son nom à l'une des fonctions opérant à l'ouverture des fichiers. Par exemple :

Osm> (define tmp (open-input-port "/tmp"))
  => #unspecified

Lorsque le port associé à un répertoire est ouvert en lecture, il retourne les noms des fichiers contenus dans ce répertoire, séparés par des retour chariot :

Osm> (let loop ([rep (read-line tmp)])
       (if (not (eof-object? rep))
         (begin
          (format #t "entrée: ~a\n" rep)
          (loop (read-line tmp)))))
entrée: 93499~df
entrée: 9e3499~df.tmp
entrée: 943327993-gtkrc-41306685
entrée: IMG_PARAMS.db
entrée: archive.500
entrée: axinstall.log
entrée: emacsEMFYx6
entrée: fvwmrcTPYp4h
  => #unspecified

La fonction port-seek permet de positionner le pointeur de fichier sur le numéro de l'entrée du répertoire correspondante, à partir de zéro. La fonction port-tell retourne le numéro de l'entrée courante.

Informations sur les fichiers d'un répertoire

Lorsque qu'un répertoire est ouvert, il est pratique de pouvoir obtenir des informations sur les fichiers qu'il contient. Pour cela, nous disposons de la fonction suivante :

(port-stat nom [répertoire])

Cette fonction retourne un vecteur contenant les informations relatives au fichier nommé nom du répertoire spécifié. Si aucun répertoire n'est spécifié, le fichier d'entrée standard est utilisé. Si une erreur se produit #f est la valeur de retour.

Le vecteur retourné contient les éléments suivants :

Port en lecture ET écriture

Par défaut, le langage Scheme permet d'ouvrir des fichiers en lecture ou en écriture.Lorsque le fichier est ouvert en écriture alors qu'il existe déjà, le contenu précédant est perdu et la taille du fichier est fixée à zéro.

L'environnement OpenScheme permet d'ouvrir des ports en lecture et écriture. Si le fichier existe, son contenu n'est pas perdu. Cependant, il sera écrasé si le pointeur de fichier n'est pas positionné à la fin de celui-ci. Les fonctions relatives à ces ports sont open-input-output-file, open-input-output-string et close-input-output-port.

De plus, les ports peuvent être ouverts en écriture de manière à ce que le contenu précédent ne soit pas altéré. Ces ports sont ouverts et fermés avec les fonctions open-append-file, close-append-port, call-with-append-file et with-append-to-file.

Connexion réseau

Les communications inter machines utilisent souvent les ports au sens de Unix ; l'objet ouvert est souvent connu sous le nom de socket. L'utilisation des sockets est maintenant disponibles sous d'autres environnement que Unix, comme des machines Windows ou Macintoch.

L'environnement OpenScheme permet ouvrir des ports distants et d'y accéder avec les fonctions d'entrées / sorties standards.

Pour ouvrir un socket, on utilise les fonctions standard d'ouverture de fichier, comme open-input-port en précisant le nom du fichier de la manière suivante (il est possible d'utiliser les fonctions d'ouverture standards ; cependant, les sockets permettent un accès bidirectionnel) :

socket://machine:port

Les fonctions pour manipuler le pointeur de fichier sont inactives sur ce type de port. Les fonctions de lecture/écriture en mode caractères sont utilisables comme si le socket était un fichier local.

Connexions FTP/HTTP

Les protocoles FTP (File Transfert Protocol) et HTTP (Hyper Text Transfert Protocol) sont très utilisés pour transférer des fichiers depuis ou vers un hôte distant. OpenScheme permet d'accéder à ces protocoles en utilisant les fonctions standards d'ouverture de fichiers. Pour cela, le fichier est spécifié de la manière suivante :

ftp://[utilisateur[:pass]@]hôte/fichier

ou

http://[utilisateur[:pass]@]hôte/fichier

Le nom d'utilisateur et le mot de passe sont optionnels. Le fichier peut spécifié soit un fichier ouvert en lecture soit en écriture. Les fonctions manipulant le pointeur de fichier sont actives.

Si fichier spécifie un répertoire distant, le nom des entrées du répertoire sont accessibles comme en mode local.

Exemple

Dans cet exemple, nous utilisons les ports sockets pour nous connecter à un serveur de mail POP afin de savoir si nous avons des mails disponibles.

Le protocole POP est très simple. Voici à la main, comment nous pouvons connaître le nombre de mail en attente.

Supposons que votre serveur de mail soit pop.serveur.fr et votre nom de login pour le mail soit cmoi et votre mot de passe mdp. Avec telnet, nous ouvrons le port 110 du serveur POP comme suit :

$ telnet pop.serveur.fr 110
Trying 192.101.21.253...
Connected to pop.serveur.fr.
Escape character is '^]'.
+OK POP3 pop.serveur.fr v5.12 server ready
USER cmoi
+OK User name accepted, password please
PASS mdp
+OK Mailbox open, 1 messages
LIST
+OK Mailbox scan listing follows
1 7503
. 
QUIT
+OK Sayonara
Connection closed by foreign host.
$

Nous commençons par nous identifier avec les commandes USER et PASS ; le nombre des messages est indiqués par le serveur. Avec la commande LISTE, nous obtenons la liste des messages ; lorsqu'il n'y a plus de message, un point est affiché. Enfin, la commande QUIT termine la session avec le serveur.

Tout cela est très simple, et nous allons programmer cela en OpenScheme !

#! /usr/local/bin/osm --banner=off --exec

(define (mail? server user pass)
 (let* (; port du serveur POP
        [port 110]
        ; adresse à ouvrir
        [url (format #f
                     "socket://~a:~a"
                     server
                     port)]
        ; le port lié à la socket
        [port (open-input-output-file url)]
        ; port pour le debug: 
        ;  #t écran, 
        ;  #f pas de message
        [debug #t])
   (if port
    (begin
     ; lire l'invite du serveur
     (format debug "~a\n" (read-line port))
     
     ; envoyer le nom d'utilisateur
     (format port "USER ~a\r\n" user)
     ; lire le résultat
     (format debug "~a\n" (read-line port))
     
     ; envoyer le mot de passe
     (format port "PASS ~a\r\n" pass)
     ; lire le résultat
     (format debug "~a\n" (read-line port))
     
     ; demander la liste des messages
     (display "LIST\r\n" port)
     ; lire le résultat
     (format debug "~a\n" (read-line port))
     
     ; lire la liste des messages
     (let ([n (let loop ()
               (let ([l (read-line port)])
                (format debug "~a\n" l)
                (if (or (eof-object? l)
                        ; si c'est un point
                        ; terminer
                        (eqv? l "."))
                  0
                  ; ajouter un et continuer
                  (+ 1 (loop)))))])
       
       ; terminer la session
       (display "QUIT\r\n" port)
       ; lire le résultat
       (format debug "~a\n" (read-line port))
       
       ; fermer le port
       (close-input-output-port port)
       
       ; retourner la valeur
       n))
     (error "Incapable d'ouvrir l'URL ~a"
            url))))

; programme principal
(let ([n (mail? "pop.serveur.fr" "cmoi" "mdp")])
  (format 
   #t
   "Vous avez ~a mail~a en attente\n"
   n
   (if (> n 1) "s" "")))

Le lecteur pourra s'il le désire consulter les RFC (Request For Comments) concernant le protocole POP et réaliser un client POP complet avec réception et émission de mail.


next up previous contents index
Next: Recherche dans une liste Up: Premier programmes Previous: Entées / sorties   Contents   Index
© 1993 to 2001 Erian Concept