Programmation et langages

Quelques bases de divers langages de programmation.

AJAX Fundamental

Les fondamentaux d'Ajax

Fichier attachéTaille
PDF icon totoriels.ajax_.fundamentals.pdf96.39 Ko

Exercice

Vous trouverez dans cette rubrique quelques exercices d'entraînement, afin de se perfectionner ou de passer le temps pendant les compilations.

Algorithmique

Quelques exercices de réflexion sur de l'algo.

Remplir son disque dur

Calculer la taille final des fichiers plop et plip :

echo "plop" > plop
for i in `seq 0 255`
do
  cat plop >> plip
  cat plip >> plop
done

Indice : beaucoup !
Pour 16 itération (0 -> 15) j'obtiens 17Mo pour plop !

Langage C

Cette section contient de nombreux tutoriels pour devenir un programmeur aguerri en C.

C Propre

Le but de ce document est, à partir de connaissances de base en C, d'expliquer comment faire un projet "propre" c'est à dire organisé clairement et dont le code est lisible.

Ce petit tutoriel est divisé en 3 parties:

  1. la première partie concerne la Compilation. Il est nécessaire pour faire du code propre de savoir ce qui va lui être fait au moment de la Compilation. Nous expliquerons donc les différentes étapes de la Compilation, et leur rôle.
  2. La deuxième partie s'occupe de l'Organisation du Projet: tout mettre dans un seul gros fichier nuit à la lisibilité et ralentit la Compilation.
  3. La troisième partie C Avancé traite de petits "Tips and tricks" en C, pour une utilisation encore plus claire, et plus optimisée.

Commentaires de Duke : il pourrait être intéressant de rajouter un paragraphe/chapitre
sur les options de gcc utiles comme de -Wall. Et eventuellement un petit rappele
des options de base que sont -o et -l -L -I etc.

Compilation

La compilation est une succession d'étapes qui transforment votre code en C en un fichier exécutable par la machine. Voici ces différentes étapes:

le Preprocessing

Le préprocesseur est un élément du compilateur qui va transformer le code en fonction de directives qui lui sont propres afin de préparer le code à être compilé.

En C, les directives du préprocesseur commencent toutes par #. Les plus connues sont #include et #define, mais dans la partie [[C Avancé]] on verra qu'il existe aussi #ifdef et #ifndef, #else, #endif, et bien d'autres.
C'est notament au niveau du préprocesseur que les fichiers .h sont inclus avec les fichiers C.

Une fois que le fichier a été préparer, le compilateur analyse le code pour passer à la génération du code assembleur correspondant.

La compilation

La compilation transforme le code C en assembleur propre au processeur: à partir de cet instant les fichiers compilés deviennent donc spécifiques à la machine.
Le code assembleur est déduit du code C et c'est à ce moment la qu'intervienne les optimisations éventuelles: déroulage de boucles, rotation de registre, etc.

La génération des fichiers objets - Assemblage

Une fois que la compilation est finie, il faut transformer ce code assembleur en code objet (code machine) utilisable par le microprocesseur. Le fichier devient alors complètement illisible. Il y a un fichier objet par fichier source en C. Ces fichiers portent l'extension .o.

Attention, c'est du code objet, mais pas du code exécutable! En effet, il ne doit y avoir qu'un seul exécutable, mais il peut y avoir plein de fichiers objets. La création de ce fichier "binaire" exécutable est dédiée au linker.

La liaison des objets pour la création du binaire

Lorsqu'un projet est divisé en plusieurs fichiers sources, il y a plusieurs fichiers objets: le but de cette étape est de lier les objets entre eux, ainsi que les bibliothèques (''librairies'' en anglais) statiques et dynamiques, afin de faire un fichier exécutable.

Le fichier créé par le linker est un fichier exécutable (binaire).

Intérêt de comprendre ces étapes

Mais pourquoi doit-on connaitre ces étapes? tout simplement parce que dans un projet dont le résultat doit être propre, il faut maitriser chacune des étapes: le bon programmeur:
* Ecrit des directives de preprocessing intelligente pour limiter le travail du compilateur
* Vérifie son code assembleur pour l'optimiser encore plus si besoin est (notamment en systèmes embarqués)
* Débuggue son programme en se servant d'un debugger qui agit sur les fichiers objets
* Fait de la programmation multifichier afin d'avoir des morceaux séparés, remplaçables, et si possibles liés dynamiquement afin de limiter la taille du binaire

Commentaire de Duke
Eventuellement ajouter le fait que l'on peut aussi créer des librairies.

Comprendre les erreurs de compilation

Comme la compilation se déroule en plusieurs phases, des erreurs peuvent intervenir dans chacune des phases. Pour les débugger correctement, il est important de savoir les identifier. En effet, ce n'est pas la peine de chercher dans les directives de preprocessing des erreurs de liens. Nous allons voir quelques erreurs classiques, apprendre à les identifier, et surtout à les corriger.

Les erreurs de préprocesseur

Les erreurs de préprocesseur arrivent vite: avant même que le code soit transformé en assembleur. En voici un exemple de programme qui va échouer au moment du preprocessing, avant meme la compilation (l'erreur du printf n'est même pas signalée):

 #include <stdio.h>
 #ifdef WIN32
 #include <conio.h>
 int main()
 {       
         printf(5);
 
         return 0;
 }

On voit que le #ifdef n'a pas été fermé par un #endif. Voici l'erreur correspondante:

f1.c:2:1: unterminated #ifdef

Les erreurs du préprocesseur sont donc très claires et faciles à corriger.

Les erreurs de code C

La première erreur est la plus fréquente:

f1.c: In function `main':
f1.c:6: error: syntax error before "return"

Cette erreur est provoquée à la compilation, par une erreur de syntaxe dans le code: un point virgule ou une parenthèse oubliée. Mais attention, l'erreur n'est pas à l'endroit indiqué, mais avant! Voici le code qui a provoquée l'erreur:

 int main()
 {
     printf("Hello World ! \n")
     return 0;
 }

On voit qu'il manque un ";" après le printf. On aurait l'erreur suivante si par exemple nous oubliions la ")" et pas le ";"

f1.c: In function `main':
f1.c:5: error: syntax error before ';' token

Une autre erreur classique est de se tromper de type lors d'un passage de paramètre: voici l'erreur correspondante:

f1.c: In function `main':
f1.c:5: warning: passing arg 1 of `printf' makes pointer from integer without a cast

provoquée par exemple par un
printf(4);

4 est un entier, et printf attend un pointeur. L'erreur inverse peut aussi se produire, lorsqu'on passe un pointeur à la place d'un entier. Il faut donc, dans une erreur de ce type, faire attention aux types signalés par le message d'erreur.

Pour la correction de cette erreur, il faut revérifier les prototypes des fonctions et les types des arguments passés.

Les erreurs de liens

Voila l'erreur de lien la plus courante:

/tmp/cconpIvx.o(.text+0x11): In function `main':
: undefined reference to `print_toto'
collect2: ld returned 1 exit status

Et le code correspondant:

 int main (){
         print_toto();
         return 0;
 }

La raison est que la fonction print_toto() est définie ailleurs (ou pas), mais déclarée nulle part dans le fichier de test. Au moment du lien, le linker ne trouve pas la définition du symbole, donc il n'est pas content.
En réalité, cette erreur est précédée d'un warning, souvent masqué (à moins d'utiliser l'option -Wall du compilateur), qui indique que la fonction est déclarée implicitement:

f1.c: In function `main':
f1.c:2: warning: implicit declaration of function `print_toto'

Voici l'autre erreur très courante que l'on rencontre au moment du link:

