The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package cmt::path; 

=head1 NAME

cmt::path - path utilities

=cut
use strict; 
use vars qw($LOGNAME $LOGLEVEL);
use cmt::lang('_def'); 
use cmt::log(2);
    our $LOGNAME    = __PACKAGE__; 
    our $LOGLEVEL   = 1;
use cmt::util();
use cmt::vcs('parse_id'); 
    my %RCSID   = parse_id('$Id: path.pm 872 2008-10-29 11:51:30Z lenik $'); 
    our $VER    = "0.$RCSID{rev}"; 
use Exporter; 

our @ISA        = qw(Exporter); 
our @EXPORT     = qw(path_normalize
                     path_split
                     path_splitext
                     path_join
                     temp_path
                     mkdir_p
                     which
                     ); 
our @EXPORT_OK  = qw($SLASH $ANYSLASH
                     path_segs
                     path_comp
                     dir_size
                     _H _S
                     qg
                     ); 

# INITIALIZORS
our $SLASH      = '/'; 
our $ANYSLASH   = '/\\\\';

our $opt_win32  = $^O eq 'MSWin32';
    if ($opt_win32) {
        eval "use Win32::File('GetAttributes'); 1"
            or die "failed to load Win32::File: $!";
    }

=head1 SYNOPSIS

    use cmt::path;
    mysub(arguments...)

=head1 DESCRIPTION

B<cmt::path> is a WHAT used for WHAT. It HOW-WORKS. 

BACKGROUND-PROBLEM. 

HOW-cmt::path-RESOLVES. 

=head1 FUNCTIONS

=cut

=head2 Path functions based on literal

=cut
=head3      normalize path

=cut
sub path_normalize($) {
    my $path = shift; 
    # print "path_normalize $path" if $opt_verbose; 
    
    return '.' if !$path; 

    # remove trailing ..../..(//)
    $path =~ s/[$ANYSLASH]*$//; 
    
    # match pure-root (///../)
    return $SLASH if !$path; 
    
    # root-absolute flag
    my $root = $path =~ s/^[$ANYSLASH]+// ? $SLASH : ''; 
    
    my @src = split(/[$ANYSLASH]/, $path); 
    my @dest; 
    foreach (@src) {
        if ($_ eq '..') {
            if ($dest[-1] eq '..') {
                push @dest, '..'; 
            } elsif (!@dest) {
                push @dest, '..' unless $root;
            } else {
                pop @dest; 
            }
        } elsif ($_ eq '' or $_ eq '.') {
            # skip
        } else {
            push @dest, $_; 
        }
    }
    $path = $root.join($SLASH, @dest) || '.'; 
    # print " -> $path\n" if $opt_verbose; 
}

=head3      split dirname and filename

=cut
sub path_split($) {
        no warnings('uninitialized'); 
    my $path = shift; 
    my ($dir, $name) = 
            ($path =~ m/^(?:(.*)[$ANYSLASH])?([^$ANYSLASH]*)$/); 
    _def($dir, ''); 
    wantarray ? ($dir, $name) : $name; 
}

=head3      split filename and extname

=cut
sub path_splitext($) {
    my $path = shift; 
    my ($dir, $name) = path_split($path); 
    my ($base, $ext) = 
            ($name =~ m/^(.*?)((?:\.[^.]*)?)$/); 
    wantarray ? ($base, $ext) : $ext; 
}

=head2 Path functions based on segments

=cut
=head3      path_segs: split into segments

=cut
sub path_segs($) {
    my $path = shift; 
    return undef unless defined $path;
    split(/[$ANYSLASH]/, $path); 
}

=head3      path_comp: compact path

=cut
sub path_comp {
    return '.' if !scalar(@_); 
    
    # print "path_comp [".join(',', @_)."]" if $opt_verbose; 
    
    my $root = $_[0] ? '' : $SLASH; 
    my @dest; 
    foreach (@_) {
        if ($_ eq '..') {
            if (! @dest) {
                push @dest, '..'; 
            } elsif ($dest[-1] eq '..') {
                push @dest, '..'; 
            } else {
                pop @dest; 
            }
        } elsif ($_ eq '' or ($_ eq '.')) {
            # skip
        } else {
            push @dest, $_; 
        }
    }
    my $path = $root.join($SLASH, @dest); 
    # print " -> $path\n" if $opt_verbose; 
}

