; .EnTete "Le-Lisp (c) version 15.2" " " "Le compilateur Termcap" ; .EnPied " " "%" " " ; .Chapitre 13 "Le Compilateur Termcap" ; ; .Centre "*****************************************************************" ; .Centre " Ce fichier est en lecture seule hors du projet ALE de l'INRIA. " ; .Centre " Il est maintenu par ILOG SA, 2 Avenue Gallie'ni, 94250 Gentilly " ; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA " ; .Centre "*****************************************************************" ; .Centre "$Header: termcap.ll,v 4.3 88/04/07 18:22:52 nuyens Exp $" (unless (>= (version) 15.2) (error 'load 'erricf 'termcap)) ; Tous les symboles pre'ce'de's de : seront cre'e's dans le package TERMCAP. (defvar #:sys-package:colon 'termcap) (add-feature 'termcap) ; Programme de ge'ne'ration de terminaux virtuels Le←Lisp ; a` partir de la base de donne'es termcap de UN*X. ; Le nom du fichier qui contient cette base de donne'e ; doit se trouver dans la variable #:system:termcap-file; ; le nom du catalogue qui contient les terminaux virtuels ; dans la variable #:system:virtty-directory ; (voir le fichier initial). ; .Section "Lecture de la base de donne'e TERMCAP" (de :tgetent (term) (let ((termcap (getenv "TERMCAP"))) (if (and termcap ; le cas ou la description est dans TERMCAP (neq #// (chrnth 0 termcap)) (index term termcap 0)) (pname termcap) (let ((line)) (with ((inchan (openi (or termcap #:system:termcap-file)))) (tag eof (until (setq line (:index term (:readline) 0))) (setq line (pname line)) (while (= (car (last line)) #/\) (setq line (nconc line (pname (:readline))))) (eof (inchan)))) line)))) (de :index (term line pos) (setq pos (index term line pos)) (and pos (let ((pos2 (+ pos (plength term)))) (if (and (or (= pos 0) (= (chrnth (1- pos) line) #/|)) (or (= (chrnth pos2 line) #/|) (= (chrnth pos2 line) #/:))) line (:index term line (1+ pos)))))) (de :readline () (let ((line (readstring))) (if (or (eqstring line "") (= (chrnth 0 line) #/#)) (:readline) line))) ; .Section "Transformation TERMCAP -> Liste Lisp" ; Cette fonction prend la liste des caracte`res et retourne ; une paire : la liste des noms de terminaux, ; (le premier est de 2 caracte`res de long (sic)) ; et une aliste (nom de l'entre'e, valeur de l'entre'e) ; La valeur de l'entre'e est toujours une liste de codes ascii ; (eventuellement vide). (de :parse (l) (let ((alist) (pos 0) (prop)) (while (neq (car l) #/:) (while (= (car l) #/|) (nextl l)) (until (or (null l) (= (car l) #/|) (= (car l) #/:)) (nextl l))) ; on lit maintenant les caracte'ristiques (untilexit eoln (while (memq (nth pos l) '(#\tab #\sp #\lf #\cr #/\ #/:)) (incr pos)) (setq prop (list (:readcn) (:readcn))) (selectq (nth pos l) (#/# (incr pos) (setq alist (acons prop (:readnumber) alist))) (#/= (incr pos) ; lit une chai↑ne eventuellement pre'ce'de'e par ; {{integer}.integer}{*} ; On jette les nombres obtenus ; (si les terminaux ne suivent pas, tant pis) (when (digitp (nth pos l)) (:readnumber) (when (eq (nth pos l) #/.) (incr pos) (:readnumber)) (if (eq (nth pos l) #/*) (incr pos))) (setq alist (acons prop (:readstring) alist))) (#/: (setq alist (acons prop nil alist))) (#/@ (incr pos) (setq alist (acons prop t alist))) (t))) (nreverse alist))))))))))) ; les fonctions de lecture suivante utilisent les variables globales ; <l> et <pos> qui sont modifie'es. (de :readcn () (incr pos) (when (>= pos (length l)) (exit eoln)) (selectq (nth (1- pos) l) (#/\ (prog1 (or (cassq (nth pos l) '((#/E . #\esc) (#/n . #\lf) (#/r . #\cr) (#/t . #\tab) (#/b . #\bs) (#/f . #↑L))) (and (digitp (nth pos l)) ; lecture en octal d'un nombre (let ((x (- (nth pos l) #/0))) (while (digitp (nth (incr pos) l)) (setq x (+ (* x 8) (- (nth pos l) #/0)))) (decr pos) x)) ; et par defaut (nth pos l)) (incr pos))) (#/↑ (prog1 (logand 31 (nth pos l)) (incr pos))) (t (nth (1- pos) l)))) ; lit un entier en de'cimal (de :readnumber () (let ((x 0)) (while (digitp (nth pos l)) (setq x (+ (* x 10) (- (nth pos l) #/0))) (incr pos)) x)) ; lit une chaine jusqu'au ":" suivant (de :readstring () (let ((liste)) (until (eq (nth pos l) #/:) (newl liste (:readcn))) (nreverse liste))) ; .Section "Fonctions de ge'ne'ration" ; On utilise trois variables de facon globale: ; - term : le nom du terminal pour lequel on genere tout ; - capas : la aliste des capacites du terminal ; - output-file : le fichier de sortie ; ge'ne`re le code d'une fonction "cursor" ; a` partir d'une chai↑ne termcap (de :cursor (l) (let ((lvar '(line col)) (lpar '(col line)) (exp) (char)) (while l (setq char (nextl l)) (if (neq char #/%) (newl exp `(tyo ,char)) (setq char (nextl l)) (selectq char (#/d (newl exp `(tyod ,(nextl lvar) 0))) (#/2 (newl exp `(tyod ,(nextl lvar) 2))) (#/3 (newl exp `(tyod ,(nextl lvar) 3))) (#/. (newl exp `(tyo ,(nextl lvar)))) (#/+ (newl exp `(tyo (+ ,(nextl l) ,(nextl lvar))))) (#/> (newl exp `(if (> ,(car lvar) ,(nextl l)) (incr ,(car lvar) ,(nextl l))))) (#/r (setq lpar (reverse lpar))) (#/i (newl exp `(incr line)) (newl exp `(incr col))) (#/% (newl exp `(tyo #/%))) (#/n (newl exp `(setq ,(car lvar) (logxor ,(car lvar) 96)))) (#/B (newl exp `(incr ,(car lvar) (* 6 (div ,(car lvar) 10))))) (#/D (newl exp `(decr ,(car lvar) (* 2 (rem ,(car lvar) 16))))) (t)))) (:de 'tycursor lpar exp))) ; Ici, on se livre a` une petite optimisation: ; on concate`ne les tyo conse'cutifs ; et on en profite pour renverser la liste. (de :de (sem par exp) (let ((l) (ltyo)) ; l: nouvelle exp; ltyo: liste des char a tyo (while exp (selectq (caar exp) (tyo (mapc (lambda (tyo) (newl ltyo tyo)) (reverse (cdr (nextl exp))))) ((incr decr setq) (newl l (nextl exp))) (t (when ltyo (newl l `(tyo ,.ltyo)) (setq ltyo nil)) (newl l (nextl exp))))) (when ltyo (newl l `(tyo ,.ltyo))) (terpri) (with ((lmargin 8)) (prin "(de #:tty:" (string term) ":" sem " " par) (while l (terpri) (outpos 4) (prin (nextl l))) (prin ")")) (terpri))) ; La me↑me mais sans parame`tre et avec n arguments. (de :den (sem . exp) (:de sem () exp)) (de :decapa (sem capa) (if (assoc capa capas) (:den sem (:cassoc capa)) (:warning sem))) (de :set (sem val) (terpri) (print "(defvar #:tty:" term ":" sem " " val ")") (print "(defvar #:tty:" sem " " val ") ; compatibilite' v15")) (de :warning (sem) (terpri) (printerror '#:tty:termcap (format () #- #:system:foreign-language "~S : ne sait pas faire" #+ #:system:foreign-language "~S : don't know how to" term) sem)) (de :cassoc (capa) `(tyo ,.(cassoc capa capas))) ; .Section "Fonction Principale" (de :compile (term output-file) (print "; On cree le fichier virtty a partir de termcap pour : " term) (let ((names (:tgetent term)) (capas) (#:system:print-for-read ())) ; on prote`ge le print (ifn names (error 'termcap 'ERRVIRTTY term)) (setq term (implode (pname term)) capas (:parse names)) ; le cas d'indirection (while (assoc '#"tc" capas) (let ((auxterm (cassoc '#"tc" capas)) (aux) (aux2)) (setq aux (:tgetent auxterm)) (ifn aux (error ':compile #- #:system:foreign-language "Ce terminal est defini par rapport a " #+ #:system:foreign-language "This terminal is defined in terms of " (string auxterm))) (setq aux (:parse aux) capas (append (remq (assoc '#"tc" capas) capas) aux)) ; pour chaque capacite supprimee, on recopie la liste (while (setq aux (car (rassq t capas))) (setq aux2 capas) (while aux2 (ifn (equal (caar aux2) aux) (nextl aux2) (rplac (nextl aux2) nil nil)))))) ; on regarde si le terminal sait adresser le curseur (ifn (assoc '#"cm" capas) (error ':compile #- #:system:foreign-language "Terminal ringard : pas d'adressage curseur" #+ #:system:foreign-language "Terminal lacks capability: cursor addressing" term)) ; on regarde si le terminal sait effacer l'ecran (ifn (or (assoc '#"cd" capas) (assoc '#"cl" capas)) (error ':compile #- #:system:foreign-language "Terminal ringard : pas d'effacement de l'ecran" #+ #:system:foreign-language "Terminal lacks capability: clear screen" term)) ; On commence la generation (with ((outchan (openo output-file))) ; Une petite banniere (print "; Le←Lisp version 15.2 :" #- #:system:foreign-language " compilation du terminal virtuel : " #+ #:system:foreign-language " compilation of virtual terminal: " term) ; Le nom du systeme (terpri) (print "(setq #:sys-package:tty '#:tty:" term ")") ; Les variables donnant le nombre de lignes et de colonnes (if (assoc '#"co" capas) (:set 'xmax (1- (cassoc '#"co" capas))) (:set 'xmax 79)) (if (assoc '#"li" capas) (:set 'ymax (1- (cassoc '#"li" capas))) (:set 'ymax 23)) (:cursor (cassoc '#"cm" capas)) ; S'il existe une visible bell, on redefinit bell (if (assoc '#"vb" capas) (:decapa 'tybeep '#"vb")) ; Effacement de l'ecran (if (assoc '#"cl" capas) (:decapa 'tycls '#"cl") (:den 'tycls (:cassoc '#"cd") `(tycursor 0 0))) ; Effacement jusqu'a la fin de ligne (:decapa 'tycleol '#"ce") ; Effacement de la fin de l'ecran (:decapa 'tycleos '#"cd") ; Effacement du caractere (if (assoc '#"dc" capas) (:den 'tydelch (:cassoc '#"ed") (:cassoc '#"dc") (:cassoc '#"dm")) (:warning 'tydelch)) ; Insertion d'une ligne (:decapa 'tyinsln '#"al") ; Effacement de la ligne (:decapa 'tydelln '#"dl") ; L'attribut soulignage ou inverse video (when (and (assoc '#"so" capas) (assoc '#"se" capas)) (:de 'tyattrib '(x) (list `(if x (tyo ,.(cassoc '#"so" capas)) (tyo ,.(cassoc '#"se" capas))))) (:set 'tyattrib ())) (when (and (assoc '#"us" capas) (assoc '#"ue" capas)) (:de 'tyattrib '(x) (list `(if x (tyo ,.(cassoc '#"us" capas)) (tyo ,.(cassoc '#"ue" capas))))) (:set 'tyattrib ())) ; Pour inserer un caractere ou une chaine (if (or (assoc '#"ic" capas) (assoc '#"im" capas) (assoc '#"ei" capas)) (:de 'tyinsch '(arg) (list (:cassoc '#"ei") `(tyo arg) (:cassoc '#"ic") (:cassoc '#"im"))) (:warning 'tyinsch)) ; La chaine d'initialisation ; La caracteristique if de termcap (let ((aux (cassoc '#"if" capas)) (l)) (when aux (with ((inchan (openi (string aux)))) (untilexit eof (newl l (readcn))))) (:den 'typrologue `(,(symbol (symbol 'tty term) 'tycls)) `(tyo ,.(nreverse l)) (:cassoc '#"is"))) ; Pour l'epilogue, on ramene simplement le curseur (:den 'tyepilogue `(tycursor 0 (sub1 ,(symbol (symbol 'tty term) 'ymax)))) ; Le curseur cache'/vu (:set 'tyshowcursor t) (close (outchan))) term))