<?xml version="1.0" encoding="ISO-8859-1" ?>
<package><component id="main">
<?component error="true" debug="true"?>
<registration progid="WshArgument.Extensions" classid="{790E104C-7A48-499F-8EDA-
6D5CD0A56D93}" description="A WSF argument extension component, focusing on validation." 
remotable="no" version="1.0">
<script language="VBScript">
<![CDATA[
	Function Register()
		With CreateObject("Scriptlet.TypeLib")
			.MajorVersion = 1: .MinorVersion = 0
			.AddURL "wshargex.wsc"
			.Path = "wshargex.tlb": .Name = .Path
			.Doc = "A WSF argument extension component, focusing on validation."
			.GUID = "{C920CF60-675F-4D7E-87E7-D9076F58BB20}"
			.Write: .Reset
		End With
	End Function

	Function Unregister(): End Function
	]]></script>
</registration>
<public>
	<property get name="IsValid" description="Whether or not the last invocation of ValidateArguments() 
returned a valid parse."/>
	<property put name="Wsh" description="Object reference to the script host to pass in to the 
component."/>
	<property get put name="TraceExecution" description=""/>
	<property name="JobId" description=""/>
	<property get put name="RequireCScript" description=""/>
	<property get put name="AllowLooseTyping" description=""/>
	<property get put name="ContinueOnFailure" description=""/>
	<property get put name="ExitErrorLevel" description=""/>
	<property get name="wshArgTypeSimple" description=""/>
	<property get name="wshArgTypeBoolean" description=""/>
	<property get name="wshArgTypeString" description=""/>
	<method name="Simple" description="" internalname="get_Simple">
		<parameter name="argName"/>
	</method>
	<method name="String" description="" internalname="get_String">
		<parameter name="argName"/>
		<parameter name="defaultValue"/>
	</method>
	<method name="Boolean" description="" internalname="get_Boolean">
		<parameter name="argName"/>
	</method>
	<method name="ValidateArguments" description=""/>
