Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

OS_GBPB 12 in a Wimp task

17 views
Skip to first unread message

Christopher Bazley

unread,
May 8, 2012, 5:09:35 AM5/8/12
to
Hello,

Two months ago I started a thread with this title but Google Groups
deems it too old to reply to. I thought some of the people (druck,
Matthew Phillips, Rick Murray, Chris Johnson, Gavin Wraith, Steve
Fryatt, etc.) might be interested to see what I eventually came up
with.

I decided to ignore the possibility of the directory being modified
between calls to OS_GBPB, although there is nothing to prevent the
implementation of my interface (below) being changed to cache the
entire contents of a directory tree in memory when an iterator object
is first created. The reasons I decided to ignore this problem are:

1. Everybody uses FilerAction, and as far as I can tell FilerAction
ignores the problem.
2. A program using one of my DirIterator objects could read all
objects between calling Wimp_Poll, if it were paranoid.
3. Given the existence of ShareFS, and given that ShareFS allows files
to be modified on a remote machine whether or not it is multi-tasking,
I don't think the proposed solutions of reading all objects between
calls to Wimp_Poll are foolproof anyway.

Presumably someone will correct me if I am wrong about number 3.

I did a lot of unit testing to verify correct behaviour (in particular
that the strong exception guarantee is honoured) and designed for
efficient minimization of calls to malloc/realloc/free, unlike my
previous, ad-hoc, attempts at using OS_GBPB 9-12. If you think the
functionality below looks useful, you can get it here:
http://www.starfighter.acornarcade.com/mysite/programming.htm#cblibrary

/* DirIter.h declares functions and types for iterator objects to
allow
traversal of a directory tree in depth-first order.

Example usage (note that nothing is printed for an empty directory):

const _kernel_oserror *e;
DirIterator *it;
for (e = diriterator_make(&it, 0, "ADFS::0.$", NULL);
e == NULL && !diriterator_is_empty(it);
e = diriterator_advance(it))
{
char buffer[256];
const size_t n = diriterator_get_object_leaf_name(
it, buffer, sizeof(buffer));
puts(buffer);
if (n >= sizeof(buffer))
fprintf(stderr, "Name truncated!\n");
}
diriterator_destroy(it);

Dependencies: ANSI C library, Acorn library kernel.
Message tokens: NoMem.
History:
CJB: 25-Mar-12: Created this header file.
*/

#ifndef DirIter_h
#define DirIter_h

/* ISO library headers */
#include <stddef.h> /* (for size_t) */
#include <stdbool.h>

/* Acorn C/C++ library headers */
#include "kernel.h"

/* Local headers */
#include "Macros.h"
#include "DateStamp.h"

typedef struct
{
OSDateAndTime date_stamp;
long int length;
unsigned int attributes;
int file_type;
}
DirIteratorObjectInfo;
/*
* Catalogue information about a filing system object.
*/

typedef struct DirIterator DirIterator;
/*
* Incomplete struct type representing a directory tree iterator.
*/

/* Flags for use with the diriterator_make function */
#define DirIterator_RecurseIntoDirectories (1u << 0)
#define DirIterator_RecurseIntoImages (1u << 1)

CONST _kernel_oserror *diriterator_make(DirIterator ** /*iterator*/,
unsigned int /*flags*/,
const char * /*path_name*/,
const char * /*pattern*/);
/*
* Creates an iterator object to allow traversal of the given
directory
* 'path_name' (e.g. "ADFS::0.$" would enumerate objects in the
root
* directory of a floppy disc). Recursion into sub-directories and
* image files is controlled by the specified flags. Only objects
with
* names that match the wildcarded string 'pattern' will be
included.
* If 'pattern' is a null pointer or "*" then all names will match.
* Returns: a pointer to an OS error block, or else NULL for
success.
*/

bool diriterator_is_empty(const DirIterator *iterator);
/*
* Finds out whether a specified iterator is empty (i.e. there is
no
* current object to be queried and cannot advance to the next
object).
* Returns: true if the iterator is empty, otherwise false.
*/

int diriterator_get_object_info(const DirIterator * /*iterator*/,
DirIteratorObjectInfo * /*info*/);
/*
* Gets catalogue information about the current object from a
specified
* directory tree iterator. Unless 'info' is a null pointer, the
object's
* file type, date stamp, length and attributes will be stored in
the
* object pointed to by 'info'. Untyped files are reported as
* FileType_None (0x3000) not FileType_Null (-1). Directories and
image
* files with a leaf name prefix by "!" are reported as
* FileType_Application (0x2000).
* Returns: type of the current object (ObjectType_NotFound if the
* iterator is empty).
*/