/tmp/ccr2MjbC.o(.text+0x0): In function `print_toto':
: multiple definition of `print_toto'
/tmp/ccYyznn1.o(.text+0x0): first defined here
/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.6/../../../../i686-pc-linux-gnu/bin/ld: Warning: size of symbol `print_toto' changed from 20 in /tmp/ccYyznn1.o to 25 in /tmp/ccr2MjbC.o
collect2: ld returned 1 exit status

Le problème ici est que la fonction print_toto() est définie à deux endroits différents: c'est uniquement au moment du link que le problème apparait car les 2 fichiers sont compilés séparément, et c'est au moment de la construction de la table des symboles générale, que le linker voit que le même symbole a été défini à deux endroits différents.

Le dénominateur commun de ces erreurs de link est qu'elles commencent toutes par:
/tmp/ccr2MjbC.o(.text+0x0):
ou quelque chose de similaire, et qu'elles finissent par:
collect2: ld returned 1 exit status

Comprendre les erreurs d'exécution

C'est l'heure de la légendaire...
Segmentation fault

La raison de cette erreur est que le programme tente d'accéder à une zone de mémoire non autorisée. Voila le code correspondant:

 char *s1, *s2;
 strcat(s1,s2);

Les deux chaines ont été déclarées, il n'y a donc aucune erreur à la compilation. En revanche, Ni l'une ni l'autre ne se sont vues allouées de mémoire. Dès lors, quand strcat() tente de les concatener, elle se heurte au fait que les pointeurs s1 et s2 sont NULL. C'est pourquoi au moment de l'exécution, la concaténation échoue.

D'une manière générale, les segmentation faults (segfaults pour les intimes) sont dues a une allocation mémoire oubliée: il faut donc revoir les pointeurs utilisés dans la zone du segfault, et vérifier que la mémoire vers laquelle ils pointent a été correctement initialisée.

La prochaine étape de ce cours (Organisation du Projet]) sera justement de voir comment améliorer son code et ses interventions dans ces différentes parties.

Commentaire de Duke :
Il pourrait être intéressant de rajouter les commandes gcc permettant de decouper la compilation :
par exemple gcc -E pour avoir le code apres le preprocesseur
Ceci juste comme une addition pour les "curieux" qui voudrait appronfondir d'eux meme
ce qu'il se passe exactement pendant la comilation

Sinon attention il y a des textes qui depassent dans les boites en pointillés
car elles ne retourne pas à la ligne toutes seules

Organisation du Projet

un bon projet, c'est quoi?

* Un projet qui marche (normal)
* Un projet qui est débuggué (ah ben oui, quand meme)
* Un projet qui est débuggable (ne vous faites pas d'illusion il reste toujours des bugs)
* Un projet dont le code peut être compris par quelqu'un d'autre que le concepteur (et c'est la que ca se corse...)

Mais que faire?

Alors comment faire pour qu'un projet satisfasse ces exigences?
* Réfléchir avant de coder, et tester ses résultats
* Ecrire des tests pour l'utilisation du projet, et pas pour le code du projet (la nuance est de taille...): par exemple, on peut écrire les tests avant d'écrire le code du projet afin d'être sur de ne pas être influencé par la façon dont le code a été fait.
* Utiliser des outils de monitoring pour vérifier les fuites de mémoires, etc
* Et bien sur, écrire du code propre, et le ranger correctement.

A titre d'exemple, les projets de plusieurs milliers de lignes de code de C sont légions. Imaginez un fichier de 10000 lignes de code: déjà c'est très lourd à utiliser et ensuite, pour comprendre le code, ca se corse: si le main est tout en bas, et la fonction qu'on veut voir tout en haut....

Il est donc très rapidement nécessaire de séparer son code en morceaux.

Séparer son code

''' Les Headers '''

Déjà il faut séparer les déclarations globales du reste du code: ces déclarations globales sont:
* Les macros
* Les constantes
* Les déclarations de fonctions
* Les déclaration de types (struct, enum, typedef ...)
* Les déclaration de variables globales (beurk!)

Il existe un type de fichier particulier pour accueillir ces déclarations: les fichiers "headers", dont l'extension est ".h" (ca doit vous rappeler des choses)

Donc pour chaque fichier .c, il faut un fichier .h qui contienne la déclaration des fonctions, des macros, des constantes, etc qui y sont utilisées.

Petit rappel...

Ca c'est une '''déclaration''':

 void fonction(int arg1, char arg2);

Ca c'est une '''définition''':

 void fonction(int arg1, char arg2) {
     int i;
     for(i=0; i<arg1; i++)
         printf("%c\n", arg2);
 }

On parle bien ici des déclarations, et pas des définitions. Ca ne compilera pas si vous avez vos définitions dans vos headers. En effet vos headers risquent d'être inclus par plusieurs fichiers c, et donc à chaque fois les fonctions redéfinies...

''' Utiliser plusieurs fichiers '''

Il est aussi bon de séparer son code en blocs logiques: par exemple, un fichier s'occupera des fonctions d'affichage, pendant qu'un autre s'occupera de l'interface avec le système, pendant qu'un autre contiendra les algorithmes de calcul.
Attention! il ne faut pas se tromper d'objectifs: on fait ca pour pouvoir facilement retrouver le bout de code qui nous intéresse: pas pour avoir des petits fichiers!! Ca ne sert à rien d'avoir "fichier1.c fichier2.c fichier3.c" et des contenus tous liés entre eux: au contraire, ca risque d'être encore moins clair qu'un seul gros fichier.
C'est quand on utilise plusieurs fichiers que les headers deviennent important: en effet, peut etre que les fonctions du fichier d'algorithmes se serviront de l'affichage ou de l'interface systeme: il faut donc qu'ils connaissent les déclarations de ces fonctions: il faudra donc include les headers des fichiers d'affichage et systeme dans le fichier C des algorithmes...

Votre projet commence à ressembler à quelque chose

''' Ecrire des tests '''

Tester le programme à la main, c'est bien, mais pas efficace quand le programme devient complexe: on ne peut pas imaginer tous les cas et les taper à la main. En conséquence de quoi il est utile d'écrire des scripts de tests, qui appeleront par exemple 1000 fois le binaire avec des arguments différents et qui renverront des erreurs si le programme bug ou renvoie des résultats incohérents.
Un script de test bien fait, c'est tout un tas de mauvaises surprises en moins lors de la correction ou de la démo du projet :)

On a parlé des headers, des définitions, déclarations, etc. Il y a des outils pour optimiser la compilation de toutes ces choses: c'est ce qu'on verra dans la troisième partie du cours, [[C Avancé]].

C Avancé

Du C Avancé, c'est du C compliqué. Avant de faire compliquer il faut faire propre.

Du code propre

Du code propre c'est quoi?
Ca se lit facilement

  1. Donc c'est indenté, c'est net, ca bave pas: si y'a trop d'indentation c'est qu'il faut probablement revoir le bout de code...
  2. Pas de lignes de 3 km
  3. C'est pas du C syntaxe C++ ou un truc moche du style
  4. Des fonctions pas trop longues: si ca fait plus d'une page, c'est que ca doit être divisé

Ca se comprend vite

  1. Ca se lit comme une histoire
  2. C'est commenté quand ca se corse MAIS...
  3. C'est commenté uniquement quand c'est nécessaire

Ca fait ce qu'on veut et pas plus

  1. Pas la peine de faire 3 fois la même chose
  2. Pas la peine de tester ces choses qui ne peuvent pas arriver
  3. Pas la peine de mettre des options dont on ne va pas se servir
  4. On ne se branle pas au nombre de lignes

C'est du code réfléchi:

  1. Pas de fuite de mémoire (on détruit ce qu'on alloue)
  2. Pas de boucles inutiles (ca prend du temps)
  3. Des variables dont la portée est controlée
  4. Des fonctions qui ne font qu'une seule chose à la fois
  5. Ca gère les situations d'erreurs

Les ptites feintes du préprocesseur

'''#define'''

permet de définir des constantes et des macros. Lorsque des valeurs constantes vont êtres utilisées au cours du programme, il est bon de les définir avec des '''#define''': le préprocesseur remplacera les constantes dans le code par leur valeur au moment de la compilation. C'est une utilisation à privilégier par rapport à définir des variables globales constantes.

'''#if #ifdef, #ifndef, #else, #endif'''

Sont des structures conditionnelles du préprocesseur. Cela permet notamment de "condamner" du code. Par exemple, si vous avez un morceau de code de debug que vous ne voulez pas exécuter au final, vous pouvez procéder de la sorte:

 #define DEBUG 1
 ...
 #if DEBUG
 printf("debug: %d\n", toto);
 #endif

Au moment de la compilation si DEBUG est strictement supérieur à 0 alors le code dans le '''#if''' sera compilé et vous aurez votre affichage de debug. Au moment de la compilation finale quand vous ne débuggerez plus, il suffira de mettre DEBUG à 0 pour que le code soit supprimé de la compilation.

Il est aussi possible de définir des constantes du préprocesseur directement dans la ligne de commande de gcc en utilisant l'option '''-D='''

L'autre façon de faire est d'utiliser #ifdef comme le montre l'example suivant. Dans ce cas, l'option de compilation serra simplement '''-D'''

 #define DEBUG
 ...
 #ifdef DEBUG
 printf("debug: %d\n", toto);
 #endif

Les petits mots clefs qui changent tout

'''static'''

Ce mot clef s'utilise lors de la déclaration d'une variable, avant d'en spécifier le type: par exemple:

 static int i;

Une variable déclarée statique voit sa portée étendue (ou limitée) à tout le fichier. L'utilité est surtout de limiter la portée d'une variable globale dans un projet multifichier, ou alors d'utiliser 2 fonctions ayant le même nom dans des fichiers différents.

'''inline'''

Ce mot clef s'utilise avec les procédures et fonctions dont le code au moment de l'assemblage du fichier doit être inclus directement dans la fonction appelante. L'utilité est d'augmenter la lisibilité du code en séparant les fonctions, mais de limiter les branchements et donc l'overhead des sauvegardes et restaurations de contexte au moment de l'exécution.

Il ne faut pas confondre l'utilisation d''''inline''' avec l'utilisation de macros: en effet, le code d'une macro est adapté dans le code appelant par le préprocesseur, alors que le code d'une procédure "'''inline'''" est adapté au moment de la construction du code assembleur. Dans le premier cas, la transformation de la macro en code assembleur est donc effectuée autant de fois que le code est recopié. Ce n'est pas le cas de la fonction '''inline'''.

Exemple:

 inline int max(int i1, int i2);

'''extern'''

Ce mot clef est utilisé pour éviter de déclarer deux fois une variable dans deux fichiers différents: il indique que la déclaration a déjà été effectuée, et qu'il n'est pas nécessaire d'allouer a nouveau de la mémoire et un espace dans la table des symboles, la variable y sera placée par le linker à partir d'un autre fichier.

Exemple:

extern int i;

'''const'''

Ce mot clef, placé comme les autres devant le type des variables, permet d'indiquer au compilateur que la variable ne doit pas être modifiée, autrement dit, elle doit être considérée comme une constante. Si, lors de la compilation, la valeur de la variable est modifiée, une erreur sera générée.

On peut par exemple l'utiliser avec les paramètres d'une fonction pour éviter qu'ils soient modifiés dans la fonction. C'est surtout utilisé pour les pointeurs, afin de s'assurer que la mémoire sur laquelle ils pointent n'est pas modifiée.

Exemple:

 void print_string(const char *s);

La gestion d'erreurs

En utilisant un header spécial du système, '''errno.h''', une variable de type entier dont le nom est '''errno''' est déclarée. Cette variable est utilisée pour contenir les codes de retour des appels systèmes, si ceux-ci plantent. Par exemple, si le mode d'ouverture de fichier passé à fopen est erroné, fopen placera la valeur '''-EINVAL''' dans errno.

Il existe un appel système dont le nom est '''perror''' qui prend en argument une chaine de caractère. Il consultera la valeur de errno, affichera l'erreur correspondante et enrichira le message d'erreur de la chaine de caractère passée en argument.

D'une manière générale, il est bon pour contrôler le déroulement du programme, particulièrement les parties sensibles, d'utiliser la gestion d'erreur afin d'éviter des failles. Par exemple, si le programme n'arrive pas à ouvrir le fichier de configuration, ce n'est pas la peine qu'il continue à tourner.

Voici un exemple très simple d'utilisation de '''perror()''':

 FILE *fd;
 if((fd = fopen("toto.conf","r")) == NULL) {
     perror("Erreur d'ouverture du fichier de configuration");
     exit(-1);
 }

Ce code permet de sortir du programme en affichant une erreur qui, au moment de l'exécution, est explicite.

Création de greffon (plugin) en C

Pré-requis

  • Base en C
  • Compilation d'une librairie dynamique (voir tutoriel librairie C).
  • Un cerveau, un pc, un compilateur C, un pot de cacahuètes...

Pourquoi ?

La capacité à charger des modules externes pour un programme peut être utile dans le sens ou elle permet :

  • D'obtenir un exécutable plus léger
  • De permettre une évolutivité par l'ajout de fonctionnalités
  • De permettre une collaboration plus facile au projet (l'ajoute d'un plugin est souvent plus simple que de modifier le code source directement)
  • distribution de fonctionnalités supplémentaire simple

Le principe

Nous devons placer le code de notre greffon dans un fichier à part. Pour cela nous utiliserons des librairies dynamiques (.so sous Linux ou .dll sous windows). Le logiciel ne peut connaître à priori les fonctionnalités du module, il doit alors faire de l'instrospection sur la librairie pour la découvrir.

En action

Pour charger la librairie lors de l'exécution de notre programme nous utiliserons a fonction dlopen(). Pour charger une fonction de la lib, nous utiliserons dlsym() et dlclose() pour la fermer.

Un peu comme dans tous les langages de programmation, nous devons avoir une fonction référence pour entrer dans notre librairie. Libre a nous de l'appeler comme bon nous semble. Mais tous les autres greffons devrons avoir la même fonction, avec la même structure. Cette fonction aura pour rôle de présenter la librairie a notre programme et entre autre lui lister les fonctions présentes.

#include <dlfcn.h>		/* dlopen dlsyms dlclose */
#include <stdio.h>
#include <stdlib.h>
 
#include <trax.h>		/* include de la librairie */
 
typedef int fonc(int);
 
int main(){
	void *lib;
	fonc *myFonc;
	if((lib = dlopen("libtrax.so", RTLD_LAZY)) == NULL){ /* charge la librairie */
		fprintf(stderr, "Impossible de charger le librairie\n");
		return -1;
	}
 
	if ((myFonc = dlsym(lib, "test")) == NULL){ /* charge la fonction "test" de la librairie */
		fprintf(stderr, "Impossible de charger la fonction d'initialisation\n");
		return -1;
	}
	myFonc(42); /* utilise la fonction test de la librairie*/
 
	printf("%d\n",test(42));
	dlclose(lib);
	return 0;
}

Création de librairies en C

Pré-requis

  • Base (pH > 7) en C
  • Compilation sépare
  • Ce tutoriel

Pourquoi ?

Depuis son invention, la roue a été de pierre, de bois, de caoutchouc, mais toujours de la même forme. Réinventer une nouvelle méthode de faire un cylindre, n'a souvent que peu d'importance.
Les librairies permettent de factoriser du code réutilisé fréquemment. Le développeur doit alors rajouter la couche qui fera de sa roue une roue différentes de celles pré-existants.

Il existe deux types de librairies : statiques ou dynamiques.

Le premier permet de de créer des librairies qui seront inclus dans le binaire lors de la compilation. Cela permet de ne pas se soucier de la présence ou non de la librairies sur le pc sur lequel va être exécute le programme. L'inconvénient étant bien évidement un binaire plus lourd.

Quant aux librairies dynamiques elles créent des objets (fichier en .so sous Linux, .dll sous Wind...) qui sont chargées par l'OS à l'exécution (à ne pas confondre avec avec le chargement dynamique vu dans un autre tutoriel).

Le principe

Les librairies ne différents des programmes > que par leur point d'entre : il n'y a pas besoin de fonction main(). Il n'y a qu'a écrire le code des fonctions comme vous l'auriez fait de manière classique.

La compilation se fait de la même manière (la compilation seulement et non l'édition de lien). Nous commençons donc par créer nos objet (.o) puis nos fichiers librairies.

En action

soit les fichiers lib0.c et lib0.h

lib0.c

  1. #include "lib0.h"
  2. int test (int x){
  3. return x+42;
  4. }

lib0.h

  1. #ifndef LIB0_H
  2. #define LIB0_H
  3. int test (int);
  4. #endif

Ligne de compilation :

  1. gcc -Wall -c -fPIC lib0.c -o lib0.o
  2. gcc -shared -Wl,-soname,libtest.so lib0.o -o libtest.so

Il est possible de donner un nom de librairie différent de celui du fichier de la librairie en utilisant l'option soname.

Les fichiers lib0.o et libtest.so sont alors généré.

Ecrivons maintenant un code qui utilise notre librairie.

  1. #include <stdio.h>
  2. #include "lib0.h"
  3.  
  4. int main(){
  5. printf("%d\n",test(42));
  6. return 0;
  7. }

que nous compilons avec la ligne

gcc -Wall -L./ -ltest main.c -o prog 

-L indique le répertoire ou cherche la librairie : ici, c'est le répertoire courant
-l précise le nom de la librairie sans le préfixe "lib"

Création de processus avec fork();

La fonction fork permet la duplication de processus. A l'issu de son appel il existera donc deux processus : le père et le fils. Il ne seront différencié que par le retour de la fonction fork et leur valeur de PID (processor Identifiant)

fork() renvoie

  • -1 en cas d'erreur
  • 0 pour le fils
  • >0 pour le fils : le nombre renvoyé est le pid du fils (qui ne peut pas être 0 puisque 0 est le pid du noyau)
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h> //fork
  4.  
  5. #include <sys/wait.h> //wait
  6. int main(){
  7. int partage = 42;
  8. int pid;
  9. switch(pid = fork()){
  10. case -1:
  11. fprintf(stderr, "Avortement\n");
  12. exit(EXIT_FAILURE);
  13.  
  14. case 0:
  15. printf("f>Je suis le fils, mon pid est %d\n",getpid());
  16. printf("f>Le pid de mon pere est : %d\n",getppid());
  17. printf("f>Je partage avec mon pere la variable partage == %d\n", partage);
  18. break;
  19.  
  20. default:
  21. printf("p>Je suis le pere. Mon pid est : %d\n",getpid());
  22. printf("p>Le pid de mon fils est : %d\n",pid);
  23. printf("p>Je partage avec mon fils la variable partage == %d\n", partage);
  24.  
  25. wait(NULL); // permet d'attendre la mort du fils
  26. }
  27. return 0;
  28. }

Le code dans les "case 0" et default sont exécuté en parallèle. Les lignes peuvent donc être entrelacées.

Un processus fils dont le père est mort renverra 1 (le pid d'init) comme ppid. Pour éviter cela nous utilison la fonction wait (ligne 25) qui fera est bloquant jusqu'au changement de status d'un fils du processus. En l'occurence nous attendons la mort du processus fils, pour que ce dernier ait le temps de demander le pid de son père.

Mesure du temps d'éxecution en C

Il peut être utile de mesurer le temps que prends l'exécution d'une partie de code et non de tout le programme. Par exemple pour comparer différentes techniques et optimisations...

Nous pouvons obtenir deux types de temps : le temps d'utilisation du processeur ou le temps d'exécution du programme. Ils peuvent différer pour les raisons suivantes :

  • le programme est exécuter sur une plateforme multi-tâches qui accorde à chaque tâche un temps processeurs.
  • votre programme utilise de entrées/sorties ou des appels systèmes qui ne demande pas ou peu d'utilisation processeurs, mais qui prennent néanmoins du temps.

Dans un premier temps, nous présenterons la fonction > qui permet d'obtenir de le temps d'utilisation processeur.

#include <stdio.h>
#include <stdlib.h>
 
/*========== parttie relative a la mesure du temps ===========*/
#include <sys/times.h>
#include <unistd.h>
typedef struct tms sTms;
typedef struct{
	int debut,fin;
	sTms sdebut, sfin;
}temps_exec;
/*========== parttie relative a la mesure du temps ===========*/
 
void temp(temps_exec *tmps){
    printf("temps réel :\t %d\ntemps utilisateur :\t %ld\ntemps system :\t %ld\n",
		tmps->fin - tmps->debut,
		tmps->sfin.tms_utime - tmps->sdebut.tms_utime,
		tmps->sfin.tms_stime - tmps->sdebut.tms_stime);
}
 
 
void attente (){
    int *temp;
    for (int i = 0 ; i < 420000 ; i++){ /* boucle d'atente */
        temp = (int *)malloc(sizeof(int)*4207);
        free(temp);
        printf("plop\n");
	}
	sleep(1);							/* attent 1 seconde */
}
 
int main(){
    temps_exec temps;
    int top = sysconf(_SC_CLK_TCK); /* recupere le nombre de tips seconde */
 
    temps.debut = times(&temps.sdebut);
	attente();
    temps.fin = times(&temps.sfin);
    temp(&temps);
 
    return 0;
}

Il est possible d'obtenir séparément, grâce a cette même fonction, le temps utilisateur et system, pour le processus père et pour ses fils (voir man).

Avec getrusage :

#include <sys/time.h>
#include <sys/resource.h>
 
void mesureTime (struct rusage *temps0, struct rusage *temps1){
 
        printf("temps utilisateur %d\ntemps systeme %d\n",
                temps1->ru_utime.tv_usec -
                temps0->ru_utime.tv_usec,
                temps1->ru_stime.tv_usec -
                temps0->ru_stime.tv_usec
 
        );
 
}
 
int main(){
        struct rusage temps0;
        struct rusage temps1;
        getrusage(RUSAGE_SELF, &temps0);
// temps à mesurer
        getrusage(RUSAGE_SELF, &temps1);
        mesureTime(&temps0, &temps1);
}

Tutorial sqlite

SQLite ?? C'est quoi ?

Non ce n'est un pas un SQL pour l'élite mais une petite librairie C qui permet d'utiliser une base de donnée embarquée.

Prérequis

Caractéristiques :

  • ACID : transaction Atomique, Cohérence, Isolation, Durabilité
  • Aucune configuration demandée
  • Implémente la plupart des requêtes SQL92
  • Toute la base de donnée en un fichier
  • Support des base de plusieurs tera octets
  • Libre (pas comme la bière)

Début en console

commande crée la base de donnée (fichier bdd.bd).

tutorial_SQLite@Club*Nix $ sqlite3 bdd.db
SQLite version 3.4.1
Enter ".help" for instructions
sqlite> CREATE TABLE table1 (id INTEGER, nom varchar(30), prenom varchar(30), message TEXT);
sqlite> INSERT INTO table1 values(42, "trax", "traxou", "je n'ai rien à dire de plus");
sqlite> SELECT * FROM table1;
42|trax|traxou|je n'ai rien à dire de plus
sqlite> .exit
  1. ouvre sqlite3 avec lui demandant d'utiliser le fichier "bdd.db" comme base de données. Si celui ci n'existe pas il est crée.
  2. Réponse de sqlite3
  3. Création d'une table "table1" à trois champs : "id" nombre entier, nom "chaine limité à 30 caractères", prénom idem, message de type text (longeur inderterminée)
  4. Insertion de valeurs
  5. Lecture de valeurs
  6. Les valeurs lues
  7. Sortie du programme (à noter le "." devant "exit"

Code C

L'exemple suivant présente l'exécution de requêtes sur la base de données.
Deux méthodes sont utilisées. La première étant une exécution dynamique : l'analyse de la reqête se fait pendant son exécution. La seconde méthode consiste à compiler la requête et à injecter les valeurs à chaque appelle (avec la méthode sqlite3_bind_le_type). Cela permet d'accroitre ma rapidité lors d'appels fréquents à une requête de même structure.

  1. #include <stdio.h>
  2. #include <sqlite3.h>
  3. #include <stdlib.h>
  4.  
  5. void step (int i){
  6. printf("step : ");
  7. switch (i){
  8. case SQLITE_DONE:
  9. printf ("ok ");
  10. break;
  11. case SQLITE_BUSY:
  12. printf ("oqp ");
  13. break;
  14. case SQLITE_ERROR:
  15. printf ("err ");
  16. break;
  17. case SQLITE_ROW:
  18. printf ("row ");
  19. break;
  20. case SQLITE_MISUSE:
  21. printf ("mis ");
  22. break;
  23. default:
  24. printf ("??? ");
  25. }
  26. printf("\n");
  27.  
  28. }
  29.  
  30. int callback (void *lokis, int nbCol, char **data, char **nomCol){
  31. for (int i = 0; i < nbCol; i++){
  32. printf ("%s \t", data[i]);
  33. }
  34. printf ("\n");
  35. return 0;
  36. }
  37. int main (int argc, char **argv){
  38. sqlite3 *db;
  39. char *zErrMsg = 0;
  40. int rc;
  41.  
  42. if (argc != 3){
  43. fprintf (stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
  44. }
  45.  
  46. rc = sqlite3_open ("bdd.db", &db);
  47. if (rc){
  48. fprintf (stderr, "Can't open database: %s\n", sqlite3_errmsg (db));
  49. sqlite3_close (db);
  50. exit (1);
  51. }
  52. /*-------------------------------- Créer une table ---------------------------*/
  53. char *requete0 = "CREATE TABLE table1 (id INTEGER, login varchar(30), pass varchar(30));";
  54. if (sqlite3_exec (db, requete0, callback, 0, &zErrMsg) != SQLITE_OK){
  55. printf (":(\n");
  56. }
  57.  
  58.  
  59. /*------------------------------- Créer un enregistrement première méthode ---*/
  60. char *requete1 = "INSERT INTO table1 values (0, 'trax', 'plop');";
  61. if (sqlite3_exec (db, requete1, callback, 0, &zErrMsg) != SQLITE_OK){
  62. printf (":(\n");
  63. }
  64.  
  65.  
  66. /*------------------------------- Créer un enregistrement deuxième méthode ---*/
  67. char *requete2 = "INSERT INTO table1 values(?, ?, ?);";
  68. sqlite3_stmt *stmt = NULL;
  69. if (sqlite3_prepare_v2 (db, requete2, 50, &stmt, NULL) != SQLITE_OK){
  70. printf (":(\n");
  71. }
  72.  
  73. sqlite3_bind_int (stmt, 1, 42);
  74. sqlite3_bind_text (stmt, 2, "traxou", 7, NULL);
  75. sqlite3_bind_text (stmt, 3, "plip", 5, NULL);
  76. step(sqlite3_step(stmt));
  77. sqlite3_reset (stmt);
  78.  
  79.  
  80. /*------------------------------- Lecture d'enregistrement -------------------*/
  81. char *requete3 = "SELECT * FROM table1;";
  82. if (sqlite3_exec (db, requete3, callback, 0, &zErrMsg) != SQLITE_OK){
  83. printf (":(\n");
  84. }
  85.  
  86. sqlite3_close (db);
  87. return 0;
  88. }

Comment compiler :
gcc test.c -o prog -lsqlite3

Tutoriel utilisation des socket réseaux

Connection d'un client TCP/IP

#define TRUE 1
#define FALSE 0
/**
 * \fn BOOL sockConnect (char *addr, int port, int *socketID)
 * \brief create and connect a socket to a given address/port
 * \param addr address to connecto on
 * \param port port to connect on
 * \param socketID socket to be created
 * \return TRUE if success, FALSE if failed
 */
int sockConnect (char *addr, int port, int *socketID){
  struct sockaddr_in sockname;
  struct hostent *host_address;
 
  if ((host_address = gethostbyname(addr)) == NULL){ // addresse translation => IP
    fprintf (stderr, "Error while converting %s to inet address", addr);
    return FALSE;
  }
  #ifdef DEBUG
  else{
    printf ("official name : %s\n",host_address->h_name);
  }
  #endif
 
 
  if ((*socketID = socket(AF_INET ,SOCK_STREAM, 0)) == -1){ // TPC/IP socket
    fprintf (stderr; "Error while creating socket");
    return FALSE;
  }
 
  sockname.sin_family = host_address->h_addrtype;
  sockname.sin_port = htons (port);
  memcpy ((char *)&sockname.sin_addr.s_addr, host_address->h_addr,host_address->h_length);
 
  if((connect(*socketID, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in))) == -1){
    fprintf (stderr, "Error while connecting socket to %s:%d",addr, port);
    return FALSE;
  }
  return TRUE;
 
‹ Effectuer des requêtes sur un serveur mysqlhautGestion des signaux ›

warning: enumeration value LES not handled in switch

L'Erreur

warning: enumeration value LES not handled in switch
#include <stdio.h>
 
typedef enum {PLOP, LES, GENS}LOL;
int main(){
        LOL i = PLOP;
        switch(i){
                case 0:
                printf("plop\n");
        }
        return 0;
}

Explications

Dans le cas ou le switch doit évaluer une énumération il faut que tous les cas soit prit en compte ou utiliser la règle default

Solution

#include <stdio.h>
 
typedef enum {PLOP, LES, GENS}LOL;
int main(){
        LOL i = PLOP;
        switch(i){
                case 0:
                printf("plop\n");
                default:
                (void)NULL;
        }
        return 0;
}

Latex

Latex

Fichier attachéTaille
PDF icon exemple.pdf670.53 Ko
Fichier entete2.tex3.14 Ko

Tutoriel LaTeX (avancé)

Ici vous trouverez toutes les astuces qui vous éviterons de devenir fou (ou pas), pour compiler, éditer, visualiser son document LaTeX.

Compendre et utiliser les différents formats de documents

Au cours des tutoriels précédents, nous avons seulement parlé de pdflatex pour générer ses fichiers finaux. Il arrive que dans certains cas, pdflatex ne puisse pas compiler le document. Cela arrive notamment quand on utilise pstricks et/ou des fichiers .ps (postscript) et .eps (encapsulated postscript) dans son document.

Mais comment faire dans ce cas ?
La solution consiste à compiler son document non pas avec pdflatex mais latex. La double (voir triple) compilation est toujours nécessaire ça ne change rien de ce côté. Par contre le fichier de sortie est maintenant un fichier .dvi. Pour le transformer en .ps on utilise la commande dvips monfichier.ps dans un shell.
Ah ouais c'est bien cool mais ça sert à quoi un fichier .ps ? Le pdf caymieux.
Bon ok c'est vrai. Bonne nouvelle on peut convertir un fichier ps en pdf facilement grâce à ps2pdf :
ps2pdf monfichier.ps

Tutoriel LaTeX (débutant)

Sommaire du tutoriel :

partie 1 : Avant propos et histoire de TeX/LaTeX

Avant propos

Ce tutoriel est une reprise de celui rédigé par Rémi Cardonna suite au cours du Club*Nix et du Club PC en juin 2005 à l'ESIEE.

Prérequis : Savoir utiliser un editeur de texte de type bloc note et savoir enregistrer un fichier avec une extension.
Les informations données on été testées sur un pc équipé de Arch Linux. Cependant, les exemples donnés devraient pouvoir fonctionner sur les pc du smig (Debian GNU/Linux) à condition de faire attention à bien enregistrer ses documents en ISO 8559 15 ; car les ordinnateurs du smig ne gère pas (ou pas bien) l'UNICODE.

Cette précaution prise nous allons pouvoir commencer à apprendre à se servir de LaTeX.

Qu'est-ce que c'est LaTeX ?

LaTeX est un système de mise en page inventé par un professeur de Mathématique et d'Informatique au milieu des années 70. Certes à l'époque Word n'existait pas encore, mais encore aujourd'hui LaTeX est très utilisé dans les domaines scientifiques et techniques, car celui-ci a été conçu pour ce type d'utilisation.

Il y a deux concepts importants à retenir pour LaTeX
* Le premier est qu'il s'agit d'un ''langage de mise en page''. Ce n'est donc pas un éditeur. Le LaTeX s'écrit donc comme du C ou du Java : on écrit le code dans un éditeur de texte, et ensuite le fichier est passé dans un compilateur. Ce dernier convertira le LaTeX en PDF, PostScript, HTML, ...
* LaTeX donne beaucoup plus d'importance à la '''structure''' d'un document, qu'à la mise en page finale. Évidemment, celle-ci peut être modifiée mais ce n'est pas le but premier de LaTeX.

On comprend bien pourquoi LaTeX est donc parfait pour écrire des rapports de TP ou de stage, des cours, etc.

partie 2 : Les premiers pas

Premiers pas en LaTeX sous Linux

Pous allons maintenant créer notre premier document LaTeX, voici la marche à suivre :
ouvrir un editeur de texte (n'importe lequel fera l'affaire) par exemple « kate » ou encore « gedit » ou alors en ligne de commande : « vim essai.tex », « emacs essai.tex » etc. Ensuite sauvegarde ton document avec le nom que tu voudra mais avec l'extension « .tex » par exemple essai.tex.

Maintenant penchons nous plus en détails sur la structure d'un document : comme nous l'avons vu, LaTeX est un language de mise en page similaire au HTML ; il utilise des « balises » pour décrire le document. Nous allons voir les premières « balises » au travers d'un premier exemple :

Les signes « % » servent à commenter des lignes dans le document ; elle n'apparaîtront pas dans le document final.

 % documentclass définit le type de texte que tu veux écrire
 % tu as le choix entre article, book, report, ...
 % chacun d'eux a des propriétés différentes
 \documentclass{article} 
 
 % \begin{} et \end{} permettent de délimiter des environnements
 % c'est à dire des zones de travail
 % ici l'environnement document représente ce qui va être
 % affiché dans le document final 
 \begin{document}
 
 \end{document}

Que remarque-t-on : les 3 premières lignes sont des commentaires et n'appraîtront pas dans le fichier final. A la 4ème ligne, on remarque le « \documentclass{article} » cette commande sert à indiquer à l'environnement LaTeX que le document est un « article ».

Il existe plusieurs sortes de documentclass : book, report, article, slide, beamer etc. Chacune de ces classes à des propriétés bien différentes, notamment pour la numérotation des chapitres. A notre niveau, et pour la plupart des travaux/rapports que nous aurons à faire à l'ESIEE, la classe article ets amplement suffisante. Les classes repport et book serait plus adaptées si l'on écrivait une thèse ou un mémoire (ce qui n'est pas notre cas).

Ensuite on remarque deux commandes :  \begin{document} et \end{document}. Ces deux commandes servent à indiquer à LaTeX que l'on débute le document (\begin{}) et que l'on termine le document (\end{}).

Bien maintenant que nous avons terminé notre document il ne reste plus qu'à le visualiser. Mais comment fait-on ? Et bien taper son texte et le mettre en forme à l'aide balises LaTeX n'est pas suffisant il faut compiler son texte afin d'avoir un résultat. En effet le fichier « .tex » n'a pas de sens en lui-même : il ne prend sa signification que pour le compilateur qui se charge de « transformer » notre fichier « .tex » en un fichier pdf lisible partout et par tous.

L'étape suivante consiste à compiler son fichier source afin d'obtenir un pdf. Comment procèder ? c'est très simple : sur les pc du smig ouvrez votre naviguateur de fichier (Konqueror) naviguez jusqu'au répertoire contenant votre fichier source et appuyez sur F4 pour lancer une console (Konsole). Ensuite tapez la commande suivante :

pdflatex essai.tex && pdflatex essai.tex

Qu'est que ça fait ? cette commande vous permet de compiler votre fichier source 2 fois d'affilé afin
de générer un document pdf. Pourquoi compiler 2 fois une fois n'est pas suffisant ? la réponse est oui compiler une fois n'est pas suffisant. Mais alors pourquoi ? tout simplement parque lors de la première compilation, LaTeX génère un fichier spécial « .toc » qui lui permet de générer la table des matières en fonction des chapitres du document ; ce fichier n'est pris en compte que lors de la compilation suivante d'où la nécessité de compiler une 2ème fois. Nous verrons comment générer une table des matières plus tard.

Revenons à notre fichier : les plus observateurs remarqueront que nous n'avons mis aucun texte entre le balises de début et fin du document. Donc n'espérez pas trouver votre fichier pdf : il n'existera pas car LaTeX remarquera que votre document est vide et ne fera... rien (enfin pas exactement).

partie 3 : l'entête ou préambule

Le préambule

Il y a une chose que je n'ai pas encore mentionnée : le prémabule qu'est-ce que c'est, à quoi sert-il. Comme dans tout texte, un fichier LaTeX commence toujours par un préambule ; le préambule défini l(es)'environnement(s) à utiliser pour créer un document, le dictionnaire des césures, les largeurs de marges, la pagination etc. Bref tout ce qui est utile pour faire un beau document. De plus le préambule contient un certain nombre d'information utiles comme l'auteur, le titre, la date etc.
Vous allez mieux comprendre avec cet exemple assez complet :

\documentclass[a4paper, 12pt]{article}
\usepackage{a4wide}
\usepackage[utf8]{inputenc}
\usepackage[french]{babel}
\usepackage{graphicx}
\usepackage{amsfonts}
\usepackage{amsmath}
 
\author{Moi}
\title{Mon premier document \LaTeX{}}

On retrouve évidemment le \documentclass{} mais cette fois ci avec un petit changement : [a4paper, 12 pt]. Explications : les commandes entre « [ » et « [ » sont des arguments optionnels c'est à dire qu'ils ne sont pas obigatoires. a4paper sert à indiquer au compilateur de produire un document destiné à être imprimer sur du papier A4 ce qui est du au fait que LaTeX est l'invention d'un américain et que le format du papier standart aux US est différent des standarts français. Enfin le 12pt sert à demander une police de 12 points pour le texte normal ; la valeur par défaut est 10 pt on peu soit ne rien mettre pour garder une police de 10 pt, soit changer en mettant 12pt ou 11pt (évitez les autres valeurs qui rendent votre document chiant à lire).

Les usepackage servent à importer des commandes déjà toutes faites afin que n'ayez pas à vous soucier de comment couper votre texte en fin de ligne gérer les accents etc. Je vais éviter de m'attarder sur les détails cependant il me faut expliquer à quoi servent les usepackage. La 2ème et la 3ème ligne servent à configurer LaTeX afin qu'il utilise des caractères unicode et un dictionnaire de césure français et des autres choses utiles pour la typographie. Nota : si après compilation votre fichier pdf affiche des caractère bizards à la place des accents c'est que votre système d'exploitation gère mal les caractères unicode ; aussi je suggère de remplacer le « utf8 » par « latin1 ». \usepackage{graphicx} sert à incorporer des images dans votre document ; attention LateX considère que vous êtes grand et ne compressera pas les images pour vous. Donc on compresse ses photos de 5 Méga Pixels avant de les mettre dans le pdf sinon on se retrouve avec un fichier de plusieurs Mo long à charger en mémoire.

Enfin \usepackage{amsfonts} et \usepackage{amsmath} servent à importer de nombreux symboles utiles pour les maths et la physique (alphabet grec, fractions etc.)

partie 4 : tables des matières, titres et sections

Table des matières, titre et sections
Pour générer le titre du document il suffit d'utiliser la commande \maketitle à insérer entre le \begin{document} et le \end{document} qui s'occuper pour nous de faire un belle page de titre à condition d'avoir bien rempli les commandes \author et \title dans le prémabule (cf préambule). Maintenant il ne me reste plus qu'à vous parler des sections et de la tables des matières avan d'avoir terminer la plus grosse partie de ce tutoriel.

Les sections : dans un document LaTeX, les sections sont comme les chapitres d'un livre : elles sont numérotées et permettent de structurer votre texte ce qui est au moins aussi important que le texte en lui même. Maintenant que vous commencez à comprendre comment se compose un document LaTeX je vais vous montrer un exemple que je commenterai après :

%préambule : à vous de faire le votre%
\begin{document}
\maketitle
\tableofcontents
\section{\LaTeX{} c'est kikoulol}
\subsection{\LaTeX  est super cool}
Non seulement il permet de faire des symboles mathématiques comme celui-ci~:$2\pi \omega t$
Ou encore des truc comme ça : $$\sum{\dfrac{z^{n}}{n+(-1)^{n-1}}}$$
\section{conclusion}
C'est génial
\end{document}

Comme vous l'avez tous compris, l'instruction \section permet de créer une nouvelle section ; \subsection permet de créer une sous section de niveau 1. Il également possible de créer une sous section de niveau 2 en utilisant l'instrcution \subsubsection. LaTeX va automatiquement numéroter les sections et sous sections pour vous grâce à l'instruction \tableofcontents qui va en prime générer une jolie tables des matières cliquable.

partie 5 : insérer des équations et gérer des listes

Liste à puces et mode mathématiques
Juste un dernier truc à savoir avant l'exemple final qui résume tout le tutoriel : comment entrer sortir de mode mathématques et faire des énumerations et des listes. Pour entre dans le mode maths on dispose de plusieurs commandes : $ $ qui va vous permettre d'écrire un symbole/une variable de manière ponctuelle entre les symboles « $ » ; $$ $$ qui va vous permettre d'écrire une ligne de calcul (idéal pour poser une égalité) et le \begin{maths} \end{maths} pour les calculs demandant plusieurs lignes. Je vous laisse découvrir les possibilités du package maths dans l'exemple a le mérite d'être plus parlant qu'un long discour ; rien ne vaut la pratique.

Une autre fonction très utilisée est la liste à puces et/ou l'énumération. Pour l'utiliser rien de plus simple : il suffit d'indiquer à LaTeX que vous voulez commencer un nouvel environnement, vous devriez avoir l'habitude maintenant, et de lui dire où le finir. Pour une liste cela donne :

\begin{itemize}
\item un premier point ;
\item un deuxième point ;
\item etc.
\item \ldots
\end{itemize}

et pour une énumération :

 
\begin{enumerate}
\item un premier point ;
\item un deuxième point ;
\item etc.
\item \ldots
\end{enumerate}

Voilà j'espère que vous avez appris plein de choses qui vous seront utiles maintenant vous n'auvez plus aucune excuse pour utiliser de Word et rendre des rapports de TP et de SH tout moches. Si vous avez un soucis ou bien des questions n'hésitez pas à passer au club, des gens compétants vous répondront.

partie 6 : Cas pratique un fichier TeX complet

On se quite sur ce dernier exemple complet et rédigé par mon prédecesseur :

Exemple complet de fichier LaTeX

  \documentclass{article} 
 % categorie de document
 
 \usepackage[latin1]{inputenc}
 % c pour les accents 
 
 \usepackage{a4wide}
 %\usepackage{amsfonts}
 %\usepackage{amsmath}
 %\usepackage{latexsym}
 \usepackage[frenchb]{babel}
 % c'est pour afficher le document comme en typographie FR %
 
 \usepackage[draft]{graphicx}
 % gère les images png, jpeg, gif
 % "draft" permet de gagner du temps pour les recompilation comportant des images
 % il ne les affiche pas mais grade les boites vides de la taille des images
 % l'option "final" est en général mise par défaut et sert à ravoir les images
 
 \title{utiliser irc}
 \author{Linda}
 % on les a pas encore afficher
 \date{21 juin 2005}
 % ça c parce que si on le dit pas, latex met la date de compilation par défaut
 
 \begin{document}
 
 \maketitle
 \newpage
 
 \tableofcontents
 \newpage
 %il faut compiler deux fois pour avoir la table des matières
 
 \section{Qu'est-ce que l'IRC ?}
 
 \subsection{Présentation rapide}
 
 L'IRC, "Internet Relay Chat", est techniquement un protocole ...
 
 \subsection{Comment se connecter à un réseau IRC}
 Il faut utiliser un client IRC, un logiciel adapté, comme ...
 
 \section{Installation de X-Chat 2}
 
 \begin{center}
 \includegraphics[width=10cm]{xchat.png}
 % 0.8*\linewidth en param si on veut que l'image fasse 80 pourcent de la page
 
 % si on veut qu'il le fasse proportionnellement sinon on rajoute height
 
 \hspace{2cm}
 % sert à espacer verticalement 2 images
 
 \includegraphics[width=10cm]{xchat.png}
 
 \end{center}
 %sert à centrer l'image
 
 
 Il faut d'abord aller télécharger le logiciel ...
 
 \subsection{Configuration X-Chat 2}
 Lorsque vous lancerez X-Chat pour la première fois ...
 
 Cliquez alors sur "Mode d'édition"...
 
 Réglez ensuite les paramètres comme suit. 
 \begin{enumerate}
    \item  Choisissez comme Pseudonyme le nom que vous aurez sur IRC...
    \item Comme nom de serveur, choisissez celui correspond à ...
    \item Dans joindre les canaux rentrez le salon clubnix...
    \item Comme jeu de caractères sélectionnez ...
    \item Cochez la case connections automatique au démarrage ...
 \end{enumerate}
 Vous pouvez vous connecter en utilisant le bouton Connexion ...
 
 \subsection{Utilisation de X-Chat 2}
 \begin{enumerate}
    \item Sur la droite il y a la liste des personnes présentes ...
    \item Ici c'est la fenêtre principale des discussions. ...
    \item Ici, c'est le sujet (aka topic) de discussion. ...
    \item Le champ où vous tapez vos messages.
    \item Les onglets des serveurs auxquels vous ...
  \end{enumerate}
 Il est possible de dialoguer en privé avec une ...
 
 Dans le menu contextuel, sélectionnez Ouvrir la ...
 
 \subsection{Aller plus loin}
 Je vous laisse maintenant découvrir par vous-même ...
 
 $$ \pi $$
 % les dollars sont un raccourci pour dire formule mathématiques
 
 $$ \frac{\pi}{2} $$
 % pi/2
 
 $$ \sqrt{\frac{\pi}{2}} $$
 % racine
 
 texte $\frac{\pi}{2}$
 %force à intégrer un paragraphe
 
 %caractères spéciaux:
 $$ \mu \vec{A}$$
 $$ \alpha $$
 %$$ \Alpha $$
 
 $$ \leq $$
 % <=
 
 $$ A_i $$
 % indice
 
 $$ A^2 $$
 %carre
 
 $$ \times $$
 
 
 $$ A_{ij}^2 $$
 
 $$ \int_0^\infty{x dx} $$
 %intégrale de x sur 0 à infini
 
 $$ \geq $$
 % >=
 
 $$\neq$$
 % =!
 \end{document}

Tutoriel LaTeX (intermédiaire)

Dans cette section vous trouverez de nombreux tutoriels pour vous expliquer le fonctionnement des différents packages disponibles pour LaTeX. Tous les tutoriels et exemples ont été réalisés par les membres du club*nix.

Document recto et recto-verso

J'ai longtemps cherché à savoir comment faire pour que lorsque je créer un document recto-verso latex me mette la table des matières sur une page impaire et non directement après la page de titre. Je viens seulement de trouver l'astuce alors je la partage dans cette section. En fait il faut utiliser un \cleardoublepage à la place du \clearpage dans l'entête du club.
Pour avoir une page vide après la page de titre (sans numéro de page ou titre) il suffit de faire :

\begin{document}
\maketitle
\thispagestyle{empty}
\clearpage
\thispagestyle{empty}
\cleardoublepage
 
\tableofcontents
\thispagestyle{empty}
\clearpage
\thispagestyle{empty}
\cleardoublepage

Les images et les figures

Il existe plusieurs manières d'ajouter des images à un document LaTeX. Nous allons voir ensemble quelques possibilités offertes par LaTeX pour la gestion des images.

La commande la plus utile est \includegraphics{image.png}. Cette commande permet de spécifier une image de type binaire (bmp, png, jpeg, etc.), pour pdflatex ou vectoriel (ps, eps, dvi ...) pour latex. Il est possible de faire une mise à l'échelle ou de forcer une taille précise (largeur ou hauteur) avec les options suivantes :

\includegraphics[scale=0.8]{image.png}
\includegraphics[heigth=10cm]{image.png}
\includegraphics[width=0.5\textwidth]{image.png}

La première commande aura pour effet d'inclure image.png avec une mise à l'échelle de 80%,
la deuxième commande aura pour effet d'inclure image.png avec une hauteur de 10cm, la largeur sera automatiquement réduite/agrandie si nécessaire,
la dernière commande aura pour effet d'inclure image.png avec une largeur égale à 50% de la largeur du texte.

Partie 1 : Les tableaux

Réaliser un tableau avec latex peut sembler simple, pourtant il arrive souvent que le résultat obtenu n'est pas du tout ce que l'on souhaitait. Dans cet article vous allez apprendre à utiliser les différents types de tableaux en latex.

Ce tutoriel s'adresse à tous ceux qui utilisent déjà latex, la lecture du tutoriel latex du club*nix est nécessaire si vous voulez profiter pleinement du présent tutoriel. Je vous invite à copier/coller ces exemples pour bien voir ce qu'ils font, note : les exemples ont étés compilés et testés sans problèmes avec Texlive mais si vous utilisez Tetex il ne devrait pas y avoir de soucis. Si jamais vous avez un problème pensez à bien vérifier que vous utilisez les bon packages. Je mets en pièce attachée le fichier entête utilisé pour compiler les exemples.

Tabular

C'est le tableau de base le plus utilisé. Son emploi est très simple : on commence avec \begin{tabular} et \end{tabular}. De plus on doit indiquer le nombre de colonne au tableau ainsi que on alignement ; pour cela on déclare le tableau comme ce qui suit :

\begin{tablular}{lcr}
1ere cellule & 2eme cellule & 3eme cellule\\
une autre ligne : & plip & plop\\
etc & \ldots & \vdots\\ 
\end{tabular}

Bon comme vous l'avez deviné, {lcr} sert à indiquer l'alignement du texte dans la cellule du tableau. Ainsi les cellules dans la première colonne seront justifiée à gauche, celles dans la deuxième colonne seront centrées, et celles dans la dernière colonne justifiées à droite. De plus on utilise le symbole & pour séparer les colonnes du tableau.

Marquer les séparateurs

Voilà vous avez appris à construire un tableau. La question qui se pose c'est comment est-ce que je fais pour pouvoir tracer des lignes qui séparent les différentes colonnes ? C'est très simple : il faut utiliser le pipe (caractère obtenu en tapant Alt gr + 6 sur un clavier azerty : | ) pour ça. Exemple :

\begin{tablular}{|l|c|r|}
\hline
1ere cellule & 2eme cellule & 3eme cellule\\ \hline
une autre ligne : & plip & plop\\ \hline
etc & \ldots & \vdots\\ \hline
\end{tabular}

Ici on note que le pipe, |, se place dans {lcr} ; il n'est pas obligatoire de mettre qu'une seule fois ainsi {|l||c|r} est tout aussi correct. De plus on peut aussi tracer des lignes horizontales avec \hline. Essayez de tester vous même les autres possibilités.

Tracer des lignes horizontales sur une ou plusieurs colonnes

On peut utiliser \cline qui prend 1 argument obligatoire : {colonne départ - colonne arrivée}
Exemple :

\begin{tabular}{|c|c|c|}
\hline
plop & plip & plup\\
\cline{1-1}
plop & plip & plup\\
\cline{2-3}
plop & plip & plup\\
\cline{1-2}
plop & plip & plup\\
\hline
\end{tabular}

Fusionner des cellules avec multicolumn

Le package multicolumn permet de "fusionner" des cellules horizontales. Il prend en argument : le nombre de colonnes, le motif, le texte à afficher dans la cellule. Exemple :

\multicolumn{5}{||c||}{plop}

multicolumn s'utilise à l'intérieur de l'environnement tabular.
Exemple :

\begin{tabular}{|c|c|c|c|}
\hline
\multicolumn{4}{|c|}{\textbf{Club*nix}}\\
\hline
\multicolumn{2}{|c|}{\LaTeX{}} & \multicolumn{2}{c|}{C}\\
\hline
furet & pdflatex & gcc & -Wall\\
\hline
debian & evinces & make & gentoo\\
\hline
\end{tabular}

Note il est également possible de fusionner des cellules verticalement avec la commande \multirow qui s'utilise presque comme \multicolumn

Package array

Le package array permet d'ajouter des nouvelles manières de formater son texte dans les cellules. Il ajoute principalement trois nouveaux mots clés : p, b et m. Ces mots doivent obligatoirement être suivis de la taille de la cellule. p permet de réaliser un alignement vertical du texte vers le haut, b un alignement vertical vers le bas et m de centrer verticalement le texte.

Exemple sans utiliser m, b ou p :

\begin{tabular}{|l|c|}
\hline
gcc &  When you invoke GCC, it normally does preprocessing, compilation, assembly and linking. The "overall options" allow you to stop this process at an intermediate stage.  For example, the -c option says not to run the linker. Then the output consists of object files output by the assembler\ldots{}\\
\hline
\end{tabular}

Vous avez remarqué ? Le texte est beaucoup trop long pour tenir sur une seule cellule et est tronqué. Pour remedier au problème on peut utiliser m{6cm}. Il faut penser à utiliser le package array :
\usepackage{array}
Exemple :

\begin{tabular}{|l|m{6cm}|}
\hline
gcc &  When you invoke GCC, it normally does preprocessing, compilation, assembly and linking. The "overall options" allow you to stop this process at an intermediate stage.  For example, the -c option says not to run the linker. Then the output consists of object files output by the assembler\ldots{}\\
\hline
\end{tabular}

Bien entendu on peut augmenter la taille du paragraphe si l'on veut plus de place mais il faut faire attention à ne pas dépasser la largeur de la page.

tableaux dont la largeur est spécifiée en argument

Dans les exemples qui suivent, la largeur du tableau est donnée en argument de la manière suivante :
\begin{tabular[xy]}{largeur}{colonnes}
où largeur correspond à celle du tableau et [xy] à x ou y comme nous allons le voir. Pour connaitre la largeur de la page, on utilise la commande \linewidth

tabularx

Tabularx fonctionne de manière similaire à tabular, sauf que celui-ci permet l'utilisation d'un nouveau motif : X qui permet d'entre en mode paragraphe (de type p). Sachant que les cellules se partagent l'espace disponible en largeur. Il faut utiliser le package tabularx :
\usepackage{tabularx}
Exemple :

\begin{tabularx}{\linewidth}{|l|X|X|}
\hline
plop & plip plip plip pliplipliplipliplip\ldots & pluplupluplupluplupluppluplup\ldots\\
\hline
\end{tabularx}

tabulary

Tabulary s'utilise de manière similaire à tabularx sauf que nous utilisons les motifs en L, C, R et J à la place de X et permettent d'entrer en mode paragraphe aligné à gauche, centré, aligné à droite et justifié. Il faut utiliser pour ça les packages tabulary et calc :

\usepackage{tabulary}
\usepackage{calc}

Exemple :

\begin{tabulary}{\linewidth}{|L|J|J|}
\hline
plop & plip plip plip pliplipliplipliplip & pluplupluplupluplupluppluplup\\
\hline
\end{tabulary}

Il s'agit en fait de l'exemple précédent on note tout de même une petite différence entre le rendu de tabularx et celui de tabulary.

Fichier attachéTaille
Fichier entete.tex373 octets

Partie 2 : Bibliographie

Il y a 2 manières pour créer une bibliographie avec latex :

  • La manière dégeulasse avec thebibliography
  • La manière propre avec BibTeX

Je vais détailler les 2 manières de faire, la première peut vous servir si vous moins de 3 références bibliographiques, si ce n'est pas votre cas, tournez vous vers BibTeX qui est bien plus puissant.

thebibliography

Faire un bibliographie avec thebibliography est assez simple. Il suffit de d'abord de construire sa bibliographie avec l'environnement thebibliography. Chaque référence doit être insérée à l'aide de la commande \bibitem{ref} avec entre acolades ref le nom de votre référence. Pour citer vos références il faut utiliser la commande \cite{ref}ref est évidemment la référence à citer.

Voici un exemple concret :

\begin{document}
 
Un peu de blabla qui sert à rien…
 
Ce livre\cite{ol9} est un bon bouquin à mettre sur sa table de chevet.
 
\begin{thebibliography}{99}
\bibitem{ol1} "OpenLDAP, Main Page" . Internet : http://www.openldap.org . Mar. 28, 2008 [May. 1, 2008]
\bibitem{ol2} "OpenLDAP Software 2.4 Administrator's Guide: Introduction to OpenLDAP Directory Services" . Internet: http://www.openldap.org/doc/admin24/intro.html#Wha\
t\%20is\%20LDAP . [May. 1, 2008]
\bibitem{ol3}  "OpenLDAP Software 2.4 Administrator's Guide: A Quick-Start guide" . Internet: http://www.openldap.org/doc/admin24/quickstart.html. [May. 1, 2008]
\bibitem{ol4} "OpenLDAP Software 2.4 Administrator's Guide: The slapd Configuration File". Internet: http://www.openldap.org/doc/admin24/slapdconfig.html. [May. 1, 200\
8]
\bibitem{ol5} "OpenLDAP, Software, Man Pages: ldif" . Internet: http://www.openldap.org/software/man.cgi . [May. 12, 2008]
\bibitem{ol6} J.H.M. Dassen (Ray), Chuck Stickelman, Susan G. Kleinmann, Sven Rudolph, Santiago Vila, Josip Rodin, Javier Fernandez-Sanguino . (2006, June 17). The Deb\
ian GNU/Linux FAQ. version CVS . [On-line]. Available: http://www.debian.org/doc/FAQ/index.en.html. [May. 1, 2006]
\bibitem{ol7} Gustavo Noronha Silva . (2005, March). APT HOWTO. version 1.8.10.4. [On-line]. Available: http://www.debian.org/doc/manuals/apt-howto . [May 1, 2008]
\bibitem{ol8} "The Horde Project" . Internet: http://www.horde.org/ . [May. 12, 2008]
\bibitem{ol9}  G. Carter. "OpenLDAP: Building a Company white Pages" in LDAP System Administration, 2nd ed. O'Reilly Editions.
\end{thebibliography}
 
\end{document}

BibTeX

Avec BibTex, c'est un peu moins simple, il vous faut procéder en 3 étapes.

Étape 1 : créer le fichier bibli.bib

Ce fichier possède une syntaxe propre. Pour créer une nouvelle entrée, il faut commencer par décrire le type de référence dont il s'agit. Cela peut être un livre (book), un passage de livre (in-book), un manuel (manual), …
Chaque type d'entrée, commençant par un @. Ainsi si je veux insérer un livre, avec ast_tfot comme référence, je commencerai mon fichier par :

@book={ast_tfot,
 
}

Ensuite il faut indiquer l'auteur du livre, manuel, etc. Ceci étant fait grâce à la séquence author = {auteur}. On peut aussi renseigner le titre, title = {titre}. Il existe plein d'autres arguments, mais je ne vais pas tout détailler ici, ça serait trop long. Sachez juste qu'il existe des champs obligatoires, en général author, et que les champs qu'il est possible de renseigner changent en fonction du type d'entrée (book, manual, …). Donc je vous laisse chercher pour les autres types d'entrées.

Voici ce que donne une entrée complète de bibliographie, notez la virgule pour séparer chaque champs :

@book{ast_tfot,
author = {Van Meggelen, Jim \and Madsen,  Leif \and Smith,  Jared},
title = {Asterisk ; The Future of Telephony},
publisher = {O'Reilly},
isbn = {0-596-51048-9, 978-0-596-51048-0},
year = {2007},
edition = {second}
}

Étape 2 : Insérer la bibligraphie dans votre fichier .tex

Construire la bibliographie ne suffit pas ; il faut maintenant dire à LaTeX d'utiliser vos références.
Ceci est fait grâce à la commande \bibliography{blibli.bib}. Mais il faut aussi dire quel style nous allons utiliser pour construire notre bibliographie. On utilise la commande \bibliographystyle{plain} avant \bibliography{blibli.bib}, pour indiquer que l'on utilise le style plain pour construire la bibliographie.
Il existe de nombreux styles pour constuire une bibliographie, on retiendra en particulier : plain, abbrv, alpha. Je vous laisse découvrir l'effet de chaque style sur la bibliographie et les citations, qui se font aussi avec la commande \cite{ref} ; par exemple pour citer mon bouquin sur Asterisk, je vais procéder de la manière suivante : \cite{ast_tfot}.

Étape 3 : compiler le tout

La compilation d'un document comportant une bibliographie externe se fait en 3 passes latex et 1 passe bibtex :

pdflatex rapport.tex
bibtex rapport
pdflatex rapport.tex
pdflatex rapport.tex

Petite explication : lors du premier passage de pdflatex, le fichier rapport.aux va être généré, c'est de ce fichier dont bibtex a besoin pour fonctionner. Ensuite l'appel à bibtex va générer les fichiers, rapport.bbl et rapport.blg dont pdflatex va se servir pour insérer la bibliographie. Le dernier passage sert en fait à construire correctement les références croisées (comme dans le cas de la table des matières et les footnotes).

Le prochain chapitre portera sur la création d'un index/glossaire, qui arrivera quand j'aurais le courage de le rédiger.
Au passage, je vous conseille d'aller voir sur ce site pour appendre à gérer les compilations LaTeX avec GNU Make.

Tutoriel LyX (IDE LaTeX)

LyX est un IDE LaTeX WYSIWYM (What You See Is What You Mean)

Qui permet de faire du LaTeX au clic en ayant un rendu en temps réel. Il est possible de mélanger les commandes LaTeX et utiliser uniquement l'interface.

Voici une petite vidéo pour crée un présentation beamer grâce à LyX


lyx.png

Fichier attachéTaille
Image icon lyx.png46.66 Ko

Installation LaTeX

Linux

Sous Linux, la distribution de LaTeX la plus utilisée est texlive. Pour l'installer, il suffit de télécharger les fichiers binaires de texlive qui sont disponibles dans les packets d'installation de la plupart des distributions. Par example, sur Ubuntu, le package à instaler est "texlive-base" ainsi que "texlive-common".
Un tutoriel d'installation avec des liens vers les codes sources est disponible ici. pour ceux qui ne veulent pas utiliser les gestionnaires de packets. Mais texlive n'est pas le seul à pouvoir compiler du latex, il existe aussi un autre environnement : "teTeX" mais il n'est plus mis à jour et peu de personnes l'utilisent encore.
Comme enviroment de développement, vous pouvez utiliser Kile, ou encore TeXMaker, .

Pour visonner les pdf, vous pouvez installer : gpdf pour Gnome, kpdf sous KDE, ou bien encore evince pour tous les bureaux.

MacOS X (beurk!)

Tout ce qu'il faut savoir sur LaTeX pour MacOS X se trouve sur cette page.

LaTeX s'installe avec le gestionaire de packagesi-Installer. Une fois l'image disque montée, copiez l'application quelque part sur votre disque.

Au premier (et unique normalement) lancement, il aura besoin d'avoir les droit d'écriture sur son dossier. Utilisez cet utilitaire pour installer les packages ''TeX'', ''Ghostscript'' et ''CM Super for TeX''.

Ensuite, vous pouvez utiliser l'environement de développement TeXShop. S'il ne vous plais pas, il y a un large choix ici

Windows®

Il suffit d'installer MikTeX qui est téléchargeable sur http://www.miktex.org/

Remarque : Sous Windows®, j'aime bien utiliser TeXnicCenter qui est un environnement intégré de développement pour LaTeX très puissant. http://www.toolscenter.org/

N'oubliez pas d'installer Ghostscript et GSview pour pouvoir visualiser et imprimer des fichiers PostScript.
http://www.ghostscript.com/

L'installation d'un environnement LaTeX sous Windows® étant souvent un véritable supplice, je vous conseille de vous tourner vers la framkey spéciale LaTeX

Présentation avec Beamer

Marre des ppt qui plantent lors de tes présentations ?
Marre des ppt qui sont tout moche parce que tu es une quiche ?
Tu as vraiment envie de te la péter ?
Tu es bien tombé

Beamer c'est quoi ?

Beamer est un paquet LaTeX qui permet de créer des présentation style power point, mais en pdf. et Donc tu n'as plus besoin de power point.
Fonctionne parfaitement sous Linux/OpenBSD/OS-X/et le reste... Plus de problèmes de version MsOffice !
Comme la plupart des documents produits avec LaTeX tu obtiens une présentation claire, un organisation propre !

Mais le pdf c'est pas tout statique ??

Avec Beamer tu peux faire presque les mêmes chose qu'avec power point, mais de manière plus stable (tu as pas tes photos/schémas qui se barrre).
Tu peux :

  • Faire apparaitre/disparaitre des item de liste
  • Jouer avec la transparence
  • Mettre des images/schéma
  • Mettre des transitions
  • Et plein d'autre trucs

Installation

Ben commencer par installer LaTeX (installation LaTeX)
Et installer le paquet "latex-beamer"

Petit exemple :

Cette exemple n'est pas vraiment un tutoriel mais présente quelque possibilité de base de Beamer. Bien évidement le code est fournit avec ce qui te permettra de reproduire les effets voulu.

Pour le visualiser correctement, le mettre en plein écran.
presentation.pdf

 
\documentclass{beamer}
 
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[french]{babel}
\usepackage{graphicx}%@@final
\usepackage{hyperref}
\usepackage{amsmath}
\usepackage{listings}
 
\lstset{tabsize=2, numbers=left, numberstyle=\tiny, stepnumber=5, numbersep=5pt,breaklines=true,
    emph={int}, emphstyle=\color{red},
    emph={[2]int,for}, emphstyle={[2]\color{red}},
    emph={[3]int,for,printf}, emphstyle={[3]\color{blue}},
    morecomment=[s][\color{green}]{/*}{*/}
}
 
\usetheme{Warsaw}
\setbeamercovered{transparent}
 
\hypersetup{%colorlinks,%
%citecolor=black,%
%filecolor=black,%
%linkcolor=black,%
urlcolor=blue}
 
\author {GIVERNAUD Omar \\ givernao@esiee.fr}
\title{Beamer par l'exemple\\ }
\institute{Club*Nix}
 
 
\begin{document}
 
\begin{frame}
	\maketitle
	\begin{center}
		\includegraphics[width =2cm]{logo.png}
	\end{center}
\end{frame}
 
\begin{frame}
	\tableofcontents
\end{frame}
 
\section{Liste de puces}
\begin{frame}
\frametitle{Liste de puces}
Mes puces
	\begin{itemize}
		\item<1->Morpions
		\item<2->Tiques
		\item<3->Mithes
	\end{itemize}
 
Le club
	\begin{enumerate}
		\item<4->{Prez : trax}
		\item<5->{Vice : tatane}
		\item<6->{Trez : phroddon}
		\item<7->{Secrétaire : dennewan}
	\end{enumerate}
 
\end{frame}
 
\section{Utilisation de blocs}
\begin{frame}{Utilisation de blocs I}
	\begin{theorem}<1->
		Laisser son écran allumer tue des TUX
	\end{theorem}
	\begin{proof}<2->
		\begin{itemize}
			\item{Fait consommer de l'électricité}
			\item{Production de $CO_2$}
			\item{Trou dans la couche d'ozone}
			\item{Réchauffement des pôles}
		\end{itemize}
	\end{proof}
	\begin{example}<3->
		En 1982, 77\% de la population a disparu pour ne laisser que 463 manchots
	\end{example}
 
 
\end{frame}
\begin{frame}{Utilisation de blocs II}
	 \begin{block}{Question}
      Calculer :
	$$\left[\sum_{n=0}^{+\infty} \left(\dfrac{5}{6}\right)^n \right]
	  \left[\sum_{n=0}^{+\infty}\left(\dfrac{7n - 3}{n^3 + 2n^2 - n -2} - \dfrac{4}{n^2+3n+2}\right)\right]$$
    \end{block}
    \begin{block}{Réponses possibles}
    	\begin{itemize}[<+-| alert@+>]
			\item 0
			\item 7
			\item 42
			\item 1
		\end{itemize}
    \end{block}
\end{frame}
 
\section{Et pour les tétards ??}
\begin{frame}[fragile]
	\frametitle{Et pour les tétards ??}
	\begin{columns}
	\column{.6\textwidth}
	\begin{semiverbatim}
\only<4->{void permute(int *a, int *b);}
int main (void)\{
	\uncover<2->{	int a = 42, b = 7;}
	\only<3->{	permute(\alert<1->{\&}a, \alert<1->{\&}b);}
{	return 0;}
	\}
\only<4->{void permute(int *a, int *b)\{}
\only<4->{\uncover<5->{	int r;}}
\only<5->{\uncover<6->{	r = \alert<5->{*}a;}}
\only<5->{\uncover<6->{	\alert<5->{*}b = \alert<5->{*}a;}}
\only<5->{\uncover<6->{	\alert<5->{*}a = r;}}
\only<4->{\}}
	\end{semiverbatim}
	\column{.4\textwidth}
	\begin{block}{Commentaire}
		Ce code effectue une permutation des variables $a$ et $b$
	\end{block}
	\end{columns}
\end{frame}
 
\begin{frame}{Contacts}
 
{\color[rgb]{0,.7,0}{Réalisé par/pour le Club*Nix}}
 
{\color[rgb]{0,0,0.7}\href{http://clubnx.esiee.fr}{http://clubnix.esiee.fr}}
 
{\color[rgb]{0,0.7,0}{Documentation officiel}}
 
{\color[rgb]{0,0,0.7}\href{http://latex-beamer.sourceforge.net}{http://latex-beamer.sourceforge.net}}
\end{frame}
 
 
\end{document}
Fichier attachéTaille
PDF icon presentation.pdf276.35 Ko

Lisp Emacs

Apprendre à scripter pour Emacs (le seul et unique editeur)
Je me suis basé sur les articles suivants :

http://xahlee.org/emacs/elisp.html

Début rapide

Pré-requis ?

  • Votre meilleurs éditeur de texte : emacs (XEmacs, ou Micro Emacs pour certains)
  • Un minimum de notion en programmation : savoir ce qu'est une variable, une condition ou une boucle
  • De la patience et un cerveau

Comment exécuter du code ?

  • Pour une seule ligne :

    C-x C-e
  • Le code sera exécuté jusqu'au curseur.

  • Un buffer entier :
    Bien que le Lisp ne soit pas à proprement parler compilé, emacs a besoin de l'analyser (parser) avant que vous n'exécutiez des commandes.
    M-x eval-buffer
    Vous pouvez par la suite appeler une fonction comme décrit plus haut.

Exemple :

(+ 2 3)

Renverra dans le mini-buffer 5.

Cette écriture inverse s'appelle Polonaise inversée (http://fr.wikipedia.org/wiki/Notation_polonaise_inverse).
Brièvement cela consiste à placer la fonction en premier puis les arguments. Finalement c'est ce qui est fait dans la plupart des langages pour les fonctions autre que celles arithmétiques : en C

printf("plop %d\n" , 42);

La fonction est bien positionnée en premier printf puis ses arguments "plop %d\n" et 42. Nous comprenons donc que + est non pas un opérateur mais bien une fonction, qu'il est d'ailleurs possible de redéfinir.

Fonction d'affichage

(print 'bonjour)

En exécutant ( C-x C-e cette ligne vous devriez voir apparaître deux fois la ligne bonjour. Nous expliquerons cela dans la partie d'écriture de fonction.

Poser une question

Question binaire (Yes or No question)

(y-or-n-p "Aimez-vous ce tutoriel Emacs Lisp ?")

Le retour est soit nil pour une réponse négative ou t pour une réponse positive.

Question ouverte

(interactive "MEntrez votre login : ")

Le "M" spécifie que la réponse est une chaîne de caractères.

Notions de base en Emacs Lisp

Listes

En Emacs Lisp presque tout est une liste, du moins tout ce qui est entre "()".

  • Le premier argument ("+") est la fonction à exécuter. Les autres sont les argument de la fonction.

    (+ 2 3) ; est une liste
  • Cette liste ne sera pas interprété par emacs parce que précédé d'un apostrophe. Cela signifie donc que c'est une liste d'éléments.

    '(bonjour les gens) ; est aussi une liste 
  • Est la liste vide. Elle équivaut exactement à nil.

    ()

"nil" et "t"

Sont respectivement les valeurs de vérité FAUX et VRAI.

Fonction

(defun plop (arg0 arg1 arg2)
)

Nous venons de déclarer la fonction plop qui prend trois arguments arg0, arg1 et arg2 en entré.

Chaque fonction en Emacs Lisp est un object et chaque object renvoie une valeur. Si celle ci n'est pas définie, la valeur renvoyé est nil.

Pour appeller notre fonction il suffit de faire :

(plop 1 2 3)

1, 2 et 3 étant les arguement passé à la fonction.

Quelques opérateurs

Optimisation de code C

Pas mal de gens disent souvent que le plus important est d'optimiser sont algorithme plutôt que sont code. Certe, mais il est également possible de tourner le problème autrement et dire que l'implémentation fait partie de l'agorithme.

Optimisation des boucles

Commençons fort !

Condition de boucle

int main() {
  int a = 42;
  int b = 72;
  int t = 0;
  for (int i = 0; i < a + b; i++) {
    t++;
  }
 
  return 0;
}

Sans optimisation de la part de gcc, le programme va effectuer 114 tour de boucle en recalculant la somme a + b à chaque tour de boucle.
En spécifiant à gcc d'optimiser ce code, la boucle va tout simple être supprimé.

Nous pouvons alors demander à gcc d'afficher le résultat. Il ne faut pas sous estimer gcc, qui aura vu que le résultat est constant et qui supprimera quand même la boucle, et affichera un résultat prés calculé.

Bis

Dans la vraie vie on fait rarement de boucles comme-ca, le resultat n'est pas constant ou voir non constant ou defois, pas de resultat :P

void faire_quelquechose(int i);
 
int main() {
  int a = 42;
  int b = 72;
  int t = 0;
  for (int i = 0, max = a + b; i < max; i++) {
    faire_quelquechose(t);
  }
 
  return 0;
}

Et la le a + b est calcule une seule fois pour plein de tour de boucle !
Et un peu plus de memoire est utilisee.

Optimisation pour les tableaux

Allocation

Histoire de commencer par le commencement, lire http://clubnix.fr/allocation_dynamique_2_dimensions_C pour plus d'informations.

Le programme suivant va allouer un tableau à deux dimensions de manière dynamique et, afficher le temps de création et de destruction du tableau.

#include <stdio.h>
#include <stdlib.h>
 
 
/*========== parttie relative a la mesure du temps ===========*/
#include <sys/times.h>
#include <unistd.h>
typedef struct tms sTms;
typedef struct{
	int debut,fin;
	sTms sdebut, sfin;
}temps_exec;
 
 
void temp(temps_exec *tmps){
    printf("temps réel :\t %d\ntemps utilisateur :\t %ld\ntemps system :\t %ld\n\n",
		tmps->fin - tmps->debut,
		tmps->sfin.tms_utime - tmps->sdebut.tms_utime,
		tmps->sfin.tms_stime - tmps->sdebut.tms_stime);
}
/*========== parttie relative a la mesure du temps ===========*/
 
 
void badInit(int C, int L){
  int **tab;
  temps_exec temps;
 
  temps.debut = times (&temps.sdebut);
  tab = (int **)malloc (sizeof(int*)*L);
  for(int i = 0 ; i < L ; i++){
    tab[i]=(int *)malloc(sizeof(int)*C);
  }
 
  for (int i = 0 ; i < L ; i++){
	free (tab[i]);
  }
  free (tab);
  temps.fin = times (&temps.sfin);
  temp (&temps);
 
 
}
 
void goodInit(int C, int L){
  int **tab;
  int *tab0;
  temps_exec temps;
 
  temps.debut = times (&temps.sdebut);
  tab = (int **)malloc (sizeof(int*)*L);
  tab0 = (int *)malloc (sizeof(int)*(C*L));
 
  for (int i = 0 ; i < L ; i++){
	tab[i] = &tab0[i*C];
  }
 
  free (tab0);
  free (tab);
  temps.fin = times (&temps.sfin);
  temp (&temps);
 
}
 
int main(){
 
  badInit (10, 1000000);
 
  badInit (1000000, 10);
 
  goodInit (10, 1000000);
 
  goodInit (1000000, 10);
 
 
  return 0;
}

Intéressons nous dans un premier temps à la fonction badInit.

Sa compléxité est en o(n) (n la deuxième dimension du tableau), ce qui ne semble pas dramatique.

Dans un premier temps, allons un tableau de 10 colonnes et 1000000 lignes
Soit 1000010 allocation mémoire. Ces allocations sont des appelles systèmes (donc appelle du kernel) qui sont couteux en temps. La première chose à faire est donc placer la plus petite dimension en premier et d'allouer un tableau de 1000000 et de 10 lignes.

Il est néanmoins possible de faire mieux et de réduire à deux allocations mémoire comme le montre la fonction gootInit (la aussi il faut penser au dimensions).

Voici les temps obtenu

$ ./tab
temps réel :     21
temps utilisateur :      12
temps system :   8
 
temps réel :     6
temps utilisateur :      6
temps system :   0
 
temps réel :     4
temps utilisateur :      2
temps system :   1
 
temps réel :     0
temps utilisateur :      0
temps system :   0

L'allocation à deux malloc permet de gagner du temps lors de la création mais pas seulement.

Parcour de tableau

Soit un tableau tab de 1000000 colonnes et 100 lignes alloué avec la fonction badInit.

Nous pouvons l'initialiser de la manière suivante :

  for (int i = 0 ; i < C ; i++){
        for (int j = 0 ; j < L ; j++){
          tab[j][i] = j;
        }
  }

Si nous parcourons le tableau par les lignes (en lisant la première colonne de chaque ligne, puis la deuxième colonne de chaque ligne...), nous obligeons le système à faire des sauts dans la mémoire.

temps réel :     1327
temps utilisateur :      1314
temps system :   11

Si nous parcourions le tableau dans le sens des colonnes

  for (int i = 0 ; i < L ; i++){
        for (int j = 0 ; j < C ; j++){
          tab[i][j] = j;
        }
  }

Nous obtenons :

temps réel :     77
temps utilisateur :      12
temps system :   65

Ce qui est bien mieux, et encore une fois la complexité algorithmique est la même.

En faisant la même chose sur un tableau initialisé avec la fonction goodInit nous obtenons les temps suivant :

temps réel :     207
temps utilisateur :      205
temps system :   2
 
temps réel :     73
temps utilisateur :      12
temps system :   62

Mais nous n'utilisaton pas le fait que le bloc mémoire soit continue, ce qui pourrait améliorer nos performances :

  for (int i = 0 ; i < C*L ; i++){
        tab[0][i] = i;
  }

Nous n'avons plus qu'une boucle, et toujours de la même compléxité algorithmique.

temps réel :     47
temps utilisateur :      46
temps system :   0

Qui dit mieux ?

Et si c'est possible. Dans les exemples précédents, nous obligions le système à recalculer les index de chaque case soit des multiplications en trop.

  tab0 = &tab[0][0];
  for (int i = 0 ; i < C*L ; i++){
        *tab0 = i;
        tab++;
  }

Soit :

temps réel :     0
temps utilisateur :      0
temps system :   0
Fichier attachéTaille
Fichier tab.c2.3 Ko

Outils à la programmation

Autotools pour faire ses Makefiles

Introduction

Vous avez surement installé à la main un paquet de source (sinon vous auriez dû :), avec le fameux ./configure && make && make install

Ou juste que vous en avez marre de faire vos Makefile à la main, pour un projet basique ça passe encore, mais ça atteint vite ses limites.

Autant de raisons pour adopter la suite Autotools

Préparation

Toutes sources seront mises dans un dossier src (tradition oblige)

Automake demande quelques fichiers :

  • AUTHORS
  • ChangeLog
  • NEWS
  • README

Par la suite nous utiliserons deux type de fichier : configure.ac, et Makefile.am
Le configure.ac permet de créer le script qui vérifiera la présence des différents éléments nécessaires (headers, librairies, ...). Les Makefile.am permettront de construire les Makefiles.

Les noms sont assez explicites.

Initialisation

Nous allons utiliser un petit projet comme base d'exemple
autotools

Executer autoscan
Il va parcourir vos fichiers pour créer des fichiers de configurations de base. Celui qui nous intéresse est nommé configure.scan qu'il faut renommer en configure.ac

#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
 
AC_PREREQ(2.61)
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_CONFIG_SRCDIR([src/rs232.c])
AC_CONFIG_HEADER([config.h])
 
# Checks for programs.
AC_PROG_CC
 
# Checks for libraries.
 
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h])
 
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
 
# Checks for library functions.
AC_CHECK_FUNCS([strtol])
 
AC_CONFIG_FILES([src/Makefile])
AC_OUTPUT

Bon pour l'instant il a remarqué nous utilisions la stdlib.h pas de quoi se relever la nuit.

Configuration

Nous allons maintenant configurer tout cela pour que ça fonctionne.

Drapeaux

Il faut configurer variables de drapeaux pour la compilation et pour la liaison :

CFLAGS="-Wall -std=gnu99"
LIBS="-lm"

(Nous n'utiliserons pas la librairie math dans notre exemple)

pkg-config

PKG_CHECK_MODULES(NIX, [gtk+-2.0 libglage-2.0])

Cette ligne va créer les variables NIX_CFLAGS qui correspond aux drapeaux pour gtk et libglade (et respectivement NIX_LDFLAGS avec les drapeaux de liaison). En plus elle les exporte automatiquement vers les Makefile.

Condition

La compilation conditionnelle peut être très intéressante pour ajouter des options de compilations ou drapeaux.

Voici un petit exemple qui permet d'activer un drapeau de debug.

Par défaut mis à l'état bas, pour l'activer il faudra mettre --enable-debug lors du configure.

AC_ARG_ENABLE([debug],AS_HELP_STRING([--enable-debug],[Debug flags]),[enable_debug=$enableval],[enable_debug="no"])
 
AC_MSG_CHECKING(debug)
AC_MSG_RESULT($enable_debug)
 
if test "x$enable_debug" = "xyes"
then
	DEBUG_CFLAGS="-g -Wall -DDEBUG"
fi
AC_SUBST([DEBUG_CFLAGS])

Mais ce n'est pas tout, cette partie permet uniquement de placer dans la variable DEBUG_CFLAGS les options qui vont bien. Il faudra par la suite utiliser cette variable dans notre Makefile.am

Comment faire une jolie documentation (avec Doxygen)

Ce tutoriel n'a rien de transcendant, mais permettra juste à quelque uns de découvrir cet outil, et vous aider à faire le premier pas.

Introduction

Coder pour soit ça peut être sympa, mais cette pratique atteint assez rapidement ses limites. Mais ouvrir son code demande un minimum de documentation. Alors voici une solution, pas magique, mais presque : Doxygen.

Quelques caractéristiques intéressantes

Petits exemples de documentation doxygen :

Ne soyons pas sectaire il n'y a pas que doxygen qui fasse ce genre de choses

Doxygen est un logiciel qui va analyser votre code pour créer un documentation. Bien évidement, il ne va pas deviner ce que veux fait la fonction la fonction void plop (gchar *msg) il faut donc lui donner un petit coup de main : utiliser des balises. Pas d'affolement c'est tout ce qu'il y a de plus facile.

Ce qui m'a donné envie d'écrire ce tutoriel c'est d'avoir dû documenter le code de notre projet bidon, il y aurait donc pas mal d'exemples basés dessus. Pour pouvoir directement accès aux fichiers se référer à la page du projet.

Pour la documentation officiel : http://www.stack.nl/~dimitri/doxygen/manual.html

Comment commenter son code ?

Quatre manières de commander son code :

/** commentaire
 * blabla
 */
/*!
 *
 */
/*!
...
 */
///
/// ...
///

Quelques balises bien utiles

La liste de la documentation officielle se trouve là : http://www.stack.nl/~dimitri/doxygen/commands.html
Nous ne présenterons ici que les balises vitales pour commencer

Documentation du code

\author trax Givneraud Omar
\version 0.3

Vous l'aurez deviné pour l'auteur et la version. A mettre en entête de de vos fichiers.

\file foobar.c
\class Eugeni
\fn void plop(gchar *msg)

Gné ça sert a quoi ? il peut pas comprendre tout seul que c'est le fichier foobar.c ou la fonction plop ?
Si il pourrait mais ça permet de pouvoir déplacer la documentation un peu on l'on veux et surtout définir la nature d'un bloc : documentation d'une fonction, un fichier ou encore une classe. L'argument est bien évidement optionnel

\brief breve description de la fonction ou du fichier

Pour une documentation longue pas de balise particulière, il suffit juste de sa placer dans un bloc de documentation doxygen.

\param nom_du_parametre sa description
\return ce que retourne la fonction

Créer une page de documentation style howto ou tutoriel

Petit exemple toujours sur le projet bidon :
http://clubnix.esiee.fr/~trax/bidon/doc/html/howto_makeplugin.html

code doxygen de la page :
http://clubnix.esiee.fr/~trax/bidon/doc/howtos/howto_plugin.doc

Pour créer une page, créer un fichier séparé de préférence et créer un bloc de documentation doxygen.
Utiliser la balise

/*!
\page reference_de_la_page Titre de la page
 */

Sections

Pour les sections (titres et sous titres) doxygen utilise une syntaxe proche de celle de LaTex

\section Titre niveau 1
\subsection Titre niveau 2
\subsubsection Titre niveau 3

Inclure du code :

\code 
le code
\endcode

Si doxygen reconnait des éléments de votre projet dans la balise code il créera directement un lien vers vers la documentation en question. Cela peut être très pratique, mais également un inconvénient : vous avez une fonction plop dans votre code, et vous voulez faire un exemple dans votre tutoriel avec une fonction plop qui n'a rien a voir avec celle du projet (ceci est également vrai pour le texte d'une page). Il existe donc la balise verbatim.

\verbatim
plop non reconnu
\endverbatim

Faire une liste

Rien de plus facile !

\li premier element
\li deuxième element

Lancer la moulinette pour obtenir sa doc

Il y a un magnifique fichier de configuration, mais pas question de l'ouvrir à la main pour l'instant : doxywizard est notre ami. Lancer le binaire et suivre les instruction, rien de plus simple. Pour avoir un fichier exemple, comme d'habitude voir dans bidon http://clubnix.esiee.fr/~trax/bidon/doc/Doxyfile2

Faire un Makefile Générique

Note, il est conseillé de savoir faire un Makefile « standard » pour profiter pleinement de la lecture de cet article.

On peut bien sûr utiliser un truc complément inutile :

CFLAGS = -W -Wall -g

Ceci aura pour effet de configurer les CFLAGS, et de compiler tous les fichiers sources du répertoire courant.

En gros ça revient a faire ça :
gcc $CFLAGS *.c
Ce qui ne sert à rien, quand on sait qu'un Makefile sert, entre autres, à compiler séparément ses sources pour ne pas compiler tout à chaque fois.
Je tairai le nom de la personne qui a cru que ça allait me servir…

Sinon, on peut faire des Makefile très simples, avec le nom de chaque règle « en dur ». Ces Makefiles sont en général ceux utilisés pour des petits programmes, dès que le nombre de fichiers sources augmente, il devient très difficile d'écrire toutes les règles à la main.

Pour résoudre ce problème, on peut utiliser des règles génériques qui vont permettre de s'affranchir du nom de chaque règle et de ses dépendances.

CC = gcc
CFLAGS = -W -Wall -g
LDFLAGS = 
 
SRC = $(wildcard *.c)
OBJS = $(SRC:.c=.o)
AOUT = prog
 
all : $(AOUT) 
 
prog : $(OBJS)
	$(CC) $(LDFLAGS) -o $@ $^
%.o : %.c
	$(CC) $(CFLAGS) -o $@ -c $<
clean :
	@rm *.o
cleaner : clean
	@rm $(AOUT)

Quelques explications :
SRC = $(wildcard *.c) : comme son nom l'indique, wildcard agit comme une wildcard, c'est à dire un caractère « joker ». Ici on s'en sert pour récupérer tous les fichiers finissant par .c du répétoire dans lequel est le Makefile.

OBJS = $(SRC:.c=.o) : permet de préciser les nom de chaque fichier .o à partir du fichier .c.

$@ : le nom de la cible

$^ : le nom de chaque dépendance

$developpez.com

Utilisation de GNU Debugger (GDB)

Votre programme crash et vous ne savez pas pourquoi ? Le mieux à faire pour comprendre et d'utiliser un outil de debugage plutôt que se retaper tout votre code... Un outil assez simple est le GNU Debugger, ou GDB. Ce programme va vous permettre de générer un BackTrace de votre programme (c'est à dire la liste de tous vos appels de fonctions etc...) qui vous permettra de savoir où ça plante, si ce n'est pourquoi ça plante.

Pour utiliser le debugger, assurez-vous d'abord que le package gdb est bien installé sur votre machine. Ensuite vous devez vous assurer que votre programme exporte des informations utiles pour gdb. En général on peut obtenir ces informations en ajoutant -ggdb aux instructions de compilations de son programme. Cela permet à gcc de savoir qu'il faut qu'il laisse des informations de debuggage supplémentaires dans le programme pour permettre à gdb de s'y retrouver plus facilement.

Pour créer le BackTrace vous devrez d'abord lancer voter programme sous GDB :

$~ gdb mon_prog

Puis, dans le terminal gdb, tapez :

(gdb) handle SIGPIPE nostop
(gdb) run

L'instruction "run" lancera le programme. Il ne vous reste plus qu'à reproduire le crash si il ne se fait pas de lui-même... Si Votre programme reste bloqué (genre boucle infini, attente d'une entrée, blocage etc...) tapez Ctrl-C pour revenir sous gdb. Puis tapez :

(gdb) bt full

ou bien :

(gdb) backtrace full

Vous verrez alors le BackTrace de votre programme s'afficher dans le terminal. Il se peut que des "??" apparaissent à la place des noms de fonction, c'est simplement que gdb n'a pas pu le lire... Sachez que pour obtenir les meilleures traces possibles, il faut que les librairies que vous utilisez aient été installé avec leur symboles de débugage.

Pour quitter GDB, tapez :

(gdb) quit

Il est aussi possible de se déplacer dans la pile d'appel des fonctions de votre programme. Pour cela, utilisez les commandes up et down. Il est également possible de consulter le contenu des variables en utilisation la commande print (p en abbrégé). L'exemple suivant permet d'afficher le contenue de la structure pointée par struct_p :

(gdb) print *(mon_pointeur_de_struct *)struct_p

Il ne vous reste plus qu'à repérer les erreurs relevées dans le BackTrace et aller les corriger dans votre code...

Pour plus d'informations, vous pouvez aussi consultez les wikis des distributions Ubuntu, Debian et Gentoo.

Tuto Shell Débutant

Introduction

Vous venez d'installer Linux sur votre PC et vous êtes émerveillé par le libre. Mais là vous vous demandez comment copier, renommer, déplacer, supprimer, etc... un fichier. Bien sur vous pouvez ouvrir votre explorateur de fichier et le faire à la main. D'accord ça marche mais comment faire dans un dossier pour supprimer tous les fichiers commençant par c ou p et de taille inférieure à 10ko tout en faisant attention à ne pas supprimer les fichier dont l'extension est .c ?
Pas facile hein ... ;)
Ne vous inquiétez pas avec une console tout cela se fait en une ligne.
Alors commençons.

Qu'est-ce qu'une console ?

Et oui on s'adresse ici aux débutants.
Pour ouvrir une console sous Gnome allez dans Applications->Accessoires->Terminal
Vous obtenez une fenêtre toute blanche (ou noire) avec quelque chose comme cela d'écrit :

user@ordi:~$

Voila vous êtes dans une console !!!!
C'est d'ici que tout ce passe. On va voir comment utiliser des fonctions sur cette console.

Les fonctions en shell

Comment se sert-on d'une fonction ?

Alors on est là devant notre jolie console et que fait-on ?
On va se servir de fonctions. Pour cela il faut savoir comment on appelle une fonction (on verra par la suite les noms de fonctions, pour le moment on fait de la théorie :) ). Pour appeller une fonction on tape juste le nom de la fonction suivie de ses options et arguments (si il y en a). La différence entre les options et les arguments est que les options sont précédées d'un tiret.
Voila un exemple d'appel d'une fonction :

user@ordi:~$ fonction -options argument1 argument2 ...
# ou bien
user@ordi:~$ fonction argument1 argument2 ... -options

Voila c'est tout simple. Comme on le voit l'ordre entre les arguments et les options n'a pas énormément d'importance et les deux appels précédents sont équivalents. Bien entendu ça ne parle pas beaucoup comme ça, mais comme on dit "rien ne vaut l'expérience", alors voyons les fonctions de base.

La fonction pwd

C'est la première fonction que nous verrons : "pwd" (Print Working Directory) soit en français : imprimer le répertoire courant. Testons la :

user@ordi:~$ pwd
/home/user

On est dans le répertoire /home/user. On appelle aussi ce répertoire : le répertoire "~" (prononcez "tilde").

La fonction ls

Fonction très utile "ls". Allez-y tapez ls dans votre console vous devez avoir un résultat du type

user@ordi:~$ ls
at76_usb-0.17         bashrc  Desktop  MEMOVIF.PDF         rapport.tex  tp3
at76_usb-0.17.tar.gz  bluej   latex    rapport_latin1.tex  tp2          WinDLX

Bien sur c'est surement différent chez vous. Vous avez compris ce que faisait cette fonction : elle liste les fichiers du répertoire courant.

Vous me direz : "C'est bien beau tout ça mais comment on fais pour savoir si on a un dossier ou un fichier exécutable ?"
C'est là que les options interviennent. Pour ls une options pratique est -l :

user@ordi:~$ ls -l
drwxr-xr-x 2 aad users  4096 jan  4 18:31 at76_usb-0.17
-rwx------ 1 aad users 55539 jan  2 02:56 at76_usb-0.17.tar.gz
-rw-r--r-- 1 aad users  2379 jan 10 03:20 bashrc
drwx------ 4 aad users  4096 sep 19 17:51 bluej
drwx------ 2 aad users  4096 sep 27 16:13 Desktop
drwxr-xr-x 4 aad users  4096 jan 10 03:20 latex
-rw------- 1 aad users 49309 jan  2 02:32 MEMOVIF.PDF
-rw-r--r-- 1 aad users  7006 déc  6 18:14 rapport_latin1.tex
-rw-r--r-- 1 aad users  7044 déc  6 18:11 rapport.tex
drwx------ 8 aad users  4096 sep 19 18:17 tp2
drwx------ 6 aad users  4096 sep 27 16:13 tp3
drwxr-xr-x 3 aad users  4096 déc  7 16:30 WinDLX

Là plein de choses sont apparues. Voyons ce que l'on apprend. Tout d'abord le début de la ligne (drwxr-xr-x par exemple pour la première ligne) : cela correspond au type de fichier et aux droits des utilisateurs sur ce fichier. le d signifie directory (dossier) -> on a donc un dossier. Si on a un tiret c'est qu'on a un fichier classique.
Le reste sont les droits en lecture(read), écriture(write), exécution (executable) des utilisateurs ; les trois premières lettres correspondent aux droits du propriétaire, les trois suivantes à ceux des utilisateurs appartenant au groupe du fichier, les trois dernier aux autres utilisateurs.
Après cela on a dans l'ordre : le propriétaire, le groupe, la taille en octet du fichier, la date de la dernière modification du fichier (mois, jour, heure), le nom du fichier.

Une autre option intéressante est -a (all) qui permet d'afficher tous les fichiers (même les cachés). Sous Linux les fichiers cachés commencent par un ".". Testons-la :

user@ordi:~$ ls -a
.                     .fontconfig        .povray
..                    .gconf             .qt
.adobe                .gconfd            rapport_latin1.tex
.appletviewer         .gimp-2.2          rapport.tex
at76_usb-0.17         .gnome             .recently-used.xbel
at76_usb-0.17.tar.gz  .gnome2            .ssh
.bash_history         .gnome2_private    .thumbnails
.bash_logout          .gstreamer-0.10    tp2
.bash_profile         .gtkrc-1.2-gnome2  tp3
bashrc                .ICEauthority      .Trash
.bashrc               .icons             .viminfo
.beagle               .java              .wapi
bluej                 .kde               WinDLX
.bluej                latex              .Xauthority
.config               .macromedia        .xchat2
Desktop               MEMOVIF.PDF        .xscreensaver
.dmrc                 .metacity          .xsession-errors
.fbrc                 .mozilla
.fluxbox              .nautilus

On voit plein de nouveaux fichiers. Ce sont les fichiers de configurations Linux. Pas la peine de s'attarder sur eux pour le moment.

Ce que l'on peut faire maintenant c'est combiner les options. Par exemple :

user@ordi:~$ ls -a -l

On a les deux options à la fois. Ce code donnera la même chose que  user@ordi:~$ ls -al .
D'autres options intéressantes sont : -h, -d, -s. Je vous laisse découvrir par vous même leur utilité. Nous verrons plus loin une méthode pour pouvoir connaitre les options d'une fonction.

La fonction cd

Maintenant que l'on peut connaitre les différents fichiers du répertoire courant, voyons comment se balader dans les différents répertoires.
Pour cela on utilise la fonction cd pour "change directory". cette fonction permet de se déplacer dans l'arborescence des dossiers. On se sert de cette fonction comme suit : user@ordi:~$ cd RépertoireCible
Par exemple si je tape ls j'ai :

at76_usb-0.17         bashrc  Desktop  MEMOVIF.PDF         rapport.tex  tp3
at76_usb-0.17.tar.gz  bluej   latex    rapport_latin1.tex  tp2          WinDLX

Desktop est un dossier. Je peux donc entrer dedans par :

user@ordi:~$ cd Desktop
user@ordi:~/Desktop$  #On est dans le répertoire Desktop

Pour revenir en arrière il suffit de faire  cd ..
.. est un dossier spécial représentant le répertoire au dessus du répertoire courant.

user@ordi:~/Desktop$ cd ..      #On remonte dans l`arborescence
user@ordi:~$ 

