opexxx
11/3/2016 - 10:02 AM

Invoke-Locate.ps1

Invoke-Locate.ps1

<#  
	.SYNOPSIS  
	Fans of (Linux/UNIX) GNU findutils' locate will appreciate Invoke-Locate, which provides similar functionality. "locate" and "updatedb" aliases are automatically created.

	.DESCRIPTION
	This script was made in the spirit of GNU locate. While the name of this script is Invoke-Locate, it actually creates two persistent aliases: locate and updatedb. A fresh index is automatically created every 6 hours, updatedb can be used force a refresh. Indexes generally takes less than three minutes. Performing the actual locate takes about 300 milliseconds. Invoke-Locate supports both case-sensitive, and case-insensitive searches, and is case-insensitive by default.

	locate queries a user-specific SQLite database prepared by updatedb (Task Scheduler) and writes file names matching the pattern to standard output, one per line. Since the back-end is SQL, SQL "LIKE" syntax can be used for the search pattern (ie % and _). Asterisks are automatically translated to % for people who are used to searching with * wildcards. So locate SQ*ite, and locate SQ%ite will return the same results.

	By default, locate does not check whether files found in database still exist; locate cannot report files created after the most recent update of the relevant database.

	.PARAMETER filename
	You actually don't have to specify the filename. Just locate whatever.

	.PARAMETER install
	"Installs" script to $env:LOCALAPPDATA\locate, which allows each user to have their own secured locate database.
		- Sets persistent locate and updatedb user aliases.
		- Checks for the existence of System.Data.SQLite.dll. If it does not exist, it will be automatically downloaded to $env:LOCALAPPDATA\locate.
		  To skip this step, download System.Data.SQLite and register it to the GAC.
		- Creates the database in $env:LOCALAPPDATA\locate.
		- Creates the initial table.
		- Creates a schedule task named "updatedb cron job user [username] (PowerShell Invoke-Locate)" that runs every 6 hours as an elevated SYSTEM account. This action is skipped if you don't have admin access.
		  *Note: even though an elevated SYSTEM account is used, home directories of other users are excluded from index.
		- Prompts users to specify if mapped drives should be indexed. Take note that mapped drives can be huge.
		- Prompts user to run updatedb for the first time.

	.PARAMETER s
	Similar to findutils locate's "-i" switch for case-insensitive, this switch makes the search sensitive. By default, Windows searches are insensitive, so the default search behavior of this script is case-insensitive.

	.PARAMETER updatedb
	Forces a fresh update of the database. This generally takes less than 3 minutes.

	.PARAMETER includemappeddrives
	Internal parameter. Tells updatedb to include mapped drives.

	.PARAMETER locatepath
	Internal parameter. Specifies locate's program directory.

	.PARAMETER userprofile
	Internal parameter. This helps support mulit-user scheduled task updates. 

	.PARAMETER homepath
	Internal parameter. This helps support mulit-user scheduled task updates.

	.NOTES  
	Author  : Chrissy LeMaire 
	Requires:     PowerShell Version 3.0
	DateUpdated: 2015-Jan-24
	Version: 0.5
	 
	.LINK
	https://gallery.technet.microsoft.com/scriptcenter/Invoke-Locate-PowerShell-0aa2673a
	 
	.EXAMPLE
	locate powershell.exe
	Case-insensitive search which return the path to any file or directory named powershell.exe	
	.EXAMPLE
	updatedb
	Forces a database refresh. This generally takes just a few minutes.
	.EXAMPLE
	locate power*.exe
	Case-insensitive search which return the path to any file or directory that starts with power and ends with .exe
	.EXAMPLE
	locate -s System.Data.SQLite
	Case-sensitive search which return the path to any file or directory named System.Data.SQLite.
	.EXAMPLE
	locate powers_ell.exe
	Similar to SQL's "LIKE" syntax, underscores are used to specify "any single character."
#> 
#Requires -Version 3.0
[CmdletBinding(DefaultParameterSetName="Default")]

Param(
	[parameter(Position=0)]
	[string]$filename,
	[switch]$install,
	[switch]$updatedb,
	[string]$locatepath,
	[string]$userprofile,
	[string]$homepath,
	[switch]$s,
	[switch]$includemappedrives
	)