size_t diriterator_get_object_path_name(const DirIterator * /
*iterator*/,
char * /
*buffer*/,
size_t /
*buff_size*/);
/*
* Gets the full path name of the current object from a specified
* directory tree iterator. If the iterator is empty then an empty
* string will be output. Copies as many characters as will fit
into
* the 'buffer' array. If 'buffer_size' is zero, nothing is written
and
* 'buffer' may be a null pointer. Otherwise, characters beyond the
* 'buffer_size'-1st are discarded and a null character is written
* at the end of the characters actually written into the array.
* Returns: the number of characters that would have been written
had
* 'buff_size' been large enough, not counting the
terminating
* null character (0 if the iterator is empty).
*/

size_t diriterator_get_object_sub_path_name(
const DirIterator * /
*iterator*/,
char * /
*buffer*/,
size_t /
*buff_size*/);
/*
* Gets the sub-path name of the current object from a specified
* directory tree iterator, i.e. components of the object's path
name
* beyond the stem specified upon creation of the iterator. (If the
stem
* was "ADFS::0.$.foo" and the current object's full path name is
* "ADFS::0.$.foo.bar.baz" then "bar.baz" is the sub-path name.)
This
* function behaves like diriterator_get_object_path_name in every
other
* respect.
*/

size_t diriterator_get_object_leaf_name(const DirIterator * /
*iterator*/,
char * /
*buffer*/,
size_t /
*buff_size*/);
/*
* Gets the leaf name of the current object from a specified
directory
* tree iterator, i.e. the final component of the object's path
name.
* (If the current object's full path name is "ADFS::
0.$.foo.bar.baz"
* then "baz" is the leaf name.) This function behaves like
* diriterator_get_object_path_name in every other respect.
*/

CONST _kernel_oserror *diriterator_advance(DirIterator * /
*iterator*/);
/*
* Advances the given iterator to the next object in the directory
tree
* that matches the wildcarded name pattern, or makes the iterator
* empty if there are no more matching objects. If the iterator was
* already empty then this function has no effect and no error is
* returned. If an error is returned then the current object is
* unchanged and subsequent attempts to advance may succeed (e.g.
if
* more free memory becomes available).
* Returns: a pointer to an OS error block, or else NULL for
success.
*/

void diriterator_destroy(DirIterator * /*iterator*/);
/*
* Frees memory that was previously allocated for a directory
iterator.
*/

#endif

jgharston

unread,
May 8, 2012, 8:58:14 PM5/8/12
to
Christopher Bazley wrote:
>     * Gets the leaf name of the current object from a specified
> directory
>     * tree iterator, i.e. the final component of the object's path
> name.

You might want to drop your line length about five characters,
it's a bit of a strain reading code that's been split over lines.

chris...@bigfoot.com

unread,
May 9, 2012, 7:33:17 PM5/9/12
to
I apologise. Lines in the header file are hard-wrapped at 76
characters but Google wrapped my post at about 64 characters.

/* DirIter.h declares functions and types for iterator objects
to allow traversal of a directory tree in depth-first order.

Example usage (note that nothing is printed for an empty
directory):

const _kernel_oserror *e;
DirIterator *it;
for (e = diriterator_make(&it, 0, "ADFS::0.$", NULL);
e == NULL && !diriterator_is_empty(it);
e = diriterator_advance(it))
{
char buffer[256];
const int n = diriterator_get_object_leaf_name(
* iterator.
*/

/* Flags for use with the diriterator_make function */
#define DirIterator_RecurseIntoDirectories (1u << 0)
#define DirIterator_RecurseIntoImages (1u << 1)

CONST _kernel_oserror *diriterator_make(DirIterator **iterator,
unsigned int flags,
const char *path_name,
const char *pattern);
/*
* Creates an iterator object to allow traversal of the
* given directory 'path_name' (e.g. "ADFS::0.$" would
* enumerate objects in the root directory of a floppy
* disc). Recursion into sub-directories and image files is
* controlled by the specified flags. Only objects with
* names that match the wildcarded string 'pattern' will be
* included. If 'pattern' is a null pointer or "*" then all
* names will match.
* Returns: a pointer to an OS error block, or else NULL for
* success.
*/

