package RISCOS::Filespec;

# Turn filename conversion off, unless parent imports the dummy symbol dont
# So. If you want to import all the symbols use the tag :all
# Importing /.*/ will also get 'dont'. You have been warned

# convert, convert_on, convert_off riscosify etc are implemented in riscos.c

require Exporter;
use Carp;

$VERSION = 0.11;
@ISA = qw(Exporter);
@EXPORT = qw();
# chown chroot fcntl ioctl link lstat readlink symlink
# Not umask as this doesn't use a filename
# Not glob as this is performed by a fuction in riscos.c
# Not stat or any of the file tests as these are performed via a wrapper in
# riscos.c

my @constants = qw (__RISCOSIFY_MASK __RISCOSIFY_NO_PROCESS
		    __RISCOSIFY_DROP_VOWEL __RISCOSIFY_NO_SUFFIX
		    __RISCOSIFY_DONT_CHECK_DIR __RISCOSIFY_CHECK_DIR_IS_SUFFIX
		    __RISCOSIFY_TRUNCATE_SHIFT __RISCOSIFY_TRUNCATE_MASK
		    __RISCOSIFY_DONT_TRUNCATE __RISCOSIFY_LONG_TRUNCATE
		    __RISCOSIFY_MEDIUM_TRUNCATE __RISCOSIFY_SHORT_TRUNCATE);

my @filespec =  qw(riscosify unixify canonicalise);

my @switch =	qw(convert convert_on convert_off);

@EXPORT_OK =   (@switch, @filespec, 'convert_set', @filespec, @constants,
		'dont');

%EXPORT_TAGS = (constants => \@constants, filespec => \@filespec,
		switch => \@switch, control => ['convert_set', @constants],
		all => [@constants, @filespec, @swtich, @convert],
		dont => ['dont']);

@EXPORT_FAIL = ('dont');

my $do = 1;
# Turn off
sub export_fail {
    shift @_;	# Remove package name
    map {
	if ($_ eq 'dont') {
	    undef $do;
	    ();
	} else {
	    $_;
	}
    } @_
}

sub import {
    my $self = shift;
    local $Exporter::ExportLevel = 1;	# Export into our parent, not us!
    $self->SUPER::import (@_);
    convert_off() if $do;	# Turn filename conversion off
}

# The riscosify magic numbers are in XS
# XS is fun :)

sub AUTOLOAD {
    my($constname);
    ($constname = $AUTOLOAD) =~ s/.*:://;
    my $val = RISCOS::Filespec::constant($constname);
    croak "Undefined subroutine $AUTOLOAD" unless defined $val;
    eval "sub $AUTOLOAD { $val }";
    goto &$AUTOLOAD;
}

1;
__END__

=head1 NAME

RISCOS::Filespec -- routines to control and perform filename conversion.

=head1 SYNOPSIS

    use RISCOS::Filespec;	# Turn off Unix filename conversion.

    use RISCOS::Filespec /riscosify/;
    @name = riscosify ('miniperlmain.c');

    use RISCOS::Filespec /canonicalise/;
    @pwd  = canonicalise '@';
    @name = canonicalise \*STDIN;

=head1 DESCRIPTION

By default this S<RISC OS> perl port assumes that all filenames are in "Unix"
format, and automatically converts to "native" format for all C<open>
C<rename> I<etc.> operations.

By using C<RISCOS::Filespec> a perl script turns off this conversion, and shows
that it understands S<RISC OS> file naming conventions. Because UnixLib
processes command line redirection before perl starts, let alone the script, the
internal conversion routine is always used for filenames in command line
redirection.

As well as providing routines to control the name conversion on perl builtins,
C<RISCOS::Filespec> provides access to the conversion routines to and from
"Unix" format, and a subroutine to convert filenames and filehandles to
canonical filenames.

The internal conversion routine to "native" format works as follows:

=over 4

=item *
Full S<RISC OS> pathnames of the form 'F<ADFS::Bagpuss.$.!Boot>' and 'F<RAM:$>'
are recognised as such and are passed back unmodified.

=item *
Pathnames starting 'F</dev/>' are converted to paths - 'F</dev/parallel>' will
return 'F<parallel:>'.

=item *
Filenames starting 'C</>' are checked in a special list - this allows 'F</tmp>'
to be mapped to 'F<<Wimp$ScrapDirE<gt>>'.

=item *
Pathnames starting with 'C</>' 'C<$>' or 'C<<>' are taken as absolute - if a
filing system name is found after 'C</>' it is returned to start the name,
otherwise 'C<$.>' is used.

=item *
Each 'C</>' delimited section of the pathname is then processed in turn.
If the section ends in a suffixes in the suffix list it is prefixed as a
directory name. Other 'C<.>' are converted to 'C</>', 'C<?>' and 'C<#>' map
to each other, characters illegal in RISCOS filenames are mapped to 'C<_>'.

=back

=head2 Subroutines to control the internal conversion routines

The following routines control filename conversion on "user" filenames supplied
by the script. Filename conversion for internal filenames in C<use> and
C<require> statements is B<always> turned on, and is unaffected by the use of
these functions. [It would be hard to change internal filenames, as the perl
source code assumes that '/' is a directory separator.]

=over 4

=item convert <new_status>

=item convert

Reports the status of filename conversion prior to the call, returning true if
filename conversion was turned on, false if turned off. If called with no
arguments, or an undefined argument, leaves the conversion setting unchanged.
Otherwise it turns conversion on if the argument is true, off it is false.

=item convert_on

Turns filename conversion on. Returns true if filename conversion was on
already, false if filename conversion was off.

=item convert_off

Turns filename conversion off. Returns true if filename conversion was B<off>
already, otherwise returns false.

