Obligement - L'Amiga au maximum

Vendredi 19 avril 2024 - 16:34  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in english


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

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


Trucs et astuces

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


Glossaire

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


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : C - écrire une commande AmigaDOS sous AmigaOS 2.0
(Article écrit par Xavier Leclercq et extrait d'Amiga News - décembre 1993)


Comment écrire une commande AmigaDOS avec le système 2.0 ? Cet article tentera de donner une réponse en proposant un modèle de programme encore appelé "squelette". Ce modèle a été écrit avec le SAS C 5.10 mais pourra, moyennant quelques adaptations, être utilisé par d'autres environnements.

Les commandes que nous écrirons utiliseront massivement les services offerts par la dos.library du 2.0. Ces nouvelles fonctions seront donc étudiées dans cet article. Nous examinerons dans un premier temps la division naturelle des programmes de commandes puis nous étudierons l'analyse de la ligne de commande, nous détaillerons ensuite les fonctions de la dos.library qui permettent d'effectuer des entrées-sorties tamponnées. Nous terminerons par le traitement des jokers ("wildcards").

Une commande

Qu'est-ce que c'est, à quoi ça sert ? Une commande AmigaDOS est un programme, une application très simple qui a un rôle bien délimité et que l'on appelle à partir du CLI ou du Shell. Ce programme se passe de toute interface utilisateur à base d'icônes, de fenêtres, de menus, etc. C'est le cas de List, Copy, More, etc. On lui fournit des paramètres au moment de l'appel puis elle s'exécute en affichant éventuellement des messages d'erreur. Elle peut aussi poser elle-même des questions de manière interactive, mais seul le clavier permet de répondre. Les commandes Shell doivent cependant offrir une souplesse d'utilisation et une certaine convivialité (même s'il s'agit d'une interface textuelle et non graphique).

Pourquoi utiliser des commandes alors que l'Amiga permet la programmation d'interfaces utilisateur conviviales ? Il y a plusieurs raisons : dès qu'il s'agit de manoeuvres un peu compliquées, si l'on par doit par exemple manipuler des chemins d'accès à des fichiers, si l'on peut opérer de plusieurs manières différentes, si l'on veut enchaîner plusieurs commandes de manière à réaliser un script, si l'on veut rediriger le résultat sur l'imprimante ou dans un fichier pour le conserver, alors il est beaucoup plus simple de considérer le programme comme une simple instruction qui peut être appelée directement ou mise dans un programme comme en BASIC.

Lors de son exécution, une commande suit plusieurs étapes : elle doit premièrement récupérer les arguments qu'on lui a passés. Ces arguments se présentent sous la forme d'une ligne de texte appelée ligne de commande. La commande doit ensuite analyser cette ligne, séparer les options des arguments et isoler chaque argument. Par exemple, dans le cas de la ligne de commandes "copy toto to ram:", l'analyse de la ligne de commande permet de déterminer que "toto" est le nom du fichier sources et que "ram:" est la destination.

L'étape suivante est le traitement que réalise la commande. Ce traitement dépend bien sûr de la commande mais on peut effectuer quelques observations : ces traitements correspondent à des algorithmes souvent simples et utilisent généralement les entrées-sorties de façon intensive. En particulier, les manipulations de textes sont courantes. Enfin, la commande libère les ressources (mémoire, fichiers ouverts, etc.) qu'elle a utilisées et communique au Shell le succès ou l'échec du traitement grâce à un code d'erreurs. Nous verrons qu'AmigaDOS propose un double système de codes d'erreurs. Détaillons maintenant chacune des étapes.

Récupérer la ligne de commande

La dos.library du 2.0 offre l'appel GetStrArg() qui permet de récupérer un pointeur sur la ligne de commande. C'est la solution la plus propre mais je dois signaler l'existence de problèmes avec le débogueur cpr de Lattice.

Dans le cas du langage C, la ligne de commande est fournie sous une forme transformée par les variables "argc" (nombre d'arguments) et "argv" (tableau contenant les arguments). Un code de démarrage (le fameux "c.o" dans le cas de Lattice) a déjà séparé les divers arguments qui composaient la ligne de commande (il s'agit d'un début d'analyse). Cette séparation peut paraître avantageuse mais nous verrons que la dos.library du système 2.0 permet de récupérer et d'analyser automatiquement la ligne de commande.

