# encoding: ascii
# api: powershell
# title: tail
# description: since this post i’m out on github :)
# version: 0.1
# type: function
# author: greg zakharov
# license: CC0
# function: Get-FileTail
# x-poshcode-id: 4844
# x-archived: 2014-04-10T15:44:07
# x-published: 2014-01-27T13:06:00
#
#
#requires -version 2.0
Set-Alias tail Get-FileTail

function Get-FileTail {
  <#
    .NOTES
        Author: greg zakharov
  #>
  [CmdletBinding(DefaultParameterSetName="FileName", SupportsShouldProcess=$true)]
  param(
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [ValidateScript({Test-Path $_})]
    [String]$FileName,
    
    [Parameter(Position=1)]
    [ValidateRange(1, 65535)]
    [UInt16]$NumberOfLines = 10,
    
    [Parameter(Position=2)]
    [Text.Encoding]$Encoding = [Text.Encoding]::Default,
    
    [Parameter(Position=3)]
    [String]$Delims = "`r`n"
  )
  
  begin {
    $FileName = cvpa $FileName
    
    function get([String]$path, [Int64]$tokens, [Text.Encoding]$enc, [String]$delims) {
      [Int32]$szChar = $enc.GetByteCount("`n")
      [Byte[]]$buff = $enc.GetBytes($delims)
      
      try {
        $fs = New-Object IO.FileStream($path, [IO.FileMode]::Open, [IO.FileAccess]::Read)
        [Int64]$tknCount = 0
        [Int64]$endReads = $fs.Length / $szChar
        
        for ([Int64]$pos = $szChar; $pos -lt $endReads; $pos += $szChar) {
          [void]$fs.Seek(-$pos, [IO.SeekOrigin]::End)
          [void]$fs.Read($buff, 0, $buff.Length)
          
          if ($enc.GetString($buff) -eq $delims) {
            $tknCount++
            if ($tknCount -eq $tokens) {
              [Byte[]]$retBuff = New-Object "Byte[]" ($fs.Length - $fs.Position)
              [void]$fs.Read($retBuff, 0, $retBuff.Length)
              return $enc.GetString($retBuff)
            }
          }
        }
        
        $fs.Seek(0, [IO.SeekOrigin]::Begin)
        $buff = New-Object "Byte[]" $fs.Length
        [void]$fs.Read($buff, 0, $buff.Length)
        return $enc.GetString($buff)
      }
      finally {
        if ($fs -ne $null) {$fs.Close()}
      }
    }
  }
  process {
    if ($PSCmdlet.ShouldProcess($FileName, "Reads last $($NumberOfLines) strings of")) {
      get $FileName $NumberOfLines $Encoding $Delims
    }
  }
  end {}
}
