Suivez-nous sur X
|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : DICE - les passages de paramètres
(Article écrit par Laurent Faillie et extrait d'Amiga News - avril 1994)
|
|
Tels des explorateurs sans peur et sans reproche, nous allons continuer notre voyage
au pays des compilateurs C, dans la contrée de DICE en particulier...
Aujourd'hui, nous allons nous arrêter sur les passages des paramètres... Beaucoup de programmes
ont besoin d'avoir des informations pour pouvoir s'exécuter et il est souvent plus simple
pour l'utilisateur de les passer en paramètres plutôt que de répondre à une requête
ou une invite de commande (pour par exemple automatiser un processus). Sur Amiga,
nous devons séparer le problème en deux, suivant que le programme est lancé depuis le
Workbench ou depuis un Shell.
Le premier monde : le Shell
Nous sommes dans le monde des privilégiés car la majorité des environnements sont de ce type :
Unix, OS9, VMS... C'est le mode de communication le plus simple à réaliser (même
un PC de base gère ceci avec le MS-DOS, c'est tout dire !). Sur Amiga,
cette interface rudimentaire s'appelait d'abord le CLI pour "Command Line Interface",
soit interface à ligne de commande en bon français. Très vite, elle s'est transformée en
Shell. Que ce soit celui de Commodore ou ceux du DP tels que CSH, ils apportent un confort
d'utilisation et une puissance dont il serait dommage de se passer.
Je parlais du monde des privilégiés : n'importe quel programme de C pur (qui n'appelle
aucune fonction système) peut être porté vers l'AmigaShell quelle que soit la machine
sur laquelle il a été créé par une simple recompilation (vive le C !) alors que ce n'est
pas le cas depuis le Workbench. Nous y reviendrons, mais pour le moment, occupons-nous
de nos arguments. Comme tout programmeur de C le sait, l'exécution d'un programme commence
par une fonction main() dont le prototype est le suivant :
int maintint ac,char **av);
|
Tiens, mais comment la première fonction d'un programme peut-elle avoir des arguments ?
Eh bien, c'est par eux que le Shell indique au programme quels sont les paramètres :
"av" est un tableau de chaînes de caractères (on aurait pu le prototyper "char av[][]").
Dans chacune se trouve un argument. Attention, dans "av[0], il y a toujours le nom du
programme. "ac" contient le nombre d'arguments. Petite devinette : quelle est la valeur
minimale de ac (sous Shell) ? La réponse est 1 évidemment, il s'agit du nom du programme.
Testons ce programme :
Remarquez que chaque mot est considéré comme un argument différent. Les espaces comme
les tabulations sont considérés comme des séparateurs et le nombre d'espaces entre
les mots est sans importance. Si nous voulons utiliser une phrase entière,
il faut l'entourer de guillemets :
Si notre programme doit utiliser des fichiers, il est souvent pratique de pouvoir utiliser
les jokers (alias "wildcards" : ?, #?, etc.). Pour les utilisateurs de CSH, pas de problème,
car c'est le Shell qui s'occupe de tout... Pour les autres (ou dans le cas où l'utilisateur désire
utiliser les jokers de Commodore qui ne sont pas gérés par les CSH inférieurs à 5.31),
DICE dispose d'une fonction nommée "expand_args()" et qui fait tout le travail pour nous :
"xac" et "xav" sont les paramètres fournis par main(), "ac" et "av" sont leurs équivalents
une fois que tous les paramètres ont été étendus. Tout est fait automatiquement,
le programmeur n'a à s'inquiéter de rien.
Pour connaître toutes les bibliothèques et tous les périphériques logiques qui se trouvent dans un
sous-répertoire (de niveau 1) de Sys:, tapez :
Notez, au passage, que j'ai mis l'expression entre guillemets sinon CSH fera tout
le travail et je n'aurai rien démontré... On remarque ici aussi la puissance des
jokers d'AmigaDOS (comparez avec le MS-DOS...). Avec AmigaOS 2.0,
la dos.library nous permet de créer des programmes dans le style des commandes
de Commodore avec les fonctions FindArg(), ReadItem(), FreeArgs() et ReadArgs().
Elles ont été vues dans cet article.
Quelques précisions, l'auteur préconise l'utilisation de "_main()" au lieu de "main"
pour récupérer la ligne de commande avant transformation en "av[][].
Je rappelle que dans ce cas, les printf(), scanf et tout ce qui gère les entrées/sorties
dans la bibliothèque du compilateur, mais aussi la gestion de la mémoire par malloc(),
calloc(), realloc(), free(), strdup()... n'est plus utilisable.
Il y a de toute façon une méthode beaucoup plus simple : chaque programme du CLI
est un processus, et on peut récupérer la ligne de commande dans sa structure de
description grâce à la fonction GetArgStr(void) ou directement dans sa structure
CLI (voir les includes du 2.0 ou plus). Des erreurs se sont d'ailleurs introduites
dans cet article. Contrairement à ce qui est dit, le mot long qui correspond à un
argument absent n'est pas NULL mais garde la valeur qu'il contenait avant l'appel
de ReadArgs(). Il ne contient pas la valeur pour un /N mais un pointeur sur cette valeur...
Je vais donc reprendre l'exemple d'Emmanuel Buu :
D'abord, remarquez que j'ai utilisé les fonctions de la dos.library pour les E/S
(Printf(), PutStr()). C'est pourquoi les entiers sont affichés par "%ld"
car je vous rappelle que les fonctions de la ROM utilisent des entiers sur 16
bits par défaut alors que tous les compilateurs modernes passent les arguments sur
des mots longs.
Remarquez aussi le "%lc" qui peut paraître assez bizarre. Le fait de tout passer
sur des mots longs peut choquer mais il faut savoir que c'est beaucoup plus
rapide ainsi pour les processeurs 32 bits (à partir du 68020)
et ne ralentit pas beaucoup les 16/32 (68000/68010). Dernière chose,
contrairement au puts(), PutStr() ne rajoute pas automatiquement de "\n" en
fin de ligne.
Bon passons maintenant à nos arguments. Suivant le type argr[x]
contient :
- "\A" ou "\K" contient un pointeur sur la chaîne donnée en argument.
- "\N" contient un pointeur sur un long (pourquoi ne pas y stocker directement la valeur,
monsieur Commodore ?).
- "\S" ou "\T" TRUE si l'option est sélectionnée.
Remarquez que vos données ne seront jamais modifiées (par exemple, "del" du programme
précédent contiendra toujours 0), ce ne sont que les pointeurs de argr qui changent
(sans doute pour faciliter la programmation de commande PURE).
Pour conclure, je dirai qu'il est très important d'initialiser le tableau
argr pour tous les mots clefs qui ne sont pas /A avant d'appeler la fonction ReadArgs()
sous peine d'avoir des valeurs bidons en retour...
Le second monde : le Workbench
Ah, le Workbench... C'est la petite touche pro d'un programme.
Si un simple double-clic de souris suffit à le lancer,
c'est déjà bien, mais si en plus il accepte des arguments de cette interface,
c'est vraiment le summum !
Avant de donner un petit plus à nos programmes, une remarque :
il est très important de se souvenir qu'un programme lancé
depuis le Workbench ne possède pas de canaux d'entrée/sortie
standard (stdin, stdout, stderr) et toute tentative de printf(),
scanf(), puts() mais aussi les perrors() ou fprintf(stderr,...)
se solderont par le réveil de notre ami le Guru...
Chez Sas (ex-Lattice), le problème est contourné en ouvrant automatiquement
une fenêtre d'entrée/sortie mais c'est vraiment disgracieux...
Avec tout autre compilateur, le lancement depuis le Workbench
n'est signalé que par "ac == 0". Tout le reste du travail doit
être fait par le programmeur mais avec DICE, c'est plus simple :
si le programme est lancé depuis son icône, il débute par wbmain().
Notez que main() doit quand même exister sinon dlink renverra une erreur.
Le programme suivant est l'équivalent du précédent : il affiche ses arguments.
Voici les fameuses structures utilisées lors du passage des arguments :
On voit tout de suite qu'il y a beaucoup d'informations dans ces structures,
mais nous n'allons voir que celles qui nous intéressent ici : wbarg->sm_NumArgs et wbarg->sm_ArgList.
- wbarg->sm_NumArgs contient le nombre d'arguments, et comme pour le CLI,
le programme lui-même est inclu dans ce nombre...
- wbarg->sm_ArgList est un tableau de structure WBArg qui contient les arguments
eux-mêmes. Devinez qui est wbarg->sm_ArgList[O] ? Il s'agit de l'exécutable
que nous lançons. Comme vous pouvez le voir, ça ne change pas vraiment par
rapport à main()...
Pourquoi cette structure WBArg alors qu'une simple chaîne, contenant à la fois
le path (chemin) et le nom du fichier, aurait suffi par argument ?
Contrairement à la majorité des systèmes d'exploitation, l'Amiga a une
notion de volume et non d'unité physique. Cela signifie qu'un fichier est
défini par son nom (évidemment) mais aussi par le volume sur lequel il se
trouve. Si, par exemple, vous chargez un fichier avec l'éditeur AZ,
lorsque vous voudrez le sauver en remplaçant l'original, l'Amiga saura sur
quelle disquette le sauver, même si vous l'avez changé de lecteur, ou la
demandera si elle n'est plus "montée". C'est cela la notion de volume et
plus encore car la disquette est aussi trouvée si une autre avec le même
nom est aussi présente.
Un volume est caractérisé par un "lock" (verrou) qui est propre à chaque
fichier. Bon, ça n'arrange pas nos affaires car un chemin
aurait été plus facile à gérer mais comme d'habitude, la dos.library
fournit la fonction adéquate : CurrentDir() qui place le répertoire
courant sur celui fourni par un "lock".
Voici, schématiquement, comment procéder pour ouvrir le premier fichier
passé en paramètres :
Bon, il est évident que des tests doivent être rajoutés mais je vous
laisse le faire vous-même...
Erratum :
Le caractère "\" est manquant dans certaines lignes de cet article.
Explication : Professional Page se sert du "\" comme code de contrôle,
et il est nécessaire, avant l'importation du texte, d'en mettre deux "\\".
Excusez-nous pour cet oubli, voici les rectifications.
|