Si la cible que l'on donne n'est pas un répertoire ou n'existe pas on génère une erreur :

user@ordi:~$ cd MEMOVIF.PDF      #La cible est un fichier
bash: cd: MEMOVIF.PDF: N`est pas un répertoire
user@ordi:~$ cd çanexistepas     #La cible n`existe pas
bash: cd: çanexistepas: Aucun fichier ou répertoire de ce type

La fonction cp

La fonction cp, comme copy, permet de copier un fichier ou un répertoire. Cette fonction est assez simple a utiliser : cp [OPTIONS] SOURCE DESTINATION où SOURCE est le fichier ou répertoire à copier et DESTINATION l'endroit où SOURCE est copiée.
Exemple :

user@ordi:~$ ls
Books   Code       driver_wifi_80211g.zip  goldie.avi     Images      john-1.7.2.tar.gz  Musique  Vidéos
Bureau  Documents  Examples                gtk-cours.pdf  john-1.7.2  Modèles            Public
user@ordi:~$ cp john-1.7.2.tar.gz Documents/
user@ordi:~$ ls Documents/
el201  en201a  in201  john-1.7.2.tar.gz  ma201  ma211_annales.tar.gz  pr201.tar.gz  sh201

Dans cet exemple, john-1.7.2.tar.gz est SOURCE et Document est DESTINATION. On ne s'est pas servi de OPTION car il n'est pas obligatoire d'en utiliser. Cependant certaines options sont assez pratiques :

  • cp -r ou cp -R: permet de copier un répertoire (recursive)
  • cp -i : permet de demander confirmation avant d'écraser un fichier déjà existant (interactive)
  • cp -v : permet d'utiliser le mode verbose : cp devient alors très bavard