On peut utiliser une méthode plus subtile : l'environnement du compilateur SAS permet de commencer un programme C avant le traitement de la ligne de commande : il est en effet inutile d'effectuer deux fois cette analyse. Le point d'entrée s'appelle "_main(char *line)". L'argument de ce point d'entrée est précisément la ligne de commande à laquelle a été rajouté le nom du programme. Cependant, en utilisant "_main()" on perd également l'accès aux fonctions d'entrée-sortie standard. Nous verrons plus loin qu'on en utilise d'autres très proches. Pour les utilisateurs d'autres environnements, la fonction "main()" peut quand même être utilisée, ainsi que le programme principal en Pascal.

Analyser la ligne de commande

Une fois la ligne de commande récupérée, il faut l'analyser. On ne peut pas se contenter de passer des paramètres en vrac et de laisser l'application se débrouiller avec. De plus, il faut un minimum de conventions pour empêcher les ambiguïtés ("copy A B" marche dans quel sens ?), et surtout il faut garantir une certaine homogénéité de la syntaxe. On peut ainsi deviner à l'avance à peu près quoi fournir à la commande sans devoir consulter le mode d'emploi (rappelez-vous Copy et DiskCopy en MS-DOS qui ne marchent pas dans le même sens !). Reprenons l'exemple de la commande Copy. Cette commande admet des arguments de la forme :

copy [from] <fichier 1>, ... <fichier n> [to] <destination> [quiet] [buffer=<nb ko>]

Ceci mérite commentaire. Tout d'abord, nous nous apercevons que les commandes AmigaDOS utilisent des mots-clés pour différencier leurs arguments. Dans l'exemple, le mot-clé "from" sert à identifier les noms des fichiers sources et le mot-clé "to" la destination. De même, diverses options sont activées à l'aide de tels mots-clés. Les crochets indiquent que ces mots-clés sont optionnels. Ceci peut se comprendre pour les options mais comment reconnaître la source et la destination sans "from" et "to" ? Par défaut, le chemin de destination est le dernier nom et tous les autres fichiers sont considérés comme des fichiers sources.

Comment peut-on savoir quelle est la syntaxe d'une ligne de commande donnée ? Si la commande est correctement programmée et suit les directives de Commodore, il suffit de taper :

<commande> ?

Dans le cas de Copy, on obtient :

C

Ceci est une description de la syntaxe de la ligne de commande. Chaque mot-clé est cité puis suivi de son type.

D'après le manuel du CLI...
  • /M signifie argument multiple. C'est-à-dire une suite de noms séparés par une virgule ou un espace.
  • /A signifie argument obligatoire. Même si le mot-clé peut être omis, on doit indiquer un argument.
  • /K signifie argument optionnel.
  • /N signifie argument numérique. L'expression qui suit le mot-clé doit être une nombre exprimé en base dix.
  • /S signifie argument booléen. Ce mot-clé n'est pas suivi d'une expression. Seule son absence ou sa présence est testée. Typiquement ON, OFF, ALL, QUIET...
  • BUF=BUFFER signifie que BUF ou BUFFER sont des mots-clés équivalents.
Cette description normalisée de la syntaxe de la ligne de commande s'appelle "template string" et permet une analyse automatique de celle-ci grâce à un appel système. Cette utilisation de mots-clés pose problème dans le cas où des fichiers portent le même nom qu'un mot-clé, ils ne peuvent être donnés comme arguments d'une commande. Il faut entourer ces noms de fichier par des guillemets, ce qui alourdit l'utilisation des commandes. Un nombre important de personnes habituées à Unix préfèrent la syntaxe du Shell Unix. Sous Unix, les options sont habituellement indiquées par des mots-clés précédés d'un tiret. Par exemple :

$ rm -r a_effacer

A cet argument rationnel, je ne peux opposer qu'une préférence personnelle. Il me semble que la syntaxe AmigaDOS est plus proche du langage naturel et elle me parait plus facile à utiliser. Il faut noter qu'AmigaDOS permet la programmation d'un syntaxe pseudo-Unix. En effet, on peut indiquer pour chaque mot-clé un équivalent. Pour convertir Copy en commande de type Unix, on peut proposer la template string suivante :