BEGIN {
	Function Install-Locate   {
		<#
		.SYNOPSIS
		  Installs Invoke-Locate.ps1 to the current user's $env:localappdata.
		#>
		
		param(
			[Parameter(Mandatory = $false)]
            [bool]$noprompt,
			[string]$locatepath
		)
		
		if ($locatepath.length -eq 0) { $locatepath = "$env:LOCALAPPDATA\locate" }
		
		# Create locate's program directory within user $env:localappdata.
		if (!(Test-path $locatepath)) { $null = New-Item $locatepath -Type Directory }
		
		# Copy the files to the new directory
		$script = "$locatepath\Invoke-Locate.ps1"
		Get-Content $PSCommandPath | Set-Content $script
		
		# Set persistent aliases by writing to $profile
		Write-Host "Setting persistent locate and updatedb aliases" -ForegroundColor Green
		$locatealias = "New-Alias -name locate -value $script -scope Global -force"
		$exists = Get-Alias locate -ErrorAction SilentlyContinue
		if ($exists -eq $null) { 
			if (!(Test-Path $profile)) {
				$profiledir = Split-Path $profile
				If (!(Test-Path $profiledir)) { $null = New-Item $profiledir -Type Directory }			
			} 
		Add-Content $profile $locatealias
		} else { Write-Warning "Alias locate exists. Skipping." }
		
		# Prompt user to see if they want to index mapped drives
		$message = "This script can index mapped drives, too."
		$question = "Would you like to index your mapped drives?"
		$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'))
		
		$decision = $Host.UI.PromptForChoice($message, $question, $choices, 1)
		if ($decision -eq 1) { 
			Write-Host "Mapped drives will not be indexed."
			 $includemappedrives = $false
		} else { 
			Write-Host "Mapped drives will be indexed."
			$includemappedrives = $true }

		# Aliases don't allow params, so a new file must be created in localappdata to support using the alias updatedb
		$updatefilename = "$locatepath\Update-LocateDB.ps1"
		if ($homepath.length -eq 0) { $homepath = "$env:HOMEDRIVE$env:HOMEPATH" }
		if ($userprofile.length -eq 0) { $userprofile = $env:USERPROFILE } 
		if (!$includemappedrives) { $maparg = '-includemappedrives:$false' } else  { $maparg = '-includemappedrives:$true' }
		Set-Content  $updatefilename "Invoke-Expression '$script -updatedb -locatepath $locatepath -homepath $homepath -userprofile $userprofile $maparg'"
		Add-Content  $updatefilename '$computername = "$($env:COMPUTERNAME)`$".ToUpper()'
		Add-Content  $updatefilename '$username = "$env:USERNAME".ToUpper()'
		Add-Content  $updatefilename 'if ($computername -eq $username) { return $true }'
		
		# Add persistent updatedb alias
		$updatealias = "New-Alias -name updatedb -value $updatefilename -scope Global -force"
		$exists = Get-Alias updatedb -ErrorAction SilentlyContinue
		if ($exists -eq $null) { Add-Content $profile $updatealias }
		else { Write-Warning "Alias updatedb exists. Skipping." }
		
		# Reload $profile. Now locate and udpatedb alises will work.
		Invoke-Expression $profile -ErrorAction SilentlyContinue
		
		# Download the DLL if System.Data.SQLite cannot be found. Copy to $env:localappdata.
		$sqlite = "System.Data.SQLite"
		$globalsqlite = [Reflection.Assembly]::LoadWithPartialName($sqlite)
		if ($globalsqlite -eq $null -and !(Test-Path("$locatepath\$sqlite.dll")) ) {
			# Check architecture
			if (!(Test-Path "locatepath\$sqlite.dll")) {
				Write-Host "Downloading $sqlite.dll" -ForegroundColor Green
				if ($env:Processor_Architecture -eq "x86")   { $url = "http://bit.ly/x86sqlitedll" } else {  $url = "http://bit.ly/x64sqlitedll"  }
					Invoke-WebRequest $url -OutFile "$locatepath\$sqlite.dll"
				}
		}
		if ($globalsqlite -eq $null) {[void][Reflection.Assembly]::LoadFile("$locatepath\$sqlite.dll")}
		
		# Setup connstring
		$database = "$locatepath\locate.sqlite"
		$connString = "Data Source=$database"
		
		#Create the database if it doesn't exist.
		if (!(Test-Path $database)) {
			Write-Host "Creating database" -ForegroundColor Green
			# Create database
			[void][System.Data.SQLite.SQLiteConnection]::CreateFile($database); 
			$connection = New-Object System.Data.SQLite.SQLiteConnection($connString)
			$connection.Open()
			
			Write-Host "Creating table" -ForegroundColor Green
			# Create table, check if primary key is automatically unique
			$table = "CREATE TABLE [Files] ([Name] nvarchar(450) PRIMARY KEY)"
			$command = $connection.CreateCommand()
			$command.CommandText = $table
			$null = $command.ExecuteNonQuery()
			$command.Dispose()
			$connection.Close()
			$connection.Dispose()
		} else { Write-Warning "database exists. Skipping." }
		
		# Create scheduled task if user is on Windows 8+ or Windows 2012+. This scheduled task will run updatedb every 6 hours,
		# as an elevated SYSTEM account. By default, the script wil not index any home directories, other than the user who installed it.	
		Write-Host "Setting up Scheduled Task to run every 6 hours" -ForegroundColor Green
		if ([Environment]::OSVersion.Version -ge (new-object 'Version' 6,2)) {
			$null = New-LocateScheduledTask -locatepath $locatepath
		} else {
			$null = New-LocateScheduledTaskWin7 -locatepath $locatepath
		}
		if ($noprompt -ne $true) {
			Write-Warning "The database must be populated before it will return any results."
			$message = $null
			$question = "Would you like to run updatedb to populate the database now?"

			$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'))
			
			$decision = $Host.UI.PromptForChoice($message, $question, $choices, 0)
			if ($decision -eq 1) { 
				Write-Host "updatedb skipped. locate will return no results, or will be out of date." -ForegroundColor Red -BackgroundColor Black
			} else { Update-LocateDB -locatepath $locatepath -homepath $homepath, -userprofile $userprofile -includemappedrives $includemappedrives }
		} else { Update-LocateDB -locatepath $locatepath -homepath $homepath -userprofile $userprofile -includemappedrives $includemappedrives }
		
		# Finish up
		Write-Host "Installation to $locatepath complete." -ForegroundColor Green 
}

	Function New-LocateScheduledTask  {
		<#
		.SYNOPSIS
		 Creates a new scheduled task in Windows 8+ and Windows 2012+. This scheduled task is run as an elevated SYSTEM account, but will only search the home directory
		 of the user that installed locate. Supports multiple users. Administrator access required because it uses a system account.
		#>
		
		param(
			[Parameter(Mandatory = $false)]
            [string]$locatepath
		)
		

		If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
		{
			Write-Warning "This script does not support creating Scheduled Tasks for non-admin users. You will have to set one up yourself. Please see http://bit.ly/1zHldCU for more information."
			return	
		}

		# Get locate program directory
		if ($locatepath -eq $null) { $locatepath = "$env:LOCALAPPDATA\locate" }
		
		# Name the task, and check to see if it exists, if so, skip.
		$taskname = "updatedb cron job user $($env:USERNAME) (PowerShell Invoke-Locate)"
		$checktask = Get-ScheduledTask $taskname -TaskPath \ -ErrorAction SilentlyContinue
		if ($checktask -ne $null) { 
			Write-Warning "$taskname exists. Skipping."
			return
		} else {
			# Script to execute
			$updatefilename = "$locatepath\Update-LocateDB.ps1"
			# Repeat timespan. 
			$repeat = (New-TimeSpan -Hours 6)
			# Run indefinitely 
			$duration = ([timeSpan]::maxvalue)
			# Terminate task if it runs for longer than an hour
			$maxduration = (New-TimeSpan -Hours 1)
			# Set the action to have powershell.exe call a script.
			$action = New-ScheduledTaskAction -Argument "$updatefilename" -WorkingDirectory $locatepath -Execute "powershell.exe"
			# Set script to run every 6 hours, indefintely. 
			$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).Date -RepetitionInterval $repeat -RepetitionDuration $duration
			# Set some options.
			$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -DontStopOnIdleEnd -ExecutionTimeLimit $maxduration
			# Register the task, run as SYTEM (NT AUTHORITY\SYSTEM). This is a super user that will be able to access what it needs.
			
			$null = Register-ScheduledTask -Settings $settings -TaskName $taskname -Action $action -Trigger $trigger -RunLevel Highest -User "NT AUTHORITY\SYSTEM"	
		}
	}
	
	Function New-LocateScheduledTaskWin7  {
		<#
		.SYNOPSIS
		 Creates a new scheduled task in Windows 7 and below. This scheduled task is run as an elevated SYSTEM account, but will only search the home directory
		 of the user that installed locate. Supports multiple users. Administrator access required because it uses a system account.
		#>
		
		param(
			[Parameter(Mandatory = $false)]
            [string]$locatepath
		)
		
		# Check if admin. 
		If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
		{
			Write-Warning "This script does not support creating Scheduled Tasks for non-admin users. You will have to set one up yourself. Please see http://bit.ly/1zHldCU for more information."
			return	
		}
		
		# Get locate program directory
		if ($locatepath -eq $null) { $locatepath = "$env:LOCALAPPDATA\locate" }
		
		# Name the task, and check to see if it exists, if so, skip.
		$taskname = "updatedb cron job user $($env:USERNAME) (PowerShell Invoke-Locate)"
		
		# Script to execute
		$updatefilename = "$locatepath\Update-LocateDB.ps1"
		$taskscheduler = New-Object -ComObject Schedule.Service
		$taskscheduler.Connect()
		
		# Place Task in root
		$rootfolder = $taskscheduler.GetFolder("\")
		$definition = $taskscheduler.NewTask(0)
		
		# Get base info
		$registrationInformation = $definition.RegistrationInfo
		
		# Run as built in
		$principal = $definition.Principal
		$principal.LogonType = 5
		$principal.UserID = "NT AUTHORITY\SYSTEM"
		$principal.RunLevel = 1
		
		# Set options
		$settings = $definition.Settings
		$settings.StartWhenAvailable = $true
		$settings.RunOnlyIfNetworkAvailable = $false
		$settings.ExecutionTimeLimit =  "PT1H"
		$settings.RunOnlyIfIdle = $false
		$settings.AllowDemandStart = $true
		$settings.AllowHardTerminate = $true
		$settings.DisallowStartIfOnBatteries = $false
		$settings.Priority = 7
		$settings.StopIfGoingOnBatteries = $false
		$settings.idlesettings.StopOnIdleEnd = $false
		
		# Set script to run every 6 hours, indefintely. 
		$triggers = $definition.Triggers
		$trigger = $triggers.Create(2)
		$trigger.Repetition.Interval = "P0DT6H0M0S"
		$trigger.Repetition.StopAtDurationEnd = $false
		$trigger.StartBoundary = (Get-Date "00:00:00" -Format s)
		
		# Set the action to have powershell.exe call a script.
		$action = $definition.Actions.Create(0)
		$action.Path = "powershell.exe"
		$action.Arguments =  $updatefilename
		$action.WorkingDirectory =  $locatepath

		# 6 = update or delete, 0 is no password needed
		$rootfolder.RegisterTaskDefinition($taskname, $definition, 6, "NT AUTHORITY\SYSTEM",$null,0)
	}
	
	Function Update-LocateDB {
		<#
		.SYNOPSIS
		  Migrates logins from source to destination SQL Servers. Database & Server securables & permissions are preserved.
		
		.EXAMPLE
		 Copy-SQLLogins -Source $sourceserver -Destination $destserver -Force $true
		
		 Copies logins from source server to destination server.
		 
		.OUTPUTS
		   A CSV log and visual output of added or skipped logins.
		#>
		
		param(
            [string]$locatepath,
			[string]$homepath,
			[string]$userprofile,
			[bool]$includemappedrives
		) 

		Write-Host "Updating locate database. This should only take a few minutes." -ForegroundColor Green
		
		# Set varables and load up assembly
		if ($locatepath.length -eq 0) {$locatepath = "$env:LOCALAPPDATA\locate" }
		if ($homepath.length -eq 0) { $homepath = "$env:HOMEDRIVE$env:HOMEPATH" }
		if ($userprofile.length -eq 0) { $userprofile = $env:USERPROFILE } 
		if ([Reflection.Assembly]::LoadWithPartialName("System.Data.SQLite") -eq $null) { [void][Reflection.Assembly]::LoadFile("$locatepath\System.Data.SQLite.dll") }
		$elapsed = [System.Diagnostics.Stopwatch]::StartNew() 
		
		$database = "$locatepath\locate.sqlite"
		$connString = "Data Source=$database"
		$connection = New-Object System.Data.SQLite.SQLiteConnection($connString)
		$connection.Open()
		$command = $connection.CreateCommand()
		
		# SQLite doesn't support truncate, let's just drop the table and add it back.
		$command.CommandText = "DROP TABLE [Files]"
		[void]$command.ExecuteNonQuery()
		$command.CommandText = "CREATE TABLE [Files] ([Name] nvarchar(450) PRIMARY KEY)"
		[void]$command.ExecuteNonQuery()
		
		Write-Host "Updating database" -ForegroundColor Green
		
		# Use a single transaction to speed up insert.
		$transaction = $connection.BeginTransaction()
		
		# Get local drives. Like GNU locate, this includes your local DVD-CDROM, etc drives.
		$disks = Get-WmiObject Win32_Volume -Filter "Label!='System Reserved'"
		
		foreach ($disk in $disks.name) {
			Get-Filenames -path $disk -locatepath $locatepath
		}
		
		# Since C:\Users is ignored by default in the above routine, $homepath and $userprofile must be explicitly indexed.
		Get-Filenames -path $homepath -locatepath $locatepath
		if ($homepath -ne $userprofile) { Get-Filenames -path $userprofile -locatepath $locatepath }
		
		# When locate was installed, the user was prompted to answer whether they wanted to index their mapped drives.
		If ($includemappedrives -eq $true) {
			$disks = Get-WmiObject Win32_MappedLogicalDisk
			foreach ($disk in $disks.name) {
				Get-Filenames -path $disk -locatepath $locatepath
			}
		}
		
		# Commit the transaction
		$transaction.Commit()
		
		# Count the number of files indexed and report
		$totaltime = [math]::Round($elapsed.Elapsed.TotalMinutes,2)
		$totaltime = (($elapsed.Elapsed.ToString()).Split("."))[0]
		$command.CommandText = "SELECT COUNT(*) FROM [Files]"
		$rowcount = $command.ExecuteScalar()
		
		Write-Host "$rowcount files on $($disks.count) drives have been indexed in $totaltime." -ForegroundColor Green
		$command.Dispose()
		$connection.Close()
		$connection.Dispose()
	}

	Function Get-Filenames {
		<#
		.SYNOPSIS
		 This function is called recursively to get filenames and insert them into the database. Skips 
		 $env:APPDATA), $env:LOCALAPPDATA, $env:TMP, $env:TEMP.
		 
		 The system drive's Users directory is also excluded, but then the locate user's homepath and userprofile
		 are explicitly included.
		 
		#>
		
		param(
			[string]$path,
            [string]$locatepath,
			[string]$homepath,
			[string]$userprofile
		) 
		
		# Set variables and load SQLite assembly
		if ($locatepath -eq $null) { $locatepath = "$env:LOCALAPPDATA\locate" }
		if ([Reflection.Assembly]::LoadWithPartialName("System.Data.SQLite") -eq $null) { [void][Reflection.Assembly]::LoadFile("$locatepath\System.Data.SQLite.dll") }
		
		# IO.Directory throws a lot of access denied exceptions, ignore them.
		Set-Variable -ErrorAction SilentlyContinue -Name files
		Set-Variable -ErrorAction SilentlyContinue -Name folders
		
		# Get the directories, and make a list of the files within them
		try
		{
		   $files = [IO.Directory]::GetFiles($path)
		   [System.IO.DirectoryInfo]$directoryInfo = New-Object IO.DirectoryInfo($path)
		   $folders = $directoryInfo.GetDirectories() | Where-Object {$_.Name -ne "`$Recycle.Bin" -and $folder -ne "System Volume Information" }
			  
		} catch { $folders = @()}
		
		# For each file, clean up the SQL syntax and insert into database.
		foreach($filename in $files) 
			{
				$filename = $filename.replace('\\','\')
				$filename = $filename.replace("'","''")
				$command.CommandText = "insert into files values ('$filename')"
				[void]$command.ExecuteNonQuery()
			}

		# Some things just don't need to be indexed (though you're free to remove any if you'd like.
		$exclude = @($env:APPDATA)
		$exclude += $env:LOCALAPPDATA
		$exclude += $env:TMP
		$exclude += $env:TEMP
		
		# Lil bit of lightweight security
		$exclude += "$env:systemdrive\Users"
		$include = @($homepath)
		$include += $userprofile
	
		# Process folders and subfolders
		foreach($folder in $folders)
		{ 
			if ($exclude -notcontains "$path$folder") { 
				Get-Filenames -path "$path\$folder" -locatepath $locatepath
				Write-Verbose "Indexing $path\$folder"
			}
		}
		
		# Remove the erroraction variable
		Remove-Variable -ErrorAction SilentlyContinue -Name files
		Remove-Variable -ErrorAction SilentlyContinue -Name folders 
	}

	Function Search-Filenames  {
		<#
		.SYNOPSIS
		 Performs a LIKE query on the SQLite database. 
		 
		 .OUTPUT
		 System.Data.Datatable
		
		#>
		
		param(
			[string]$filename,
            [string]$locatepath,
			[bool]$s
		) 
		
		# Get variables, load assembly
		if ($locatepath -eq $null) { $locatepath = "$env:LOCALAPPDATA\locate" }
		if ([Reflection.Assembly]::LoadWithPartialName("System.Data.SQLite") -eq $null) { [void][Reflection.Assembly]::LoadFile("$locatepath\System.Data.SQLite.dll") }
		
		# Setup connect
		$database = "$locatepath\locate.sqlite"
		$connString = "Data Source=$database"
		try { $connection = New-Object System.Data.SQLite.SQLiteConnection($connString) }
		catch { throw "Can't load System.Data.SQLite.SQLite. Architecture mismatch or access denied. Quitting." }
		$connection.Open()
		$command = $connection.CreateCommand()
		
		# Allow users to use * as wildcards.
		$filename = $filename.Replace("`*","`%")
		if ($s -eq $false) {
			$sql = "PRAGMA case_sensitive_like = 0;select name from files where name like '%$filename%'"
		} else { $sql = "PRAGMA case_sensitive_like = 1;select name from files where name like '%$filename%'" }
		Write-Verbose "SQL string executed: $sql"
		$command.CommandText = $sql
		
		# Create datatable and fill it with results
		$datatable = New-Object System.Data.DataTable
		$datatable.load($command.ExecuteReader())
		$command.Dispose()
		$connection.Close()
		$connection.Dispose()
		
		# return the datatable (which just includes one column, 'name'
		return $datatable
		
	}
}