Comme pour ls, il n'est pas interdit d'utiliser plusieurs options à la fois ; ainsi cp -rvi Document/en201a ~/cours permet de de copier le répertoire en201a situé dans ./Document vers /home/user/cours.

user@ordi:~$ ls
Books   Code   Documents               Examples    gtk-cours.pdf  john-1.7.2         Modèles  Public
Bureau  cours  driver_wifi_80211g.zip  goldie.avi  Images         john-1.7.2.tar.gz  Musique  Vidéos
user@ordi:~$ cp -rvi Documents/en201a/ ~/cours
`Documents/en201a/` -> '/home/user/cours/en201a'
`Documents/en201a/en201a3_v2.pdf` -> '/home/user/cours/en201a/en201a3_v2.pdf'
`Documents/en201a/en201a4_v2.pdf` -> '/home/user/cours/en201a/en201a4_v2.pdf'
`Documents/en201a/en201a3_v1.pdf` -> '/home/user/cours/en201a/en201a3_v1.pdf'
user@ordi:~$ ls cours/
en201a

Les 3 options citées précédemment ne sont pas inutiles puisque qu'on les retrouve dans beaucoup d'autres commandes pour le même usage.

La fonction rm

La fonction rm, comme remove, permet de supprimer définitivement un fichier. Attention rm ne demande aucune confirmation et une fois que le fichier est supprimé il est impossible de le récupérer. Donc il faut être très prudent lorsqu'on utilise rm. Tip : l'utilisation de rm -i permet d'éviter les accidents aussi je conseille vivement de toujours utiliser rm -i à la place de rm, on peut même aller jusqu'à faire un alias dans son bashrc.
Exemple :

