PrependLicense.psm1
$SummaryTable = @{} function Add-GPLHeader { [CmdletBinding()] Param ( [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string[]]$Path, [Parameter(Mandatory = $True)] [string]$ProgramName, [Parameter(Mandatory = $True)] [string]$ProgramDescription, [Parameter(Mandatory = $True)] [string]$Author, [switch]$WhatIf ) $Header = New-Header -Type 'GPL' -Path $Path -ProgramName $ProgramName -ProgramDescription $ProgramDescription -Author $Author $ConfirmationMessage = New-ConfirmationMessage -Type 'GPL' -Header $Header -WhatIf:$WhatIf.IsPresent $Decision = Request-Confirmation -Message $ConfirmationMessage -WhatIf:$WhatIf.IsPresent if ($Decision -eq $True) { Start-PrependProcess -Path $Path -Header $Header -WhatIf:$WhatIf.IsPresent } else { Write-Output -InputObject 'Procedure has been cancelled, no files have been modified.' } } function Add-MITHeader { [CmdletBinding()] Param ( [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string[]]$Path, [Parameter(Mandatory = $True)] [string]$ProgramName, [Parameter(Mandatory = $True)] [string]$Author, [switch]$WhatIf ) $Header = New-Header -Type 'MIT' -Path $Path -ProgramName $ProgramName -Author $Author $ConfirmationHeader = New-ConfirmationMessage -Type 'MIT' -Header $Header -WhatIf:$WhatIf.IsPresent $Decision = Request-Confirmation -Message $ConfirmationHeader -WhatIf:$WhatIf.IsPresent if ($Decision -eq $True) { Start-PrependProcess -Path $Path -Header $Header -WhatIf:$WhatIf.IsPresent } else { Write-Output -InputObject 'Procedure has been cancelled, no files have been modified.' } } function Add-Header { [CmdletBinding()] Param ( [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string[]]$Path, [Parameter(Mandatory = $True)] [string]$Header, [Parameter(Mandatory = $False)] [AllowEmptyString()] [AllowNull()] [string]$Include, [switch]$WhatIf ) $ConfirmationMessage = New-ConfirmationMessage -Type '' -Header $Header -WhatIf:$WhatIf.IsPresent $Decision = Request-Confirmation -Message $ConfirmationMessage -WhatIf:$WhatIf.IsPresent if ($Decision -eq $True) { Start-PrependProcess -Path $Path -Header $Header -Include $Include -WhatIf:$WhatIf.IsPresent } else { Write-Output -InputObject 'Procedure has been cancelled, no files have been modified.' } } function New-Header { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $True)] [ValidateSet("GPL", "MIT")] [string]$Type, [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string[]]$Path, [Parameter(Mandatory = $False)] [string]$ProgramName, [Parameter(Mandatory = $False)] [string]$ProgramDescription, [Parameter(Mandatory = $False)] [string]$Author ) $Year = (Get-Date).Year if ($Type -eq 'GPL') { $Header = @" ${ProgramName} - ${ProgramDescription} Copyright (C) ${Year} ${Author} This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. "@ } elseif ($Type -eq 'MIT') { $Header = @" The MIT License (MIT) Copyright ${Year} ${Author} Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. "@ } $Header } function New-ConfirmationMessage { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $True)] [ValidateSet("GPL", "MIT", "")] [AllowEmptyString()] [string]$Type, [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$Header, [switch]$WhatIf ) # if it's not a custom header, don't PadRight if ($Type.Length -gt 0) { $PaddedType = $Type.PadRight(4) } else { $PaddedType = $Type } if ($WhatIf.IsPresent -eq $False) { $ConfirmationMessage = @" The following ${PaddedType}license will be prepended to all recognized file types: ${Header} "@ } else { $ConfirmationMessage = @" The following ${PaddedType}license *would* be prepended to all recognized file types: ${Header} "@ } $ConfirmationMessage } function Start-PrependProcess { [CmdletBinding()] Param ( [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string[]]$Path, [Parameter(Mandatory = $True)] [string]$Header, [Parameter(Mandatory = $False)] [AllowEmptyString()] [AllowNull()] [string]$Include, [switch]$WhatIf ) try { if ((Test-Path $Path) -eq $False) { if (Test-Path -PathType Container -IsValid) { throw [System.IO.DirectoryNotFoundException]::new() } elseif (Test-Path -PathType Leaf -IsValid) { throw [System.IO.FileNotFoundException]::new() } else { throw [System.IO.IOException]::new() } } $IsContainer = Resolve-Path $Path | Test-Path -IsValid -PathType Container if ($IsContainer -eq $True) { Get-ChildItem -Path $Path -Recurse | ForEach-Object -Process { if ($_.PSIsContainer -eq $False) { Get-FileObject -FilePath $_.FullName -Header $Header -Include $Include -WhatIf:$WhatIf.IsPresent | ` Write-File | Write-Summary } } } else { Get-FileObject -FilePath $_.FullName -Header $Header -Include $Include -WhatIf:$WhatIf.IsPresent | Write-File | Write-Summary } Format-SummaryTable -WhatIf:$WhatIf.IsPresent # clear table to be used again in session... $SummaryTable.Clear() } catch [System.IO.DirectoryNotFoundException] { Write-Error -Message ("The following directory cannot be found: $Path") } catch [System.IO.FileNotFoundException] { Write-Error -Message ("The following file cannot be found: $Path") } catch [System.IO.IOException] { Write-Error -Message ("The following is invalid: $Path") } catch { Write-Error -Message ("An error occurred when attempting to prepend the following target: $Path") } } function Set-SummaryTable { Param ( [Parameter(Mandatory = $True)] [string]$FileExtension, [Parameter(Mandatory = $True)] [bool]$Modified ) # TODO: perhaps Group-Object can be used in here if ($SummaryTable.ContainsKey($FileExtension) -eq $True) { ($SummaryTable[$FileExtension].Count)++ } else { $YesNo = if ($Modified -eq $True) {'Yes'} else {'No'} $NewEntry = [PSCustomObject]@{Count = 1; Modified = $YesNo} $SummaryTable.Add($FileExtension, $NewEntry) } } function Format-SummaryTable { [CmdletBinding()] Param ( [switch]$WhatIf ) if ($WhatIf.IsPresent) { Write-Output @" Since the 'WhatIf' was switched, below is the what would of happened summary: "@ } Format-Table @{Label = "Found Files"; Expression = {($_.Name)}}, ` @{Label = "Count"; Expression = {($_.Value.Count)}}, ` @{Label = "Modified"; Expression = {($_.Value.Modified)}}` -AutoSize -InputObject $SummaryTable } function Request-Confirmation { Param ( [Parameter(Mandatory = $True, Position = 1)] [string]$Message, [switch]$WhatIf ) if ($WhatIf.IsPresent -eq $false) { $Question = 'Do you want to proceed in modifying file(s)?' } else { $Question = 'Do you want to simulate what will happen?' } $Choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $Choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList "&Yes")) $Choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList "&No")) [bool]$Decision = !($Host.UI.PromptForChoice($Message, $Question, $Choices, 1)) $Decision } function Get-FileTypeBrackets { [CmdletBinding()] [OutputType([PSCustomObject])] Param ( [Parameter(Mandatory = $True)] [string]$FileExtension ) $KeyFound = $FileTypeTable.GetEnumerator() | Where-Object -Property Value -Match $FileExtension | Select-Object -ExpandProperty Name if ($KeyFound) { $BracketsRaw = $BracketTable[$KeyFound] $Brackets = [PSCustomObject]@{ Opening = $BracketsRaw.Split(',')[0].Trim() Closing = $BracketsRaw.Split(',')[1].Trim() } } else { $Brackets = $null } $Brackets } <# .SYNOPSIS Set variable in an object for Write-File which is next function in the pipeline .DESCRIPTION Opens StreamReader to set variables for Write-File which is next in the pipeline. This will close StreamReader after all variables are set. #> function Get-FileObject { [CmdletBinding()] Param ( [Parameter(Mandatory = $True)] [string]$FilePath, [Parameter(Mandatory = $True)] [string]$Header, [Parameter(Mandatory = $True)] [AllowEmptyString()] [AllowNull()] [string]$Include, [switch]$WhatIf ) $Data = [PsObject]@{ Header = $Header FileItem = $null FileAsString = '' EOL = '' Encoding = $null EndsWithEmptyNewLine = $false Brackets = '' ToInclude = $false WhatIf = $WhatIf.IsPresent } $Data.FileItem = Get-Item -Path $FilePath if (!$Include) { $Data.Brackets = Get-FileTypeBrackets -FileExtension $Data.FileItem.Extension } elseif ($Include.Split(',').Where( {$_ -like ('*' + $Data.FileItem.Extension)})) { $Data.ToInclude = $True } if ($Data.Brackets -or $Data.ToInclude) { New-Object -TypeName System.IO.StreamReader -ArgumentList $Data.FileItem.FullName -OutVariable StreamReader | Out-Null $Data.FileAsString = $StreamReader.ReadToEnd(); [byte]$CR = 0x0D # 13 or \r\n or `r`n [byte]$LF = 0x0A # 10 or \n or `n $FileAsBytes = [System.Text.Encoding]::ASCII.GetBytes($Data.FileAsString) $FileAsBytesLength = $FileAsBytes.Length $IndexOfLF = $FileAsBytes.IndexOf($LF) if (($IndexOfLF -ne -1) -and ($FileAsBytes[$IndexOfLF - 1] -ne $CR)) { $Data.EOL = 'LF' if ($FileAsBytesLength) { $Data.EndsWithEmptyNewLine = ($FileAsBytes.Get($FileAsBytesLength - 1) -eq $LF) -and ` ($FileAsBytes.Get($FileAsBytesLength - 2) -eq $LF) } } else { $Data.EOL = 'CRLF' if ($FileAsBytesLength) { $Data.EndsWithEmptyNewLine = ($FileAsBytes.Get($FileAsBytesLength - 1) -eq $LF) -and ` ($FileAsBytes.Get($FileAsBytesLength - 3) -eq $LF) } } $StreamReader.Dispose() } $Data } <# .SYNOPSIS From the variables set from Get-FileObject, Write-File will make logical decisions on what and how the contents are arranged and written to file. .DESCRIPTION Long description .PARAMETER Header User defined header .PARAMETER Include Inclusion filter for files .PARAMETER WhatIf If true running in simulation mode .EXAMPLE An example .NOTES General notes #> function Write-File { [CmdletBinding()] Param ( [Parameter(ValueFromPipeline = $True)] [PsObject]$Data <# $Data = [PsObject]@{ Header = $Header FileItem = $null FileAsString = '' EOL = '' Encoding = $null EndsWithEmptyNewLine = $false Brackets = '' ToInclude = $false WhatIf = $WhatIf.IsPresent } #> ) if ($Data.Brackets -or $Data.ToInclude) { # If running in destructive mode (not in WhatIf) pass just the FullName to StreamWriter. # If running in destructive mode then it MUST have $True passed-in as second parameter # which signifies to append. Otherwise it will delete all contents of file. if (!$Data.WhatIf) { $StreamWriterArguments = $Data.FileItem.FullName } else { $StreamWriterArguments = @($Data.FileItem.FullName, $True) } New-Object -TypeName System.IO.StreamWriter -ArgumentList $StreamWriterArguments -OutVariable StreamWriter | Out-Null $Data.Encoding = $StreamWriter.Encoding # if this is a HTML file, remove and capture DTD tag. this will be attached later in this function if ($Data.FileItem.Extension -eq ".html") { $DTDTagMatch = $Data.FileAsString | Select-String -Pattern '.*DOCTYPE.*' if ($DTDTagMatch.Matches.Success) { $Data.FileAsString = $Data.FileAsString.Replace($DTDTagMatch.Matches.Value, "").TrimStart() } } # if ToInclude is true just prepended header to file contents without brackets... if ($Data.ToInclude) { $HeaderPrependedToFileString = @" ${Header} $($Data.FileAsString) "@ } else { $HeaderPrependedToFileString = @" $($Data.Brackets.Opening) ${Header} $($Data.Brackets.Closing) $($Data.FileAsString) "@ } # add the DTD tag at the very top of file, if there was one... if ($DTDTagMatch.Matches.Success) { $HeaderPrependedToFileString = $HeaderPrependedToFileString.Insert(0, $DTDTagMatch.Matches.Value + "`r`n") } # if $Data.EOL equals 'CRLF' we shouldnt have to do anything since # PowerShell defaults to the same EOL markings (at least on Windows). # but if this file has lone LF endings, edit $HeaderPrependedToFileString # to have just LF endings too. # # Although the following may be benefical here in some environments: # $OutputEncoding # $OFS = $Info.LineEnding # $StreamWriter.NewLine = $True (although, this is a get/set prop PowerShell # cant set it) # $TextWriter.CoreNewLine (StreamWriter inherits from this class) if ($Data.EOL -eq 'LF') { $HeaderPrependedToFileString = $HeaderPrependedToFileString -replace "`r", "" if ($Data.EndsWithEmptyNewLine) { $HeaderPrependedToFileString + "`n" } } elseif ($Data.EndsWithEmptyNewLine) { $HeaderPrependedToFileString + "`r`n" } try { if (!$Data.WhatIf) { $StreamWriter.Write($HeaderPrependedToFileString) } $StreamWriter.Flush() $StreamWriter.Close() } catch { Write-Error ("PrependLicense failed to call Dispose() successfully with: " + $Data.FileItem.FullName) } } $Data } function Write-Summary { [CmdletBinding()] Param ( [Parameter(ValueFromPipeline = $True)] [PsObject]$Data <# $Data = [PsObject]@{ Header = $Header FileItem = $null FileAsString = '' EOL = '' Encoding = $null RemoveLastLine = $false Brackets = '' ToInclude = $false WhatIf = $WhatIf.IsPresent } #> ) if ($Data.Brackets -or $Data.ToInclude) { Set-SummaryTable -FileExtension $Data.FileItem.Extension -Modified $True } else { Set-SummaryTable -FileExtension $Data.FileItem.Extension -Modified $False } if ($Data.WhatIf) { if ($Data.EOL) { Write-Output -InputObject ("What if: For file " + $Data.FileItem.FullName + ", will be encoded as '" + $Data.Encoding.WebName + "' with end-of-line markings of '" + $Data.EOL + "'") } else { Write-Output -InputObject ("What if: For file " + $Data.FileItem.FullName + ", will be encoded as '" + $Data.Encoding.WebName) } } if (!$Data.FileAsString) { if ($Data.WhatIf) { Write-Output -InputObject ("What if: Would ignore modifying on unrecognized target: " + $Data.FileItem.FullName) } elseif ($Verbose.IsPresent) { Write-Verbose ("VERBOSE: Ignoring unrecognized target: " + $Data.FileItem.FullName) } } } # "imports" $FileTypeTable and $BracketTable Invoke-Expression -Command (Get-Content -Raw -Path $PSScriptRoot'\PrependLicenseVariables.ps1') |