=item convert_set <flags>

Sets the flags for filename conversion as detailed in the table below.
convert_set will C<croak> if reserved bits in the flags are not zero - always
use the C<__RISCOSIFY_*> subroutines provided to construct flag settings.
convert_set will warn if it is called in array context - use an explicit
C<scalar> if necessary, as it is proposed to change the return value for an
array context.

=over 4

=item __RISCOSIFY_NO_PROCESS

If this bit is set then filename conversion is B<off>, and no further action is
taken. C<riscosify> and C<unixify> return the filename unmodified, perl builtins
perform no filename conversion.

=item __RISCOSIFY_DONT_TRUNCATE

=item __RISCOSIFY_LONG_TRUNCATE

=item __RISCOSIFY_MEDIUM_TRUNCATE

=item __RISCOSIFY_SHORT_TRUNCATE

Set the length to truncate each filename segment. C<__RISCOSIFY_DONT_TRUNCATE>
turns truncation off, the default setting. Filing systems such as C<ADFS>
currently will still truncate filenames to 10 characters, but not truncating
filenames internally allows perl to work well with filing systems that support
longer names.

C<__RISCOSIFY_SHORT_TRUNCATE> truncates to B<10> characters - current
C<FileCore> filing systems such as C<ADFS> and C<SCSIFS> have a 10 character
limit.

C<__RISCOSIFY_MEDIUM_TRUNCATE> truncates to B<19> characters - SparkFS 1.28 and
earlier have a bug relating to filename truncation.

C<__RISCOSIFY_LONG_TRUNCATE> truncates to B<55> characters - the maximum length
name allowed by the C<longfiles> module.

=item __RISCOSIFY_DROP_VOWEL

If the path element is too long, drop vowels before truncating.
(C<a> C<e> C<i> C<o> C<u> independent of locale setting.)

=item __RISCOSIFY_NO_SUFFIX

Disable 'suffix' checking - UnixLib keeps a list of suffixes that it recognises.
If a filename ends in a known suffix, it is used as a directory name.
Hence 'F<foo.bar>' is mapped to 'F<foo/bar>' ('F<bar>' not a recognised suffix)
whereas 'F<perl.c>' is mapped to 'F<c.perl>'  ('F<c>' is by default in the
recognised list).

Currently the default list is specified by C<Unixfs$sfix> set by the F<!Boot>
file - removing 'C<pl>' and 'C<pm>' from this list will cause C<use> and
C<require> to fail unless you rename all files in the library from F<pm.*> to
F<*.pm>
[This is feasible, and if a future filecore supports long filenames will become
the default distribution arrangement.]

By default suffix checking is on, so this bit is unset.

=item __RISCOSIFY_DONT_CHECK_DIR

By default this flag is off. If enabled, and the conversion routine is presented
with 'F<!Perl/lib/File.pm>' it will check whether a directory
'F<!Perl.lib.File>' exists. If it does, it will return 'F<!Perl.lib.File.pm>',
if it does not it will return 'F<!Perl.lib.File/pm>'.

=item __RISCOSIFY_CHECK_DIR_IS_SUFFIX

By default this flag is off. If enabled it will restrict the match of the above
test to only match directories that are found in the suffix list. This allows
a degree of "intuition" when presented with a filename that could be in "Unix"
or "native" format:
'F<c.riscos>' will map to 'F<c.riscos>' assuming the directory 'F<c>' exists.
'F<riscos.c>' will map to 'F<c.riscos>'

=item __RISCOSIFY_MASK

Returns the mask of acceptable flag bits.

=back

=item riscosify <filename> [<flags>]

=item riscosify <filehandle> [<flags>]

Converts a filename to "native" format using the same routine as the builtin
name conversion. If one argument is supplied uses the same flags as the builtin
conversion routine - hence if C<convert_off> has been called C<riscosify> will
return the filename unchanged. If two arguments are supplied the second is used
as the flags to the conversion routine.
Note that C<riscosify> performs a many to one mapping, I<e.g.>
'F<c/pp_hot>' and 'F<pp_hot.c>' both map to 'F<c.pp_hot>'

If passed a filehandle (I<i.e> reference to a typeglob, or an IO object) will
convert that file handle to the name of the file it refers to, or undefined if
the handle is not open on disc file. For a filehandle the value of flags doesn't
affect the conversion, but will be faulted if it contains reserved bits set.

=item unixify <filename> [<flags>]
=item unixify <filehandle> [<flags>]


Converts a filename or filehanedle to "Unix" format (currently this routine is
roughly C<tr :/.#?:./?#/ if convert_on();> but with knowledge about C</../> and
filing system special fields. Swapping back of suffixes may be available in
future. Note that as C<riscosify> performs a many to one mapping, correct
recovery of the name by C<unixify> is impossible 100% of the time.

=item canonicalise

canonicalises a filename (I<i.e.> converts it to a full pathame, and removes any
wildcards. If passed a filehandle (I<i.e> reference to a typeglob, or an IO
object) will convert that file handle to the name of the file it refers to, or
undefined if the handle is not open on disc file.

With one filename argument C<canonicalise> is effectively

	riscosify
	OS_FSControl 37
	unixify

and with one filehandle argument

	OS_Args 7
	unixify

If a second argument is given it is used as flags for C<riscosify> and
C<unixify>. If three arguments are given, the second is the flag for
C<riscosify>, the third the flag for C<unixify>.

=item RISCOS::Filespec::convert_internal

returns the riscosify flags used by internal opens (I<e.g.> use). This value is
needed for the throwback module.

=back

=head1 BUGS

Aside from the deficiencies noted above, none known.

=head1 AUTHOR

Nicholas Clark <F<nick@unfortu.net>>