=head3      path_join: join paths

=cut
sub path_join {
    my @seqs; 
    my $test_root;
    $test_root = qr/^[$ANYSLASH]/; 
    # if ("os" == "windows") {
    $test_root = qr/^([$ANYSLASH]|[A-Za-z]:)/; 
    
    foreach (@_) {
        my @this_segs = path_segs $_; 
        if (defined $_ and /$test_root/) {
            @seqs = @this_segs; 
        } else {
            push @seqs, @this_segs; 
        }
    }
    path_comp(@seqs); 
}

=head2 Filesystem utilities

=cut
=head3      dir_size

=cut
sub dir_size {
    my ($path, $rec) = @_; 
    # TODO - ...
}

my $_TEMP_HOME;
=head3      temp_home

=cut
sub temp_home {
    unless (defined $_TEMP_HOME) {
        my $t = $ENV{'TEMP'}; 
        $t = $ENV{'TMP'} if (!$t); 
        if (!$t) {
            mkdir '/tmp' if (! -e '/tmp'); 
            $t = '/tmp'; 
        }
        $t =~ s/[\/\\]$//; 
        $_TEMP_HOME = $t;
    }
    return $_TEMP_HOME;
}

=head3      temp_path

=cut
our $_uid = 0; 
sub temp_path {
    my $name = shift;
    my $uid = $$.'_'.$_uid++; 
    if (defined $name) {
        $name =~ s/\?/$uid/g;
    } else {
        $name = "cmt_$uid.tmp";
    }
    return temp_home . $SLASH . $name;
}

=head3      qg: quote globbing pattern

=cut
sub qg {
    my $p = shift; 
    $p =~ s/([.+@\#\$^&()])/\\$1/g; 
    $p =~ s/\*/.*/g; 
    $p =~ s/\?/./g;
    return qr/^$p$/i; 
}

=head3      mkdir_p

=cut
sub mkdir_p {
    my $path = shift; 
    return 1 if -d $path; 
    return 1 if mkdir $path;
    my $i = rindex($path, '/'); 
    my $j = rindex($path, '\\'); 
    my $last = $i > $j ? $i : $j;
    return 0 if $last < 0; 
    return 0 unless mkdir_p(substr($path, 0, $last));
    return 1 if -d $path;
    mkdir $path;
}

=head3      _H

=cut
sub _H {
    my $path = shift; 
    return undef unless -e $path;
    if ($opt_win32) {
        GetAttributes($path, my $attrib);
        return 1 if $attrib & 2;
    }
    my (undef, $base) = path_split $path;
    return 1 if $base =~ /^\./;
    return 0;
}

my %_SYSFILES = (
    '.svn'  => 1, 
    'CVS'   => 1, 
);
=head3      _S

=cut
sub _S {
    my (undef, $base) = path_split shift;
    return 1 if $_SYSFILES{$base};
    return 0;
}

=head3      which

=cut
sub which {
    my $name    = shift; 
    my @PATH    = split(';', $ENV{PATH});       # unix ':'
    my @PATHEXT = split(';', $ENV{PATHEXT});    # unix ':'
    for my $path (@PATH) {
        my $pn = path_join($path, $name); 
        return $pn if -f $pn;
        for my $ext (@PATHEXT) {
            my $pnx = $pn.$ext; 
            return $pnx if -f $pnx;
        }
    }
    return undef;
}

=head1 DIAGNOSTICS

(No Information)

=cut
# (HELPER FUNCTIONS)

=head1 HISTORY

=over

=item 0.x

The initial version. 

=back

=head1 SEE ALSO

The L<cmt/"Perl_simple_module_template">

=head1 AUTHOR

Xima Lenik <name@mail.box>

=cut
1