Programmation Tcl

Et c'est reparti pour de la prog !! Ouais !!

Tcl est un langage de programmation dont vous avez sûrement déjà entendu parler. Mais pas tout seul : on entend parler plus souvent de Tcl/Tk ou de Tcl/Expect etc.
Il faut l'avouer Tcl est loin d'être un langage évolué, il peut être très intéressant pour débuter en programmation mais ses capacités sont très limitées. Tcl est la langage utilisé pour coder les eggdrops (j'entre pas plus dans ce sujet que je ne maîtrise pas).
Cet article est une suite de trois articles : maintenant nous allons étudier le langage Tcl, ensuite nous utiliserons Expect qui peut être vu comme une "extension" de Tcl et pour finir, l'objectif final : nous essayerons de coder un vers avec ces langages.
Pourquoi choisir Tcl pour faire un worm plutôt que le C ? Premièrement parce que l'objectif était avant tout de faire un article pas forcément technique sur les worms. Il s'agissait plutôt de comprendre la façon dont ils fonctionnent, les différentes étapes etc. Ensuite c'est mon premier worm donc faut pas s'attendre à une version en C#31337 (c'est une nouvelle version du C LoL).

C parti : les variables
Je le répète : vous n'avez pas besoin d'avoir déjà programmé pour faire du Tcl. C'est un langage très intuitif (les instructions sont parlantes). A part ça Tcl est surtout un langage interprété, tout comme le Perl, le Bash, le PHP. Bref pas de compilation comme en C, C++... On appelle un programme en Tcl un script.
Pour faire du Tcl vous avez besoin d'un interpréteur Tcl. Celui-ci se nomme Tclsh. Il peut être utilisé comme un shell, ce qui explique le 'sh' à la fin de Tclsh.
Tcl est à priori dispo pour Windows, il suffit de le télécharger. Pour les utilisateurs de Linux, il est déjà installé (faites un 'whereis tclsh' pour savoir ou se trouve l'interpréteur).
Tcl signifie Tool Command Language. C'est donc un programme qui ne fait qu'appeler des commandes, tout comme le fait un programme bash ou batch.
Tcl ne manipule que des chaînes de caractères. Bien sûr on peut générer des nombres, les additionner... mais Tcl les conserve en mémoire comme étant des chaînes de caractères.

La syntaxe du langage est très simple ;-) :
commande arg arg arg ...
Bref c comme si vous tappiez une commande sous Unix.
Il n'est pas nécessaire de déclarer les variables en Tcl (comme pour le PHP). Les variables commencent par le caractère dollar ($). Lorsque l'on fait référence à une variable, Tcl la remplace par sa valeur et exécute la commande demandée. L'affectation d'une valeur à une variable ne demande pas la présence du $.
Exemple :

set nom "Alice"
puts "Mon nom est $nom"

Donnera l'affichage : Mon nom est Alice
A la première ligne, la variable 'nom' est crée et la valeur 'Alice' lui est attribuée. A la seconde ligne, Tcl remplace '$nom' par 'Alice' (substitution) et lance la commande puts qui permet l'affichage de texte à l'écran.
Deuxième exemple :

set jour 23
set mois 11
set annee 2003
set date "$jour:$mois:$annee"
puts $date

Provoque l'affichage : 23:11:2003

Les commandes
Maintenant que nous avons vu la substitution des variables, pourquoi ne pas étudier la substitution des commandes. Cette méthode, très utilisée en Tcl, fonctionne de la même façon que pour les variables : Tcl commence par faire les calculs demandés par la ou les commandes. Le résultat est une chaîne de caractère qui vient remplacer les commandes qui l'ont générées. Tcl effectue ensuite la commande principale. Cela passera mieux avec un exemple ;-)

set ope "puts HelloWorld"
eval $ope

On obtient : HelloWorld. La commande eval permet comme son nom l'indique d'évaluer une commande. Ici on déclare une variable 'ope' qui contient la commande 'puts HelloWorld'.
La deuxième étape est l'évaluation de cette commande. Tcl voit qu'il doit évaluer la variable 'ope'. Comme c'est une variable, il la substitue par sa valeur : 'puts HelloWorld' puis il lance la commande obtenue.
Une autre façon de faire des substitutions de commandes est d'utiliser des crochets. Dans l'exemple suivant nous appelons la commande expr qui permet d'effectuer des opérations mathématiques.

set euros 15
set francs [expr $euros*6.55957]
puts $francs

donne 98.39355. Les crochets remplacent donc une commande par son résultat.
Quelques précisions à apporter sur la syntaxe de Tcl : tous les arguments doivent être des chaînes de caractères. Les arguments doivent être séparés par un espace ou une tabulation. Les commandes sont soit séparées par un point virgule ou un retour à la ligne.

Les caractères spéciaux
Il suffit de mettre un backslash devant un caractère pour lui enlever ou lui rajouter un sens spécial.

set phrase "J'ai 20\$ dans\nmon portefeuille"
puts $phrase

donnera :
J'ai 20$ dans
mon portefeuille

Vous trouverez les caractères spéciaux dans cette annexe.

La commande expr permet d'effectuer beaucoup d'expressions arithmétiques.
expr 1 == 0 donnera 0 (opération booléenne) expr 5+4 renverra 9.
Les opérateurs arithmétiques et fonctions mathématiques disponibles sont listées ici.

Une méthode utilisée pour empécher la substitution des caractères spéciaux et donc des variables etc. est l'utilisation des accolades. Les accolades empèchent la substitution. Tout ce qui se trouve entre accolades est pris tel quel, même les passages à la lignes. Reprenons la phrase suivante :

set phrase {J'ai 20$ dans
mon portefeuille
}

donne le même résultat que tout à l'heure.

Les commentaires
Ils commencent par le cacarctère dièse (#). Si ils se trouvent après une instruction cette instruction doit se terminer par un point-virgule.
#Ce script affiche la longueur de la chaine "Hello" additionné à la valeur 7
set len [expr [string length Hello] + 7] ; #deux évaluations de commande
puts $len ;# Affiche 12

Ne vous inquietez pas pour les commandes, elles sont toutes dans l'annexe.

D'autres commandes
unset permet la destruction d'une variable : unset phrase (on ne met pas le dollar)
append permet de concaténer des variables
set phrase "LOTFREE"
append phrase " n°6"
puts $phrase

donne LOTFREE n°6

La commande format permet de choisir un format d'affichage de la même façon que les commandes printf en C
set a 24.236784; set b secondes
puts [format "a = %5.3f %s" $a $b]

donne a = 24.237 secondes

Il existe bien d'autres commandes ce serait trop long de toutes les passer en revue.

Les listes
Les listes constituent une des capacités intéressantes de Tcl. Les commandes dédiées aux listes commencent généralement par la lettre 'l'.
Une liste n'est rien de plus qu'un ensemble de chaînes de caractères séparées par des blancs.
set a 5;# a=5
set liste1 {un deux trois} ;# liste1="un deux trois"
set liste2 "4 $a 6" ;# liste2="4 5 6"
set liste3 [list $liste1 $liste2]
puts liste3 ;# Donne {un deux trois} {4 5 6}

Les listes permettent d'ajouter des éléments de façons dynamique et d'y accèder simplement etc.
Malheureusement ça permet pas de tuer ce con de Pikachu !!

Les tableaux
set montableau(0) "Zéro"
set montableau(1) "Un"
set montableau(2) "Deux"
for {set i 0}{i < 3}{incr i 1}{
   puts $montableau($i)
}

donne :
Zéro
Un
Deux

Comme pour le PHP un tableau peut avoir des indices non numériques : set montableau(animal) "Kangourou" ;-)

Les variables d'environnement sont accessibles à travers le tableau prédéfinis 'env'. Ex : puts $env(DISPLAY)

Les tests
Comme dans tout langage de programmation il est possible de faire des branchements conditionnels.
Les structures possibles sont les suivantes :
if {test} {
   instructions_à_effectuer_si_la_condition_est_vérifiée
}

Il est possible d'exécuter des instructions si la condition n'est pas vrai :
if {test}{
   instructions_à_effectuer_si_la_condition_est_vérifiée
} else {
   instructions_à_effectuer_si_la_condition_n'est_pas_vérifiée
}

Enfin avec plusieurs tests à la suite :
if {test_1}{
   instructions_à_réaliser_si_la_condition_test_1_est_vrai
} elseif {test_2}{
   instructions_à_réaliser_si_la_condition_test_1_n'est_pas_réalisé_mais_que_la_condition_test_2_est_bonne
} else {
   instructions_à_effectuer_si_aucune_des_conditions_ne_s'est_réalisée
}

Attention !! On ne peut insérer de sauts de ligne qu'à l'intérieur des accolades alors utilisez-les toujours de la façon que l'on vient de voir.

Pour ce qui est des conditions, ce doit être des variables dites 'booléennes' (qui prennent comme valeur, soit vrai, soit faux).
Pour obtenir une valeur booléenne on peut par exemple comparer deux chiffres.
Inférieur : $a < $b
Supérieur : $a > $b
Egal : $a == $b
Différent : $a != $b
Supérieur ou égal : $a >= $b
Inférieur ou égal : $a <= $b

Vous n'êtes pas obligé de comparer des variables (ouf !!), vous pouvez comparer une variable avec une valeur ou deux variables ensembles etc.
if {$nom == "sirius" } {
...
} elseif { $val < 5}{ ...

Les boucles
La façon dont les boucles sont formées n'est pas si éloignées des structures conditionnelles. Commençont par la boucle for :
for {initialisations} {test_de_fin_de_boucle} {incrémentations}{
   corps_de_la_boucle
}

Exemple :
for {set i 0} {$i < 6} {incr i}{
   puts -nonewline $i
}

donnera comme résultat : 012345
L'option nonewline permet simplement de ne pas passer à la ligne à chaque affichage.

La boucle while, permet de répéter une instruction (ou une suite d'instructions) tant que la condition générale est vérifiée.
set i 10
while {$i > 0}{
   puts $i
   incr  i  -1
}

feras passer la variable $i de 10 à 0.

Une commande dérivée de la boucle for est la boucle foreach qui permet de traiter tous les élèments d'une liste.
foreach variable {liste_de_variables_à_prendre}{
   corps_de_boucle
}

exemple :
puts "Les distribs Linux sont :"
foreach nom {Mandrake RedHat Debian Knoppix Slackware Suse Caldera}{
   puts $nom
}

Affichera les différentes distrib Linux. Cette commande est très intéressante avec les variables de type liste.
Comme on a pu le constater, à chaque passage dans la boucle la variable après le mot clé foreach prend pour valeur l'élément suivant de la liste. L'affectation peut se faire avec deux valeurs :
foreach {style artiste} "Punk Rancid   Ska Ska-p  Rap Svinkels" {
   puts "$artiste joue du $style"
}

Les branchements à choix multiples
Ils permettent de comparer une variable à un grand nombre de valeurs.
La syntaxe est : switch chaîne valeur_1 bloc_1 valeur_2 bloc_2 ... valeur_n bloc_n default bloc_default
Un bloc est le mot pour désigner une suite d'instructions. Voici un exemple type :
switch $val {
  0 {puts "Zéro"}
  1 {puts "Un"}
  2 {puts "Deux"}
  default {puts "Désolé je ne sais pas traduire ce nombre"}
}

La principale différence avec les autres langages de programmation c'est qu'il n'y a pas besoin de mettre un "break" à la fin de chaque bloc.
Toutefois les commandes 'break' et 'continue' existent. 'break' permet de quitter une boucle for, while ou foreach et 'continue' permet de passer au prochain tour de boucle sans terminer la boucle en cours.

Les procédures
Pour ce qui connaissent pas le mot 'procédure', et bien en fait ce sont des fonctions qui renvoient un résultat. Du moins dans le langage Tcl c'est comme ça puisque les fonctions n'existent pas chez lui.
proc nom_de_la_procédure {arguments}{
   corps_de_la_procédure
}

Bien que les arguments soient entre accolades, il ne s'agit pas forcément de listes. Cela peut être tout type de variable. Voici une procédure qui prend un nombre en arguments et renvoie sa valeur au carré :
proc carre {valeur}{
   set resultat [expr pow($valeur,2)]
   return $resultat
}

Pour appeler cette fonction on peut par exemple faire :
set x 2
puts [carre $x];
#La substitution de commandes, vous vous rappelez j'espère ;-)
et on obtient : 4.0

On aurait obtenu le même résultat si on n'avait pas mis la commande return. Pourquoi ? Parce que en l'absence de valeur de retour, Tcl renvoie le résultat de la dernière commande.
Une procédure qui calcule une moyenne à partir des chiffres d'une liste :
proc moyenne {liste}{
   set longueur [llength $liste]; #On récupère le nombre de chiffres dans la liste
   if {$longueur > 0}{
      set somme 0
      for {set i 0} {$i <= [expr $longueur-1]} {incr i}{
         incr somme [lindex $liste $i]; #On fait la somme de tous les chiffres de la liste
      }
      append longueur .0 ;#Un 'truc' pour obtenir un réel au final
      return [expr $somme/$longueur] ;#Moyenne = somme_des_chiffres / nombre_de_chiffres
   }
}

Bon là on a fait une bonne partie. Passons aux Flux.
Déjà on connait la commande d'écriture sur un flux : puts
La commande de lecture est gets
Ok on sait lire et écrire mais avant tout il faut ouvrir le flux, pour cela on utilise la commande open. Quand on a fini les traitements on appelle la commande close pour fermer ce flux. Voici un exemple pour illustrer tout cela :
set f [open "l33t.txt" "w"]
puts $f "It\'s a l33t text"
puts $f "Enjoy it"
close $f

Commençons par la commande 'open' : le premier argument est le fichier à ouvrir et le second est le mode d'ouverture. Voici les différents modes d'ouverture d'un fichier :
* r : ouvre le fichier en lecture seulement, le fichier doit déjà exister.
* r+ : ouvre le fichier en lecture et écriture, le fichier doit déjà exister.
* w : ouvre le fichier en écriture seulement, il le remet à 0 si il existe déjà sinon il crée un nouveau fichier
* w+ : ouvre le fichier en lecture et écriture, il le remet à 0 si il existe déjà sinon il crée un nouveau fichier
* a : ouvre le fichier en écriture seulement, crée un nouveau fichier vide s'il n'existe pas, se positionne à la fin du fichier.
* a+ : ouvre le fichier en lecture et écriture, crée un nouveau fichier vide s'il n'existe pas, se positionne à la fin du fichier.

La commande open renvoie un 'canal', c'est lui qui représente le flux sur lequel on va écrire. Bref c'est un 'file descriptor'. La suite du programme est simple : on écrit deux lignes et on ferme le fichier.
Un exemple de lecture dans un fichier :
set  f  [open "l33t.txt" "r"]
set  ligne1  [gets $f]
set  long_ligne2  [gets  $f  ligne2]
close  $f

puts "ligne 1 : $ligne1"
puts "ligne 2 : $ligne2"
puts "Longueur de la ligne 2 : $long_ligne2"

On obtient :
ligne 1 : It's a l33t text
ligne 2 : Enjoy it
Longueur de la ligne 2 : 8

Et une lecture au clavier :
puts  -nonewline  "Entrez votre nom :"
gets  sdtin  nom

L'utilisation des sockets est presque similaire. Il suffit d'utiliser la commande socket à la place de open.
Utilisez les nouvelles sections des manpages pour avoir plus d'infos sur les commandes Tcl. Par exemple faites un "man n socket" sous Linux.

Bon on va pouvoir passer à expect maintenant :-)