Controlling Empty Arrays in Classic ASP - asp-classic

OK I'm a complete newbie to ASP.
I have a client with different content loading depending on what is passed in an array.
select case lcase(arURL(4))
Sometimes though, arURL(4) might be empty, in them cases I'm getting the following error:
Error running function functionName(), the error was:
Subscript out of range
Does anybody know a way to fix this?
Thanks
OK further code as requested. It is horrible code and I don't mean to cause anybody a headache, so please excuse it. Thanks again ........
function GetContent()
dim strURL, arURL, strRetval
select case lcase(request.ServerVariables("URL"))
case "/content.asp"
strURL = ""
arURL = split(request.querystring("url"), "/")
if request("page") = "" then
select case lcase(arURL(2))
case "searches"
select case lcase(arURL(1))
case "looking"
select case lcase(arURL(3))
case "ohai"
strRetval = "Lorem"
case "blahblah"
strRetval = "Lorem Ipsum"
case "edinburgh"
select case lcase(arURL(4))
case "ohai"
strRetval = "Ipsum"
case "ohno"
strRetval = "Lorem"
end select
case "bristol"
select case lcase(arURL(4))
case "some_blahblah"
strRetval = "LOREM"
case "overthere"
strRetval = "LOREM"
case "blahblah"
strRetval = "LOREM"
end select
case "cambridge"
select case lcase(arURL(4))
case "some_rubbish"
strRetval = "Lorem"
end select
case else
strRetval = " "
end select
case else
strRetval = " "
end select
case else
strRetval = " "
end select
end if
end select
strRetval = strRetval & "<style>h2{border: 0px);</style>"
GetContent = strRetval
end function

You are using value passed over the querystring and split it by "/" character - when the value does not contain "enough" slashes, you will get error and the code will crash.
For example, if the querystring parameter url will be only "/something" then even arURL(2) will fail since the array has only two items. (First one is empty string, second is "something")
To avoid all this mess, best way I can advice is writing custom function that will take array and index as its arguments and return either the item in the given index if exists otherwise empty string:
Function GetItemSafe(myArray, desiredIndex, defValue)
If (desiredIndex < LBound(myArray)) Or (desiredIndex > UBound(myArray)) Then
If IsObject(defValue) Then
Set GetItemSafe = defValue
Else
GetItemSafe = defValue
End If
Else
If IsObject(myArray(desiredIndex)) Then
Set GetItemSafe = myArray(desiredIndex)
Else
GetItemSafe = myArray(desiredIndex)
End If
End If
End Function
(ended up with more generic version, letting the calling code decide what is the default value in case index is out of array range)
Having this, change your code to use the function instead of accessing the array directly.
This line for example:
select case lcase(arURL(2))
Should become this instead:
select case lcase(GetItemSafe(arURL, 2, ""))
Change the rest of those lines accordingly and you'll no longer get errors when the given value won't be valid.