bool diriterator_is_empty(const DirIterator *iterator);
/*
* Finds out whether a specified iterator is empty (i.e.
* there is no current object to be queried and cannot
* advance to the next object).
* Returns: true if the iterator is empty, otherwise false.
*/

int diriterator_get_object_info(const DirIterator *iterator,
DirIteratorObjectInfo *info);
/*
* Gets catalogue information about the current object from
* a specified directory tree iterator. Unless 'info' is a
* null pointer, the object's file type, date stamp, length
* and attributes will be stored in the object pointed to by
* 'info'. Untyped files are reported as FileType_None
* (0x3000) not FileType_Null (-1). Directories and image
* files with a leaf name prefix by "!" are reported as
* FileType_Application (0x2000).
* Returns: type of the current object (ObjectType_NotFound
* if the iterator is empty).
*/

size_t diriterator_get_object_path_name(
const DirIterator *iterator,
char *buffer,
size_t buff_size);
/*
* Gets the full path name of the current object from a
* specified directory tree iterator. If the iterator is
* empty then an empty string will be output. Copies as many
* characters as will fit into the 'buffer' array. If
* 'buffer_size' is zero, nothing is written and 'buffer'
* may be a null pointer. Otherwise, characters beyond the
* 'buffer_size'-1st are discarded and a null character is
* written at the end of the characters actually written
* into the array.
* Returns: the number of characters that would have been
* written had 'buff_size' been large enough, not
* counting the terminating null character (0 if
* the iterator is empty).
*/

size_t diriterator_get_object_sub_path_name(
const DirIterator *iterator,
char *buffer,
size_t buff_size);
/*
* Gets the sub-path name of the current object from a
* specified directory tree iterator, i.e. components of the
* object's path name beyond the stem specified upon
* creation of the iterator. (If the stem was
* "ADFS::0.$.foo" and the current object's full path name
* is "ADFS::0.$.foo.bar.baz" then "bar.baz" is the sub-path
* name.) This function behaves like
* diriterator_get_object_path_name in every other respect.
*/

size_t diriterator_get_object_leaf_name(
const DirIterator * iterator,
char * buffer,
size_t buff_size);
/*
* Gets the leaf name of the current object from a specified
* directory tree iterator, i.e. the final component of the
* object's path name. (If the current object's full path
* name is "ADFS::0.$.foo.bar.baz" then "baz" is the leaf
* name.) This function behaves like
* diriterator_get_object_path_name in every other respect.
*/

CONST _kernel_oserror *diriterator_advance(
DirIterator *iterator);
/*
* Advances the given iterator to the next object in the
* directory tree that matches the wildcarded name pattern,
* or makes the iterator empty if there are no more matching
* objects. If the iterator was already empty then this
* function has no effect and no error is returned. If an
* error is returned then the current object is unchanged
* and subsequent attempts to advance may succeed (e.g. if
* more free memory becomes available).
* Returns: a pointer to an OS error block, or else NULL for
* success.
*/

void diriterator_destroy(DirIterator *iterator);
/*
* Frees memory that was previously allocated for a
* directory iterator.
*/

#endif

Matthew Phillips

unread,
May 15, 2012, 2:15:28 AM5/15/12
to
In message <5df9c846-2490-4f99...@a5g2000vbc.googlegroups.com>
on 8 May 2012 Christopher Bazley wrote:

> Two months ago I started a thread with this title but Google Groups deems
> it too old to reply to. I thought some of the people (druck, Matthew
> Phillips, Rick Murray, Chris Johnson, Gavin Wraith, Steve Fryatt, etc.)
> might be interested to see what I eventually came up with.