FROM/M, TO/A, ALL=-r/S, QUIET=-q/S, BUF=-b/K/N, CLONE=-c/S, DATES=-d/S, NOPRO=-n/S, COM=-c/S, NOREQ=-n/S:

Cependant, on ne peut pas comme sous Unix grouper les options derrière un tiret comme :

$ ls -la

Comment procède-t-on pratiquement à l'analyse de la ligne de commande ? Les possibilités sont tellement nombreuses qu'il serait fastidieux de procéder soi-même. La dos.library du 2.0 offre un appel système qui effectue cette analyse : ReadArgs().

Le premier argument de cet appel n'est autre que la template string qui indique à l'appel comment il doit comprendre la ligne. Cette chaîne de caractères joue le même rôle que la chaîne de formatage dans un appel scanf(). Le second argument est un tableau de pointeur ou seront rangés les différents pointeurs sur les arguments toujours dans l'ordre indiqué dans la template string. Ce tableau comprend autant d'éléments que la template string contient de mots-clés. Si un argument est absent, le pointeur correspondant est nul. Les objets pointés diffèrent selon le type d'argument :
  • Pour un argument de type /M, le pointeur indique un tableau contenant les noms et terminé par un pointeur nul.
  • Pour un argument de type /N, le pointeur indique un nombre rangé dans un entier long.
  • Pour un argument de type /S, le pointeur est non nul si le mot-clé est présent, nul sinon.
  • Pour les autres arguments, le pointeur indique la chaîne de caractères.