What that error is saying at the most basic level is that you're trying to get information from an array element that doesn't exist, eg arURL may have been declared for 3 elements, but accessing the 4th generates the "subscript out of range" error.
If you're keying on the last element in the array, you might look at the UBound() function, which returns the high index element in an array, eg:
select case lcase(arURL(ubound(arURL))
However, there might be something else going on in the code that would change how you determine which element should be used as the target of the "select case," hence the suggestion to post more of the code.

Related

Is possible to add a String.contains more than one value?

I want to make a control, when I create a companyId, to not permit to create id with special characters like, (&), (/), (), (ñ), ('):
If txtIdCompany.Text.Contains("&") Then
// alert error message
End If
But I can't do this:
If txtIdCompany.Text.Contains("&", "/", "\") Then
// alert error message
End If
How can I check more than one string in the same line?
You can use collections like a Char() and Enumerable.Contains. Since String implements IEnumerable(Of Char) even this concise and efficient LINQ query works:
Dim disallowed = "&/\"
If disallowed.Intersect(txtIdCompany.Text).Any() Then
' alert error message
End If
here's a similar approach using Enumerable.Contains:
If txtIdCompany.Text.Any(AddressOf disallowed.Contains) Then
' alert error message
End If
a third option using String.IndexOfAny:
If txtIdCompany.Text.IndexOfAny(disallowed.ToCharArray()) >= 0 Then
' alert error message
End If
If txtIdCompany.Text.Contains("&") Or txtIdCompany.Text.Contains("\") Or txtIdCompany.Text.Contains("/") Then
// alert error message
End If

calling list item where var = value

is there a way of calling a specific list of lists without having to check (or loop) them all?
it is easier to understand with an example....
lets say
callList(5).key = "1234"
callList(5).callOpened = "11/26/13"
now i want to do something like
textbox_callOpened.text = callList(where key = "1234").callOpened
i also need to know what index that was at for there are many more items that i need to output too.
You can use LINQ. Add Imports System.Linq at top of the file and use First method with lambda expression as a predicate to get what you need:
' that gives you item matching your predicate '
Dim item = callList.First(Function(x) x.Key = "1234")
' you can use it to set the property '
textbox_callOpened.text = item.callOpened
pseudo code, didn't test but should work
dim something = callList.firstordefault(function(d) d.key = "1234")
if something is not nothing then
textbox_callOpened.text = something.callOpened
else
'cant find an element with key 1234
end if

converting code into classic asp

I want to convert the below string into classic asp code can any one help
email has some value but it is not going inside the Loop
Can any one help me.
If (IsEmpty(email) And IsNull(email)) Then
EndIf
The code looks like its VBScript already so there is no "conversion" needed, however the code is faulty. Should be:
If IsEmpty(email) Or IsNull(email) Then
End If
a variable cannot both be empty and contain a Null at the same time hence the orginal conditional expression was always false.
You could always try:
If IsEmpty(email) = True Then
'uninitialized
ElseIf IsNull(email) = True Then
'contains null value
ElseIf email = ""
'contains zero length string
Else
'Response.Write email
'MsgBox email
End If
In most cases I try to code so that the variable is guaranteed to be initialized so you don't need to run the IsEmpty check.
Option Explicit
Dim email
email = ""
Why don't you just check the length of the email variable:
If Len(Trim(email)) > 0 Then
Else
YOUR CODE HERE
End If

Why is this looping infinitely?

So I just got my site kicked off the server today and I think this function is the culprit. Can anyone tell me what the problem is? I can't seem to figure it out:
Public Function CleanText(ByVal str As String) As String
'removes HTML tags and other characters that title tags and descriptions don't like
If Not String.IsNullOrEmpty(str) Then
'mini db of extended tags to get rid of
Dim indexChars() As String = {"<a", "<img", "<input type=""hidden"" name=""tax""", "<input type=""hidden"" name=""handling""", "<span", "<p", "<ul", "<div", "<embed", "<object", "<param"}
For i As Integer = 0 To indexChars.GetUpperBound(0) 'loop through indexchars array
Dim indexOfInput As Integer = 0
Do 'get rid of links
indexOfInput = str.IndexOf(indexChars(i)) 'find instance of indexChar
If indexOfInput <> -1 Then
Dim indexNextLeftBracket As Integer = str.IndexOf("<", indexOfInput) + 1
Dim indexRightBracket As Integer = str.IndexOf(">", indexOfInput) + 1
'check to make sure a right bracket hasn't been left off a tag
If indexNextLeftBracket > indexRightBracket Then 'normal case
str = str.Remove(indexOfInput, indexRightBracket - indexOfInput)
Else
'add the right bracket right before the next left bracket, just remove everything
'in the bad tag
str = str.Insert(indexNextLeftBracket - 1, ">")
indexRightBracket = str.IndexOf(">", indexOfInput) + 1
str = str.Remove(indexOfInput, indexRightBracket - indexOfInput)
End If
End If
Loop Until indexOfInput = -1
Next
End If
Return str
End Function
Wouldn't something like this be simpler? (OK, I know it's not identical to posted code):
public string StripHTMLTags(string text)
{
return Regex.Replace(text, #"<(.|\n)*?>", string.Empty);
}
(Conversion to VB.NET should be trivial!)
Note: if you are running this often, there are two performance improvements you can make to the Regex.
One is to use a pre-compiled expression which requires re-writing slightly.
The second is to use a non-capturing form of the regular expression; .NET regular expressions implement the (?:) syntax, which allows for grouping to be done without incurring the performance penalty of captured text being remembered as a backreference. Using this syntax, the above regular expression could be changed to:
#"<(?:.|\n)*?>"
This line is also wrong:
Dim indexNextLeftBracket As Integer = str.IndexOf("<", indexOfInput) + 1
It's guaranteed to always set indexNextLeftBracket equal to indexOfInput, because at this point the character at the position referred to by indexOfInput is already always a '<'. Do this instead:
Dim indexNextLeftBracket As Integer = str.IndexOf("<", indexOfInput+1) + 1
And also add a clause to the if statement to make sure your string is long enough for that expression.
Finally, as others have said this code will be a beast to maintain, if you can get it working at all. Best to look for another solution, like a regex or even just replacing all '<' with <.
In addition to other good answers, you might read up a little on loop invariants a little bit. The pulling out and putting back stuff to the string you check to terminate your loop should set off all manner of alarm bells. :)
Just a guess, but is this like the culprit?
indexOfInput = str.IndexOf(indexChars(i)) 'find instance of indexChar
Per the Microsoft docs, Return Value -
The index position of value if that string is found, or -1 if it is not. If value is Empty, the return value is 0.
So perhaps indexOfInput is being set to 0?
What happens if your code tries to clean the string <a?
As I read it, it finds the indexChar at position 0, but then indexNextLeftBracket and indexRightBracket both equal 0, you fall into the else condition, and then you insert a ">" at position -1, which will presumably insert at the beginning, giving you the string ><a. The new indexRightBracket then becomes 0, so you delete from position 0 for 0 characters, leaving you with ><a. Then the code finds the <a in the code again, and you're off to the races with an infinite memory-consuming loop.
Even if I'm wrong, you need to get yourself some unit tests to reassure yourself that these edge cases work properly. That should also help you find the actual looping code if I'm off-base.
Generally speaking though, even if you fix this particular bug, it's never going to be very robust. Parsing HTML is hard, and HTML blacklists are always going to have holes. For instance, if I really want to get a <input type="hidden" name="tax" tag in, I'll just write it as <input name="tax" type="hidden" and your code will ignore it. Your better bet is to get an actual HTML parser involved, and to only allow the (very small) subset of tags that you actually want. Or even better, use some other form of markup, and strip all HTML tags (again using a real HTML parser of some description).
I'd have to run it through a real compiler but the mindpiler tells me that the str = str.Remove(indexOfInput, indexRightBracket - indexOfInput) line is re-generating an invalid tag such that when you loop through again it finds the same mistake "fixes" it, tries again, finds the mistake "fixes" it, etc.
FWIW heres a snippet of code that removes unwanted HTML tags from a string (It's in C# but the concept translates)
public static string RemoveTags( string html, params string[] allowList )
{
if( html == null ) return null;
Regex regex = new Regex( #"(?<Tag><(?<TagName>[a-z/]+)\S*?[^<]*?>)",
RegexOptions.Compiled |
RegexOptions.IgnoreCase |
RegexOptions.Multiline );
return regex.Replace(
html,
new MatchEvaluator(
new TagMatchEvaluator( allowList ).Replace ) );
}
MatchEvaluator class
private class TagMatchEvaluator
{
private readonly ArrayList _allowed = null;
public TagMatchEvaluator( string[] allowList )
{
_allowed = new ArrayList( allowList );
}
public string Replace( Match match )
{
if( _allowed.Contains( match.Groups[ "TagName" ].Value ) )
return match.Value;
return "";
}
}
That doesn't seem to work for a simplistic <a<a<a case, or even <a>Test</a>. Did you test this at all?
Personally, I hate string parsing like this - so I'm not going to even try figuring out where your error is. It'd require a debugger, and more headache than I'm willing to put in.

code critique - am I creating a Rube Goldberg machine?

I'm making a fair amount of calls to database tables via ADO.
In the spirit of keeping things DRY, I wrote the following functions to return an array of values from a recordset.
Is this hare brained?
I use it mainly for grabbing a set of combo-box values and the like, never for enormous values. Example Usage (error handling removed for brevity):
Function getEmployeeList()
getEmployeeList= Array()
strSQL = "SELECT emp_id, emp_name from employees"
getEmployeeList = getSQLArray( strSQL, "|" )
End Function
Then I just do whatever I want with the returned array.
Function getSQLArray( SQL, delimiter )
'*************************************************************************************
' Input a SQL statement and an optional delimiter, and this function
' will return an array of strings delimited by whatever (pipe defaults)
' You can perform a Split to extract the appropriate values.
' Additionally, this function will return error messages as well; check for
' a return of error & delimiter & errNum & delimiter & errDescription
'*************************************************************************************
getSQLArray = Array()
Err.Number = 0
Set objCon = Server.CreateObject("ADODB.Connection")
objCon.Open oracleDSN
Set objRS = objCon.Execute(SQL)
if objRS.BOF = false and objRS.EOF = false then
Do While Not objRS.EOF
for fieldIndex=0 to (objRS.Fields.Count - 1)
If ( fieldIndex <> 0 ) Then
fieldValue = testEmpty(objRS.Fields.Item(fieldIndex))
recordString = recordString & delimiter & fieldValue
Else
recordString = CStr(objRS.Fields.Item(fieldIndex))
End If
Next
Call myPush( recordString, getSQLArray )
objRS.MoveNext
Loop
End If
Set objRS = Nothing
objCon.Close
Set objCon = Nothing
End Function
Sub myPush(newElement, inputArray)
Dim i
i = UBound(inputArray) + 1
ReDim Preserve inputArray(i)
inputArray(i) = newElement
End Sub
Function testEmpty( inputValue )
If (trim( inputValue ) = "") OR (IsNull( inputValue )) Then
testEmpty = ""
Else
testEmpty = inputValue
End If
End Function
The questions I'd have are:
Does it make sense to abstract all the recordset object creation/opening/error handling into its own function call like this?
Am I building a Rube Goldberg machine, where anyone maintaining this code will curse my name?
Should I just suck it up and write some macros to spit out the ADO connection code, rather than try doing it in a function?
I'm very new to asp so I have holes in its capabilities/best practices, so any input would be appreciated.
There's nothing wrong with doing it your way. The ADO libraries were not really all that well designed, and using them directly takes too many lines of code, so I always have a few utility functions that make it easier to do common stuff. For example, it's very useful to make yourself an "ExecuteScalar" function that runs SQL that happens to return exactly one value, for all those SELECT COUNT(*)'s that you might do.
BUT - your myPush function is extremely inefficient. ReDim Preserve takes a LONG time because it has to reallocate memory and copy everything. This results in O(n2) performance, or what I call a Shlemiel the Painter algorithm. The recommended best practice would be to start by dimming, say, an array with room for 16 values, and double it in size whenever you fill it up. That way you won't have to call ReDim Preserve more than Lg2n times.
I wonder why you are not using GetRows? It returns an array, you will find more details here: http://www.w3schools.com/ado/met_rs_getrows.asp
A few notes on GetRows:
Set objRS = Server.CreateObject ("ADODB.Recordset")
objRS.Open cmd, , adOpenForwardOnly, adLockReadOnly
If Not objRS.EOF Then
astrEmployees = objRS.GetRows()
intRecFirst = LBound(astrEmployees, 2)
intRecLast = UBound(astrEmployees, 2)
FirstField = 0
SecondField = 1
End If
'2nd field of the fourth row (record) '
Response.Write (SecondField, 3)
Yes, it makes sense to factor out common tasks. I don't see anything wrong with the general idea. I'm wondering why you're returning an array of strings separated by a delimiter; you might as well return an array of arrays.

Resources