user@ordi:~$ ls Documents/
el201  en201a  in201  john-1.7.2.tar.gz  ma201  ma211_annales.tar.gz  pr201.tar.gz  sh201
user@ordi:~$ rm -i Documents/john-1.7.2.tar.gz 
rm: remove regular file `Documents/john-1.7.2.tar.gz`? oui
user@ordi:~$ ls Documents/
el201  en201a  in201  ma201  ma211_annales.tar.gz  pr201.tar.gz  sh201

NB : rm ne peut pas détruire un répertoire, il faut utiliser rmdir qui permet de supprimer un répertoire vide. Si l'on veut effacer un répertoire non-vide il faut utiliser l'option -R de rm.
Quelques options utiles à savoir :

  • rm -f : force le système à effacer le fichier et passe-outre les vérifications (comme avec l'option i par exemple). Attention soyez prudent !
  • rm -R : permet d'effacer un répertoire.

L'étoile de la mort

Maintenant que l'on sait copier et supprimer des fichiers on va s'intéresser au méta-caractères. "Mais qu'est ce que c'est ?" Ce sont des caractères spéciaux qui représentent plusieurs autres caractères. Pour mieux voir leur utilité je vais les tester sur le dossier suivant :

user@ordi:~$ ls
test1  test2  test3  testA  testF  testAZ

Ces caractères sont les suivants :

  • [] : Les crochets permettent de dire que le caractère à la place des crochet doit être dans la liste des caractères entre les crochets. Exemple :

    user@ordi:~$  ls test[12]
    test1  test2

    Pour désigner tous les caractères entre a et q par exemple on utilise le tiret :

    user@ordi:~$ ls test[A-Q] #équivalent à ls test[a-q]
    testA  testF
  • ? : Le point d'interrogation représente n'importe quel caractère. Il ne représente cependant pas le caractère vide. Exemple :
    user@ordi:~$ ls test?    #On cherche les fichier du nom de test suivi d`un seul caractère
    test1  test2  test3  testA  testF
    user@ordi:~$ ls test??   #Ici suivi de deux caractères
    testAZ
    user@ordi:~$ ls test???  #Ici de trois
    ls: test???: Aucun fichier ou répertoire de ce type
  • * : L'étoile. Le caractère le plus dangereux car il représente toutes les chaînes de caractères, y compris la chaîne vide "". Exemple :
    user@ordi:~$ ls test*    #tous les fichiers dont le nom commence par test.
    test1  test2  test3  testA  testAZ  testF
    user@ordi:~$ ls t*       #tous les fichiers dont le nom commence par t.
    test1  test2  test3  testA  testAZ  testF
    user@ordi:~$ rm *        #Attention : On supprime tous les fichiers sans demande de confirmation
    user@ordi:~$ ls          #On vérifie et le dossier est vide
    user@ordi:~$

    Comme on le voit l'étoile est dangereuse car sans faire attention on peut lancer $ rm -r * qui supprime tous les fichiers du répertoire courant sans confirmation. Alors, on ne le répétera jamais assez, faites attention avec l'étoile.

La fonction mv

mv comme move, permet de déplacer des fichiers et des dossiers. Ce qui veux bien dire que le fichier n'existe qu'en un seul exemplaire à la fin de la commande, par opposition à une copie.

man

Voici une section spéciale car man n'est pas une commande comme cp, cd, ls ou encore rm qui permet de naviguer sur son disque dur ou de toucher aux fichiers. Non man permet de consulter les pages de manuel et c'est très important; car dans 9 cas sur 10 lorque l'on se demande comment marche une commande ou encore quelles options permettent de copier un fichier tout en gardant les anciennes versions en backup, la réponse se touve dans la page man. Bien plus encore man contient également de l'aide précieuse pour le programmeur C ; car de nombreuses pages man sur les librairies standards existent. Essayez donc man printf ou encore man cp. Attention sur certaines distributions Linux, les pages man ne sont pas installées complètement; c'est le cas sur Ubuntu. Pour les installer il faut taper cette ligne de commande dans la console et saisir son mot de passe :

sudo apt-get install manpages manpages-dev manpages-posix manpages-posix-dev

Si vous n'êtes pas sous Ubuntu ou Debian, il faut utiliser le gestionnaire de paquets de votre distribution pour ça renseignez-vous soit sur le site de la distribution Linux en question soit sur google. Après avoir vu ces quelques commandes de base et appris à vous servir des pages man, vous voilà fin prêt à devenir un expert de la console un pur tétard bash ou encore un hardcore g33k qui code sous emacs en console.

Et pour finir commence à te former tout seul : man man !

Tutoriel Subversion

Introduction

C'est bon, vous vous lancez avec quelques amis dans un projet de programation. Enfin, vous allez vous tapez vos quelques milliers de codes, passez vos aprem' les yeux collés sur votre écrans et vos soirées à bouffer des pizzas en débugant ce que vous avez fait dans la journée. C'est beau ! Seul problème : vous n'allez pas bosser tous ensemble sur le même PC ou sur des fichiers entièrement différents... Dans ces cas là, intégrer les modifications de chacun devient vraiment la galère et demande encore plus de travail que le « simple » debugage.
Pour régler cette difficulté, il existe un petit système « magique », Subversion, permettant de garder sur un serveur les différentes versions de votre projet. Grâce à ça, vous pourrez partagez les sources rapidement et facilement (il suffira de se connecter au serveur pour tout récupérer). Mais le principal intérêt de Subversion est de faire des mises à jour rapides et simples de vos sources en fonction des modifications faites par les autres membres de votre groupe. Voilà donc quelques bases pour son utilisation.

Créer un Repository

La première chose à faire est de créer un Repository, c'est à dire le dossier dans lequel vous stockerez les différentes versions de votre projet. Le serveur le plus connu pour le créer est SourceForge. Vous pouvez aussi voir avec le Club*Nix si il y a moyen de s'arranger.
De manière générale, pour créer un repository, la commande est :

$~ svnadmin create svn.mad/projet/repas

Il vous reste maintenant à y importer vos dossiers et fichiers :

$~ svn import bouffe svn.mad/projet/repas -m "Premier commit"
 
 
Adding    bouffe/viande/steak.c
Adding    bouffe/viande/jambon.c
Adding    bouffe/garniture/haricot.c
Adding    bouffe/garniture/frite.c
Adding    bouffe/menu.c
 
Commited revision 1

Cette commande a copié les différents fichiers contenu dans le dossier bouffe sur le serveur SVN dans le repository projet/repas en laissant comme commentaire qu'il s'agit de premier commit.
La commande import ne sert qu'à la création du repository. Nous verrons plus loin comment y rajouter d'autres fichiers.

Pour savoir ce qui se trouve dans le repository, on utilise la commande list :

$~ svn list svn.mad/projet/repas/
 
viande/
garniture/
menu.c

Enfin, pour récupérer les fichiers sur un SVN pour la toute première fois, on utilise la commande checkout :

$~ ls
projet1/ projet2/
 
$~ svn checkout svn.mad/projet/repas/
A    repas/viande/steack.c
A    repas/viande/jambon.c
A    repas/garniture/haricot.c
A    repas/garniture/frite.c
A    repas/menu.c
Checked out revision 42
 
$~ ls
projet1/ projet2/ repas/

Le checkout ne doit être fait qu'une seule fois : la première fois que vous récupérez les fichiers. Par la suite, il faut utiliser le commande update qui sera décrite un peu plus loin.

Passons aux choses sérieuses

Maintenant qu'on a vu comment se créer un repository et comment on le remplit pour la première fois. Maintenant, vous avez bosser sur votre projet, vous y avez ajouté ou supprimé des fichiers, vous y avez apporté des modifications... Mais tout ça seulement de votre côté et vous voulez bien que tout votre groupe puisse en profiter et vous aimeriez profiter des changements qu'ils ont fait.

Etat du repository et de votre copie

Pour connaître l'état du repository comparé à l'état de votre copie de travail vous avez les commandes status et diff, la première indiquant quel est le statut de chaque document de votre copie par rapport au repository et la seconde indique quelles sont les différences entre les documents de votre copie et le reste. Ainsi, en utilisant status :

$~ svn status
 
A    viande/gigot.c
D    viande/saucisse.c
C    garniture/frite.c
M    garniture/haricot.c

Petite explication des items précédents les noms de fichiers :

  • A signifie que le fichier sera ajouté au repository à votre prochain commit
  • D signifie que votre prochain commit supprimera le fichier
  • C signifie qu'il y a un conflit entre l'état de votre copie du fichier et son état dans le repository
  • M signifie que vous avez fait des changements dans le fichier
  • Le conflit signifie que vous avez fait des modifications sur le fichier mais que entre temps, avant que vous l'ayez transmis au serveur SVN, quelqu'un a modifié le même fichier et que ces changements sont en conflits avec ce que vous avez fait.

    Le diff permet de repérer les différences entre la dernière version du repository et votre copie :

    $~ svn diff
     
    Index: menu.c
    =========================
    --- menu.c (revision 21)
    +++ menu.c (working copy)
    @@ -5,15 +5,17 @@
    int main(void){
    -    addHaricot(5);
    +    addFrite(5);
    +    addSteack(5);
        return 0;
    }

    Ici, on voit que le code de la fonction main dans le fichier menu.c a été modifié : on lui a retiré 5 portions de haricot pour lui rajouter 5 portions de frite et 5 steacks. Grace à diff vous pouvez ainsi facilement créer des patchs :

    $~ svn diff > patch

    Se mettre à jour

    Mettre à jour sa copie de travail se fait en une seule commande, update, qui synchronise votre copie avec la dernière version disponible sur le svn :

    $~ svn update
     
    U    menu.c
    U    garniture/frite.c
    Updated to revision 43.

    Modifier la copie locale

    Bon je vais pas vous apprendre à modifier un fichier : vous tapez, vous enregistrez et c'est fini. Les problèmes arrivent quand vous voulez ajouter, supprimer ou déplacer un fichier ou un dossier. Il faut en effet tenir au courant le SVN que vous allez effectuer ce genre de modif'. Pour cela, on utilise les commandes add, delete, mkdir, copy et move.

  • $~ svn add garniture/pates.c va ajouter le fichier au prochain commit
  • $~ svn delete viande/saucisse.c supprimera le fichier au prochain commit
  • $~ svn mkdir dessert créera le dossier dessert au prochain commit
  • Les commandes copy et move ont des noms assez descriptifs et font la même chose que les commandes bash du même nom ;)

    Faire le commit (enfin...!)

    Avant de faire un commit, SVN vérifiera en premier qu'il n'y a pas de conflits entre votre copie et la dernière version présente sur le SVN. Si il y en a vous avez deux possibilités : soit vous annulez tous les changements que vous avez fait dans les fichiers en conflit, soit vous changez les fichiers juste au endroits genants. L'annulation pure et simple des modifications que vous avez faites passe par $~ svn revert file.c

    Une fois que vous avez réglé tous les conflits, il faut le signaler au SVN par $~ svn resolved file.

    Vous pouvez maintenant atteindre le Sacré Graal : transférer vos modifications sur le SVN :

    $~ svn commit -m "Nouveau commit"

    L'option -m permet d'associer un commit au message afin de décrire vite fait vos changements aux autres membres de votre équipe.

    Conclusion

    Vous avez maintenant des bases concernant l'utilisation de SVN. J'ai précisé les différentes actions de base qu'il est possible d'invoquer à partir d'une ligne de commande pour que vous sachiez un peu ce qui se passe. Maintenant, il existe de nombreux clients Subversion (tant sous Windows que sous Linux) comme SmartSVN pour n'en citer qu'un.
    Rappelez-vous : vous êtes nombreux à travailler sur un projet logiciel ? Utilisez SVN pour ne plus perdre du temps à vous transférer les fichiers via une clé USB puis à repérer les différences entre tous les fichiers...

    (Tuto réalisé par MAD[dollm@esiee.fr])

    Web

    Expérience avec le PSR-0 : Drupal, Zend Framework 2, Symfony 2

    Introduction

    Le PSR-0 est une norme du PHP Framework Interop Group basée sur les espaces de noms (namespaces, PHP >= 5.3) pour permettre la création de composants interopérables et chargeables (par autoloading) sans efforts.

    Ceci est une petite expérience permettant de montrer l’intérêt du PSR-0.

    Il faut un niveau un peu avancé en PHP objet pour bien comprendre cet article.

    Le projet

    Dans cette expérience, nous allons créer une petite application la plus robuste possible en utilisant des composants de différents frameworks.

    Le projet choisi est une application pour uploader un fichier sur un serveur en SSH.

    Composants (Packages) et dépendances

    Choix des composants

    Base Silex\Silex Micro-framework basé sur Symfony 2 avec une bonne intégration de Twig
    ServiceManager Zend\ServiceManager ServiceManager simple à utiliser à partir d’un array de configuration
    Formulaires Zend\Form Utile pour le rendu de formulaire et facilite la validation des données
    Transfert de fichiers Drupal\Core\FileTransfer\SSH Implémentation de FileTransfer de Drupal 8 utilisé pour les mises à jour. Facilement remplaçable par FTP ou Local
    Templates Twig\Twig Système de templates pour les vues MVC de plus en plus répandu
    Thème de base Bootstrap Style prêt à être utilisé pour qu’on puisse se concentrer sur le code.

    On va voir plus loin comment le PSR-0 nous simplifie la vie pour utiliser tous ces différents composants.

    Gestion des dépendances

    Pour la gestion des dépendances et la génération de l’autoloader, on utilisera Composer.

    Arborescence du projet

    J’ai choisis d’utiliser 2 fichiers php à la Zend pour la configuration de l’application.
    Le fichier config/global.config.php retourne un array avec la configuration globale à écrire dans le référentiel de code.
    Le fichier config/local.config.php contient la configuration renseignée par l’utilisateur de l’application. Il n’est pas ajouté dans le référentiel de code.
    Les fichiers uploadés seront stockés dans le dossier /upload.
    Le dossier /views contiendra les vues Twig.

    /config/global.config.php
    /config/local.config.php
    /css
    /img
    /js
    /upload
    /vendor
    /views
    /composer.json
    /index.php

    Mise en place du projet

    Création du composer.json

    {
       "name" : "Simple Uploader",
       "description" : "PSR0",
       "type" : "application",
       "authors" : [{
               "name" : "Philippe Lewin",
               "email" : "philippe.lewin @ gmail.com"
           }
       ],
       "keywords" : [
           "demo",
           "psr0"
       ],
       "homepage" : "http://www.clubnix.fr",
       "license" : [
           "GPL-3.0+"
       ],
       "require" : {
           "easyrdf/easyrdf" : "0.8.0-beta.1",
           "twig/twig" : ">=1.8,<2.0-dev",
           "symfony-cmf/routing" : ">=1.0.1",
           "php" : ">=5.3.3",
           "drupal/drupal" : "8.x-dev",
           "kriswallsmith/assetic" : "v1.1.0-alpha1",
           "silex/silex" : "1.0.*@dev",
           "zendframework/zendframework" : ">=2.1.0"
       },
    
       "minimum-stability" : "dev",
       "repositories" : [
               "type" : "vcs",
               "url" : "git://github.com/drupal/drupal.git"
           }, {
               "type" : "vcs",
               "url" : "git://github.com/kriswallsmith/assetic.git"
           }, {
               "type" : "vcs",
               "url" : "git://github.com/symfony-cmf/Routing.git"
           }
       ],
       "config" : {
           "process-timeout" : 3000
       },
       "minimun-stability" : "dev"
    }

    Installation des dépendances

    $ composer install

    Loading composer repositories with package information
     Installing dependencies 
     - Installing symfony/yaml (v2.2.0)
        Loading from cache
     - Installing psr/log (1.0.0)
        Loading from cache
     - Installing twig/twig (v1.12.1)
        Loading from cache
     - Installing doctrine/common (2.3.0)
        Loading from cache
     - Installing phpunit/php-text-template (dev-master 1eeef10)
        Cloning 1eeef106193d2f8c539728e566bb4793071a9e18
     - Installing phpunit/phpunit-mock-objects (1.2.x-dev 8e9d897)
        Cloning 8e9d897b39ca6310ccc2d81b8f29cabd5ce12786
     - Installing phpunit/php-timer (1.0.x-dev ecf7920)
        Cloning ecf7920b27003a9412b07dad79dbb5ad1249e6c3
     - Installing phpunit/php-token-stream (dev-master c25dd88)
        Cloning c25dd88e1592e66dee2553c99ef244203d5a1b98
     - Installing phpunit/php-file-iterator (dev-master 2deb24c)
        Cloning 2deb24c65ea78e126daa8d45b2089ddc29ec1d26
     - Installing phpunit/php-code-coverage (1.2.x-dev b0ec2d0)
        Cloning b0ec2d014fe497315aad876c6289a38fd4c075ff
     - Installing phpunit/phpunit (3.7.15)
        Loading from cache
     - Installing easyrdf/easyrdf (0.8.0-beta.1)
        Loading from cache
     - Installing symfony/http-foundation (v2.2.0)
        Loading from cache
     - Installing symfony/event-dispatcher (v2.2.0)
        Loading from cache
     - Installing symfony/http-kernel (v2.2.0)
        Loading from cache
     - Installing symfony/routing (v2.2.0)
        Loading from cache
     - Installing symfony-cmf/routing (dev-master 1d9cff6)
        Cloning 1d9cff60cf3690831b0555dbc1f4b1b81cd50de3
     - Installing symfony/process (2.1.x-dev 3b1b54a)
        Cloning 3b1b54a6148e505132e397ae05cbf58c9f6b9f29
     - Installing kriswallsmith/assetic (v1.1.0-alpha1)
        Loading from cache
     - Installing guzzle/common (v3.1.0)
        Loading from cache
     - Installing guzzle/stream (v3.1.0)
        Loading from cache
     - Installing guzzle/parser (v3.1.0)
        Loading from cache
     - Installing guzzle/http (v3.1.0)
        Loading from cache
     - Installing symfony/translation (dev-master 4c354b0)
        Cloning 4c354b0f1bb4278c0179a38f8ef850a06f943cc3
     - Installing symfony/validator (v2.2.0)
        Loading from cache
     - Installing symfony/serializer (v2.2.0)
        Loading from cache
     - Installing symfony/dependency-injection (v2.2.0)
        Loading from cache
     - Installing symfony/class-loader (v2.2.0)
        Loading from cache
     - Installing drupal/drupal (8.x-dev ca665f7)
        Cloning ca665f7be315aedc499228b156bd194982d1cd74
     - Installing pimple/pimple (dev-master v1.0.2)
        Cloning v1.0.2
     - Installing silex/silex (1.0.x-dev 7ae0fd8)
        Cloning 7ae0fd8b871eaebf95b856940c47679da40666c6
     - Installing zendframework/zendframework (dev-master 2e22109)
        Cloning 2e22109954ccbc43b9c97e4d47b7b97129e3b5de
    phpunit/php-code-coverage suggests installing ext-xdebug (>=2.0.5)
    phpunit/phpunit suggests installing phpunit/php-invoker (>=1.1.0,<1.2.0)
    symfony/http-kernel suggests installing symfony/browser-kit (2.2.*)
    symfony/http-kernel suggests installing symfony/config (2.2.*)
    symfony/http-kernel suggests installing symfony/console (2.2.*)
    symfony/http-kernel suggests installing symfony/finder (2.2.*)
    symfony/routing suggests installing symfony/config (2.2.*)
    kriswallsmith/assetic suggests installing leafo/lessphp (Assetic provides the integration with the lessphp LESS compiler)
    kriswallsmith/assetic suggests installing leafo/scssphp (Assetic provides the integration with the scssphp SCSS compiler)
    kriswallsmith/assetic suggests installing ptachoire/cssembed (Assetic provides the integration with phpcssembed to embed data uris)
    symfony/translation suggests installing symfony/config (2.2.*)
    symfony/validator suggests installing symfony/locale (2.2.*)
    symfony/validator suggests installing symfony/config (2.2.*)
    symfony/dependency-injection suggests installing symfony/config (2.2.*)
    silex/silex suggests installing symfony/browser-kit (>=2.1,<2.4-dev)
    silex/silex suggests installing symfony/css-selector (>=2.1,<2.4-dev)
    silex/silex suggests installing symfony/dom-crawler (>=2.1,<2.4-dev)
    silex/silex suggests installing symfony/form (>= 2.1.4,<2.4-dev)
    zendframework/zendframework suggests installing zendframework/zendservice-recaptcha (ZendService\ReCaptcha for rendering ReCaptchas in Zend\Captcha and/or Zend\Form)
    zendframework/zendframework suggests installing zendframework/zendpdf (ZendPdf for creating PDF representations of barcodes)
    zendframework/zendframework suggests installing ircmaxell/random-lib (Fallback random byte generator for Zend\Math\Rand if OpenSSL/Mcrypt extensions are unavailable)
    zendframework/zendframework suggests installing ext-intl (ext/intl for i18n features (included in default builds of PHP))
    zendframework/zendframework suggests installing doctrine/annotations (Doctrine Annotations >=1.0 for annotation features)
    zendframework/zendframework suggests installing ocramius/proxy-manager (ProxyManager to handle lazy initialization of services)
    Writing lock file
    Generating autoload files

    Téléchargement de Twitter Bootstrap

    http://twitter.github.io/bootstrap/assets/bootstrap.zip
    Copier les dossiers css, img et js dans le dossier du projet
    Création des fichiers de configuration et d’index.php

    config/global.config.php

    <?php
    return array (
       'upload-form' => array(
       // description du formulaire ici
       ),
       'factories' => array(
       // factories pour le ServiceManager de Zend
       ),
       'invokables' => array (
       // invokables pour le Service Manager de Zend
       ),
    );

    config/local.config.php

    <?php
    return array(
    );

    Fichier index.php

    Dans la première version d’index.php on fait l’inclusion de vendor/autoload.php qui est l’autoloader généré par Composer. Composer a généré ce fichier en se basant sur les déclarations “PSR-0” des composer.json des différents packages du dossier vendor. Grâce à l’autoloader dans vendor/autoload.php, lorsqu’une classe est utilisée, le fichier .php contenant la classe sera automatiquement chargée depuis les sources du bon package.
    Toujours dans ce fichier index.php, la configuration est aussi chargée et le ServiceManager est configuré.
    On en profite pour réaliser un “Hello World” en affichant un message lorsqu’on arrive sur la route “/”.

    <?php
    require_once 'vendor/autoload.php';
     
    use Zend\ServiceManager\Config as ServiceManagerConfig;
    use Zend\ServiceManager\ServiceManager;
     
    $globalConfig = require_once 'config/global.config.php';
    $localConfig  = require_once 'config/local.config.php';
     
    $config = array_merge_recursive($globalConfig, $localConfig);
     
    $sm = new ServiceManager(new ServiceManagerConfig($config));
     
    $app = new Silex\Application();
     
    $app->get('/', function () {
        return "Hello World";
    });
     
    $app->run();

    Premier test de l’application

    On utilise le serveur intégré à php 5.3
    Dans le dossier du projet :
    php -S 127.0.0.1:8080

    Ouvrir le navigateur à l’adresse http://127.0.0.1:8080/ “Hello World” devrait s’afficher.

    Création des vues Twig

    Intégration de Twig dans notre application Silex

    On commence par ajouter le TwigServiceProvider à l’application Silex pour supporter les vues Twig.
    Pour bien faire, on va utiliser une factory du service manager pour contenir l’étape de configuration de notre objet Silex\Application.

    Au passage, on va activer le mode debug de Silex au cas où.

    Dans global.config.php :

    'factories' => array(
        'App' => function($sm) {
            $app = new \Silex\Application();
            $app['debug'] = true;
     
            $app->register($sm->create('TwigServiceProvider'), array('twig.path' => __DIR__.'/../views'));
     
            return $app;
        },
        ),
        'invokables' => array (
            'TwigServiceProvider'        => 'Silex\Provider\TwigServiceProvider',
        ),

    Dans index.php, on remplace
    $app  = new Silex\Application();
    par
    $app  = $sm->create('App');

    Création de notre template

    views/template.twig

    <!DOCTYPE html>
    <html lang="fr">
     <head>
        <meta charset="utf-8">
        <title>Simple Upload Form</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="description" content="Simple Upload Form">
        <meta name="author" content="Philippe Lewin">
        <link href="css/bootstrap.min.css" rel="stylesheet">
        <link href="css/custom.css" rel="stylesheet">
        <link href="css/bootstrap-responsive.min.css" rel="stylesheet">
    </head>
    <body>
       <div class="container">
       {% block content %}{% endblock %}
       </div>
       <script src="js/bootstrap.min.js"></script>
    </body>
    </html>

    css/custom.css

    body {
       padding-top: 40px;
       padding-bottom: 40px;
       background-color: #f5f5f5;
    }
    .form-upload {
       max-width: 300px;
       padding: 19px 29px 29px;
       margin: 0 auto 20px;
       background-color: #fff;
       border: 1px solid #e5e5e5;
       -webkit-border-radius: 5px;
       -moz-border-radius: 5px;
       border-radius: 5px;
       -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
       -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
       box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
    }
    .form-upload .form-upload-heading,
    .form-upload input[type="text"],
    .form-upload input[type="password"] {
       font-size: 16px;
       height: auto;
       margin-bottom: 15px;
       padding: 7px 9px;
    }

    Le template est basé sur l’exemple “sign-in” http://twitter.github.io/bootstrap/examples/signin.html

    Création des blocs index.twig et thanks.twig

    index.twig contient les éléments affichés dans la page d’accueil (le formulaire) et thanks.twig contient un message de remerciement pour informer à l’utilisateur que l’upload c’est bien passé.

    Je ne me casse pas la tête sur l’internationalisation pour ce petit projet.

    index.twig

    Pour l’instant, on n’a pas encore défini le formulaire, ce fichier est pour l’instant assez vide.

    {% extends "template.twig" %}
     
    {% block content %}
       <!-- ... code du formulaire qui va venir … -->
    {% endblock %}

    thanks.twig

    {% extends "template.twig" %}
     
    {% block content %}
       <h2>Merci</h2>
       <p>Le fichier a bien été téléchargé sur le serveur.<p>
    {% endblock %}

    Test avec une vue Twig

    On remplace la route / dans le fichier index.php :

    $app->get('/', function () use ($app) {
       return $app['twig']->render('index.twig', array (
           // modèle à fournir à la vue
       ));
    });

    On remarque que la closure doit déclarer utiliser $app pour pouvoir utiliser twig.

    Lorsqu’on aura notre formulaire de construit, il faudra le passer à la vue via l’array en paramètre de la méthode render.

    Si tout va bien jusque là, une page planche devrait s’afficher et le code HTML doit être généré.

    Mise en place de notre formulaire Zend Form

    Éléments du formulaire

    Notre formulaire va comporter un champ texte pour le login ssh, un champ password pour le mot passe, un champ fichier (pour sélectionner le fichier à uploader) et un bouton valider.

    Nous allons utiliser Zend\Form\Factory pour créer l’objet formulaire à partir d’un array de configuration. Il faudra donc ajouter Zend\Form\Factory au service manager.

    Modification dans global.config.php

    'upload-form' => array(
           'hydrator' => 'Zend\Stdlib\Hydrator\ArraySerializable',
           'attributes' => array('class' => 'form-upload'),
           'elements' => array (
               array (
                   'spec' => array (
                       'name'       => 'username',
                       'attributes' => array(
                           'type'  => 'text',
                           'placeholder' => 'Username',
                           'class'       => 'input-block-level',
                           'required'    => 'required',
                       ),
                   ),
               ),
               array (
                   'spec' => array (
                       'name'       => 'password',
                       'attributes' => array (
                           'type'  => 'password',
                           'placeholder' => 'Password',
                           'class'       => 'input-block-level',
                           'required'    => 'required',
                       ),
                   ),
               ),
               array (
                   'spec' => array (
                       'type' => 'Zend\Form\Element\File',
                       'name' => 'file',
                       'attributes' => array(
                           'class' => 'input-block-level',
                           'required' => 'required',
                       ),
                   ),
               ),
               array (
                   'spec' => array (
                       'name' => 'send',
                       'attributes' => array (
                           'type'  => 'submit',
                           'value' => 'Envoyer',
                           'class' => 'btn btn-large btn-primary',
                       ),
                   ),
               ),
           ),
       ),
     
    // …
     
    'invokables' => array (
           'FormFactory'                => 'Zend\Form\Factory',
           'TwigServiceProvider'        => 'Silex\Provider\TwigServiceProvider',
    ),

    On remarque que j’utilise l’attribut HTML5 placeholder à la place de labels.

    Création du formulaire dans l’index

    On peut maintenant créer notre formulaire à partir de la configuration.

    Dans index.php, juste avant la création de l’application :
    $form = $sm->get('FormFactory')->createForm($config['upload-form']);

    On modifie aussi le contrôleur pour la route “/”, il doit fournir le formulaire à la vue :

    $app->get('/', function () use ($app, $form) {
       $form->prepare();
       return $app['twig']->render('index.twig', array (
           'uploadForm' => $form,
       ));
    });

    Utiliser les aides de vue dans Twig

    Et là c’est la catastrophe. Pour afficher le formulaire Zend, on dispose d’aides de vue (les classes Zend\Form\View\Helper\Form*) mais pour une vue Zend View. Inversement, on dispose d’helpers Twig pour des formulaires Symfony 2. On n’utilise pas Zend View ni de formulaire Symfony 2 actuellement donc comment allons nous faire ?

    La doc de Twig est une grande aide :
    http://twig.sensiolabs.org/doc/advanced.html

    A première vue, la solution la plus simple est de faire de nos View Helper des variables Twig globales. C’est ce que nous allons faire.

    Nous allons utiliser ces 3 ViewHelpers : Zend\Form\View\Helper\{Form, FormElementErrors, FormInput}

    Ajout des invokables :

        'invokables' => array (
           'FormFactory'                => 'Zend\Form\Factory',
           'ViewForm'                   => 'Zend\Form\View\Helper\Form',
           'ViewFormElementErrors'      => 'Zend\Form\View\Helper\FormElementErrors',
           'ViewFormInput'              => 'Zend\Form\View\Helper\FormInput',
           'TwigServiceProvider'        => 'Silex\Provider\TwigServiceProvider',
       ),

    Injection des variables globales depuis la factory App

        'factories' => array(
           'App' => function($sm) {
               $app = new \Silex\Application();
               $app['debug'] = true;
     
               $app->register($sm->create('TwigServiceProvider'), array('twig.path' => __DIR__.'/../views'));
     
               $twigGlobalHelpers = array(
                   'form'               => 'ViewForm',
                   'formInput'          => 'ViewFormInput',
                   'formElementErrors'  => 'ViewFormElementErrors',
               );
     
               foreach($twigGlobalHelpers as $helperName => $helperInvokable) {
                   $app['twig']->addGlobal($helperName, $sm->get($helperInvokable));
               }
     
               return $app;
           },
       ),

    Le block index qui affiche le formulaire

    Maintenant que les aides de vues sont ajoutés et que le contrôleur fourni à la vue l’objet formulaire, nous pouvons finir le code Twig :

    index.twig

    {% extends "template.twig" %}
     
    {% block content %}
       {{ form.openTag(uploadForm)|raw }}
       <h2 class="form-upload-heading">Envoyer un fichier</h2>
       {% for i in ['username', 'password', 'file'] %}
           {% set element = uploadForm.get(i) %}
           {{ formInput.render(element)|raw }}
           {{ formElementErrors.render(element)|raw }}
           <br />
       {% endfor %}
     
       {{ formInput.render(uploadForm.get("send"))|raw }}
     
       {{ form.closeTag(uploadForm)|raw }}
    {% endblock %}

    Twig protège toutes les sorties affichées, comme les View Helpers Zend le font déjà, on doit utiliser le filtre “raw” pour empêcher un deuxième niveau de htmlentities().

    Tester l’affichage du formulaire

    Le formulaire devrait s’afficher lorsqu’on charge la page “/”.

    Traitement du formulaire d’upload

    Ajout d’un nouveau contrôleur pour cette route

    Étant donné que rien n’a été précisé, le formulaire est en méthode POST avec comme target la même page.

    Pour pouvoir répondre à la soumission du formulaire on ajoute un contrôleur pour la route en post dans index.php :

    $app->post('/', function () use ($app, $form, $config) {
       // Code traitement du formulaire ici
       return $app['twig']->render('thanks.twig');
    });

    Validation des du formulaire et réaffichage du formulaire si erreur

    Dans le contrôleur qu’on vient de créer, on injecte les paramètres POST et FILES dans l’objet formulaire et on vérifie si ces données valident le formulaire.

    Si les paramètres de la requête ne valident pas le formulaire, on réaffiche le formulaire en appelant le premier contrôleur.

      $parameters = array_merge_recursive($_POST, $_FILES);
       $form->setData($parameters);
       if (!$form->isValid()) {
           $subRequest = Request::create('/', 'GET');
           return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
       }

    Attention à bien ajouter ces lignes use en haut de index.php.

    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpKernel\HttpKernelInterface;

    Envoi du fichier par SSH

    Pour utiliser les classes FileTransfer de Drupal, je fais appel à la méthode statique factory de la classe que je veux instancier. Cette méthode prend 2 paramètres : un chemin “jail” et un array avec les identifiants de connexion. J’ai pris soin de nommer les noms des champs du formulaire comme l’attend cette méthode pour ne pas passer par une étape de conversion.

    Dans la configuration local.config.php

    return array(
       'file-transfer' => array(
           'class' => 'Drupal\Core\FileTransfer\SSH',
           'jail'  => realpath(__DIR__ . '/../upload'),
       ),
    );

    Code complet du nouveau contrôleur

    $app->post('/', function () use ($app, $form, $config) {
       $parameters = array_merge_recursive($_POST, $_FILES);
       $form->setData($parameters);
       if (!$form->isValid()) {
           $subRequest = Request::create('/', 'GET');
           return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
       }
       $validatedData = $form->getData();
       $fileTransfer = $config['file-transfer']['class']::factory($config['file-transfer']['jail'], $validatedData);
       $fileTransfer->copyFile($validatedData['file']['tmp_name'], $config['file-transfer']['jail'] . '/' . uniqid());
       return $app['twig']->render('thanks.twig');
    });
    Fichier attachéTaille
    Image icon simple-upload-html.png12.41 Ko
    Fichier simple-upload-mockup.svg13.43 Ko