Le troisième argument est une structure RDArgs qui permet l'analyse. Cette structure est détaillée dans le fichier dos/rdargs.h ainsi que les opérations nécessaires à son initialisation. En cas d'utilisation simple (c'est presque toujours le cas), le programmeur n'a même pas à se soucier de ceci : il suffit de passer un troisième argument nul et l'appel ira analyser directement la ligne de commande. De plus, si l'utilisateur demande de l'aide (avec "?"), l'appel affiche la template string et redemande la ligne de commande exactement comme les commandes DOS.

L'appel retourne un pointeur sur une structure RDArgs. Il est important de conserver ce pointeur car en cas d'analyse réussie, le système alloue de la mémoire pour y placer les résultats. Cette mémoire devra être libérée à la fin de la commande par un appel FreeArgs(). Si l'analyse échoue, le résultat de ReadArgs() est nul. Exemple : si la template string est "DE/A,VERS/A,DELAI/N", l'analyse de la ligne de commande suivante "VERS Italie DE France DELAI 5" conduira au tableau suivant :
  • Premier élément : "France"
  • Second élément : "Italie"
  • Troisième élément : 5
AmigaDOS rétablit l'ordre et effectue automatiquement les conversions. Commençons maintenant notre squelette, c'est-à-dire un programme modèle incomplet qu'il suffira de compléter pour écrire une commande.

C

Avant de continuer, nous allons aborder le traitement des erreurs.

Les erreurs

Au niveau du DOS, les erreurs sont définies par un code qui indique le type d'erreur et un niveau qui indique sa gravité. La seule nouveauté du système 2.0 de ce point de vue est d'introduire une sortie standard d'erreur à l'instar du "stderr" d'Unix. Ceci permet de rediriger sélectivement les messages d'erreurs et de les séparer des sorties normales de la commande.

La plupart des appels de la dos.library ne retournent pas de code d'erreurs mais le placent quelque part dans le système. Le programmeur peut alors récupérer ce code en appelant IoErr() afin de traiter lui-même ces erreurs. Dans le cas des commandes, le traitement se limite souvent à fermer les ressources et à afficher un message. La dos.library offre une fonction qui associe au code un message standardisé et l'affiche dans la sortie d'erreur. Ce message peut être précédé d'une chaîne de caractères indiquée par le programmeur. En fait, cette fonction ressemble beaucoup à l'appel Unix "perror()". On peut supposer que les messages qu'affichent cet appel seront automatiquement traduits quand sortira le système d'exploitation en français.

La gravité de l'erreur est indiquée par un nombre retourné à la fin de la commande (ce nombre doit être placé dans le registre d0, ce qui est obtenu en C en le donnant en argument de la "fonction" exit()). Par convention, 0 correspond à une exécution sans erreur, 5 à un avertissement (warning) et 20 à une erreur fatale. Ce niveau d'erreur peut être utilisé par certaines commandes (voir la description de la commande "if") et s'il est plus élevé que le niveau défini par la commande "failat", il interrompt le script en cours.

Nous pouvons maintenant continuer notre squelette.

C

Le corps de la commande

Notre squelette est terminé. Pour l'utiliser il faut le remplir en complétant la template string et la fonction FinCommande(). Ensuite, il faut écrire à la suite le corps du programme lui-même dans la fonction Main. Comme nous l'avons signalé dans l'introduction, l'auteur d'une commande aura souvent à manier les entrées-sorties de façon intensive.

Le système 2.0 apporte toute la gestion des entrées-sorties dédiées au texte. Habituellement reléguée dans des bibliothèques C, elles sont maintenant directement accessibles grâce à la dos.library. Voici les nouvelles fonctions qui concernent les entrées-sorties texte :

C
C

Leur utilisation intensive permet de réduire sensiblement la taille des exécutables puisqu'elles sont incluses dans une bibliothèque partageable. Une commande est un programme qui est chargé fréquemment donc plus il est petit, plus vite il est chargé. Bien entendu, leur utilisation rend les programmes non portables mais on se soucie peu de portabilité quand on écrit des commandes pour Amiga. Cet argument s'applique aux commandes, qui sont proches du système, et non pas à l'ensemble des programmes !

Un autre problème fréquemment rencontré lors d'écriture de commandes est le traitement des jockers. AmigaDOS définit en effet tout une panoplie de possibilités pour spécifier plusieurs fichiers. Ainsi `(#?.info) signifie tous les fichiers sauf ceux qui se terminent par ".info". Le problème de savoir si un nom de fichier correspond à un motif ("pattern" en anglais) donné requiert beaucoup de travail. Heureusement, la dos.library apporte trois appels qui permettent de chercher dans un répertoire tous les fichiers dont le nom correspond à un motif : MatchFirst(), MatchNext() et MatchEnd().

Voici comment l'on doit utiliser ces appels. Il faut disposer d'une structure AnchorPath que nous appellerons "APath". Des précisions sur l'emploi des trois appels et la description de cette structure sont disponibles dans dos/dosasl.h. Certains champs de cette structure permettent d'influer sur la recherche mais pour une utilisation simple, il est inutile de détailler cette structure.

C

MatchFirst() initialise la recherche, MatchNext() la poursuit. On récupère un nom de fichier après chaque appel. Enfin, MatchEnd() libère les ressources allouées pendant la recherche. Ici, la recherche est effectuée dans le répertoire courant. Le programmeur peut récupérer le nom du fichier trouvé dans le FileBlockInfo que contient la structure AnchorPath. S'il alloue de la mémoire à la fin de cette structure et qu'il place la taille de cette zone mémoire dans AnchorPath.ap_Strlen, le nom et le chemin complet du fichier seront stockés dans ce tampon. La recherche s'interrompt si MatchFirst() ou MatchNext() renvoie NO_MORE_ENTRIES.

Il existe un second appel qui permet de stocker dans un tampon l'ensemble des FileBlockInfo des fichiers correspondant à un motif donné : il s'agit d'ExAll(). Je ne le détaillerai pas mais le lecteur intéressé peut consulter les ROM Kernel Manuals.

Conclusion

Toutes ces améliorations apportées par le système 2.0 rendent l'écriture des commandes très aisée. Avec le système 1.3, il aurait fallu tout programmer soi-même : l'analyse de la ligne de commande, le traitement des jockers et l'affichage des messages d'erreur. Il faut savoir que ces progrès ont été fortement inspirés par l'arp.library dont Commodore a intégré la plupart des fonctions dans la dos.library. Les programmeurs qui utilisent ARP ne seront donc pas du tout dépaysés.


[Retour en haut] / [Retour aux articles]