CWDSupport

Current release is v0.3
Author: Benoît Gilon
html3.gif - 1868 bytes
(CWDSupport stands for Current Working Directory Support)
Back to the main CWDSupport page
This article is about a tool that started from the basic idea of providing a private CSD ( "Current Selected Directory") to every Wimp task (hence the title). It ended up to provide to every registered Wimp task a dedicated private environment consisting of: This should benefit to applications running under the TaskWindow module as well as to (hopefully uncommon) Wimp based programs which change system settings and for which you don't have source and/or a suitable developement environment to rebuild the executable files.
The suggested solution is based on the use of a RISC OS module named CWDSupport who will be in charge, among other things, to install one post-poll filter and one pre-poll filter in the name of every task which registers with the module. The core functionality (and most of the tricky bits) will be accessed in those areas: In addition, the module sets up a vector routine (OS_FSControlV) to be able to detect changes to file system settings while the task is active.

How to use it

Depending on the kind of applications you would like to deal with, two paths are provided: There is also two ways to unregister a task with the module. The CWDShow command gives details about tasks currently registered with the module: here is a sample of its output:
*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.>

The task from which the CWDShow command was issued is marked with a '>' sign at the beginning of the line. Here we find out that the Alarm task (actually the Acorn Alarm application) was registered by using the CWDRegisterCatch Alarm$Path command.

How it works

File system relevant context handling and switching

Every registered application is seen in the module as a data structure (typedef ttask) where two lists are maintained: one giving the list of file systems known by the task (beginning with the temporary file system as the task was registered) and the other for managing the system variable private store (as shown with the display resulting from the CWDShow command issuing). Below is a code extract which illustrates the FS setting handling inside the module.
/* 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;
}

System variables handling

The pre_filt_varjob and post_filt_varjob functions bodies are from the vrutils.c source code module. One difference from the FS handling is that it is based on a registration system (see the CWDRegVar, CWDUnregVar and CWDRegisterCatch commands) and not on new FS discoveries on the run‘ (via the use of the OS_FSControlV vector).

Another implementation difference from the FS handling is that the allocation of memory for individual variable values is dynamic and thus is able to deal with system variables of type code (which can be of any lengh). The CWDRegVar and CWDRegisterCatch accept patterns in the parameter specification and scans the current system variable setting (just like the Show command) for variable names which satisfies the pattern.
This is not so for the CWDUnregVar which does'nt accept wild chars as parameters.

"Unsollicited" mode (CWDRegisterCatch)

The catchnext.c source code module holds every detail about the processus initiated by a CWDRegisterCatch command submission.

The latter implies the creation of a post-poll filter for all tasks but with a mask filtering all Wimp events but Wimp_UserMessage and Wimp_UserMessageRecorded.

As such an event occurs, the module is only insterested in message with an action code set to Wimp_MTaskInitialise. As soon as a message of that kind is detected, then the post-filter is removed. Below is the code which illustrates the mechanism.

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;
}
The regcatch_next function is called as part of the CWDRegisterCatch command handling by the module.

Conclusion

With CWDSupport, you have some material from which you can develop your own ideas (by reusing some code sample from the module). Eg why not use CWDSupport as a companion utility for a telnet session server application?

As a companion utility, you can download the SYSCSD archive which makes the CLI prompt display the current FS and CSD (just by issuing the command *SetMacro CLI$Prompt <Sys$CSD> * after having run the SysCSD03 utility).
!AAsm source code is included in the archive so that you can check by yourself it is not a virus.

Development notes

While developing this utility, I found some strange behaviour which is described below; if you have found anything similar, or can offer any explanations, do please let me know.