Sorry you haven't had many replies. The approach looks quite useful and if I
have a need for traversing a whole tree like this I will try to remember not
to reinvent the wheel! Code examples are always useful to study: at the
least I usually learn something about C which I did not know before (like,
embarrassingly, that you can do sizeof() on a char array, which I must have
known once but I have been working so long with the Impact source code, which
doesn't use this, that I have forgotten).

> 3. Given the existence of ShareFS, and given that ShareFS allows files to
> be modified on a remote machine whether or not it is multi-tasking, I don't
> think the proposed solutions of reading all objects between calls to
> Wimp_Poll are foolproof anyway.

You could be right about ShareFS. I don't think it came up in the discussion
last time. I don't know enough myself to say one way or the other, and
everyone else has been silent!

> If you think the functionality below looks useful, you can get it here:
> http://www.starfighter.acornarcade.com/mysite/programming.htm#cblibrary

The other bits in CBLibrary (implementation of appnotes 240 and 241) could be
useful to some people. Coincidentally I recently added the receiving end of
drag-and-drop to Impact. When I first looked at these appnotes, I adapted
Impact to produce Message_Dragging, including when dragging from a Save box.
But I finally got round to implementing receipt, and supporting the
clipboard, in the latest release. Most of it was quite good fun.
Implementing selection of text was the biggest part of the job. Impact's
text fields look like standard writable icons but are really rendered and
handled entirely by the Impact library. The number of things you have to do
to support text selection is incredible: drag, adjust-drag, adjust-click,
double-click, triple-click, various keypresses, etc. My implementation still
has a few rough edges as I was disappointed to find that Wimp_AutoScroll
doesn't handle autoscrolling both a child and a parent window, so I'll have
to do that myself.

Looking at your code, I think I probably don't use "const" enough myself.

> CONST _kernel_oserror *diriterator_make(DirIterator ** /*iterator*/,
> unsigned int /*flags*/,
> const char * /*path_name*/,
> const char * /*pattern*/);

What is the reason for putting the parameter names in comments? I've not
seen that before. And what is the CONST in capitals for?

--
Matthew Phillips
Durham

Christopher Bazley

unread,
May 19, 2012, 11:23:36 AM5/19/12
to
On May 15, 7:15 am, Matthew Phillips <spam20...@yahoo.co.uk> wrote:
> In message <5df9c846-2490-4f99-87f2-9dcb9eb8e...@a5g2000vbc.googlegroups.com>
>  on 8 May 2012 Christopher Bazley  wrote:
>
> > Two months ago I started a thread with this title but Google Groups deems
> > it too old to reply to. I thought some of the people (druck, Matthew
> > Phillips, Rick Murray, Chris Johnson, Gavin Wraith, Steve Fryatt, etc.)
> > might be interested to see what I eventually came up with.
>
> Sorry you haven't had many replies.  The approach looks quite useful and if I
> have a need for traversing a whole tree like this I will try to remember not
> to reinvent the wheel!  Code examples are always useful to study: at the
> least I usually learn something about C which I did not know before (like,
> embarrassingly, that you can do sizeof() on a char array, which I must have
> known once but I have been working so long with the Impact source code, which
> doesn't use this, that I have forgotten).
>
> > 3. Given the existence of ShareFS, and given that ShareFS allows files to
> > be modified on a remote machine whether or not it is multi-tasking, I don't
> > think the proposed solutions of reading all objects between calls to
> > Wimp_Poll are foolproof anyway.
>
> You could be right about ShareFS.  I don't think it came up in the discussion
> last time.  I don't know enough myself to say one way or the other, and
> everyone else has been silent!

My statement was based on a recollection of modifying files on my
brother's Risc PC whilst he played a single-tasking game. Thinking
about it, ShareFS wouldn't be much use if it needed cooperation from
the current foreground task. Unless my memory is at fault, the real
situation is therefore exactly as it would be on an operating system
with pre-emptive multi-tasking: the contents of a filing system may
change concurrently at any time between SWI calls.

> > If you think the functionality below looks useful, you can get it here:
> >http://www.starfighter.acornarcade.com/mysite/programming.htm#cblibrary
>
> The other bits in CBLibrary (implementation of appnotes 240 and 241) could be
> useful to some people.  Coincidentally I recently added the receiving end of
> drag-and-drop to Impact.  When I first looked at these appnotes, I adapted
> Impact to produce Message_Dragging, including when dragging from a Save box.

That's a neat idea. I implemented the same thing as extensions to the
Toolbox's draggable gadget and SaveAs dialogue box class, but I've
become demoralized by the fact that all of my Toolbox improvements
seem destined to stagnate on one branch of the operating system whilst
active development moves to another. It's hard to imagine a more
injurious state of affairs to the future of RISC OS.

> But I finally got round to implementing receipt, and supporting the
> clipboard, in the latest release.  Most of it was quite good fun.
> Implementing selection of text was the biggest part of the job.  Impact's
> text fields look like standard writable icons but are really rendered and
> handled entirely by the Impact library.  The number of things you have to do
> to support text selection is incredible: drag, adjust-drag, adjust-click,
> double-click, triple-click, various keypresses, etc.  My implementation still

Been there, done that, too! Text area gadgets have exactly the problem
to solve, but I went further than the window manager by adding some of
the more exotic multiple-click actions described in the RISC OS 3
Style Guide (e.g. 'select word', 'select paragraph', 'select all',
'select to end of word', etc.)

> has a few rough edges as I was disappointed to find that Wimp_AutoScroll
> doesn't handle autoscrolling both a child and a parent window, so I'll have
> to do that myself.

Are you sure you really want to do that? It sounds confusing.

> Looking at your code, I think I probably don't use "const" enough myself.
>
> > CONST _kernel_oserror *diriterator_make(DirIterator ** /*iterator*/,
> >                                         unsigned int   /*flags*/,
> >                                         const char   * /*path_name*/,
> >                                         const char   * /*pattern*/);
>
> What is the reason for putting the parameter names in comments?  I've not
> seen that before.  And what is the CONST in capitals for?

The parameter names are in comments because I copied the style of the
ANSI C library headers supplied with Acorn C/C++, which declare
themselves to be "Copyright (C) Codemist Ltd." If I had to guess the
reason, I would say it is probably to speed up compilation.

CONST is in capitals because originally CBLibrary didn't use const
qualifiers on _kernel_oserror pointers. To aid compilation of old my
older programs, you can therefore define a pre-processor macro named
CBLIB_OBSOLETE, in which case the CONST macro expands to no token
instead of 'const'.

I find it frustrating that the libraries shipped with Acorn C/C++ do
not use 'const' in many places where they should; not only on error
pointers (because the caller of a SWI has no way of knowing whether an
error block is writable or not), but also for string arguments to
Toolbox methods. This means I have to use casts to remove the 'const'
qualifier from parameters when calling those sloppily-specified
functions, thereby undermining the type-safety of the language.

Cheers,

Chris Bazley

Matthew Phillips

unread,
May 20, 2012, 11:15:03 AM5/20/12
to
In message <4c24eff3-2d37-48b5...@8g2000vbu.googlegroups.com>
on 19 May 2012 Christopher Bazley wrote:

> My statement was based on a recollection of modifying files on my brother's
> Risc PC whilst he played a single-tasking game. Thinking about it, ShareFS
> wouldn't be much use if it needed cooperation from the current foreground
> task. Unless my memory is at fault, the real situation is therefore exactly
> as it would be on an operating system with pre-emptive multi-tasking: the
> contents of a filing system may change concurrently at any time between SWI
> calls.

Quick test:
1. Turn on Iyonix and RISC PC
2. Press F12 on RISC PC
3. Successfully accessed files on RISC PC via ShareFS from Iyonix.

Your memory is quite correct! I've noticed this before also.

> > The number of things you have to do to support text selection is
> > incredible: drag, adjust-drag, adjust-click, double-click, triple-click,
> > various keypresses, etc.
>
> Been there, done that, too! Text area gadgets have exactly the problem
> to solve, but I went further than the window manager by adding some of
> the more exotic multiple-click actions described in the RISC OS 3
> Style Guide (e.g. 'select word', 'select paragraph', 'select all',
> 'select to end of word', etc.)

I have re-read the Style Guide (issue 1, July 1993) but I cannot find
anything about "select to end of word". Whereabouts did you find this? The
only multiple-clicks I know about are double-click for word, and triple-click
for a whole line or paragraph according to context (page 15, but confusingly
not mentioned on page 75, "Selecting text"). Impact, like many editors, goes
further with quadruple and quintuple click for paragraph and whole document.

> > My implementation still has a few rough edges as I was disappointed to
> > find that Wimp_AutoScroll doesn't handle autoscrolling both a child and a
> > parent window, so I'll have to do that myself.
>
> Are you sure you really want to do that? It sounds confusing.

I don't think it would be if done right. Imagine a browser window, for
example, with a text area which has been scrolled partly out of the visible
area. You might reasonably expect that hovering over the edge of the parent
window would scroll the main browser window until the whole of the text area
was visible, and then scroll down the text area until you had reached the
point you wanted for dropping the text. If you were actually wanting to
scroll further down the page beyond the text area, you might have to move to
outside the text area and thereby autoscroll the main window, or perhaps
scrolling of the main window would resume once the child window (the text
area) had reached the bottom.

--
Matthew Phillips
Durham
0 new messages