CWDSupport

La version courante est v0.3
Auteur: Benoît Gilon
html3.gif - 1868 bytes

Retour à la page principale de CWDSupport.

Introduction

Cet article décrit un outil qui partait de l'idée basique d'offrir un CSD ("Current Selected Directory") privé à toute tâche Wimp (d'où le titre). Il s'est finalisé pour fournir à toute tâche Wimp enregistrée un environnement privé constitué de:

Le bénéfice doit être apporté aux applications tournant sous taskwindow comme à des programmes Wimp pour lesquels vous pouvez ne pas disposer du source et/ou d'un environnement de développement adéquat.

Une manière simple d'y arriver serait d'implémenter cette facilité dans une librairie telle que celle décrite sur une autre page; cependant, cela restreindrait par trop le champ d'application que je souhaite aussi vaste que possible (programmes écrits dans d'autres langages, programmes n'utilisant pas la librairie Uname etc..).

La solution proposée est basée sur l'utilisation d'un module CWDSupport, chargé d'installer entre autres, pour chaque tâche enregistrée auprès de lui, un filtre pré-polling comme un filtre post-polling.

De plus, le module met en place une routine sur le vecteur OS_FSControlV afin de détecter les changements dans la configuration des systèmes de fichiers "surveillés" tant que la tâche Wimp est active.

Mode opératoire

Deux modes sont possibles:
  1. Vous travaillez sous taskwindow où vous pouvez modifier le source de votre application Wimp; il suffit alors de soumettre la commande du module CWDRegister pour enregistrer la tâche avec le module CWDSupport. Pour un programme C, ceci peut être réalisé en appelant la fonction C system() après l'appel au SWI Wimp_Initialise; Tapez juste CWDRegister au prompt CLI * d'une fenêtre taskwindow; A partir de ce moment, il est possible de déclarer certaines variables d'environnement comme privées pour cette tâche: c'est aussi simple que de soummettre la commande CWDRegVar (par ex. CWDRegVar File*Path Sys$RCLimit).
  2. Vous travaillez avec une application Wimp pour laquelle vous ne disposez pas du source, alors modifiez le fichier !Run de lancement pour y insérer (juste avant l'exécution du fichier !RunImage) la commande CWDRegisterCatch laquelle peut accepter une syntaxe équivalente à CWDRegVar (production des mêmes effets). À partir de ce moment là, la prochaine tâche Wimp qui s'initialisera sera d'office enregistrée avec le module et les variables spécifiées déclarées dans son contexte privé.
Le désenregistrement pourra s'effectuer de deux façons: La commande CWDShow permet de connaitre les tâches enregistrées avec le module:
Voici un exemple d'utilisation de cette commande:
*CWDShow
>struct 021ea954 id = 0 tskhdl = 7cc020f8 taskname TaskWindow mode solicited
  fsname ADFS fsnum 8
   CSD pathwsf 022909ec <> resofpath 00000000 specialfield 00000000
   PSD pathwsf 02290afc <> resofpath 00000000 specialfield 00000000
   URD pathwsf 02290c0c <> resofpath 00000000 specialfield 00000000
   Lib pathwsf 02290d1c <> resofpath 00000000 specialfield 00000000
  registered vars
   none
 struct 021f1fe4 id = 1 tskhdl = 7d402100 taskname Alarm mode unsolicited
  fsname ADFS fsnum 8
   CSD pathwsf 0228ec7c <> resofpath 00000000 specialfield 00000000
   PSD pathwsf 0228ed8c <> resofpath 00000000 specialfield 00000000
   URD pathwsf 0228ee9c <> resofpath 00000000 specialfield 00000000
   Lib pathwsf 0228efac <> resofpath 00000000 specialfield 00000000
  registered vars
   name <Alarm$Path> exists Y vartype <0>
   value <Choices:Alarm.,Resources:$.Apps.!Alarm.,Resources:$.Resources.Alarm.>

Ici deux tâches le sont, la tâche qui a émis la commande CWDShow est marquée par un signe > en début de ligne; la tâche Alarm a été enregistrée par l'artifice de la commande CWDRegisterCatch Alarm$Path.

Comment ça marche

Action sur le système de fichiers

Chaque application enregistrée se voit associer une structure de données (type ttask) contenant deux listes: une liste contenant les désignations des systèmes de fichiers connus de la tâche (à commencer par le système de fichier temporaire lorsque la tâche s'est lancé) et l'autre liste contenant les spécifications de variables privées (ces deux listes constituant l'essentiel du résultat de la commande CWDShow). Voici un extrait montrant le traitement effectué au sein du module lors de gestion de contexte pour les systèmes de fichiers:
/* This function is a copy routine relevant to FS setting contexts
 * as managed from inside the module */
static void ctx_cpy(tpathelm *pdest, const tpathelm *psrc)
{
const ptrdiff_t  moff= (char *) pdest - (char *) psrc;
strcpy(pdest->pathwithsf, psrc->pathwithsf);
pdest->restofpath= psrc->restofpath ?
	(psrc->restofpath + moff) : NULL;
pdest->specialfield= psrc->specialfield ?
	(psrc->specialfield + moff) : NULL;
}

static _kernel_oserror *mswp(tfspath *mpt,
	BOOL getonly,
	size_t ipath)
{
_kernel_oserror *p;
_kernel_swi_regs r;
tpathelm *mptelm= &(mpt->mpaths[ipath]), lelm;
/* Read current setting into the lelm struct */
r.r[0]= 54;
r.r[1]= (int) &(lelm.pathwithsf);
r.r[2]= ipath;
r.r[3]= (int) &(mpt->fsname);
r.r[5]= sizeof(lelm.pathwithsf);
p= osfscontrol(&r);
if(p) return p;
lelm.pathwithsf[sizeof(lelm.pathwithsf)-r.r[5]]= '\0';
lelm.restofpath= (char *) r.r[1];
lelm.specialfield= (char *) r.r[6];
if(!getonly)
	{
	/* Set the current setting from struct pointed by mptelm */
	r.r[0]= 53;
	r.r[1]= (int) mptelm->restofpath;
	r.r[2]= ipath;
	r.r[3]= (int) &(mpt->fsname);
	r.r[6]= (int) mptelm->specialfield;
	p= osfscontrol(&r);
	}
ctx_cpy(mptelm, &lelm);
return p;
}

/* Swap the FS setting between the task context and the
 * global context */
_kernel_oserror *setting_switch(ttask *pt, BOOL getonly)
{
_kernel_oserror *p;
_kernel_swi_regs r;
struct MinNode *pw;
tfspath *mpt;
int ipath, curfsnumber= gettempfsnumber(&p);
if(p) return p;
if(curfsnumber!= pt->fsnumber)
	{
	if(!getonly) {
	 r.r[0]= 14; /* Set the current file system */
	 r.r[1]= pt->fsnumber;
	 p= osfscontrol(&r);
	 }
	pt->fsnumber= curfsnumber;
	if(p) return p;
	}
/* Browse through the list of FS known to the task */
for(pw= pt->malfs.mlh_Head; pw->mln_Succ; pw= pw->mln_Succ)
	{
	mpt= (tfspath *) ((char *) pw - offsetof(tfspath, mnode));
	for(ipath= 0; ipath < NPATHS; ipath++)
		{
		p= mswp(mpt, getonly, ipath);
		if(p) return p;
		}
	}
return p;
}

int post_filter_handler(_kernel_swi_regs *pr, void *pw)
{
ttask *lgpt;
if(pr->r[0] == -1) return 1;
lgpt= FindTask(pr->r[2], TRUE);
if(lgpt == NULL) return 1;
gpt= lgpt;
post_filt_varjob(lgpt); /* process system variables */
(void) setting_switch(lgpt, FALSE); /* process FS settings */
bact= TRUE;
return 1;
}

int pre_filter_handler(_kernel_swi_regs *pr, void *pw)
{
ttask *lgpt= FindTask(pr->r[2], TRUE);
if(!lgpt) return 1;
gpt= NULL;
bact= FALSE;
validate_and_unify(lgpt); /* manage new FS discoveries */
pre_filt_varjob(lgpt); /* process system variables */
(void) setting_switch(lgpt, FALSE); /* process FS settings */
return 1;
}

Gestion des variables d'environnement

Les corps des fonctions pre_filt_varjob et post_filt_varjob se trouvent dans le module source vrutils.c. Une différence à noter par rapport à la gestion des systèmes de fichiers est que la gestions des variables se base sur un système d'enregistrement(voir les commandes CWDRegVar, CWDUnregVar et CWDRegisterCatch) et non sur un système de découvertes au fil de l'eau de nouveaux système de fichiers (à travers l'utilisation du vecteur OS_FSControlV).

Une autre différence d'implémentation par rapport à la gestion des systèmes de fichiers est que l'allocation mémoire pour stocker les noms et valeurs de variables d'environnement est dynamique et peut donc supporter les variables de type code n'ayant pas de taille maximum.

Les commandes CWDRegVar et CWDRegisterCatch acceptent des caractères joker dans la spécification de leur paramètres et fonctionnent donc comme la commande RISC OS Show.
Ce n'est pas le cas de la commande CWDUnregVar qui n'accepte pas de caractères joker dans ses paramètres de noms de variables.

Mode non supervisé (CWDRegisterCatch)

Le module catchnext.c contient les traitements nécessaires au fonctionnement suivant ce mode:
  1. La commande déclenche la création d'un filtre post-poll pour toutes les tâches avec le masque du filtre ne laissant passer que les évènements Wimp Wimp_UserMessage et Wimp_UserMessageRecorded.
  2. Lors de l'arrivée d'un tel évènemnt, nous sélectionnons uniquement le premier message dont le code action est Wimp_MTaskInitialise. Aussitôt qu'un tel évènement se produit, le module supprime le filtre post-poll.
static BOOL bglob;
static char lbuf[256];
static int largc;
...

static _kernel_oserror *set_filter(
   BOOL mount, void *pw)
{
extern int post_generic(
   _kernel_swi_regs *, void *);
static const int filter_mask =
   ~(Wimp_Poll_UserMessageMask |
     Wimp_Poll_UserMessageRecordedMask);
static const char fname[]= "CWDcatch";

_kernel_swi_regs r;
_kernel_oserror *p;
r.r[0]= (int) fname;
r.r[1]= (int) post_generic;
r.r[2]= (int) pw; /* pw */
r.r[3]= 0;
r.r[4]= filter_mask;
p= _kernel_swi(mount ? Filter_RegisterPostFilter : 
Filter_DeRegisterPostFilter, &r, &r);
if(!p) bglob= mount;
return p;
}
...
int post_generic_handler(
   _kernel_swi_regs *pr, void *pw)
{
WimpUserMessageEvent *pum;
ttask *pt;

pum= (WimpUserMessageEvent *) pr->r[1];
if(pum->hdr.action_code ==
	Wimp_MTaskInitialise)
	{
	pt= sNewtask(pum->hdr.sender, TRUE, pw, NULL);
	if(pt && largc > 0)
	 traite_insert_strings(lbuf, pt, FALSE, largc); 
	(void) set_filter(FALSE, pw);
	}
return 1;
}

_kernel_oserror *regcatch_next(
   void *pw,
   const char *str,
   int argc)
{
_kernel_oserror *p;
p= bglob ? NULL : set_filter(TRUE, pw);
if(!p) { /* copy arg string to static buffer for future use */
	largc= argc;
	strncpy(lbuf, str, sizeof(lbuf) -1);
	}
return p;
}
La fonction regcatch_next est appelée pour traiter la commande CWDRegisterCatch.

Conclusion

Vous disposez avec CWDSupport d'un outil dont l'étude vous permettra d'aller plus loin (par réultilisation de portions de code). Ainsi, pourquoi ne pas utiliser CWDSupport comme module compagnon à un serveur de sessions de type terminal comme telnetd?

Les personnes intéressées pourront trouver utile le petit utilitaire compagnon SYSCSD qui permet que le prompt CLI d'une taskwindow affiche le CSD courant (juste en soumettant la commande *SetMacro CLI$Prompt <Sys$CSD> * après le lancement de l'utilitaire SYSCSD03.
Le code source !AAsm est inclus dans l'archive afin que vous puissiez vérifier par vous même qu'il ne s'agit pas d'un virus.

Notes de développement