# encoding: ascii
# api: csharp
# title: Experimental.IO
# description: A simple implemenation of the Experimental.IO LongPath library from the Microsoft Base Class Library project as a module.  
# version: 2.0
# type: module
# author: Joel Bennett
# license: CC0
# function: Where-Wildcard
# x-poshcode-id: 2488
# x-archived: 2017-02-26T16:21:07
# x-published: 2011-02-03T22:02:00
#
# It has the following functions, with limited error handling: Get-LongPath, Copy-LongPath, Move-LongPath, Remove-LongPath, Get-ContentLongPath, Set-ContentLongPath … it also has a Format-Color function you can use to colorize the output (or the output of Get-ChildItem).
# Since the LongPath variants do NOT create a FileInfo object (because those don’t support long paths), Get-LongPath is much faster than Get-ChildItem, but it outputs only strings. A version of this PowerShell Module for LongPath files is available on my site with the binary assembly included in a Experimental.IO.zip
#
## Requires the Experimental.IO "LongPath" library from the BCL team: http://bcl.codeplex.com/
## Compile it against .Net 3.5 (for PowerShell's sake) and place it the module folder with this psm1
if(!("Microsoft.Experimental.IO.LongPathDirectory" -as [type])) {
   Add-Type -Path $PSScriptRoot\Microsoft.Experimental.IO.dll
}

Add-Type -TypeDefinition @"
using System;
using System.ComponentModel;
using System.Management.Automation;
using System.Collections.ObjectModel;

