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 : Assembleur - une commande Touch
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - mars 1996)
|
|
Aujourd'hui, je vous propose, à travers un exemple concret, l'examen des puissantes fonctions de motifs d'AmigaDOS 2.0 et
supérieurs.
Présentation
Ceux d'entre vous qui connaissent Unix ne seront pas dépaysés, puisqu'il s'agit de la commande Touch, dont le rôle est de
modifier la date de dernière altération d'un ou plusieurs fichiers et/ou répertoires. Il en existe plusieurs versions pour
l'Amiga, mais le but n'était pas d'innover. J'ai visé, à l'écriture de ce programme, la symétrie parfaite avec des commandes
dont vous avez l'habitude, comme Copy. Vous entrez donc une ligne de commandes avec différents arguments dont seul FILES est
obligatoire (voir le code source pour les autres). Il s'agit d'une liste de motifs. Tous les fichiers correspondant à l'un
de ces motifs seront pris en compte par la commande, grâce à l'utilisation des fonctions Match#? de la dos.library.
Les fonctions
Avant de pouvoir utiliser ces fonctions, dérivées des fonctions correspondantes de l'ARP, il faut initialiser une structure
AnchorPath (définie dans dos/dosasl.i) :
struct AnchorPath {
struct AChain *ap_Base;
#define ap_First ap_Base
struct AChain *ap_Last;
#define ap_Current ap_Last
LONG ap_BreakBits;
LONG ap_FoundBreak;
BYTE ap_Flags;
BYTE ap_Reserved;
WORD ap_Strlen;
struct FileInfoBlock ap_Info;
UBYTE ap_Buf[1];
};
|
Cette structure, comme son nom l'indique, sert surtout de point d'ancrage et réalise ainsi une sorte de chaînage pour les
appels consécutifs à MatchFirst() et MatchNext(). Le champ le plus important est la structure FileInfoBlock qu'elle contient,
donnant la description du fichier trouvé (grâce à Examine()). On peut également trouver un FileLock sur son répertoire dans
le champ an_Lock de la structure AChain pointée par le champ ap_Current. Les champs ap_BreakBits et ap_FoundBreak permettent
d'arrêter la recherche à la réception d'un signal Ctrl-C|D|E|F.
Après l'initialisation de cette structure, on la passe en paramètre, ainsi que le motif à confronter, à la fonction MatchFirst(),
qui détermine le premier objet convenable. Sa description est placée dans ap_Info et une structure AChain est allouée. Les
appels suivants se font avec MatchNext(), qui retourne à chaque fois les objets suivants de la même façon (plus besoin du motif).
En retour, en D0, ces fonctions retournent un code d'erreur, ou 0 si tout s'est bien passé. S'il n'y a plus d'objets correspondants,
ERROR_NO_MORE_ENTRIES est dans D0. Rien de bien sorcier. Lorsque vous avez terminé, vous devez appeler MatchEnd() pour libérer
toute la mémoire nécessaire.
Les drapeaux
Examinons le champ ap_Flags de la structure AnchorPath : elle contient un masque de drapeaux.
APF_ITSWILD : signale que le motif en est bien un, qu'il contenait des jokers. Dans le cas contraire, si l'objet n'existe pas,
c'est ERROR_OBJECT_NOT_FOUND qui est retourné à la place de NO_MORE_ENTRIES. Ce drapeau est initialisé par MatchFirst().
APF_DODIR : c'est à vous de mettre ce drapeau à 1 quand vous voulez que Match#? vous retourne tout le contenu du répertoire
actuellement examiné (option ALL du programme).
APF_DIDDIR : lorsque le contenu d'un répertoire entré avec DODIR est épuisé, le répertoire est ramené avec ce drapeau, pour bien
signaler qu'on en sort. Vous devez alors effacer ce drapeau. Les autres drapeaux sont sans grand intérêt pour nous et la place
m'est comptée, je ne m'étendrai donc pas.
La modification
Le changement se fait par la fonction SetFileDate() du DOS, avec la structure DateStamp initialisée par DateStamp()... vous
verrez tout cela dans les commentaires du programme. Comme "Touch ENV: All" vous le prouvera, cela est considéré comme une
modification et déclenche des notifications (Amiga News n°72).
include exec/exec.i
include exec/exec_lib.i
include dos/dos.i
include dos/dos_lib.i
include dos/dosasl.i
include dos/dosextens.i
; le programme, pour pouvoir être résident, doit être ré-entrant.
; pour cela, il est nécessaire de ne pas stocker les variables dans
; la mémoire du programme, mais dans un bloc alloué en début de
; programme et dont l'adresse est conservé dans un registre.
; On pourrait aussi utiliser la pile, mais c'est du masochisme en
; assembleur.
STRUCTURE Mesvariables,0
APTR var_ExecBase ; struct ExecBase *
APTR var_DOSBase ; struct DOSBase *
LABEL var_ArgArray
APTR var_Files_Arg ; STRPTR *
LONG var_All_Arg ; booléen
LONG var_Quiet_Arg ; booléen
LONG var_NoReq_Arg ; booléen
APTR var_RDArgs ; struct RDArgs *
APTR var_OldWindowPtr ; struct Window *
UWORD var_PrintOffset
LABEL variables_SIZEOF
move.l 4.w,a6
moveq #20,d7 ; code de retour: FAIL
moveq #variables_SIZEOF,d0
move.l #MEMF_CLEAR!MEMF_PUBLIC,d1
jsr _LVOAllocMem(a6)
move.l d0,a3 ; a3= pointeur sur les variables (dans
move.l a3,d0 ; tout le programme)
beq Xit
move.l a6,var_ExecBase(a3)
lea DOS.Name(pc),a1 ; ouverture du DOS
moveq #37,d0
jsr _LVOOpenLibrary(a6)
move.l d0,var_DOSBase(a3)
beq.s FreeVars
move.l d0,a6 ; lecture des arguments
move.l #Args.Template,d1
lea var_ArgArray(a3),a0
move.l a0,d2
moveq #0,d3
jsr _LVOReadArgs(a6)
move.l d0,var_RDArgs(a3)
bne.s ArgsOK
bsr PrintError
bra.s CloseDOS
; le champ pr_WindowPtr de la structure Process est un pointeur
; sur une structure Window, décrivant la fenêtre utilisée comme
; référence pour l'apparition de requêtes DOS du style "Veuillez
; insérer le volume...". Les valeurs spéciales sont 0 (écran public
; par défaut) et -1 (pas de requête du tout, annulation automatique).
; Si l'argument NOREQ a été donné, on y met -1 en sauvegardant
; l'ancienne valeur.
ArgsOK move.l var_ExecBase(a3),a6
move.l ThisTask(a6),a4
move.l pr_WindowPtr(a4),var_OldWindowPtr(a3)
tst.l var_NoReq_Arg(a3)
beq.s ReqOK
move.l #-1,pr_WindowPtr(a4)
ReqOK move.l var_Files_Arg(a3),a1
ArgLoop move.l (a1)+,d0 ; argument FILES suivant
beq.s ArgEnd
move.l d0,a0
moveq #10,d7
bsr.s TouchFiles ; toucher ces fichiers
bne.s ArgLoop
ArgEnd move.l var_OldWindowPtr(a3),pr_WindowPtr(a4)
move.l var_RDArgs(a3),d1 ; libération des arguments
move.l var_DOSBase(a3),a6
jsr _LVOFreeArgs(a6)
CloseDOS
move.l var_DOSBase(a3),a1 ; fermeture du DOS
move.l var_ExecBase(a3),a6
jsr _LVOCloseLibrary(a6)
FreeVars
move.l a3,a1 ; libération des variables
moveq #variables_SIZEOF,d0
jsr _LVOFreeMem(a6)
Xit move.l d7,d0
rts
; La routine suivante prend en paramètre un pointeur sur un
; motif (l'un de ceux donnés sur la ligne de commande) et touche
; tous les fichiers qui le concernent.
TouchFiles ; (Z=0)Success=TouchFiles(Pattern)(A0)
movem.l d0-d2/a0-a1/a4-a6,-(sp)
moveq #0,d2 ; code de retour
move.l a0,a4
move.l #ap_SIZEOF+ds_SIZEOF,d0 ; allocation de AnchorPath
move.l #MEMF_PUBLIC!MEMF_CLEAR,d1 ; +DateStamp
move.l var_ExecBase(a3),a6
jsr _LVOAllocMem(a6)
move.l d0,a5
move.l a5,d0
beq.s .Fail
move.l var_DOSBase(a3),a6
move.l a4,d1
move.l a5,d2
jsr _LVOMatchFirst(a6) ; premier
cmp.l #ERROR_NO_MORE_ENTRIES,d0 ; fini?
beq.s .End
tst.l d0
bne.s .ScanError
move.l a5,a0
bsr.s TouchFile ; toucher cet objet
beq.s .TouchError
.TouchLoop
move.l a5,d1
jsr _LVOMatchNext(a6) ; suivant
cmp.l #ERROR_NO_MORE_ENTRIES,d0 ; fini?
beq.s .End
tst.l d0
bne.s .ScanError
move.l a5,a0
bsr.s TouchFile ; toucher celui-ci
bne.s .TouchLoop
bra.s .TouchError
.ScanError
bsr PrintFault ; afficher l'erreur
jsr _LVOSetIoErr(a6) ; régler le résultat secondaire
.TouchError
.Ret move.l a5,d1
jsr _LVOMatchEnd(a6) ; fin
move.l a5,a1
move.l var_ExecBase(a3),a6
move.l #ap_SIZEOF+ds_SIZEOF,d0 ; libération d'AnchorPath
jsr _LVOFreeMem(a6) ; et DateStamp
.Fail move.l d2,d0
movem.l (sp)+,d0-d2/a0-a1/a4-a6
rts
.End moveq #-1,d2 ; pour Z
moveq #0,d7
moveq #0,d1
jsr _LVOSetIoErr(a6)
bra.s .Ret
; La routine suivante se charge de modifier la date d'altération de
; l'objet (fichier ou répertoire) représenté dans la structure AnchorPath
; passée en paramètre. Toutes les informations qui le concernent se
; trouve dans la structure FileInfoBlock qu'elle contient. On peut
; également trouver un FileLock de son répertoire dans la dernière
; structure AChain, utilisé ici pour un CurrentDir().
TouchFile ; (Z=0)Success=TouchFile(AnchorPath)(A0)
movem.l d0-d3/a0-a2/a6,-(sp)
move.l a0,a2
move.l ap_Current(a2),a0
move.l an_Lock(a0),d1
move.l var_DOSBase(a3),a6
jsr _LVOCurrentDir(a6)
move.l d0,d3
lea ap_SIZEOF(a2),a0
move.l a0,d1 ; on a une structute DateStamp ici
jsr _LVODateStamp(a6) ; quelle heure est-il?
move.l d0,d2
btst #APB_DIDDIR,ap_Flags(a2) ; on sort d'un
beq.s .Touch ; répertoire?
subq.w #1,var_PrintOffset(a3) ; alors 1 décalage en
bra.s .DirDone ; moins
.Touch move.w var_PrintOffset(a3),d0 ; affiche autant d'espace qu'indiqué
lea Spaces.Msg(pc),a0 ; dans ce compteur (incrémenté chaque
.SpaceLoop ; fois qu'on entre dans un
bsr.s Print ; répertoire
dbra d0,.SpaceLoop
lea ap_Info+fib_FileName(a2),a0 ; affiche le nom
bsr.s Print
lea Dots.Msg(pc),a0 ; des points de suspension
bsr.s Print
bsr Flush
tst.l ap_Info+fib_DirEntryType(a2) ; fichier ou rép?
bmi.s .File
tst.l var_All_Arg(a3) ; entrer dans ce répertoire (argument ALL)?
beq.s .File
bset #APB_DODIR,ap_Flags(a2) ; le signaler.
addq.w #1,var_PrintOffset(a3) ; décaler d'1 cran vers la droite
.Filelea ap_Info+fib_FileName(a2),a0
move.l a0,d1 ; le nom de l'objet à toucher
jsr _LVOSetFileDate(a6) ; la DateStamp est dans D2
.DirDone
move.l d3,d1
move.l d0,d3 ; conserve le code d'erreur
jsr _LVOCurrentDir(a6) ; ancien répertoire
tst.l d3
bne.s .OK
bsr.s PrintError
bra.s .Ret
.OK btst #APB_DIDDIR,ap_Flags(a2) ; ne pas répéter "touched"
bne.s .Ret ; dans si on sort d'un rep
lea Touched.Msg(pc),a0
bsr.s Print
.Ret bclr #APB_DIDDIR,ap_Flags(a2) ; on efface ce drapeau
tst.l d3 ; règle Z
movem.l (sp)+,d0-d3/a0-a2/a6
rts
; Cette routine affiche le message pointé par A0 si l'argument
; QUIET n'a pas été donné. L'avantage de QUIET sur >NIL: est que
; les messages d'erreur seront quand même affichés (autre routine)
Print ; Print(Str)(A0)
movem.l d0-d1/a0-a1/a6,-(sp)
tst.l var_Quiet_Arg(a3)
bne.s .Quiet
move.l a0,d1
move.l var_DOSBase(a3),a6
jsr _LVOPutStr(a6)
.Quiet movem.l (sp)+,d0-d1/a0-a1/a6
rts
; Affiche le contenu de IoErr()
PrintError ; PrintError()
movem.l d0-d1/a0-a1/a6,-(sp)
move.l var_DOSBase(a3),a6
jsr _LVOIoErr(a6)
bsr.s PrintFault
move.l d0,d1
jsr _LVOSetIoErr(a6)
movem.l (sp)+,d0-d1/a0-a1/a6
rts
; affiche le message d'erreur correspondant au code
PrintFault ; PrintFault(Code)(D0)
movem.l d0-d2/a0-a1/a6,-(sp)
move.l d0,d1
moveq #0,d2
move.l var_DOSBase(a3),a6
jsr _LVOPrintFault(a6)
movem.l (sp)+,d0-d2/a0-a1/a6
rts
; Cette routine est nécessaire si on veut que le DOS affiche les
; messages sans attendre un LineFeed lorsqu'on utilise les routines
; tamponnées (comme PutStr).
Flush ; Flush(): force l'écriture sur la console
movem.l d0-d1/a0-a1/a6,-(sp)
move.l var_DOSBase(a3),a6
jsr _LVOOutput(a6)
move.l d0,d1
jsr _LVOFlush(a6)
movem.l (sp)+,d0-d1/a0-a1/a6
rts
DOS.Name dc.b 'dos.library',0
Args.Template dc.b 'FILES/M/A,ALL/S,QUIET/S,NOREQ/S',0
Spaces.Msg dc.b ' ',0
Dots.Msg dc.b '..',0
Touched.Msg dc.b 'touched.',10,0
|
|