PROCESS {
	# Set locate's program directory
	if ($locatepath.length -eq 0) { $locatepath = "$env:LOCALAPPDATA\locate" }
	if ($install -eq $true){ Install-Locate -noprompt $false -locatepath $locatepath; return }
	
	# Check to see if the SQLite database exists, if it doesn's, prompt the user to install locate and populate the database.
	$locatedb = "$locatepath\locate.sqlite"

	if (!(Test-Path $locatedb)) {
		Write-Warning "locate database not found"
		$question = "Would you like to run the installer and populate the database now?"
		$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'))
		
		$decision = $Host.UI.PromptForChoice($message, $question, $choices, 0)
		if ($decision -eq 1) { 
			Write-Host "Install skipped and no database to query. Quitting." -ForegroundColor Red -BackgroundColor Black
			break
		} else { Install-Locate -noprompt $true -locatepath $locatepath }
	}
	
	# If updatedb is called
	if ($updatedb -eq $true) { Update-LocateDB -locatepath $locatepath -homepath $homepath, -userprofile $userprofile -includemappedrives $includemappedrives; return }
	
	# If no arguments are passed, error out.
	if ($filename.length -eq 0) { throw "You need to pass an argument." }
	
	# Perform a search, and specify the case sensitivty. Output match strings.
	$dt = (Search-Filenames  $filename -locatepath $locatepath -s $s)
	$dt.name
}

END {
	# Clean up connections, if needed
	if ($command.connection -ne $null) { $command.Dispose() }
	if ($connection.state -ne $null) { $connection.Close(); $connection.Dispose() }
}