namespace Huddled.Experimental.IO {
   [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
   public class TransformAttribute : ArgumentTransformationAttribute {
      private ScriptBlock _scriptblock;
      private string _noOutputMessage = "Transform Script had no output.";

      public override string ToString() {
         return string.Format("[Transform(Script='{{{0}}}')]", Script);
      }

      public override Object Transform( EngineIntrinsics engine, Object inputData) {
         try {
            Collection<PSObject> output =
               engine.InvokeCommand.InvokeScript( engine.SessionState, Script, inputData );
            
            if(output.Count > 1) {
               Object[] transformed = new Object[output.Count];
               for(int i =0; i < output.Count;i++) {
                  transformed[i] = output[i].BaseObject;
               }
               return transformed;
            } else if(output.Count == 1) {
               return output[0].BaseObject;
            } else {
               throw new ArgumentTransformationMetadataException(NoOutputMessage);
            }
         } catch (ArgumentTransformationMetadataException) {
            throw;
         } catch (Exception e) {
            throw new ArgumentTransformationMetadataException(string.Format("Transform Script threw an exception ('{0}'). See `$Error[0].Exception.InnerException.InnerException for more details.",e.Message), e);
         }
      }
      
      public TransformAttribute() {
         this.Script = ScriptBlock.Create("{`$args}");
      }
      
      public TransformAttribute( ScriptBlock Script ) {
         this.Script = Script;
      }

      public ScriptBlock Script {
         get { return _scriptblock; }
         set { _scriptblock = value; }
      }
      
      public string NoOutputMessage {
         get { return _noOutputMessage; }
         set { _noOutputMessage = value; }
      }  
   }
}
"@ 

function Where-Wildcard {
param(
   [Parameter(ValueFromPipeline=$true)]
   [string[]]$InputObject
,
   [Parameter(Position=0)]
   [string[]]$Like = "*"
,
   [Parameter(Position=1)]
   [string[]]$NotLike = $null
)
process {
   foreach($item in $InputObject) {
      $filename = Split-Path $item -Leaf
      $passthru = $false
      
      foreach($pattern in $Like) {
         $passthru = $passthru -or $filename -like $pattern
      }
      foreach($pattern in $NotLike) {
         $passthru = $passthru -and !($filename -like $pattern)
      }
      if($passthru) { 
         Write-Output $item
      }
   }
}
}


function Get-LongPath {
[CmdletBinding(DefaultParameterSetName="AllItems")]
param(
   [Parameter(Position=0,ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
   [Alias("FullName")]   
   [string[]]$LiteralPath = $pwd
,
   [string[]]$Include = "*"
,
   [string[]]$Exclude = $null
,
   [Parameter(ParameterSetName="DirectoriesOnly")]
   [Alias("od","do")]
   [switch]$DirectoriesOnly
, 
   [Parameter(ParameterSetName="FilesOnly")]
   [Alias("of","fo")]
   [switch]$FilesOnly
,
   [switch]$Recurse
,
   [switch]$Indent
,
   [switch]$OrderDirectoriesFirst
)
begin {
   if($Recurse -and $Indent -and (Test-Path variable:script:pad)) {
      $script:pad += "  "
   } else {
      $script:pad = ""
   }
   $null = $PSBoundParameters.Remove("LiteralPath")
   if($PSCmdlet.ParameterSetName -eq "FilesOnly") {
      Write-Verbose $LiteralPath
   }
   
}
process {
   foreach($Path in $LiteralPath) {
      if(![Microsoft.Experimental.IO.LongPathDirectory]::Exists($Path)){
         $Include = Split-Path $Path -Leaf
         $Path = Split-Path $Path 
      }
   
      switch($PSCmdlet.ParameterSetName) {
         "FilesOnly" {
            if($Recurse) {
               [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateFileSystemEntries( $Path ) | ForEach-Object{ 
                  if( [Microsoft.Experimental.IO.LongPathDirectory]::Exists( $_ ) ) {
                     Get-LongPath $_ @PSBoundParameters
                  } else {
                     Where-Wildcard -InputObject $_ -Like $Include -NotLike $Exclude | ForEach-Object{ $script:pad + $_ }
                  }
               }
            } else {
               [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateFiles( $Path ) | Where-Wildcard -like $Include -notLike $Exclude
            }
         }
         "DirectoriesOnly" {
            if($Recurse) {
               [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateDirectories( $Path ) | ForEach-Object{ 
                  Where-Wildcard -InputObject $_ -Like $Include -NotLike $Exclude | ForEach-Object{ $script:pad + $_ + "\" }
                  if($recurse) {
                     Get-LongPath $_ @PSBoundParameters
                  }
               }
            } else {
               [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateDirectories( $Path ) | Where-Wildcard -Like $Include -notLike $Exclude
            }
         }
         "AllItems" {
            if($OrderDirectoriesFirst) {
               [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateDirectories( $Path ) | Where-Wildcard -Like $Include -NotLike $Exclude | ForEach-Object{ $script:pad + $_ + "\" }
               [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateFiles( $Path ) | Where-Wildcard -like $Include -notLike $Exclude
            } else {
               if($recurse) {
                  [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateFileSystemEntries( $Path ) | ForEach-Object{ 
                     if( [Microsoft.Experimental.IO.LongPathDirectory]::Exists( $_ ) ) {
                        Where-Wildcard -InputObject $_ -Like $Include -NotLike $Exclude | ForEach-Object{ $script:pad + $_ + "\" }
                        Get-LongPath $_ @PSBoundParameters
                     } else {
                        Where-Wildcard -InputObject $_ -Like $Include -NotLike $Exclude | ForEach-Object{ $script:pad + $_ }
                     }
                  }
               } else { 
                  [Microsoft.Experimental.IO.LongPathDirectory]::EnumerateFileSystemEntries( $Path ) | 
                     Where-Wildcard -like $Include -notLike $Exclude |
                     ForEach-Object {
                        if( [Microsoft.Experimental.IO.LongPathDirectory]::Exists( $_ ) ) {
                           $script:pad + $_ + "\"
                        } else {
                           $script:pad + $_
                        }
                     }
               }
            }
         }
      }
   }
}
end {
   if($Indent) {
      if($script:pad.Length -gt 0) {
         $script:pad = $script:pad.SubString(0, $script:pad.Length - 2)
      } else {
         remove-item variable:script:pad -EA 0
      }
   }
}
}

$ExperimentalFormatColums = 4


function Format-Color {
param(
   [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
   [Alias("FullName")]
   [string[]]$InputObject
,
   [ConsoleColor]$Directories = 'Cyan'
,
   [ConsoleColor]$Executable = 'Green'
,
   [int]$Columns = $ExperimentalFormatColums
)
begin {
   $NoNewLine = $columns -gt 0
   $Count = 0
}

process {
   foreach($item in $InputObject) {
      $Count = $Count + 1
      $filename = Split-Path $item -Leaf

      $output = $item
      if($NoNewLine) {
         $width = $Host.UI.RawUI.BufferSize.Width / $Columns
         if( [Microsoft.Experimental.IO.LongPathDirectory]::Exists( $item ) ) {
            $output = "$filename\".PadRight( $width ).SubString(0,$width-1).PadRight( $width )
         } else {
            $output = $filename.PadRight( $width ).SubString(0,$width-1).PadRight( $width )
         }
      }
      
      if( [Microsoft.Experimental.IO.LongPathDirectory]::Exists( $item ) ) {
         Write-Host $output -Fore $Directories -NoNewLine:$NoNewLine
      } 
      elseif( Where-Wildcard -InputObject $item -Like "*.exe","*.cmd","*.bat","*.ps1") {
         Write-Host $output -Fore $Executable -NoNewLine:$NoNewLine
      }
      else {
         Write-Host $output -NoNewLine:$NoNewLine
      }
      
      #  if($NoNewLine -and (($Count % $Columns) -eq 0)) {
         #  Write-Host
      #  }
   }
}
end {
   if($NoNewLine -and (($Count % $Columns) -ne 0)) {
      Write-Host
   }
}
}


function Copy-LongPath {
param(
   [Parameter(ValueFromPipeline=$true)]
   [string[]]$LiteralPath
,
   [Parameter(ValueFromPipeline=$true)]
   [string]$Destination
,
   [switch]$Force
)
process {
   foreach($item in $LiteralPath) {
      if( [Microsoft.Experimental.IO.LongPathDirectory]::Exists($Destination) ) {
         $target = Join-Path $Destination (Split-Path $item -Leaf)
      } else {
         $target = $Destination
      }
      
      [Microsoft.Experimental.IO.LongPathFile]::Copy($item, $target, $force)
   }
}
}


function Move-LongPath {
param(
   [Parameter(ValueFromPipeline=$true)]
   [string[]]$LiteralPath
,
   [Parameter(ValueFromPipeline=$true)]
   [string]$Destination
,
   [switch]$Force
)
process {
   foreach($item in $LiteralPath) {
      if( [Microsoft.Experimental.IO.LongPathDirectory]::Exists($Destination) ) {
         $target = Join-Path $Destination (Split-Path $item -Leaf)
      } else {
         $target = $Destination
      }
      if([Microsoft.Experimental.IO.LongPathFile]::Exists($target) -and $Force) {
         [Microsoft.Experimental.IO.LongPathFile]::Delete($target)
      }
      [Microsoft.Experimental.IO.LongPathFile]::Copy($item, $target)
   }
}
}

function Remove-LongPath {
param(
   [Parameter(ValueFromPipeline=$true)]
   [string[]]$LiteralPath
)
process {
   foreach($item in $LiteralPath) {
      [Microsoft.Experimental.IO.LongPathFile]::Delete($item)
   }
}
}


function Get-ContentLongPath {
param(
   [Parameter(ValueFromPipeline=$true,Position=0)]
   [string[]]$LiteralPath
,
   [switch]$All
)
process {
   foreach($path in $LiteralPath) {
      $stream = [Microsoft.Experimental.IO.LongPathFile]::Open( $path, "Open", "Read" )
      $reader = New-Object System.IO.StreamReader $stream, $true
      if($All) {
         $reader.ReadToEnd()
      } else {
         while(!$reader.EndOfStream) {
            $reader.ReadLine()
         }
      }
      $reader.Close()
      $stream.Close()
   }
}
}

function Set-ContentLongPath {
param(
   [Parameter(Position=0)]
   [string[]]$LiteralPath
,
   [Parameter(Position=1, ValueFromPipeline=$true)]
   [string[]]$Value
,
   [System.Text.Encoding]
   [Huddled.Experimental.IO.Transform({
      param([string]$Encoding)
         $coding = [System.Text.Encoding]::GetEncodings() | Where-Object { $_.Name -like $Encoding } | Select -First 1
         if(!$coding) {
            $coding = [System.Text.Encoding]::GetEncodings() | Where-Object { $_.DisplayName -like $Encoding } | Select -First 1
         }
         [Text.Encoding]::GetEncoding( $coding.CodePage ) 
   })]
   $Encoding = "UTF-8"
)
begin {
   $streams = @()
   $writers = @()
   
   foreach($item in $LiteralPath) {
      $stream = [Microsoft.Experimental.IO.LongPathFile]::Open( $LiteralPath, "OpenOrCreate", "Write" )
      $streams += $stream
      $writers += New-Object System.IO.StreamWriter $stream, $encoding
   }
}
process {
   foreach($writer in $writers) {
      foreach($v in $Value) {
         $writer.WriteLine( $v )
      }
   }
}
end {
   foreach($writer in $writers) { $writer.Close() }
   foreach($stream in $streams) { $stream.Close() }
}
}



New-Alias fco  Format-Color        -ErrorAction SilentlyContinue
New-Alias glp  Get-LongPath        -ErrorAction SilentlyContinue
New-Alias cplp Copy-LongPath       -ErrorAction SilentlyContinue
New-Alias mvlp Move-LongPath       -ErrorAction SilentlyContinue
New-Alias rmlp Remove-LongPath     -ErrorAction SilentlyContinue
New-Alias gclp Get-ContentLongPath -ErrorAction SilentlyContinue
New-Alias sclp Set-ContentLongPath -ErrorAction SilentlyContinue

Export-ModuleMember -Alias * -Function Copy-LongPath, Format-Color, Get-ContentLongPath, Get-LongPath, Move-LongPath, Remove-LongPath, Set-ContentLongPath