</public>
<object id="fso" progid="Scripting.FileSystemObject"/>
<object id="xml" progid="MSXML2.DOMDocument"/>
<script language="VBScript"><![CDATA[
Option Explicit
'
' Initialization of global/public variables
'
Const wshArgTypeSimple = 0
Const wshArgTypeBoolean = 11
Const wshArgTypeString = 8
Dim AllowLooseTyping, ContinueOnFailure, ExitErrorLevel
Dim IsValid, JobId, RequireCScript, TraceExecution, Wsh

Initialize()

Sub Initialize()
	xml.async = False
	AllowLooseTyping = False
	ContinueOnFailure = False
	ExitErrorLevel = 2
	IsValid = vbNull
	JobId = vbNullString
	RequireCScript = False
	Set Wsh = Nothing
	TraceExecution = 0
End Sub

function put_Wsh(value)
	Set Wsh = value
end function

function get_TraceExecution()
	get_TraceExecution = TraceExecution
end function
function put_TraceExecution(value)
	TraceExecution = CBool(value)
end function

function get_RequireCScript()
	get_RequireCScript = RequireCScript
end function
function put_RequireCScript(value)
	RequireCScript = CBool(value)
end function

function get_AllowLooseTyping()
	get_AllowLooseTyping = AllowLooseTyping
end function
function put_AllowLooseTyping(value)
	AllowLooseTyping = CBool(value)
end function

function get_ContinueOnFailure()
	get_ContinueOnFailure = ContinueOnFailure
end function
function put_ContinueOnFailure(value)
	ContinueOnFailure = CBool(value)
end function

function get_ExitErrorLevel()
	get_ExitErrorLevel = ExitErrorLevel
end function
function put_ExitErrorLevel(value)
	ExitErrorLevel = CLng(value)
end Function

function get_IsValid()
	get_IsValid = IsValid
end function

function ValidateArguments()
	Trace "Entering ValidateArguments()"
	' takes a ref to WScript as an argument.
	' Proceeds to do full parse and assembly of argument data.
	' To make checking IsValid unnecessary if we are NOT using
	' the default exit on failure, this function returns True if the
	' validation succeeded, false otherwise.
	ValidateArguments = False
	' We try to die on failures; this ensures that if errors and
	' interaction are suppressed, we don't try to validate anyway.
	' The fact that this is a function instead of a sub ensures that
	' the user can actually TEST the return value without even looking
	' at ValidationFailed as described above.
	Trace "Checking Whether host is valid."
	If Not ValidScriptHost(Wsh) Then Exit Function
	Trace "Host is valid."
	Trace "Loading script from " & Wsh.ScriptFullName
	If Not xml.Load(Wsh.ScriptFullName) Then Exit Function
	Trace "Loaded script successfully"
	Dim root
	if JobId = vbNullString Then
		Trace "No job specified, checking all named/unnamed elements."
		set root = xml
	Else
		Trace "job specified: " & JobId
		set root = xml.selectSingleNode("//job[@id='" & JobId & "']")
	end if
	Dim node
	Trace "starting named node validation."
	For Each node in root.selectNodes("//named")
		validateNamed node
	Next
	Trace "starting unnamed node validation."
	validateUnNamed root.selectNodes("//unnamed")
	ValidateArguments = IsValid
	Trace "Exiting ValidateArguments() at end"
end function


	private sub validateNamed(node)
		' Look for required named arguments find or Die.
		' takes collection of "named" nodes as an argument
		dim name, value, nameType
		name = NodeAttribValue(node, "name")
		Trace "checking named argument node with name " & name
		If Wsh.Arguments.Named.Exists(name) Then
			Trace "This argument was found on the command line as well"
			If Not AllowLooseTyping Then
				Trace "We require strict argument types."
				' We need to validate argument types.
				value = Wsh.Arguments.Named(name)
				Trace "The command-line argument's vartype and value: " _
					& VarType(value) & " " & value
				nameType = tovbVarType( NodeAttribValue(node, "type") )
				Trace "The argument spec is for an argument of type: " _
					& nameType
				If Not VarType(value) = nameType Then Die()
			End If
		ElseIf CBool( NodeAttribValue(node, "required") ) <> 0 then
			Trace "This is a required argument, but it wasn't supplied."
			' if required="true", confirm user supplied it or Die.
			Die()
		' Argument "Typechecking"
		end if
	end sub


	private sub ValidateUnNamed(unnamedNodes)
		dim MaxIsMin, required, node
		required = 0: MaxIsMin = true
		' count required arguments, determine whether this is open number
		for each node in unnamedNodes
			' see if argument count is unbounded
			if toLong(NodeAttribValue(node, "many")) = 1 then MaxIsMin = false
			' add the count of required arguments from the unnamed
			required = required + toLong(NodeAttribValue( node, "required"))
		next
		Trace "We require " & required & " unnamed arguments."
		Trace "Is the maximum unnamed count the same as minimum? " _
			& CStr(MaxIsMin)
		'now we test to see if we have enough
		dim count: count = Wsh.Arguments.UnNamed.Count
		If count < required then
			Die()
		ElseIf count = required Then
			' we're fine - required = count
		ElseIf MaxisMin Then
			' We have more than the count and shouldn't. Die!
			Die()
		End If
	end sub


	private function NodeAttribValue(node, attributeName)
		dim Attribute
		set Attribute = node.attributes.getNamedItem(attributeName)
		if typename(Attribute) = "Nothing" then
			NodeAttribValue = Empty
		else
			NodeAttribValue = Attribute.nodeValue
		end if
	end function


	private function tovbVarType(typeString)
		' Casts a named argument type attribute value to a vb VarType value.
		' If the type value is not recognized, this returns null.
		select case lcase(typeString)
			case "boolean" tovbVarType = vbBoolean
			case "string" tovbVarType = vbString
			case "simple" tovbVarType = vbEmpty
			case else tovbVarType = vbNull
		end select
	end function


	private function toLong(value)
		' takes any normal numeric or boolean string and coerces to long;
		' treats true as +1
		if StrComp(value, "false", vbTextCompare) = 0 then
			toLong = 0
		elseif StrComp(value, "true", vbTextCompare) = 0 then
			toLong = 1
		else
			toLong = CLng( value )
		end if
	end function


	private function ValidScriptHost(host)
		' This routine first confirms that the hosting class actually IS
		' the WSH host - a necessity for using the WScript.Arguments
		' collection at this point, and for throwing a usable error in
		' another host.  Strictly speaking, this is an ugly hack though.
		' Someone could create a class with the name "IHost_Class", and
		' a REALLY cool argument parser would take parsed arguments from
		' anywhere. All of the validation checks here other than testing
		' whether CScript is required as a host are designed to guard
		' against accidental misuse by a scripter, not an end-user.
		' I don't want to do the work that being cool would require, though.
		If RequireCScript And Not HostIsCscript() Then
				ValidScriptHost = False
				' We're going to abort processing anyway, but let's try to
				' Die() first. Trying to Die() will call the help, and then
				' the user can see the note that the scripter (hopefully) put
				' into the help text about needing cscript for some reason.
				Die()
		ElseIf Not (TypeName(host) = "IHost_Class") Then
			' MsgBox. Ugly again...this is the only way to get the point
			' across if an erstwhile user has his or her error display suppressed
			' in Internet Explorer.
			MsgBox "This class MUST be used from WSH. Currently hosted by:" _
				& vbCrLf & TypeName(host)
			ValidScriptHost = False
		ElseIf host.Version < 5.6 Then
			' We aren't going to be able to work on a lesser version of WSH.
			' Just to be courteous, we will use WScript.Echo in case this is
			' a console session...
			Wsh.Echo "This code must be used within a WSF file hosted", _
				"in WSH 5.6 or higher with VBScript 5 or higher installed."
			Wsh.Echo "The correct version of WSH can be downloaded", _
			"from http://msdn.microsoft.com/scripting."
			Die()
		Else
			' We don't require cscript and we are indeed in WSH, so
			' we're valid!
			ValidScriptHost = True
		End If
	end function


	private sub Die()
		Trace "Entering Die()."
		Trace "Value of ContinueOnFailure is: " & CStr(ContinueOnFailure)
		IsValid = False
		if Not ContinueOnFailure then
			Trace "We are exiting on failures; you should see help text."
			Wsh.Arguments.ShowUsage()
			Wsh.Quit(ExitErrorLevel)
		End If
	end sub

	private sub Trace(s)
		If TraceExecution Then fso.GetStandardStream(2).WriteLine s
	end sub

	public function HostIsCscript()
		HostIsCscript = _
			LCase(Right(Wsh.FullName, 11)) = "cscript.exe"
		Trace "Is the host is cscript? " & HostIsCscript
	end function

function get_Simple(ByVal argName)
	' Returns true/false for whether a simple argument ArgName was specified.
	get_Simple = Wsh.Arguments.Named.Exists(argName)
end function

function get_Boolean(ByVal argName)
	' Get an optional boolean named argument if it exists;
	' if it does not exist, return the default value.
	If Wsh.Arguments.Named.Exists(argName) Then
		get_Boolean = CBool(argName)
	Else
		get_Boolean = CBool(defaultValue)
	End If
end function

function get_String(ByVal argName, ByVal defaultValue)
	' If a string named argument was specified on the script's
	' command line, return it. If it wasn't, return a default value.
	If Wsh.Arguments.Named.Exists(argName) Then
		get_String = WScript.Arguments.Named(argName)
	Else
		get_String = defaultValue
	End If
end function

function get_wshArgTypeSimple
	get_wshArgTypeSimple = wshArgTypeSimple
end function
function get_wshArgTypeBoolean
	get_wshArgTypeBoolean = wshArgTypeBoolean
end function
function get_wshArgTypeString
	get_wshArgTypeString = wshArgTypeString
end function

]]></script>
</component>
</package>