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