<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://script.quakenet.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Aca20031</id>
	<title>Scriptwiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://script.quakenet.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Aca20031"/>
	<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/Special:Contributions/Aca20031"/>
	<updated>2026-06-04T00:33:53Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.0</generator>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=COM_Tutorial&amp;diff=6167</id>
		<title>COM Tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=COM_Tutorial&amp;diff=6167"/>
		<updated>2015-07-19T23:18:16Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Adding Stack example, Powershell information (Except i cant upload the pics yet)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;mIRC supports interaction with COM through dynamic binding.  COM in mIRC is an advanced topic and usually requires knowledge in strongly typed language. Knowledge of C++ is a big plus.&lt;br /&gt;
&lt;br /&gt;
For this tutorial to make sense, you should at a minimum know&lt;br /&gt;
* How to use [[Identifiers]] in mIRC&lt;br /&gt;
* About identifier properties (e.g. $identifier().prop)&lt;br /&gt;
* That the [[noop]] command will execute but not print or take an action&lt;br /&gt;
* What objects are in object oriented programming&lt;br /&gt;
* Basics of strongly typed languages (e.g. int vs short vs string)&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t sure you know each of these things, turn around and save yourself the time.&lt;br /&gt;
&lt;br /&gt;
==What is COM?==&lt;br /&gt;
COM stands for [https://msdn.microsoft.com/en-us/library/windows/desktop/ms680573(v=vs.85).aspx Component Object Model] and the details of this are quite complicated. We will be skipping over too many specifics, but MSDN has more.&lt;br /&gt;
&lt;br /&gt;
To speak generally, COM is a Windows technology for creating and accessing [https://en.wikipedia.org/wiki/Object_(computer_science) Objects] no matter what language. For example, you could create an object with methods and properties in C++ and use it in mIRC. &lt;br /&gt;
&lt;br /&gt;
==Specific Object-Oriented Concepts==&lt;br /&gt;
&lt;br /&gt;
mIRC allows us to create an instance of a class using [[comopen]] and to call methods, get the value of properties, or set the value of properties using [[$com]] and [[$comcall]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;[[$com]] and [[$comcall]] are basically identical except that [[$comcall]] is asynchronous and will call an [[alias]] (like a callback function) when the call completes.&lt;br /&gt;
&lt;br /&gt;
Where a language such as Java might an infinite number of types (String, Socket, Integer, InputStream, etc) COM has only a handful - albeit a large handful.  In COM, every variable is represented as VARIANT. This is a [https://msdn.microsoft.com/en-us/library/windows/desktop/ms221627(v=vs.85).aspx C structure] and it can be used to represent many things.  The way this works in C that the &amp;quot;vt&amp;quot; member is set to the type of variable the Variant is to represent, and then the corresponding field is set to the value.&lt;br /&gt;
&lt;br /&gt;
For example, a Variant can represent a byte like this:&lt;br /&gt;
 VARIANT v; v.vt = VT_BYTE; v.bVal = 4;&lt;br /&gt;
Or, it can be changed to a float like this:&lt;br /&gt;
 v.vt = VT_FLOAT; v.fltVal = 4.0f;&lt;br /&gt;
&lt;br /&gt;
If you are familiar with the common types, you will probably recognize most of them.  What you might not recognize is &#039;&#039;&#039;dispatch&#039;&#039;&#039; and &#039;&#039;&#039;unknown&#039;&#039;&#039; and these are very important.&lt;br /&gt;
&lt;br /&gt;
Unknown (IUnknown) is a COM specific construct that is beyond the scope of this tutorial.  If you need it, you probably know what it is (and if you do, know that you can call QueryService/Release/AddRef through [[$com]] and [[$comcall]] - this is only useful though if you eventually get a &#039;&#039;&#039;dispatch&#039;&#039;&#039; back (see below))&lt;br /&gt;
&lt;br /&gt;
===Methods===&lt;br /&gt;
There are 4 things you can do to a COM object via Dispatches&lt;br /&gt;
* Call a method on them&lt;br /&gt;
* Get the value of a property they have&lt;br /&gt;
* Set the value of a property they have&lt;br /&gt;
* Set the value of a property they have to a reference&lt;br /&gt;
&lt;br /&gt;
The third parameter of [[$com]] allows you to pick from one of these 4 things by passing it in either 1 (call a method), 2 (get a property), 4 (put a property) or 8 (put a property reference).  We&#039;ll see an example of this soon.&lt;br /&gt;
&lt;br /&gt;
===Dispatch===&lt;br /&gt;
In COM, a dispatch is how we support complex types.  It represents an IDispatch object in C, which is important because it has the Invoke method which lets you call members.&lt;br /&gt;
&lt;br /&gt;
Ok let&#039;s back up. You know how Java and C# represent all complex types as &amp;quot;Object&amp;quot; - sort of like a super-class?  A String is an Object, an InputStream is an Object, etc? In COM, IDispatch serves a similar function. It represents a COM object what you can call methods or get and set properties on. If you have one, you can pass it as an argument to [[$com]] and [[$comopen]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Still lost on what a Dispatch is? It&#039;s OK - move on and use the examples to help gain an understanding.&lt;br /&gt;
&lt;br /&gt;
==Programmatic and Class IDs==&lt;br /&gt;
In order to get started in COM, you need a [[https://msdn.microsoft.com/en-us/library/dd542719(VS.85).aspx ProgID]].  A ProgID essentially represents the name of a class that supports COM instantiation via dynamic binding (which is what we are doing here).  &lt;br /&gt;
&lt;br /&gt;
In C#, you might do this&lt;br /&gt;
 var nameOfStack = new System.Collections.Stack()&lt;br /&gt;
&lt;br /&gt;
If Stack was exported as a com object with a prog ID (and it is!) you would do this in mIRC&lt;br /&gt;
 comopen nameOfStack System.Collections.Stack&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
&#039;&#039;You can skip this part - it&#039;s just some extra info&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Internally, program IDs are mapped to GUIDs in the Registry. For example, check out HKEY_CLASSES_ROOT\System.Collections.Stack\CLSID - this is the Class ID for the Stack class. &lt;br /&gt;
&lt;br /&gt;
If you want to do this same thing in C++, you would use code like the following:&lt;br /&gt;
 CLSID id;&lt;br /&gt;
 CLSIDFromProgID(L&amp;quot;System.Collections.Stack&amp;quot;, &amp;amp;id);&lt;br /&gt;
 IDispatch *disp;&lt;br /&gt;
 CoCreateInstance(id, nullptr, CLSCX_INPROC_SERVER,IID_IDispatch, (LPVOID*)&amp;amp;disp);&lt;br /&gt;
==Example 1, titleof==&lt;br /&gt;
Enough theory, let&#039;s jump in. Here are some things you need to know&lt;br /&gt;
* Internet Explorer exports it&#039;s [https://msdn.microsoft.com/en-us/library/aa752127(v=vs.85).aspx IWebBrowser2] interface as the progid InternetExplorer.Application&lt;br /&gt;
* It has a method called [https://msdn.microsoft.com/en-us/library/aa752093(v=vs.85).aspx Navigate] which takes a string and goes there&lt;br /&gt;
* Navigate is asynchronous. It will return as soon as the loading has started.&lt;br /&gt;
* It has a property called [https://msdn.microsoft.com/en-us/library/aa752066(v=vs.85).aspx ReadyState]. This is an [https://msdn.microsoft.com/en-us/library/bb268229(v=vs.85).aspx enum which is set to 4 when it is done.]&lt;br /&gt;
* It has a property called [https://msdn.microsoft.com/en-us/library/aa752057(v=vs.85).aspx LocationName] This is a String set to the title of the page.&lt;br /&gt;
&lt;br /&gt;
We&#039;re going to make an identifier that takes a URL and returns the title of the page. For example:&lt;br /&gt;
 $titleof(google.com) == Google&lt;br /&gt;
 $titleof(facebook.com) == Welcome to Facebook - Log In, Sign Up or Learn More&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Disclaimer: Titles may have changed since I wrote this&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We&#039;re going to do this by instantiating WebBrowser2, navigating to the URL we are passed, waiting until the page is loaded, then returning the location.&lt;br /&gt;
&lt;br /&gt;
 alias titleof  {&lt;br /&gt;
  if (!$isid) { return }&lt;br /&gt;
 &lt;br /&gt;
  ; Instantiate the class&lt;br /&gt;
  .comopen titleof InternetExplorer.Application&lt;br /&gt;
 &lt;br /&gt;
  ; Call Navigate. Pass it one parameter of type bstr (string) - the URL we were passed. 1 = DISPATCH_METHOD because we are calling a method&lt;br /&gt;
  noop $com(titleof, Navigate, 1, bstr, $1-)&lt;br /&gt;
 &lt;br /&gt;
  ; [[$com]](&amp;lt;dispatch&amp;gt;).result returns the value the last call returned. Loop until it is 4 ([https://msdn.microsoft.com/en-us/library/bb268229(v=vs.85).aspx READYSTATE_COMPLETE])&lt;br /&gt;
  while ($com(titleof).result != 4) {&lt;br /&gt;
    ; Get (2 = DISPATCH_PROPERTYGET) the value of the ReadyState property&lt;br /&gt;
    noop $com(titleof, ReadyState, 2)&lt;br /&gt;
    ; Now we&#039;ll loop again and check the value to see if it was 4 or not.&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Get the value of the LocationName property&lt;br /&gt;
  noop $com(titleof, LocationName, 2)&lt;br /&gt;
 &lt;br /&gt;
 ; Save it so we can cleanup&lt;br /&gt;
  var %value = $com(titleof).result&lt;br /&gt;
 &lt;br /&gt;
  ; Quit closes InternetExplorer&#039;s background tasks&lt;br /&gt;
  noop $com(titleof, Quit, 1)&lt;br /&gt;
 &lt;br /&gt;
  ; Close the COM object to tidy up&lt;br /&gt;
  .comclose titleof&lt;br /&gt;
 &lt;br /&gt;
 ; Return what we saved&lt;br /&gt;
  return %value&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Example 2: Using the managed stack==&lt;br /&gt;
Now let&#039;s use [[https://msdn.microsoft.com/en-us/library/system.collections.stack(v=vs.110).aspx System.Collections.Stack]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
alias stacktest {&lt;br /&gt;
  ;Instantiate it&lt;br /&gt;
  .comopen stack System.Collections.Stack&lt;br /&gt;
  ; Call push, push &amp;quot;First&amp;quot; onto the stack&lt;br /&gt;
  noop $com(stack, Push, 1, bstr, First)&lt;br /&gt;
  echo -atg Pushed &amp;quot;First&amp;quot; onto the stack&lt;br /&gt;
  ; Call Push, push &amp;quot;Second onto the stack&amp;quot;&lt;br /&gt;
  noop $com(stack, Push, 1, bstr, Second)&lt;br /&gt;
  echo -atg Pushed &amp;quot;Second&amp;quot; onto the stack&lt;br /&gt;
 &lt;br /&gt;
  ; See how many elements are in the stack&lt;br /&gt;
  noop $com(stack, Count, 2)&lt;br /&gt;
  echo -atg There are now $com(stack).result elements in the stack&lt;br /&gt;
 &lt;br /&gt;
  ; Pop off the first item&lt;br /&gt;
  noop $com(stack, pop, 1)&lt;br /&gt;
  echo -atg Pop: $com(stack).result&lt;br /&gt;
  noop $com(stack, Count, 2)&lt;br /&gt;
  echo -atg There are now $com(stack).result elements in the stack&lt;br /&gt;
 &lt;br /&gt;
  ; Clean it up&lt;br /&gt;
  .comclose stack&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Powershell Help==&lt;br /&gt;
PowerShell is a program which shipped with Windows since Windows 7, and it provides some cool ways to test and view these COM objects before doing them in mIRC. Try out the following&lt;br /&gt;
```TODO: Picture for powershell-stack.PNG```&lt;br /&gt;
&lt;br /&gt;
And use the get-members (GM) pipe to list what all your COM objects can do. This can give you a good idea of what you can do from mIRC and it&#039;s a bit easier to debug and test.&lt;br /&gt;
```TODO: Picture for stack-gm.PNG```&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;This page not yet completed...Sorry!&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:COM Objects]]&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=COM_Tutorial&amp;diff=6166</id>
		<title>COM Tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=COM_Tutorial&amp;diff=6166"/>
		<updated>2015-07-19T22:58:01Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Broke code formatting there...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;mIRC supports interaction with COM through dynamic binding.  COM in mIRC is an advanced topic and usually requires knowledge in strongly typed language. Knowledge of C++ is a big plus.&lt;br /&gt;
&lt;br /&gt;
For this tutorial to make sense, you should at a minimum know&lt;br /&gt;
* How to use [[Identifiers]] in mIRC&lt;br /&gt;
* About identifier properties (e.g. $identifier().prop)&lt;br /&gt;
* That the [[noop]] command will execute but not print or take an action&lt;br /&gt;
* What objects are in object oriented programming&lt;br /&gt;
* Basics of strongly typed languages (e.g. int vs short vs string)&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t sure you know each of these things, turn around and save yourself the time.&lt;br /&gt;
&lt;br /&gt;
==What is COM?==&lt;br /&gt;
COM stands for [https://msdn.microsoft.com/en-us/library/windows/desktop/ms680573(v=vs.85).aspx Component Object Model] and the details of this are quite complicated. We will be skipping over too many specifics, but MSDN has more.&lt;br /&gt;
&lt;br /&gt;
To speak generally, COM is a Windows technology for creating and accessing [https://en.wikipedia.org/wiki/Object_(computer_science) Objects] no matter what language. For example, you could create an object with methods and properties in C++ and use it in mIRC. &lt;br /&gt;
&lt;br /&gt;
==Specific Object-Oriented Concepts==&lt;br /&gt;
&lt;br /&gt;
mIRC allows us to create an instance of a class using [[comopen]] and to call methods, get the value of properties, or set the value of properties using [[$com]] and [[$comcall]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;[[$com]] and [[$comcall]] are basically identical except that [[$comcall]] is asynchronous and will call an [[alias]] (like a callback function) when the call completes.&lt;br /&gt;
&lt;br /&gt;
Where a language such as Java might an infinite number of types (String, Socket, Integer, InputStream, etc) COM has only a handful - albeit a large handful.  In COM, every variable is represented as VARIANT. This is a [https://msdn.microsoft.com/en-us/library/windows/desktop/ms221627(v=vs.85).aspx C structure] and it can be used to represent many things.  The way this works in C that the &amp;quot;vt&amp;quot; member is set to the type of variable the Variant is to represent, and then the corresponding field is set to the value.&lt;br /&gt;
&lt;br /&gt;
For example, a Variant can represent a byte like this:&lt;br /&gt;
 VARIANT v; v.vt = VT_BYTE; v.bVal = 4;&lt;br /&gt;
Or, it can be changed to a float like this:&lt;br /&gt;
 v.vt = VT_FLOAT; v.fltVal = 4.0f;&lt;br /&gt;
&lt;br /&gt;
If you are familiar with the common types, you will probably recognize most of them.  What you might not recognize is &#039;&#039;&#039;dispatch&#039;&#039;&#039; and &#039;&#039;&#039;unknown&#039;&#039;&#039; and these are very important.&lt;br /&gt;
&lt;br /&gt;
Unknown (IUnknown) is a COM specific construct that is beyond the scope of this tutorial.  If you need it, you probably know what it is (and if you do, know that you can call QueryService/Release/AddRef through [[$com]] and [[$comcall]] - this is only useful though if you eventually get a &#039;&#039;&#039;dispatch&#039;&#039;&#039; back (see below))&lt;br /&gt;
&lt;br /&gt;
===Methods===&lt;br /&gt;
There are 4 things you can do to a COM object via Dispatches&lt;br /&gt;
* Call a method on them&lt;br /&gt;
* Get the value of a property they have&lt;br /&gt;
* Set the value of a property they have&lt;br /&gt;
* Set the value of a property they have to a reference&lt;br /&gt;
&lt;br /&gt;
The third parameter of [[$com]] allows you to pick from one of these 4 things by passing it in either 1 (call a method), 2 (get a property), 4 (put a property) or 8 (put a property reference).  We&#039;ll see an example of this soon.&lt;br /&gt;
&lt;br /&gt;
===Dispatch===&lt;br /&gt;
In COM, a dispatch is how we support complex types.  It represents an IDispatch object in C, which is important because it has the Invoke method which lets you call members.&lt;br /&gt;
&lt;br /&gt;
Ok let&#039;s back up. You know how Java and C# represent all complex types as &amp;quot;Object&amp;quot; - sort of like a super-class?  A String is an Object, an InputStream is an Object, etc? In COM, IDispatch serves a similar function. It represents a COM object what you can call methods or get and set properties on. If you have one, you can pass it as an argument to [[$com]] and [[$comopen]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Still lost on what a Dispatch is? It&#039;s OK - move on and use the examples to help gain an understanding.&lt;br /&gt;
&lt;br /&gt;
==Programmatic and Class IDs==&lt;br /&gt;
In order to get started in COM, you need a [[https://msdn.microsoft.com/en-us/library/dd542719(VS.85).aspx ProgID]].  A ProgID essentially represents the name of a class that supports COM instantiation via dynamic binding (which is what we are doing here).  &lt;br /&gt;
&lt;br /&gt;
In C#, you might do this&lt;br /&gt;
 var nameOfStack = new System.Collections.Stack()&lt;br /&gt;
&lt;br /&gt;
If Stack was exported as a com object with a prog ID (and it is!) you would do this in mIRC&lt;br /&gt;
 comopen nameOfStack System.Collections.Stack&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
&#039;&#039;You can skip this part - it&#039;s just some extra info&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Internally, program IDs are mapped to GUIDs in the Registry. For example, check out HKEY_CLASSES_ROOT\System.Collections.Stack\CLSID - this is the Class ID for the Stack class. &lt;br /&gt;
&lt;br /&gt;
If you want to do this same thing in C++, you would use code like the following:&lt;br /&gt;
 CLSID id;&lt;br /&gt;
 CLSIDFromProgID(L&amp;quot;System.Collections.Stack&amp;quot;, &amp;amp;id);&lt;br /&gt;
 IDispatch *disp;&lt;br /&gt;
 CoCreateInstance(id, nullptr, CLSCX_INPROC_SERVER,IID_IDispatch, (LPVOID*)&amp;amp;disp);&lt;br /&gt;
==Example 1, titleof==&lt;br /&gt;
Enough theory, let&#039;s jump in. Here are some things you need to know&lt;br /&gt;
* Internet Explorer exports it&#039;s [https://msdn.microsoft.com/en-us/library/aa752127(v=vs.85).aspx IWebBrowser2] interface as the progid InternetExplorer.Application&lt;br /&gt;
* It has a method called [https://msdn.microsoft.com/en-us/library/aa752093(v=vs.85).aspx Navigate] which takes a string and goes there&lt;br /&gt;
* Navigate is asynchronous. It will return as soon as the loading has started.&lt;br /&gt;
* It has a property called [https://msdn.microsoft.com/en-us/library/aa752066(v=vs.85).aspx ReadyState]. This is an [https://msdn.microsoft.com/en-us/library/bb268229(v=vs.85).aspx enum which is set to 4 when it is done.]&lt;br /&gt;
* It has a property called [https://msdn.microsoft.com/en-us/library/aa752057(v=vs.85).aspx LocationName] This is a String set to the title of the page.&lt;br /&gt;
&lt;br /&gt;
We&#039;re going to make an identifier that takes a URL and returns the title of the page. For example:&lt;br /&gt;
 $titleof(google.com) == Google&lt;br /&gt;
 $titleof(facebook.com) == Welcome to Facebook - Log In, Sign Up or Learn More&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Disclaimer: Titles may have changed since I wrote this&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We&#039;re going to do this by instantiating WebBrowser2, navigating to the URL we are passed, waiting until the page is loaded, then returning the location.&lt;br /&gt;
&lt;br /&gt;
 alias titleof  {&lt;br /&gt;
  if (!$isid) { return }&lt;br /&gt;
 &lt;br /&gt;
  ; Instantiate the class&lt;br /&gt;
  .comopen titleof InternetExplorer.Application&lt;br /&gt;
 &lt;br /&gt;
  ; Call Navigate. Pass it one parameter of type bstr (string) - the URL we were passed. 1 = DISPATCH_METHOD because we are calling a method&lt;br /&gt;
  noop $com(titleof, Navigate, 1, bstr, $1-)&lt;br /&gt;
 &lt;br /&gt;
  ; [[$com]](&amp;lt;dispatch&amp;gt;).result returns the value the last call returned. Loop until it is 4 ([https://msdn.microsoft.com/en-us/library/bb268229(v=vs.85).aspx READYSTATE_COMPLETE])&lt;br /&gt;
  while ($com(titleof).result != 4) {&lt;br /&gt;
    ; Get (2 = DISPATCH_PROPERTYGET) the value of the ReadyState property&lt;br /&gt;
    noop $com(titleof, ReadyState, 2)&lt;br /&gt;
    ; Now we&#039;ll loop again and check the value to see if it was 4 or not.&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Get the value of the LocationName property&lt;br /&gt;
  noop $com(titleof, LocationName, 2)&lt;br /&gt;
 &lt;br /&gt;
 ; Save it so we can cleanup&lt;br /&gt;
  var %value = $com(titleof).result&lt;br /&gt;
 &lt;br /&gt;
  ; Quit closes InternetExplorer&#039;s background tasks&lt;br /&gt;
  noop $com(titleof, Quit, 1)&lt;br /&gt;
 &lt;br /&gt;
  ; Close the COM object to tidy up&lt;br /&gt;
  .comclose titleof&lt;br /&gt;
 &lt;br /&gt;
 ; Return what we saved&lt;br /&gt;
  return %value&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Example 2: Using the managed stack==&lt;br /&gt;
Now let&#039;s use System.Collections.Stack&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;This page not yet completed...Sorry!&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:COM Objects]]&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=COM_Tutorial&amp;diff=6165</id>
		<title>COM Tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=COM_Tutorial&amp;diff=6165"/>
		<updated>2015-07-19T22:57:08Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Created page with &amp;quot;mIRC supports interaction with COM through dynamic binding.  COM in mIRC is an advanced topic and usually requires knowledge in strongly typed language. Knowledge of C++ is a ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;mIRC supports interaction with COM through dynamic binding.  COM in mIRC is an advanced topic and usually requires knowledge in strongly typed language. Knowledge of C++ is a big plus.&lt;br /&gt;
&lt;br /&gt;
For this tutorial to make sense, you should at a minimum know&lt;br /&gt;
* How to use [[Identifiers]] in mIRC&lt;br /&gt;
* About identifier properties (e.g. $identifier().prop)&lt;br /&gt;
* That the [[noop]] command will execute but not print or take an action&lt;br /&gt;
* What objects are in object oriented programming&lt;br /&gt;
* Basics of strongly typed languages (e.g. int vs short vs string)&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t sure you know each of these things, turn around and save yourself the time.&lt;br /&gt;
&lt;br /&gt;
==What is COM?==&lt;br /&gt;
COM stands for [https://msdn.microsoft.com/en-us/library/windows/desktop/ms680573(v=vs.85).aspx Component Object Model] and the details of this are quite complicated. We will be skipping over too many specifics, but MSDN has more.&lt;br /&gt;
&lt;br /&gt;
To speak generally, COM is a Windows technology for creating and accessing [https://en.wikipedia.org/wiki/Object_(computer_science) Objects] no matter what language. For example, you could create an object with methods and properties in C++ and use it in mIRC. &lt;br /&gt;
&lt;br /&gt;
==Specific Object-Oriented Concepts==&lt;br /&gt;
&lt;br /&gt;
mIRC allows us to create an instance of a class using [[comopen]] and to call methods, get the value of properties, or set the value of properties using [[$com]] and [[$comcall]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;[[$com]] and [[$comcall]] are basically identical except that [[$comcall]] is asynchronous and will call an [[alias]] (like a callback function) when the call completes.&lt;br /&gt;
&lt;br /&gt;
Where a language such as Java might an infinite number of types (String, Socket, Integer, InputStream, etc) COM has only a handful - albeit a large handful.  In COM, every variable is represented as VARIANT. This is a [https://msdn.microsoft.com/en-us/library/windows/desktop/ms221627(v=vs.85).aspx C structure] and it can be used to represent many things.  The way this works in C that the &amp;quot;vt&amp;quot; member is set to the type of variable the Variant is to represent, and then the corresponding field is set to the value.&lt;br /&gt;
&lt;br /&gt;
For example, a Variant can represent a byte like this:&lt;br /&gt;
 VARIANT v; v.vt = VT_BYTE; v.bVal = 4;&lt;br /&gt;
Or, it can be changed to a float like this:&lt;br /&gt;
 v.vt = VT_FLOAT; v.fltVal = 4.0f;&lt;br /&gt;
&lt;br /&gt;
If you are familiar with the common types, you will probably recognize most of them.  What you might not recognize is &#039;&#039;&#039;dispatch&#039;&#039;&#039; and &#039;&#039;&#039;unknown&#039;&#039;&#039; and these are very important.&lt;br /&gt;
&lt;br /&gt;
Unknown (IUnknown) is a COM specific construct that is beyond the scope of this tutorial.  If you need it, you probably know what it is (and if you do, know that you can call QueryService/Release/AddRef through [[$com]] and [[$comcall]] - this is only useful though if you eventually get a &#039;&#039;&#039;dispatch&#039;&#039;&#039; back (see below))&lt;br /&gt;
&lt;br /&gt;
===Methods===&lt;br /&gt;
There are 4 things you can do to a COM object via Dispatches&lt;br /&gt;
* Call a method on them&lt;br /&gt;
* Get the value of a property they have&lt;br /&gt;
* Set the value of a property they have&lt;br /&gt;
* Set the value of a property they have to a reference&lt;br /&gt;
&lt;br /&gt;
The third parameter of [[$com]] allows you to pick from one of these 4 things by passing it in either 1 (call a method), 2 (get a property), 4 (put a property) or 8 (put a property reference).  We&#039;ll see an example of this soon.&lt;br /&gt;
&lt;br /&gt;
===Dispatch===&lt;br /&gt;
In COM, a dispatch is how we support complex types.  It represents an IDispatch object in C, which is important because it has the Invoke method which lets you call members.&lt;br /&gt;
&lt;br /&gt;
Ok let&#039;s back up. You know how Java and C# represent all complex types as &amp;quot;Object&amp;quot; - sort of like a super-class?  A String is an Object, an InputStream is an Object, etc? In COM, IDispatch serves a similar function. It represents a COM object what you can call methods or get and set properties on. If you have one, you can pass it as an argument to [[$com]] and [[$comopen]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Still lost on what a Dispatch is? It&#039;s OK - move on and use the examples to help gain an understanding.&lt;br /&gt;
&lt;br /&gt;
==Programmatic and Class IDs==&lt;br /&gt;
In order to get started in COM, you need a [[https://msdn.microsoft.com/en-us/library/dd542719(VS.85).aspx ProgID]].  A ProgID essentially represents the name of a class that supports COM instantiation via dynamic binding (which is what we are doing here).  &lt;br /&gt;
&lt;br /&gt;
In C#, you might do this&lt;br /&gt;
 var nameOfStack = new System.Collections.Stack()&lt;br /&gt;
&lt;br /&gt;
If Stack was exported as a com object with a prog ID (and it is!) you would do this in mIRC&lt;br /&gt;
 comopen nameOfStack System.Collections.Stack&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Details===&lt;br /&gt;
&#039;&#039;You can skip this part - it&#039;s just some extra info&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Internally, program IDs are mapped to GUIDs in the Registry. For example, check out HKEY_CLASSES_ROOT\System.Collections.Stack\CLSID - this is the Class ID for the Stack class. &lt;br /&gt;
&lt;br /&gt;
If you want to do this same thing in C++, you would use code like the following:&lt;br /&gt;
 CLSID id;&lt;br /&gt;
 CLSIDFromProgID(L&amp;quot;System.Collections.Stack&amp;quot;, &amp;amp;id);&lt;br /&gt;
 IDispatch *disp;&lt;br /&gt;
 CoCreateInstance(id, nullptr, CLSCX_INPROC_SERVER,IID_IDispatch, (LPVOID*)&amp;amp;disp);&lt;br /&gt;
==Example 1, titleof==&lt;br /&gt;
Enough theory, let&#039;s jump in. Here are some things you need to know&lt;br /&gt;
* Internet Explorer exports it&#039;s [https://msdn.microsoft.com/en-us/library/aa752127(v=vs.85).aspx IWebBrowser2] interface as the progid InternetExplorer.Application&lt;br /&gt;
* It has a method called [https://msdn.microsoft.com/en-us/library/aa752093(v=vs.85).aspx Navigate] which takes a string and goes there&lt;br /&gt;
* Navigate is asynchronous. It will return as soon as the loading has started.&lt;br /&gt;
* It has a property called [https://msdn.microsoft.com/en-us/library/aa752066(v=vs.85).aspx ReadyState]. This is an [https://msdn.microsoft.com/en-us/library/bb268229(v=vs.85).aspx enum which is set to 4 when it is done.]&lt;br /&gt;
* It has a property called [https://msdn.microsoft.com/en-us/library/aa752057(v=vs.85).aspx LocationName] This is a String set to the title of the page.&lt;br /&gt;
&lt;br /&gt;
We&#039;re going to make an identifier that takes a URL and returns the title of the page. For example:&lt;br /&gt;
 $titleof(google.com) == Google&lt;br /&gt;
 $titleof(facebook.com) == Welcome to Facebook - Log In, Sign Up or Learn More&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Disclaimer: Titles may have changed since I wrote this&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We&#039;re going to do this by instantiating WebBrowser2, navigating to the URL we are passed, waiting until the page is loaded, then returning the location.&lt;br /&gt;
&lt;br /&gt;
 alias titleof  {&lt;br /&gt;
  if (!$isid) { return }&lt;br /&gt;
&lt;br /&gt;
  ; Instantiate the class&lt;br /&gt;
  .comopen titleof InternetExplorer.Application&lt;br /&gt;
&lt;br /&gt;
  ; Call Navigate. Pass it one parameter of type bstr (string) - the URL we were passed. 1 = DISPATCH_METHOD because we are calling a method&lt;br /&gt;
  noop $com(titleof, Navigate, 1, bstr, $1-)&lt;br /&gt;
&lt;br /&gt;
  ; [[$com]](&amp;lt;dispatch&amp;gt;).result returns the value the last call returned. Loop until it is 4 ([https://msdn.microsoft.com/en-us/library/bb268229(v=vs.85).aspx READYSTATE_COMPLETE])&lt;br /&gt;
  while ($com(titleof).result != 4) {&lt;br /&gt;
    ; Get (2 = DISPATCH_PROPERTYGET) the value of the ReadyState property&lt;br /&gt;
    noop $com(titleof, ReadyState, 2)&lt;br /&gt;
    ; Now we&#039;ll loop again and check the value to see if it was 4 or not.&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ; Get the value of the LocationName property&lt;br /&gt;
  noop $com(titleof, LocationName, 2)&lt;br /&gt;
&lt;br /&gt;
 ; Save it so we can cleanup&lt;br /&gt;
  var %value = $com(titleof).result&lt;br /&gt;
&lt;br /&gt;
  ; Quit closes InternetExplorer&#039;s background tasks&lt;br /&gt;
  noop $com(titleof, Quit, 1)&lt;br /&gt;
&lt;br /&gt;
  ; Close the COM object to tidy up&lt;br /&gt;
  .comclose titleof&lt;br /&gt;
&lt;br /&gt;
 ; Return what we saved&lt;br /&gt;
  return %value&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Example 2: Using the managed stack==&lt;br /&gt;
Now let&#039;s use System.Collections.Stack&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;This page not yet completed...Sorry!&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:COM Objects]]&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6154</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6154"/>
		<updated>2015-06-10T21:02:14Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Adding known issue about not supporting arrays in POST&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- | http.close $1 } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
 ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file|bvar&amp;gt;&lt;br /&gt;
 ;     Downloads a given HTTP resource to the given file or binvar&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving it&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; You can mark a socket with data of your choice (it&#039;s a transparent write to a hashtable) similar to sockmark&lt;br /&gt;
 ; /http.mark &amp;lt;handle&amp;gt; [data] - Writes the data. Specify no data to delete the mark&lt;br /&gt;
 ; $http.mark(handle) - Reads the data&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    POSTing arrays is not currently supported&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They are always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %destination = $2-&lt;br /&gt;
   if ($exists(%destination)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %destination&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (&amp;amp;* iswm %destination) { &lt;br /&gt;
     .hadd %hashtable bvarout %destination&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ;  Open file handler stream for writing&lt;br /&gt;
     .fopen -n %hashtable %destination&lt;br /&gt;
     if ($ferr) {&lt;br /&gt;
       echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
     .hadd %hashtable fstream %hashtable&lt;br /&gt;
   }&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 alias http.mark {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if ($isid) {&lt;br /&gt;
     ; As an identifier, read the mark&lt;br /&gt;
     return $hget(%hashtable,mark)&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; As a command, set the mark&lt;br /&gt;
     if (!$hget(%hashtable)) {&lt;br /&gt;
       echo -atg * /http.mark - No such handle $1&lt;br /&gt;
     }&lt;br /&gt;
     elseif (!$2) { &lt;br /&gt;
       .hdel %hashtable mark&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .hadd %hashtable mark $2-&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%exname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
   if ($hget($1, bvarout)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     var %bvarout = $ifmatch&lt;br /&gt;
     bcopy %bvarout 1 &amp;amp;bvBody 1 -1&lt;br /&gt;
     ; Need to use -n so that the binvar is still in scope&lt;br /&gt;
     ; So we need to do this last&lt;br /&gt;
     .signal -n http $right($1,-5) SAVED %bvarout&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %pathIndex&lt;br /&gt;
   var %protocol&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6153</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6153"/>
		<updated>2015-06-10T17:17:15Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: POST, should pick it up from the external name not the internal name&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- | http.close $1 } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
 ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file|bvar&amp;gt;&lt;br /&gt;
 ;     Downloads a given HTTP resource to the given file or binvar&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving it&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; You can mark a socket with data of your choice (it&#039;s a transparent write to a hashtable) similar to sockmark&lt;br /&gt;
 ; /http.mark &amp;lt;handle&amp;gt; [data] - Writes the data. Specify no data to delete the mark&lt;br /&gt;
 ; $http.mark(handle) - Reads the data&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They area always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %destination = $2-&lt;br /&gt;
   if ($exists(%destination)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %destination&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (&amp;amp;* iswm %destination) { &lt;br /&gt;
     .hadd %hashtable bvarout %destination&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ;  Open file handler stream for writing&lt;br /&gt;
     .fopen -n %hashtable %destination&lt;br /&gt;
     if ($ferr) {&lt;br /&gt;
       echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
     .hadd %hashtable fstream %hashtable&lt;br /&gt;
   }&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 alias http.mark {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if ($isid) {&lt;br /&gt;
     ; As an identifier, read the mark&lt;br /&gt;
     return $hget(%hashtable,mark)&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; As a command, set the mark&lt;br /&gt;
     if (!$hget(%hashtable)) {&lt;br /&gt;
       echo -atg * /http.mark - No such handle $1&lt;br /&gt;
     }&lt;br /&gt;
     elseif (!$2) { &lt;br /&gt;
       .hdel %hashtable mark&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .hadd %hashtable mark $2-&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%exname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
   if ($hget($1, bvarout)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     var %bvarout = $ifmatch&lt;br /&gt;
     bcopy %bvarout 1 &amp;amp;bvBody 1 -1&lt;br /&gt;
     ; Need to use -n so that the binvar is still in scope&lt;br /&gt;
     ; So we need to do this last&lt;br /&gt;
     .signal -n http $right($1,-5) SAVED %bvarout&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %pathIndex&lt;br /&gt;
   var %protocol&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6152</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6152"/>
		<updated>2015-05-22T19:46:53Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: +http.close to example&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- | http.close $1 } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
 ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file|bvar&amp;gt;&lt;br /&gt;
 ;     Downloads a given HTTP resource to the given file or binvar&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving it&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; You can mark a socket with data of your choice (it&#039;s a transparent write to a hashtable) similar to sockmark&lt;br /&gt;
 ; /http.mark &amp;lt;handle&amp;gt; [data] - Writes the data. Specify no data to delete the mark&lt;br /&gt;
 ; $http.mark(handle) - Reads the data&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They area always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %destination = $2-&lt;br /&gt;
   if ($exists(%destination)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %destination&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (&amp;amp;* iswm %destination) { &lt;br /&gt;
     .hadd %hashtable bvarout %destination&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ;  Open file handler stream for writing&lt;br /&gt;
     .fopen -n %hashtable %destination&lt;br /&gt;
     if ($ferr) {&lt;br /&gt;
       echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
     .hadd %hashtable fstream %hashtable&lt;br /&gt;
   }&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 alias http.mark {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if ($isid) {&lt;br /&gt;
     ; As an identifier, read the mark&lt;br /&gt;
     return $hget(%hashtable,mark)&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; As a command, set the mark&lt;br /&gt;
     if (!$hget(%hashtable)) {&lt;br /&gt;
       echo -atg * /http.mark - No such handle $1&lt;br /&gt;
     }&lt;br /&gt;
     elseif (!$2) { &lt;br /&gt;
       .hdel %hashtable mark&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .hadd %hashtable mark $2-&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%inname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
   if ($hget($1, bvarout)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     var %bvarout = $ifmatch&lt;br /&gt;
     bcopy %bvarout 1 &amp;amp;bvBody 1 -1&lt;br /&gt;
     ; Need to use -n so that the binvar is still in scope&lt;br /&gt;
     ; So we need to do this last&lt;br /&gt;
     .signal -n http $right($1,-5) SAVED %bvarout&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %pathIndex&lt;br /&gt;
   var %protocol&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6151</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6151"/>
		<updated>2015-05-22T19:31:26Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: line break fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
The script works in 3 phases&lt;br /&gt;
1.) Parsing. $json.parse will return the root object, and it can be quite slow. Try to do this as little as possible and re-use the value.&lt;br /&gt;
2.) Querying. $json and $mjson can be used to read the data and is generally very fast, but requires a parsed object.&lt;br /&gt;
3.) Deleting. json.freeall can be used to delete a json object. Call it on the root object when you are done to clean up.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
$mjson provides a simpler interface for quickly querying data, but it is less powerful than $json. Here is an example of how you can use $mjson to get the name of the first employee &lt;br /&gt;
&lt;br /&gt;
 alias json-example-simple {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
 &lt;br /&gt;
  echo -atg The first employee is: $mjson(%aEmployees,0,name)&lt;br /&gt;
 &lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
As you can see, this is a much simpler way to just get information quickly.  However this method does not allow you to get meta-information, such as how many employees there are.&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Given a handle (use json.parse first) allows you to index an array or get a handle to a property by its name&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to&lt;br /&gt;
 ;    Get a property value&lt;br /&gt;
 ;    Index into an array&lt;br /&gt;
 ;    Get the Nth property name of an object&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 3) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot index data type&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 !isnum) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 isnum) {&lt;br /&gt;
     ; Indexing object by index offset&lt;br /&gt;
     var %thisIndex = 0&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget(%value,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       var %propName = $gettok(%item,2-,$asc(.))&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         inc %thisIndex&lt;br /&gt;
       }&lt;br /&gt;
       if (%thisIndex == $2) {&lt;br /&gt;
         return %propName&lt;br /&gt;
       }&lt;br /&gt;
       if ($2 == 0) { return %thisIndex }&lt;br /&gt;
       return $null&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; mjson is a very thin wrapper to automatically walk down&lt;br /&gt;
 ; the object chain. &lt;br /&gt;
 alias mjson {&lt;br /&gt;
  var %object = $1&lt;br /&gt;
  var %propertyIndex = 2&lt;br /&gt;
  while (%propertyIndex &amp;lt; $0) {&lt;br /&gt;
    var %object = $json(%object,$($ $+ %propertyIndex,2))&lt;br /&gt;
    inc %propertyIndex&lt;br /&gt;
  }&lt;br /&gt;
  ; Return value of last object&lt;br /&gt;
  return $json(%object,$($ $+ %propertyIndex,2)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
  var %bv = $json.createbvar&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  var %escape = 0&lt;br /&gt;
  :loop&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
    if (%escape) {&lt;br /&gt;
      ; If the escape character is escaped, append it&lt;br /&gt;
      dec %escape&lt;br /&gt;
      goto append&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      inc %escape&lt;br /&gt;
      ; Skip appending&lt;br /&gt;
      goto trim&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (%escape) {&lt;br /&gt;
    if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
  }&lt;br /&gt;
  :append&lt;br /&gt;
  ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    bset %bv $calc($bvar(%bv,0) +1) 0&lt;br /&gt;
    return %bv&lt;br /&gt;
  }  &lt;br /&gt;
  ; Add this character to the end&lt;br /&gt;
  bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
  if (%escape) { dec %escape }&lt;br /&gt;
  :trim&lt;br /&gt;
  ; Trim from the start&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
  if ($bvar($1,0)) {&lt;br /&gt;
    goto loop&lt;br /&gt;
  }&lt;br /&gt;
  return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
    return $json.errors().EXPECTED_START&lt;br /&gt;
  }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Create a linked list for the data&lt;br /&gt;
  var %datatable = $json.createtable(1)&lt;br /&gt;
  ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
  var %typetable = $json.createtable(1)&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
    json.debug Parsed empty array successfully&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    return %datatable %typetable&lt;br /&gt;
  }&lt;br /&gt;
  :member&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  ; Parse members&lt;br /&gt;
  var %any = $json.parseany($1)&lt;br /&gt;
  var %type = $gettok(%any,1,32)&lt;br /&gt;
  var %value = $gettok(%any,2-,32)&lt;br /&gt;
  if (%type == ERROR) {&lt;br /&gt;
    json.debug Error parsing array member: %value&lt;br /&gt;
    json.freeall 2 %datatable %typetable&lt;br /&gt;
    return %value&lt;br /&gt;
  }&lt;br /&gt;
  else {&lt;br /&gt;
    ; 0 indexed&lt;br /&gt;
    var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
    ; Binary data&lt;br /&gt;
    json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
    if (%type == 3) {&lt;br /&gt;
      hadd -b %datatable %newindex %value&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      hadd %datatable %newindex %value&lt;br /&gt;
    }&lt;br /&gt;
    hadd %typetable %newindex %type&lt;br /&gt;
  }&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    goto member&lt;br /&gt;
  }&lt;br /&gt;
  elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
    json.debug Parsed array successfully&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    return %datatable %typetable&lt;br /&gt;
  }&lt;br /&gt;
  :exit&lt;br /&gt;
  json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
  json.freeall 2 %datatable %typetable&lt;br /&gt;
  return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6150</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6150"/>
		<updated>2015-05-22T19:30:39Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: +mjson&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
The script works in 3 phases&lt;br /&gt;
1.) Parsing. $json.parse will return the root object, and it can be quite slow. Try to do this as little as possible and re-use the value.&lt;br /&gt;
2.) Querying. $json and $mjson can be used to read the data and is generally very fast, but requires a parsed object.&lt;br /&gt;
3.) Deleting. json.freeall can be used to delete a json object. Call it on the root object when you are done to clean up.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
$mjson provides a simpler interface for quickly querying data, but it is less powerful than $json. Here is an example of how you can use $mjson to get the name of the first employee &lt;br /&gt;
&lt;br /&gt;
 alias json-example-simple {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
&lt;br /&gt;
  echo -atg The first employee is: $mjson(%aEmployees,0,name)&lt;br /&gt;
 &lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
As you can see, this is a much simpler way to just get information quickly.  However this method does not allow you to get meta-information, such as how many employees there are.&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Given a handle (use json.parse first) allows you to index an array or get a handle to a property by its name&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to&lt;br /&gt;
 ;    Get a property value&lt;br /&gt;
 ;    Index into an array&lt;br /&gt;
 ;    Get the Nth property name of an object&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 3) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot index data type&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 !isnum) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 isnum) {&lt;br /&gt;
     ; Indexing object by index offset&lt;br /&gt;
     var %thisIndex = 0&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget(%value,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       var %propName = $gettok(%item,2-,$asc(.))&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         inc %thisIndex&lt;br /&gt;
       }&lt;br /&gt;
       if (%thisIndex == $2) {&lt;br /&gt;
         return %propName&lt;br /&gt;
       }&lt;br /&gt;
       if ($2 == 0) { return %thisIndex }&lt;br /&gt;
       return $null&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; mjson is a very thin wrapper to automatically walk down&lt;br /&gt;
 ; the object chain. &lt;br /&gt;
 alias mjson {&lt;br /&gt;
  var %object = $1&lt;br /&gt;
  var %propertyIndex = 2&lt;br /&gt;
  while (%propertyIndex &amp;lt; $0) {&lt;br /&gt;
    var %object = $json(%object,$($ $+ %propertyIndex,2))&lt;br /&gt;
    inc %propertyIndex&lt;br /&gt;
  }&lt;br /&gt;
  ; Return value of last object&lt;br /&gt;
  return $json(%object,$($ $+ %propertyIndex,2)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
  var %bv = $json.createbvar&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  var %escape = 0&lt;br /&gt;
  :loop&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
    if (%escape) {&lt;br /&gt;
      ; If the escape character is escaped, append it&lt;br /&gt;
      dec %escape&lt;br /&gt;
      goto append&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      inc %escape&lt;br /&gt;
      ; Skip appending&lt;br /&gt;
      goto trim&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (%escape) {&lt;br /&gt;
    if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
  }&lt;br /&gt;
  :append&lt;br /&gt;
  ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    bset %bv $calc($bvar(%bv,0) +1) 0&lt;br /&gt;
    return %bv&lt;br /&gt;
  }  &lt;br /&gt;
  ; Add this character to the end&lt;br /&gt;
  bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
  if (%escape) { dec %escape }&lt;br /&gt;
  :trim&lt;br /&gt;
  ; Trim from the start&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
  if ($bvar($1,0)) {&lt;br /&gt;
    goto loop&lt;br /&gt;
  }&lt;br /&gt;
  return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
    return $json.errors().EXPECTED_START&lt;br /&gt;
  }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Create a linked list for the data&lt;br /&gt;
  var %datatable = $json.createtable(1)&lt;br /&gt;
  ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
  var %typetable = $json.createtable(1)&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
    json.debug Parsed empty array successfully&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    return %datatable %typetable&lt;br /&gt;
  }&lt;br /&gt;
  :member&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  ; Parse members&lt;br /&gt;
  var %any = $json.parseany($1)&lt;br /&gt;
  var %type = $gettok(%any,1,32)&lt;br /&gt;
  var %value = $gettok(%any,2-,32)&lt;br /&gt;
  if (%type == ERROR) {&lt;br /&gt;
    json.debug Error parsing array member: %value&lt;br /&gt;
    json.freeall 2 %datatable %typetable&lt;br /&gt;
    return %value&lt;br /&gt;
  }&lt;br /&gt;
  else {&lt;br /&gt;
    ; 0 indexed&lt;br /&gt;
    var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
    ; Binary data&lt;br /&gt;
    json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
    if (%type == 3) {&lt;br /&gt;
      hadd -b %datatable %newindex %value&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      hadd %datatable %newindex %value&lt;br /&gt;
    }&lt;br /&gt;
    hadd %typetable %newindex %type&lt;br /&gt;
  }&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    goto member&lt;br /&gt;
  }&lt;br /&gt;
  elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
    json.debug Parsed array successfully&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    return %datatable %typetable&lt;br /&gt;
  }&lt;br /&gt;
  :exit&lt;br /&gt;
  json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
  json.freeall 2 %datatable %typetable&lt;br /&gt;
  return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6143</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6143"/>
		<updated>2015-04-02T16:19:48Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: pathIndex should be a local variable&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
 ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file|bvar&amp;gt;&lt;br /&gt;
 ;     Downloads a given HTTP resource to the given file or binvar&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving it&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; You can mark a socket with data of your choice (it&#039;s a transparent write to a hashtable) similar to sockmark&lt;br /&gt;
 ; /http.mark &amp;lt;handle&amp;gt; [data] - Writes the data. Specify no data to delete the mark&lt;br /&gt;
 ; $http.mark(handle) - Reads the data&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They area always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %destination = $2-&lt;br /&gt;
   if ($exists(%destination)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %destination&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (&amp;amp;* iswm %destination) { &lt;br /&gt;
     .hadd %hashtable bvarout %destination&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ;  Open file handler stream for writing&lt;br /&gt;
     .fopen -n %hashtable %destination&lt;br /&gt;
     if ($ferr) {&lt;br /&gt;
       echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
     .hadd %hashtable fstream %hashtable&lt;br /&gt;
   }&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 alias http.mark {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if ($isid) {&lt;br /&gt;
     ; As an identifier, read the mark&lt;br /&gt;
     return $hget(%hashtable,mark)&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; As a command, set the mark&lt;br /&gt;
     if (!$hget(%hashtable)) {&lt;br /&gt;
       echo -atg * /http.mark - No such handle $1&lt;br /&gt;
     }&lt;br /&gt;
     elseif (!$2) { &lt;br /&gt;
       .hdel %hashtable mark&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .hadd %hashtable mark $2-&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%inname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
   if ($hget($1, bvarout)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     var %bvarout = $ifmatch&lt;br /&gt;
     bcopy %bvarout 1 &amp;amp;bvBody 1 -1&lt;br /&gt;
     ; Need to use -n so that the binvar is still in scope&lt;br /&gt;
     ; So we need to do this last&lt;br /&gt;
     .signal -n http $right($1,-5) SAVED %bvarout&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %pathIndex&lt;br /&gt;
   var %protocol&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6140</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6140"/>
		<updated>2015-02-22T01:17:18Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: - Extra space&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
 ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file|bvar&amp;gt;&lt;br /&gt;
 ;     Downloads a given HTTP resource to the given file or binvar&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving it&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; You can mark a socket with data of your choice (it&#039;s a transparent write to a hashtable) similar to sockmark&lt;br /&gt;
 ; /http.mark &amp;lt;handle&amp;gt; [data] - Writes the data. Specify no data to delete the mark&lt;br /&gt;
 ; $http.mark(handle) - Reads the data&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They area always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %destination = $2-&lt;br /&gt;
   if ($exists(%destination)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %destination&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (&amp;amp;* iswm %destination) { &lt;br /&gt;
     .hadd %hashtable bvarout %destination&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ;  Open file handler stream for writing&lt;br /&gt;
     .fopen -n %hashtable %destination&lt;br /&gt;
     if ($ferr) {&lt;br /&gt;
       echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
     .hadd %hashtable fstream %hashtable&lt;br /&gt;
   }&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 alias http.mark {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if ($isid) {&lt;br /&gt;
     ; As an identifier, read the mark&lt;br /&gt;
     return $hget(%hashtable,mark)&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; As a command, set the mark&lt;br /&gt;
     if (!$hget(%hashtable)) {&lt;br /&gt;
       echo -atg * /http.mark - No such handle $1&lt;br /&gt;
     }&lt;br /&gt;
     elseif (!$2) { &lt;br /&gt;
       .hdel %hashtable mark&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .hadd %hashtable mark $2-&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%inname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
   if ($hget($1, bvarout)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     var %bvarout = $ifmatch&lt;br /&gt;
     bcopy %bvarout 1 &amp;amp;bvBody 1 -1&lt;br /&gt;
     ; Need to use -n so that the binvar is still in scope&lt;br /&gt;
     ; So we need to do this last&lt;br /&gt;
     .signal -n http $right($1,-5) SAVED %bvarout&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6139</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6139"/>
		<updated>2015-02-22T01:15:43Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: http.mark&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
  ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file|bvar&amp;gt;&lt;br /&gt;
 ;     Downloads a given HTTP resource to the given file or binvar&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving it&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; You can mark a socket with data of your choice (it&#039;s a transparent write to a hashtable) similar to sockmark&lt;br /&gt;
 ; /http.mark &amp;lt;handle&amp;gt; [data] - Writes the data. Specify no data to delete the mark&lt;br /&gt;
 ; $http.mark(handle) - Reads the data&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They area always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %destination = $2-&lt;br /&gt;
   if ($exists(%destination)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %destination&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (&amp;amp;* iswm %destination) { &lt;br /&gt;
     .hadd %hashtable bvarout %destination&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ;  Open file handler stream for writing&lt;br /&gt;
     .fopen -n %hashtable %destination&lt;br /&gt;
     if ($ferr) {&lt;br /&gt;
       echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
     .hadd %hashtable fstream %hashtable&lt;br /&gt;
   }&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 alias http.mark {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if ($isid) {&lt;br /&gt;
     ; As an identifier, read the mark&lt;br /&gt;
     return $hget(%hashtable,mark)&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; As a command, set the mark&lt;br /&gt;
     if (!$hget(%hashtable)) {&lt;br /&gt;
       echo -atg * /http.mark - No such handle $1&lt;br /&gt;
     }&lt;br /&gt;
     elseif (!$2) { &lt;br /&gt;
       .hdel %hashtable mark&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .hadd %hashtable mark $2-&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%inname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
   if ($hget($1, bvarout)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     var %bvarout = $ifmatch&lt;br /&gt;
     bcopy %bvarout 1 &amp;amp;bvBody 1 -1&lt;br /&gt;
     ; Need to use -n so that the binvar is still in scope&lt;br /&gt;
     ; So we need to do this last&lt;br /&gt;
     .signal -n http $right($1,-5) SAVED %bvarout&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6138</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6138"/>
		<updated>2015-02-22T00:44:05Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: + http.save to &amp;amp;bvar&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
 ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file|bvar&amp;gt;&lt;br /&gt;
 ;     Downloads a given HTTP resource to the given file or binvar&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving it&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They area always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %destination = $2-&lt;br /&gt;
   if ($exists(%destination)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %destination&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (&amp;amp;* iswm %destination) { &lt;br /&gt;
     .hadd %hashtable bvarout %destination&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ;  Open file handler stream for writing&lt;br /&gt;
     .fopen -n %hashtable %destination&lt;br /&gt;
     if ($ferr) {&lt;br /&gt;
       echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
     .hadd %hashtable fstream %hashtable&lt;br /&gt;
   }&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%inname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
   if ($hget($1, bvarout)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     var %bvarout = $ifmatch&lt;br /&gt;
     bcopy %bvarout 1 &amp;amp;bvBody 1 -1&lt;br /&gt;
     ; Need to use -n so that the binvar is still in scope&lt;br /&gt;
     ; So we need to do this last&lt;br /&gt;
     .signal -n http $right($1,-5) SAVED %bvarout&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6133</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6133"/>
		<updated>2015-01-23T06:29:16Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: /* Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Given a handle (use json.parse first) allows you to index an array or get a handle to a property by its name&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to&lt;br /&gt;
 ;    Get a property value&lt;br /&gt;
 ;    Index into an array&lt;br /&gt;
 ;    Get the Nth property name of an object&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 3) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot index data type&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 !isnum) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 isnum) {&lt;br /&gt;
     ; Indexing object by index offset&lt;br /&gt;
     var %thisIndex = 0&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget(%value,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       var %propName = $gettok(%item,2-,$asc(.))&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         inc %thisIndex&lt;br /&gt;
       }&lt;br /&gt;
       if (%thisIndex == $2) {&lt;br /&gt;
         return %propName&lt;br /&gt;
       }&lt;br /&gt;
       if ($2 == 0) { return %thisIndex }&lt;br /&gt;
       return $null&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
  var %bv = $json.createbvar&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  var %escape = 0&lt;br /&gt;
  :loop&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
    if (%escape) {&lt;br /&gt;
      ; If the escape character is escaped, append it&lt;br /&gt;
      dec %escape&lt;br /&gt;
      goto append&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      inc %escape&lt;br /&gt;
      ; Skip appending&lt;br /&gt;
      goto trim&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (%escape) {&lt;br /&gt;
    if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
  }&lt;br /&gt;
  :append&lt;br /&gt;
  ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    bset %bv $calc($bvar(%bv,0) +1) 0&lt;br /&gt;
    return %bv&lt;br /&gt;
  }  &lt;br /&gt;
  ; Add this character to the end&lt;br /&gt;
  bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
  if (%escape) { dec %escape }&lt;br /&gt;
  :trim&lt;br /&gt;
  ; Trim from the start&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
  if ($bvar($1,0)) {&lt;br /&gt;
    goto loop&lt;br /&gt;
  }&lt;br /&gt;
  return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
    return $json.errors().EXPECTED_START&lt;br /&gt;
  }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Create a linked list for the data&lt;br /&gt;
  var %datatable = $json.createtable(1)&lt;br /&gt;
  ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
  var %typetable = $json.createtable(1)&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
    json.debug Parsed empty array successfully&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    return %datatable %typetable&lt;br /&gt;
  }&lt;br /&gt;
  :member&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  ; Parse members&lt;br /&gt;
  var %any = $json.parseany($1)&lt;br /&gt;
  var %type = $gettok(%any,1,32)&lt;br /&gt;
  var %value = $gettok(%any,2-,32)&lt;br /&gt;
  if (%type == ERROR) {&lt;br /&gt;
    json.debug Error parsing array member: %value&lt;br /&gt;
    json.freeall 2 %datatable %typetable&lt;br /&gt;
    return %value&lt;br /&gt;
  }&lt;br /&gt;
  else {&lt;br /&gt;
    ; 0 indexed&lt;br /&gt;
    var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
    ; Binary data&lt;br /&gt;
    json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
    if (%type == 3) {&lt;br /&gt;
      hadd -b %datatable %newindex %value&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      hadd %datatable %newindex %value&lt;br /&gt;
    }&lt;br /&gt;
    hadd %typetable %newindex %type&lt;br /&gt;
  }&lt;br /&gt;
  noop $json.consumewhitespace($1)&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    goto member&lt;br /&gt;
  }&lt;br /&gt;
  elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
    json.debug Parsed array successfully&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    return %datatable %typetable&lt;br /&gt;
  }&lt;br /&gt;
  :exit&lt;br /&gt;
  json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
  json.freeall 2 %datatable %typetable&lt;br /&gt;
  return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6132</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6132"/>
		<updated>2015-01-23T06:07:39Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Given a handle (use json.parse first) allows you to index an array or get a handle to a property by its name&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to&lt;br /&gt;
 ;    Get a property value&lt;br /&gt;
 ;    Index into an array&lt;br /&gt;
 ;    Get the Nth property name of an object&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 3) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot index data type&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 !isnum) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 isnum) {&lt;br /&gt;
     ; Indexing object by index offset&lt;br /&gt;
     var %thisIndex = 0&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget(%value,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       var %propName = $gettok(%item,2-,$asc(.))&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         inc %thisIndex&lt;br /&gt;
       }&lt;br /&gt;
       if (%thisIndex == $2) {&lt;br /&gt;
         return %propName&lt;br /&gt;
       }&lt;br /&gt;
       if ($2 == 0) { return %thisIndex }&lt;br /&gt;
       return $null&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
  var %bv = $json.createbvar&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  var %escape = 0&lt;br /&gt;
  :loop&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
    if (%escape) {&lt;br /&gt;
      ; If the escape character is escaped, append it&lt;br /&gt;
      dec %escape&lt;br /&gt;
      goto append&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      inc %escape&lt;br /&gt;
      ; Skip appending&lt;br /&gt;
      goto trim&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (%escape) {&lt;br /&gt;
    if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
  }&lt;br /&gt;
  :append&lt;br /&gt;
  ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    bset %bv $calc($bvar(%bv,0) +1) 0&lt;br /&gt;
    return %bv&lt;br /&gt;
  }  &lt;br /&gt;
  ; Add this character to the end&lt;br /&gt;
  bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
  if (%escape) { dec %escape }&lt;br /&gt;
  :trim&lt;br /&gt;
  ; Trim from the start&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
  if ($bvar($1,0)) {&lt;br /&gt;
    goto loop&lt;br /&gt;
  }&lt;br /&gt;
  return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6131</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6131"/>
		<updated>2015-01-23T02:31:38Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: /* Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Given a handle (use json.parse first) allows you to index an array or get a handle to a property by its name&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to&lt;br /&gt;
 ;    Get a property value&lt;br /&gt;
 ;    Index into an array&lt;br /&gt;
 ;    Get the Nth property name of an object&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 3) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot index data type&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 !isnum) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 isnum) {&lt;br /&gt;
     ; Indexing object by index offset&lt;br /&gt;
     var %thisIndex = 0&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget(%value,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       var %propName = $gettok(%item,2-,$asc(.))&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         inc %thisIndex&lt;br /&gt;
       }&lt;br /&gt;
       if (%thisIndex == $2) {&lt;br /&gt;
         return %propName&lt;br /&gt;
       }&lt;br /&gt;
       if ($2 == 0) { return %thisIndex }&lt;br /&gt;
       return $null&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
  var %bv = $json.createbvar&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  var %escape = 0&lt;br /&gt;
  :loop&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
    if (%escape) {&lt;br /&gt;
      ; If the escape character is escaped, append it&lt;br /&gt;
      dec %escape&lt;br /&gt;
      goto append&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      inc %escape&lt;br /&gt;
      ; Skip appending&lt;br /&gt;
      goto trim&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if (%escape) {&lt;br /&gt;
    if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
  }&lt;br /&gt;
  :append&lt;br /&gt;
  ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    bset %bv $calc($bvar(%bv,0) +1) 0&lt;br /&gt;
    return %bv&lt;br /&gt;
  }  &lt;br /&gt;
  ; Add this character to the end&lt;br /&gt;
  bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
  :trim&lt;br /&gt;
  ; Trim from the start&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
  ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
  if ($bvar($1,0)) {&lt;br /&gt;
    goto loop&lt;br /&gt;
  }&lt;br /&gt;
  return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6130</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6130"/>
		<updated>2015-01-23T02:24:01Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Parse string puts a null terminates and reads empty strings correctly now&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Given a handle (use json.parse first) allows you to index an array or get a handle to a property by its name&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to&lt;br /&gt;
 ;    Get a property value&lt;br /&gt;
 ;    Index into an array&lt;br /&gt;
 ;    Get the Nth property name of an object&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 3) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot index data type&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 !isnum) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 isnum) {&lt;br /&gt;
     ; Indexing object by index offset&lt;br /&gt;
     var %thisIndex = 0&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget(%value,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       var %propName = $gettok(%item,2-,$asc(.))&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         inc %thisIndex&lt;br /&gt;
       }&lt;br /&gt;
       if (%thisIndex == $2) {&lt;br /&gt;
         return %propName&lt;br /&gt;
       }&lt;br /&gt;
       if ($2 == 0) { return %thisIndex }&lt;br /&gt;
       return $null&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
  var %bv = $json.createbvar&lt;br /&gt;
  if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
&lt;br /&gt;
  var %escape = 0&lt;br /&gt;
  :loop&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
    if (%escape) {&lt;br /&gt;
      ; If the escape character is escaped, append it&lt;br /&gt;
      dec %escape&lt;br /&gt;
      goto append&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      inc %escape&lt;br /&gt;
      ; Skip appending&lt;br /&gt;
      goto trim&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if (%escape) {&lt;br /&gt;
    if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
    if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  :append&lt;br /&gt;
  ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
  if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
    json.trimstart $1 1&lt;br /&gt;
    bset %bv $calc($bvar(%bv,0) +1) 0&lt;br /&gt;
    return %bv&lt;br /&gt;
  }  &lt;br /&gt;
  ; Add this character to the end&lt;br /&gt;
  bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
  :trim&lt;br /&gt;
  ; Trim from the start&lt;br /&gt;
  json.trimstart $1 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
  if ($bvar($1,0)) {&lt;br /&gt;
    goto loop&lt;br /&gt;
  }&lt;br /&gt;
  return %bv&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6129</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6129"/>
		<updated>2014-12-12T17:29:56Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Indexing into objects to get property count and name&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Given a handle (use json.parse first) allows you to index an array or get a handle to a property by its name&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to&lt;br /&gt;
 ;    Get a property value&lt;br /&gt;
 ;    Index into an array&lt;br /&gt;
 ;    Get the Nth property name of an object&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 3) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot index data type&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 !isnum) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 1 &amp;amp;&amp;amp; $2 isnum) {&lt;br /&gt;
     ; Indexing object by index offset&lt;br /&gt;
     var %thisIndex = 0&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget(%value,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       var %propName = $gettok(%item,2-,$asc(.))&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         inc %thisIndex&lt;br /&gt;
       }&lt;br /&gt;
       if (%thisIndex == $2) {&lt;br /&gt;
         return %propName&lt;br /&gt;
       }&lt;br /&gt;
       if ($2 == 0) { return %thisIndex }&lt;br /&gt;
       return $null&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6128</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6128"/>
		<updated>2014-12-07T01:50:38Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: json.freeall is not a public alias&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type != 2) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot use indexer on non-array value&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6127</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6127"/>
		<updated>2014-12-07T01:47:05Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Fixed ParseObject failures not being cleaned up because we were not passing the type&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type != 2) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot use indexer on non-array value&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias -l json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall 1 %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6126</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6126"/>
		<updated>2014-12-07T01:40:48Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type != 2) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot use indexer on non-array value&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias -l json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6125</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6125"/>
		<updated>2014-12-07T01:40:23Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Fixed cleanup of arrays to pass binvar properly instead of evaluating it&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
   else return UNKNOWN TYPE $1&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type != 2) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot use indexer on non-array value&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   ; json.debug Recurse on:   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),1)&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias -l json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug FreeAll: Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       var %elementType = $ifmatch&lt;br /&gt;
       if (%elementType == 3) {&lt;br /&gt;
         inc %i&lt;br /&gt;
         continue&lt;br /&gt;
       }&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,%elementType,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.debug ParseObject Exiting with error %error&lt;br /&gt;
     json.freeall %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug ParseArray: Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ParseArray failed with an unknown error. Read characters: $bvar($1,1)&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6124</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6124"/>
		<updated>2014-12-07T01:08:45Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: /* Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
&lt;br /&gt;
 ;mJSON by Ben Buzbee (ben@st0rm.net)&lt;br /&gt;
 ;&lt;br /&gt;
 ;&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ;&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ;&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type != 2) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot use indexer on non-array value&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias -l json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,$ifmatch,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.freeall %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNKNOWN&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6123</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6123"/>
		<updated>2014-12-07T01:07:45Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.type(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
&lt;br /&gt;
 ;mJSON by Ben Buzbee (ben@st0rm.net)&lt;br /&gt;
 ;&lt;br /&gt;
 ;&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ;&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ;&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   tokenize 32 $1-&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type != 2) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot use indexer on non-array value&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias -l json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,$ifmatch,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
       echo -atg hadd %table value. $+ %strPropName %value&lt;br /&gt;
       echo -atg $hget(%table,value. $+ %strPropName)&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.freeall %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNKNOWN&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6122</id>
		<title>MJSON</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MJSON&amp;diff=6122"/>
		<updated>2014-12-06T23:31:16Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Created page with &amp;quot;This script is a 100% native way of parsing JSON with an easy API  ==Example== To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir   [     {   ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This script is a 100% native way of parsing JSON with an easy API&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
To use the below example, Save the following JSON in &amp;quot;json-example.txt&amp;quot; in $mircdir&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Todd&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:false,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:{&amp;quot;Name&amp;quot;:&amp;quot;Marie&amp;quot;},&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:100000&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;name&amp;quot;:&amp;quot;Gena&amp;quot;,&lt;br /&gt;
        &amp;quot;IsManager&amp;quot;:true,&lt;br /&gt;
        &amp;quot;Spouse&amp;quot;:null,&lt;br /&gt;
        &amp;quot;Sallary&amp;quot;:157103e-3&lt;br /&gt;
    }&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And then run /json-example with the script loaded.&lt;br /&gt;
&lt;br /&gt;
 alias json-example {&lt;br /&gt;
  bread json-example.txt 0 $file(json-example.txt).size &amp;amp;bvar&lt;br /&gt;
 &lt;br /&gt;
  ; Parse top level&lt;br /&gt;
  var %aEmployees = $json.parse(&amp;amp;bvar)&lt;br /&gt;
  echo -atg The top level is a(n) $json.gettype(%aEmployees)&lt;br /&gt;
 &lt;br /&gt;
  var %cEmployees = $json(%aEmployees).length&lt;br /&gt;
  echo -atg Number of employees: %cEmployees&lt;br /&gt;
 &lt;br /&gt;
  var %i = 0&lt;br /&gt;
  while (%i &amp;lt; %cEmployees) {&lt;br /&gt;
    var %oEmployee = $json(%aEmployees,%i)&lt;br /&gt;
    inc %i&lt;br /&gt;
 &lt;br /&gt;
    echo -atg Name: $json(%oEmployee,name).text&lt;br /&gt;
    echo -atg IsManager: $json(%oEmployee,ismanager).text&lt;br /&gt;
    var %spouse = $json(%oEmployee,spouse)&lt;br /&gt;
    if (%spouse) { echo -atg Spouse: $json(%spouse,name).text  }&lt;br /&gt;
    echo -atg Sallary: $json(%oEmployee,Sallary).text&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  ; Free memory&lt;br /&gt;
  json.freeall %aEmployees&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Script==&lt;br /&gt;
&lt;br /&gt;
 ;mJSON by Ben Buzbee (ben@st0rm.net)&lt;br /&gt;
 ;&lt;br /&gt;
 ;&lt;br /&gt;
 ;$json.parse(&amp;amp;bvar | json string) - Parses a json and returns you a &amp;quot;handle&amp;quot;. Use this in the other functions&lt;br /&gt;
 ; If the handle is &amp;quot;ERROR N&amp;quot; then N is an error code&lt;br /&gt;
 ;&lt;br /&gt;
 ; $json(handle,N/PROPERTY) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ;&lt;br /&gt;
 ; $json.freeall(handle)&lt;br /&gt;
 ; Frees memory used to parse the given handle&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 alias json.parse {&lt;br /&gt;
   var %bvar = $false&lt;br /&gt;
   if (&amp;amp;* iswm $1) {&lt;br /&gt;
     %bvar = $1&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     %bvar = $json.createbvar&lt;br /&gt;
     bset -t %bvar 1 $1-&lt;br /&gt;
   }&lt;br /&gt;
   return $json.parseany(%bvar)&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.type(handle) - Returns ARRAY, OBJECT, or Data&lt;br /&gt;
 alias json.type {&lt;br /&gt;
   if ($1 == 1) { return OBJECT }&lt;br /&gt;
   elseif ($1 == 2) { return ARRAY }&lt;br /&gt;
   elseif ($1 == 3) { return DATA }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json(handle [,N/PROPERTY]) &lt;br /&gt;
 ;  Give a handle (use json.parse first) allows you to index an array get a handle to a property&lt;br /&gt;
 ; .text - Returns a data property or array index as text, e.g. $json($json.parse({&amp;quot;foo&amp;quot;:&amp;quot;bar&amp;quot;}),bar).text&lt;br /&gt;
 ; .bvar - Returns a data property or array index as a binvar&lt;br /&gt;
 ; .length - If the property is an array, or the handle is an array that isn&#039;t indexed, returns its length&lt;br /&gt;
 alias json {&lt;br /&gt;
   var %type = $gettok($1,1,32)&lt;br /&gt;
   var %value = $gettok($1,2-,32)&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type != 2) {&lt;br /&gt;
     ; Can&#039;t index non array&lt;br /&gt;
     echo -atg $json Cannot use indexer on non-array value&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Indexing into an array&lt;br /&gt;
   if ($2 isnum &amp;amp;&amp;amp; %type == 2) {&lt;br /&gt;
     var %datatable = $gettok(%value,1,32)&lt;br /&gt;
     var %typetable = $gettok(%value,2,32)&lt;br /&gt;
     var %thistype = $hget(%typetable,$2)&lt;br /&gt;
     var %thisdata = $hget(%datatable,$2)&lt;br /&gt;
     if (!%thistype) { return $null }&lt;br /&gt;
     if (%thistype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %thisdata&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype == 3) {&lt;br /&gt;
       var %bv = $json.createbvar&lt;br /&gt;
       noop $hget(%datatable,$2,%bv) &lt;br /&gt;
       return %thistype %bv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%thistype != 3) {&lt;br /&gt;
       return %thistype %thisdata&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   elseif (%type == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
     ; If we&#039;re not indexing, but we have an array with the .length property, return its length&lt;br /&gt;
     ; Return length of the data table for the array&lt;br /&gt;
     return $hget($gettok(%value,1,32),0).item&lt;br /&gt;
   }&lt;br /&gt;
   ; Getting a property of an object&lt;br /&gt;
   elseif (%type == 1) {&lt;br /&gt;
     var %proptype = $json.getproperty(%value,$2).type&lt;br /&gt;
     var %propvalue = $json.getproperty(%value,$2).value&lt;br /&gt;
     var %propbv = $json.getproperty(%value,$2).bvar&lt;br /&gt;
     if (!%proptype) { return $null }&lt;br /&gt;
     if (%proptype == 3 &amp;amp;&amp;amp; $prop == text) {&lt;br /&gt;
       return %propvalue&lt;br /&gt;
     } &lt;br /&gt;
     elseif (%proptype == 3 &amp;amp;&amp;amp; $prop == bvar) {&lt;br /&gt;
       return %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 3) {&lt;br /&gt;
       return %proptype %propbv&lt;br /&gt;
     }&lt;br /&gt;
     elseif (%proptype == 2 &amp;amp;&amp;amp; $prop == length) {&lt;br /&gt;
       ; Return length of the data table for the array&lt;br /&gt;
       return $hget($gettok(%propvalue,1,32),0).item&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       return %proptype %propvalue&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     echo -atg Unknown usage of $!json --&amp;gt; Prop: $prop P1: $1 P2: $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Hash table layout:&lt;br /&gt;
 ;   Name: json.&amp;lt;ticks&amp;gt;&amp;lt;rand1,4294967295&amp;gt;&lt;br /&gt;
 ;   Key: Name of json property&lt;br /&gt;
 ;   Value &amp;lt;N&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;       N: 1 = pointer to another object, 3 = raw value&lt;br /&gt;
 ;          2 = &amp;lt;pointer to data array&amp;gt; &amp;lt;pointer to assoc array indicating types of data array&amp;gt;&lt;br /&gt;
 ; $json.proxyrecurse(idname, param1, param2)&lt;br /&gt;
 alias -l json.proxyrecurse2 {&lt;br /&gt;
   return $($+($,$1,$chr(40),$2,$chr(44),$3,$chr(41)),2)&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.freeall &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 alias -l json.freeall {&lt;br /&gt;
   if (!$isid) {&lt;br /&gt;
     noop $json.proxyrecurse2(json.freeall,$1,$2-)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   json.debug ?4freeall $1-&lt;br /&gt;
   ; For binary data, just return&lt;br /&gt;
   if ($1 == 3) {&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; For arrays, call on each element then hfree tables&lt;br /&gt;
   elseif ($1 == 2) {&lt;br /&gt;
     var %datatable = $gettok($2,1,32)&lt;br /&gt;
     var %typetable = $gettok($2,2,32)&lt;br /&gt;
     json.debug Freeing array, datatable: %datatable - typetable: %typetable&lt;br /&gt;
     var %i = 0&lt;br /&gt;
     while ($hget(%typetable,%i)) {&lt;br /&gt;
       noop $json.proxyrecurse2(json.freeall,$ifmatch,$hget(%datatable,%i))&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     .hfree %datatable&lt;br /&gt;
     .hfree %typetable&lt;br /&gt;
   }&lt;br /&gt;
   ; For objects, call on every property then hfree table&lt;br /&gt;
   elseif ($1 == 1) {&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($hget($2,%i).item) {&lt;br /&gt;
       inc %i&lt;br /&gt;
       var %item = $ifmatch&lt;br /&gt;
       if (type.* iswm %item) {&lt;br /&gt;
         var %membername = $gettok(%item,2-,$asc(.))&lt;br /&gt;
         noop $json.proxyrecurse2(json.freeall,$hget($2,%item), $hget($2,value. $+ %membername))&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     .hfree $2&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Parses an array, object, string or int, depending on the next character, and returns &amp;lt;type&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ; or returns &amp;quot;ERROR &amp;lt;error&amp;gt;&lt;br /&gt;
 alias -l json.parseany {&lt;br /&gt;
   ; Now, read the value of the property&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_START) { &lt;br /&gt;
     json.debug Parseany detected a string&lt;br /&gt;
     var %strVal = $json.parsestring($1)&lt;br /&gt;
     if (%strVal isnum) {&lt;br /&gt;
       json.debug Error parsing string: %error&lt;br /&gt;
       return ERROR %strVal&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read string value: $bvar(%strval,1-).text&lt;br /&gt;
     return 3 %strval&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     json.debug ParseAny: detected an object&lt;br /&gt;
     var %obj = $json.parseobject($1)&lt;br /&gt;
     if (%obj isnum) { json.debug ParseAny: Error reading object subvalue ( $+ %obj $+ ) | return ERROR %obj } &lt;br /&gt;
     json.debug ParseAny: Read object subvalue as %obj&lt;br /&gt;
     return 1 %obj&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_START) {&lt;br /&gt;
     json.debug Parseany detected an array&lt;br /&gt;
     var %array = $json.parsearray($1)&lt;br /&gt;
     if (%array isnum) {&lt;br /&gt;
       json.debug ParseAny: error reading array subvalue: %array&lt;br /&gt;
       return ERROR %array&lt;br /&gt;
     }&lt;br /&gt;
     json.debug ParseAny: Read array subvalue as %array&lt;br /&gt;
     return 2 %array&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,4).text == null) {&lt;br /&gt;
     json.debug ParseAny found NULL value&lt;br /&gt;
     json.trimstart $1 4&lt;br /&gt;
     return 3 $null&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1,5).text == false || $bvar($1,1,4).text == true) {&lt;br /&gt;
     var %value = $iif($bvar($1,1,5).text == false,$false,$true)&lt;br /&gt;
     ; if true, trim &#039;true&#039; off the top, otherwise trim &#039;false&#039;&lt;br /&gt;
     if (%value) { json.trimstart $1 4 }&lt;br /&gt;
     else { json.trimstart $1 5 }&lt;br /&gt;
     json.debug ParseAny found bool value: %value&lt;br /&gt;
     return 3 %value&lt;br /&gt;
   }&lt;br /&gt;
   ; Must be an int&lt;br /&gt;
   else {&lt;br /&gt;
     var %int = $json.parseint($1) &lt;br /&gt;
     if (%int isnum) { &lt;br /&gt;
       json.debug Error reading int subvalue: %int&lt;br /&gt;
       return ERROR %int&lt;br /&gt;
     }&lt;br /&gt;
     if ($bvar(%int,0) == 0) {&lt;br /&gt;
       return ERROR $json.errors().UNEXPECTED_CHAR&lt;br /&gt;
     }&lt;br /&gt;
     json.debug Read int subvalue into var %int as $bvar(%int,1-).text&lt;br /&gt;
     return 3 %int&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseobject(&amp;amp;binvar) - returns object name or error code&lt;br /&gt;
 alias -l json.parseobject {&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_START) {&lt;br /&gt;
     ; Trim off {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   var %table = $json.createtable&lt;br /&gt;
   var %error = 0&lt;br /&gt;
   :property&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { %error = $json.errors().EXPECTED_START | goto return }&lt;br /&gt;
   ; Property name&lt;br /&gt;
   var %strPropName = $json.parsestring($1)&lt;br /&gt;
   if (%strPropName isnum) { json.debug Error reading property name for object $1 }&lt;br /&gt;
   else {&lt;br /&gt;
     %strPropName = $bvar(%strPropName,1-).text&lt;br /&gt;
     json.debug ParseObject: Read property %strPropName&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     if ($bvar($1,1) != $json.chars().MAPPING) { %error = $json.errors().EXPECTED_MAPPING | goto return }&lt;br /&gt;
     ; trim the mapping character&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     noop $json.consumewhitespace($1)&lt;br /&gt;
     var %any = $json.parseany($1)&lt;br /&gt;
     var %type = $gettok(%any,1,32)&lt;br /&gt;
     var %value = $gettok(%any,2-,32)&lt;br /&gt;
     if (%type == ERROR) {&lt;br /&gt;
       %error = %value&lt;br /&gt;
       goto return&lt;br /&gt;
     }&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       ; rvalue&lt;br /&gt;
       hadd %table type. $+ %strPropName 3&lt;br /&gt;
       if (%value == $true || %value == $false) {&lt;br /&gt;
         json.debug PasrseObject; Found boolean value for %strPropName : %value&lt;br /&gt;
         hadd %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
       elseif (!$bvar(%value,0)) {&lt;br /&gt;
         hadd %table value. $+ %strPropName $null&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         hadd -b %table value. $+ %strPropName %value&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 2) {&lt;br /&gt;
       ; Arrays&lt;br /&gt;
       json.debug Read array %strPropName as %value for object %table&lt;br /&gt;
       hadd %table type. $+ %strPropName 2&lt;br /&gt;
       ; Add array hashtable&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
       echo -atg hadd %table value. $+ %strPropName %value&lt;br /&gt;
       echo -atg $hget(%table,value. $+ %strPropName)&lt;br /&gt;
     }&lt;br /&gt;
     else if (%type == 1) {&lt;br /&gt;
       hadd %table type. $+ %strPropName 1&lt;br /&gt;
       ; Add object naem to table&lt;br /&gt;
       hadd %table value. $+ %strPropName %value&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; Check to see if there are more properties&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().OBJECT_END) { json.debug Successful object parsing. | json.trimstart $1 1 | goto return }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().DELIMITER) { json.trimstart $1 1 | goto property }&lt;br /&gt;
   else { return $json.errors().UNEXPECTED_CHAR }&lt;br /&gt;
   :return&lt;br /&gt;
   if (%error) { &lt;br /&gt;
     json.freeall %table&lt;br /&gt;
     return %error &lt;br /&gt;
   }&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parsestring(&amp;amp;binvar) - returns string (binvar) or error code&lt;br /&gt;
 alias -l json.parsestring {&lt;br /&gt;
   var %bv = $json.createbvar&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().STRING_START) { return $json.errors().EXPECTED_START }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   var %escape = 0&lt;br /&gt;
   :loop&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_ESCAPE) {&lt;br /&gt;
     if (%escape) {&lt;br /&gt;
       ; If the escape character is escaped, append it&lt;br /&gt;
       dec %escape&lt;br /&gt;
       goto append&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       inc %escape&lt;br /&gt;
       ; Skip appending&lt;br /&gt;
       goto trim&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%escape) {&lt;br /&gt;
     if ($bvar($1,1) == $asc(r)) { bset $1 1 $cr | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(n)) { bset $1 1 $lf | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(t)) { bset $1 1 9 | dec %escape }&lt;br /&gt;
     if ($bvar($1,1) == $asc(b)) { bset $1 1 8 | dec %escape }&lt;br /&gt;
   }&lt;br /&gt;
   :append&lt;br /&gt;
   ; Add this character to the end&lt;br /&gt;
   bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
   :trim&lt;br /&gt;
   ; Trim from the start&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; If we&#039;re at the end of a string and not escaped, bail out&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().STRING_END &amp;amp;&amp;amp; !%escape) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %bv&lt;br /&gt;
   }  &lt;br /&gt;
   ; Make sure we haven&#039;t consumed all the input&lt;br /&gt;
   if ($bvar($1,0)) {&lt;br /&gt;
     goto loop&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.parseint(&amp;amp;binvar) - returns an int or error code&lt;br /&gt;
 alias -l json.parseint {&lt;br /&gt;
   var %bv = &amp;amp;json.createbvar&lt;br /&gt;
   ; Lax on the number format since its a string for us anyway&lt;br /&gt;
   while ($bvar($1,1).text isnum || $bvar($1,1) == $asc(.) || $bvar($1,1) == $asc(-) || $bvar($1,1) == $asc(+) || $bvar($1,1) == $asc(e)) {&lt;br /&gt;
     bset %bv $calc($bvar(%bv,0) +1) $bvar($1,1)&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 $json.parsearray - returns array tables (data, types) or error code&lt;br /&gt;
 alias -l json.parsearray {&lt;br /&gt;
   if ($bvar($1,1) != $json.chars().ARRAY_START) {&lt;br /&gt;
     return $json.errors().EXPECTED_START&lt;br /&gt;
   }&lt;br /&gt;
   json.trimstart $1 1&lt;br /&gt;
   ; Create a linked list for the data&lt;br /&gt;
   var %datatable = $json.createtable(1)&lt;br /&gt;
   ; Create a linked list for the types (associative with respect to the data list)&lt;br /&gt;
   var %typetable = $json.createtable(1)&lt;br /&gt;
   :member&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   ; Parse members&lt;br /&gt;
   var %any = $json.parseany($1)&lt;br /&gt;
   var %type = $gettok(%any,1,32)&lt;br /&gt;
   var %value = $gettok(%any,2-,32)&lt;br /&gt;
   if (%type == ERROR) {&lt;br /&gt;
     json.debug Error parsing array member: %value&lt;br /&gt;
     json.freeall 2 %datatable %typetable&lt;br /&gt;
     return %value&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; 0 indexed&lt;br /&gt;
     var %newindex = $calc($hget(%datatable,0).item)&lt;br /&gt;
     ; Binary data&lt;br /&gt;
     json.debug Add type %type array index %newindex as %value&lt;br /&gt;
     if (%type == 3) {&lt;br /&gt;
       hadd -b %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       hadd %datatable %newindex %value&lt;br /&gt;
     }&lt;br /&gt;
     hadd %typetable %newindex %type&lt;br /&gt;
   }&lt;br /&gt;
   noop $json.consumewhitespace($1)&lt;br /&gt;
   if ($bvar($1,1) == $json.chars().DELIMITER) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     goto member&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($bvar($1,1) == $json.chars().ARRAY_END) {&lt;br /&gt;
     json.debug Parsed array successfully&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
     return %datatable %typetable&lt;br /&gt;
   }&lt;br /&gt;
   json.freeall 2 %datatable %typetable&lt;br /&gt;
   return $json.errors().UNKNOWN&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.consumewhitespace(&amp;amp;binvar) - returns number consumed&lt;br /&gt;
 alias -l json.consumewhitespace {&lt;br /&gt;
   while ($bvar($1,0) &amp;amp;&amp;amp; ($bvar($1,1) == 32 || $bvar($1,1) == 9 || $bvar($1,1) == 160 || $bvar($1,1) == 10 || $bvar($1,1) == 13)) {&lt;br /&gt;
     json.trimstart $1 1&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; /json.trimstart &amp;amp;binvar length - trims length characters from the start of $binvar&lt;br /&gt;
 alias -l json.trimstart {&lt;br /&gt;
   ; json.debug Trimstart: $1 $2 -- Size of bvar: $bvar($1,0)&lt;br /&gt;
   if ($bvar($1,0) == 1) { bunset $1 }&lt;br /&gt;
   else { bcopy -c $1 1 $1 $calc($2 +1) -1 }&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.getpropery(table, property).type - Returns number representing type of property&lt;br /&gt;
 ; $json.getproperty(table, property).value - Returns value of property&lt;br /&gt;
 ; $json.getproperty(table, property).bvar - Returns value of property as bvar&lt;br /&gt;
 ; $json.getproperty(table, property).arraytypetable - If property is array, returns its type table&lt;br /&gt;
 ; $json.getproperty(table, property).arraydatatable - If property is table,r eturns its data table&lt;br /&gt;
 alias -l json.getproperty {&lt;br /&gt;
   var %type = $hget($1,type. $+ $2)&lt;br /&gt;
   var %value = $hget($1,value. $+ $2)&lt;br /&gt;
   if ($prop == type) { return %type }&lt;br /&gt;
   if ($prop == bvar) { noop $hget($1, value. $+ $2, &amp;amp;bvReturn) | return &amp;amp;bvReturn }&lt;br /&gt;
   if ($prop == arraytypetable) { return $gettok(%value,2,32) }&lt;br /&gt;
   if ($prop == arraydatatable) { return $gettok(%value,1,32) }&lt;br /&gt;
   return %value&lt;br /&gt;
 }&lt;br /&gt;
 ; $json.createtable - Creates a hashtable and returns its name&lt;br /&gt;
 ; $json.createtable(N) - gives it size N&lt;br /&gt;
 alias -l json.createtable {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %table = $+(json.,%ticks,.,%rand)&lt;br /&gt;
   hmake %table $iif($1,$1,10)&lt;br /&gt;
   return %table&lt;br /&gt;
 }&lt;br /&gt;
 ; Creates a random binvar name to avoid collisions&lt;br /&gt;
 alias -l json.createbvar {&lt;br /&gt;
   var %ticks = $ticks&lt;br /&gt;
   var %rand = $rand(1,4294967295)&lt;br /&gt;
   var %bv = $+(&amp;amp;jsonbv.,%ticks,.,%rand)&lt;br /&gt;
   return %bv&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.chars {&lt;br /&gt;
   ; {&lt;br /&gt;
   if ($prop == OBJECT_START) { return 123 } &lt;br /&gt;
   ; }&lt;br /&gt;
   if ($prop == OBJECT_END) { return 125 } &lt;br /&gt;
   ; &amp;quot;&lt;br /&gt;
   if ($prop == STRING_START || $prop == STRING_END) { return 34 }&lt;br /&gt;
   if ($prop == STRING_ESCAPE) { return $asc(\) }&lt;br /&gt;
   if ($prop == ARRAY_START) { return $asc([) }&lt;br /&gt;
   if ($prop == ARRAY_END) { return $asc(]) }&lt;br /&gt;
   ; ,&lt;br /&gt;
   if ($prop == DELIMITER) { return 44 }&lt;br /&gt;
   if ($prop == MAPPING) { return $asc(:) }&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.errors {&lt;br /&gt;
   if ($prop == EXPECTED_START) { return 1 }&lt;br /&gt;
   if ($prop == EXPECTED_END) { return 2 }&lt;br /&gt;
   if ($prop == EXPECTED_DELIMITER) { return 3 }&lt;br /&gt;
   if ($prop == EXPECTED_MAPPING) { return 4 }&lt;br /&gt;
   if ($prop == UNEXPECTED_CHAR) { return 5 }&lt;br /&gt;
   return 255&lt;br /&gt;
 }&lt;br /&gt;
 alias -l json.debug {&lt;br /&gt;
   ; echo -atg Debug: $1-&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6119</id>
		<title>MHTTP</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=MHTTP&amp;diff=6119"/>
		<updated>2014-11-11T01:44:15Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Created page with &amp;quot;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including * URL Pars...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Often times you may want to download a web page from the internet using HTTP, but HTTP can be complicated.  This script handles most of the basics for you including&lt;br /&gt;
* URL Parsing&lt;br /&gt;
* SSL &lt;br /&gt;
* Cookies&lt;br /&gt;
* Sending POST data&lt;br /&gt;
* Following redirects&lt;br /&gt;
* Handling CHUNKED encoding&lt;br /&gt;
&lt;br /&gt;
Typical usage simply involves using the /http.open, save and close commands to download a file, and creating a signal event to do something with it once it is done.&lt;br /&gt;
== Example ==&lt;br /&gt;
 alias DownloadGooglesLogo {&lt;br /&gt;
  http.open google https://www.google.com/images/srpr/logo11w.png&lt;br /&gt;
  http.save -f google logo.png&lt;br /&gt;
 }&lt;br /&gt;
 on *:SIGNAL:http: {&lt;br /&gt;
   if ($1 == google) {&lt;br /&gt;
      if ($2 == SAVED) { run $3- } &lt;br /&gt;
      if ($2 == PROGRESS) { echo -atg Download progress: $bytes($3).suf $+ / $+ $bytes($4).suf downloaded }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Script ==&lt;br /&gt;
  ; Ben&#039;s (ben@st0rm.net) HTTP for mIRC script (mHTTP)&lt;br /&gt;
 ; Used to download over HTTP&lt;br /&gt;
 ; Basic usage&lt;br /&gt;
 ; /http.open [name] &amp;lt;URL&amp;gt; &lt;br /&gt;
 ;     Opens an HTTP handle for the given URL&lt;br /&gt;
 ; /http.save [-f] &amp;lt;name&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
 ;     Downloads the HTTP resource to the given file&lt;br /&gt;
 ;     -f forces the file to be overwritten&lt;br /&gt;
 ; /http.close &amp;lt;name&amp;gt; &lt;br /&gt;
 ;     Closes and HTTP handle for the given URL&lt;br /&gt;
 ;&lt;br /&gt;
 ; Here are some things you can do after opening a handle, but before saving/downloading it as a file&lt;br /&gt;
 ; Manage POST data:&lt;br /&gt;
 ;   /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
 ; Manage cookies &lt;br /&gt;
 ;   /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
 ;   $http.cookie(handle,N) - gets Nth cookie&lt;br /&gt;
 ;&lt;br /&gt;
 ; After a request completes (e.g. after a save) you can get some data:&lt;br /&gt;
 ;     $http.responseheader(handle, header, N) - Returns value of given responseheader, or $false. Only useful after a request is made&lt;br /&gt;
 ;&lt;br /&gt;
 ; Signals&lt;br /&gt;
 ;   In order to tell via script when something happens (your file is being saved, for example) use&lt;br /&gt;
 ;   on *:SIGNAL:http:&lt;br /&gt;
 ;   where $1 = Handle name, $2 = Event name, $3- = parameters&lt;br /&gt;
 ;   Events:&lt;br /&gt;
 ;        COMPLETED &amp;lt;statuscode&amp;gt;   -  when a request completes&lt;br /&gt;
 ;        SAVED &amp;lt;location&amp;gt;         - when a save completes&lt;br /&gt;
 ;        REDIRECT &amp;lt;location&amp;gt;      - when a request is redirected elsewhere &lt;br /&gt;
 ;        PROGRESS &amp;lt;bytes&amp;gt; [total size] - When part but not all of the data is downloaded. Total size is not available for chunked transfer&lt;br /&gt;
 ;       &lt;br /&gt;
 ;&lt;br /&gt;
 ; Known issues:&lt;br /&gt;
 ;    Cookies do not respect attributes such as domain, path, expiration, or secure. They area always sent&lt;br /&gt;
 ;    Relative redirects don&#039;t work, only absolute&lt;br /&gt;
 ;    Redirects from insecure to secure links when you don&#039;t have SSL will not be handled well&lt;br /&gt;
 ; Creates a request that when completed is saved to a file&lt;br /&gt;
 alias http.save {&lt;br /&gt;
   if ($1 == -f) {&lt;br /&gt;
     var %force = $true&lt;br /&gt;
     tokenize 32 $2-&lt;br /&gt;
   }&lt;br /&gt;
   else { var %force = $false }&lt;br /&gt;
   if ($0 &amp;lt; 2) { &lt;br /&gt;
     echo -atg * /http.save: Use /http.save &amp;lt;http handle&amp;gt; &amp;lt;file&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) { &lt;br /&gt;
     echo -atg * /http.save: No such HTTP handle exists. Use /http.open first&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($sock(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.save: There is a pending request for this HTTP handle already. A connection is open the the remote host. Close it, or wait for it to complete &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %file = $2-&lt;br /&gt;
   if ($exists(%file)) {&lt;br /&gt;
     if (%force) {&lt;br /&gt;
       .remove %file&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       echo -atg * /http.save: File already exists. Try /http.save -f to force an overwrite&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ;  Open file handler stream for writing&lt;br /&gt;
   .fopen -n %hashtable %file&lt;br /&gt;
   if ($ferr) {&lt;br /&gt;
     echo -atg * /http.save: Could not use file. Error: $ferr&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Store fstream name in hashtable to indicate that we want to save this once it completes&lt;br /&gt;
   .hadd %hashtable fstream %hashtable&lt;br /&gt;
   ; Set redirect counter to 0&lt;br /&gt;
   .hadd %hashtable redirects 0&lt;br /&gt;
   ; Start socket&lt;br /&gt;
   http.sockstart %hashtable&lt;br /&gt;
 }&lt;br /&gt;
 ; Opens socket&lt;br /&gt;
 alias -l http.sockstart {&lt;br /&gt;
   if ($hget($1,secure) == $true) {&lt;br /&gt;
     if ($sock($1) &amp;amp;&amp;amp; $sock($1).ssl) { &lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     } &lt;br /&gt;
     else {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
       if (!$sslready) {&lt;br /&gt;
         echo -atg HTTP Error - request $qt($right($1,-5)) would require you to use SSL but you are not SSL ready. Please see http://mirc.com/ssl.html&lt;br /&gt;
         http.close $right($1,-5)&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       sockopen -e $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     if ($sock($1)) {&lt;br /&gt;
       http.sendrequest $1&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       sockopen $1 $hget($1, host) $hget($1, port)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Sends the request. Assumes the socket is open and ready&lt;br /&gt;
 alias -l http.sendrequest {&lt;br /&gt;
   if (!$sock($1)) { echo -atg HTTP Internal error. Socket not open in /http.sendrequest | return }&lt;br /&gt;
   var %inname = $1&lt;br /&gt;
   var %exname = $right($1,-5)&lt;br /&gt;
   ; Cleanup from previous requests&lt;br /&gt;
   .hdel %inname responseheaders&lt;br /&gt;
   .hdel %inname body&lt;br /&gt;
   .hdel %inname contentlength&lt;br /&gt;
   .hdel %inname chunkleft&lt;br /&gt;
   ; Get POST binvar&lt;br /&gt;
   var %postBV = $http.postdata(%inname)&lt;br /&gt;
   ; Use GET if there&#039;s no post data, POST otherwise&lt;br /&gt;
   if ($bvar(%postBV,0) == 0) {&lt;br /&gt;
     sockwrite -n %inname GET $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     sockwrite -n %inname POST $hget(%inname,path) HTTP/1.1 &lt;br /&gt;
   }&lt;br /&gt;
   ; Send the host&lt;br /&gt;
   sockwrite -n %inname Host: $hget(%inname,host)&lt;br /&gt;
   ; Send the user-agent&lt;br /&gt;
   sockwrite -n %inname User-agent: $hget(%inname,user-agent)&lt;br /&gt;
   sockwrite -n %inname Connection: Keep-Alive&lt;br /&gt;
   ; Only plain-text supported&lt;br /&gt;
   sockwrite -n %inname Accept: text/plain; q=0.5, text/html&lt;br /&gt;
   ; Send cookies&lt;br /&gt;
   if ($http.cookie(%exname, 0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname Cookie: $+ $chr(32)&lt;br /&gt;
     var %i = 1&lt;br /&gt;
     while ($http.cookie(%exname, %i)) {&lt;br /&gt;
       sockwrite %inname $http.urlencode($ifmatch) $+ ;&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
     sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
   ; If POST send content type and length&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite -n %inname Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
     sockwrite -n %inname Content-Length: $v1&lt;br /&gt;
   }&lt;br /&gt;
   ; End request headers with empty line&lt;br /&gt;
   sockwrite -n %inname&lt;br /&gt;
   ; If POST, send data&lt;br /&gt;
   if ($bvar(%postBV,0) &amp;gt; 0) {&lt;br /&gt;
     sockwrite %inname %postBV&lt;br /&gt;
     ; sockwrite -n %inname&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Just some bookeeping in case we miss it &lt;br /&gt;
 on *:SOCKCLOSE:http.*: {&lt;br /&gt;
   if ($fopen($sockname)) { .fclose $sockname }&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKOPEN:http.*: {&lt;br /&gt;
   http.sendrequest $sockname&lt;br /&gt;
 }&lt;br /&gt;
 on *:SOCKREAD:http.*: {&lt;br /&gt;
   if ($sockerr &amp;gt; 0) {&lt;br /&gt;
     echo -atg * HTTP handle $qt($right($sockname,-5)) failed with socket error $sockerr&lt;br /&gt;
     http.close $right($sockname,-5)&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ;Read all the data into &amp;amp;bvTemp&lt;br /&gt;
   :read&lt;br /&gt;
   ; First, read this buffer&lt;br /&gt;
   sockread &amp;amp;bvThis&lt;br /&gt;
   if ($sockbr &amp;gt; 0) {&lt;br /&gt;
     ; If data was read, copy it to the on-going buffer and continue&lt;br /&gt;
     bcopy &amp;amp;bvTemp $calc($bvar(&amp;amp;bvTemp,0) +1) &amp;amp;bvThis 1 -1&lt;br /&gt;
     goto read&lt;br /&gt;
   }&lt;br /&gt;
   if ($bvar(&amp;amp;bvTemp,0) == 0) { echo -atg HTTP sockread called with no data to read... | return }&lt;br /&gt;
   ; Do we need to parse headers?&lt;br /&gt;
   ; If response headers not set, this call has the headers&lt;br /&gt;
   if (!$hget($sockname,responseheaders)) {&lt;br /&gt;
     ; Find the response headers&lt;br /&gt;
     var %end = $bfind(&amp;amp;bvTemp,1, 13 10 13 10)&lt;br /&gt;
     if (%end == 0) { echo -atg Error: No headers found, but no content length so this can&#039;t be a subsequent call | return }&lt;br /&gt;
     ; Increase to include the 3 extra characters&lt;br /&gt;
     inc %end 3&lt;br /&gt;
     bcopy &amp;amp;bvResponseHeaders 1 &amp;amp;bvTemp 1 %end&lt;br /&gt;
     ; If there&#039;s still non-header data copy it over&lt;br /&gt;
     if (%end &amp;lt; $bvar(&amp;amp;bvTemp, 0)) { &lt;br /&gt;
       bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%end + 1) -1&lt;br /&gt;
     }&lt;br /&gt;
     else { bunset &amp;amp;bvTemp }&lt;br /&gt;
     ; If this is the first header response, save in hash table&lt;br /&gt;
     if (!$hget($sockname, responseheaders)) {&lt;br /&gt;
       .hadd -b $sockname responseheaders &amp;amp;bvResponseHeaders &lt;br /&gt;
       http.OnHeaders $sockname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; there&#039;s no more to read, bail&lt;br /&gt;
   if (!$bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
     ; If that&#039;s all this response has to offer, we&#039;re done&lt;br /&gt;
     if ($hget($sockname,contentlength) == 0) {&lt;br /&gt;
       http.ondone $sockname&lt;br /&gt;
     }&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Read body&lt;br /&gt;
   var %contentlength = $hget($sockname,contentlength)&lt;br /&gt;
   if (%contentlength == $null || %contentlength == $false) {&lt;br /&gt;
     var %chunked = $true&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     var %chunked = $false&lt;br /&gt;
   }&lt;br /&gt;
   ; Set bvBody to the body ready so far&lt;br /&gt;
   if ($hget($sockname,body)) {&lt;br /&gt;
     noop $hget($sockname, body, &amp;amp;bvBody)&lt;br /&gt;
   }&lt;br /&gt;
   if (%chunked) {&lt;br /&gt;
     :readChunk&lt;br /&gt;
     var %chunkleft = $hget($sockname, chunkleft)&lt;br /&gt;
     if (!%chunkleft) {&lt;br /&gt;
       ; If we&#039;ve read all of a chunk, or this is the first one, get its size&lt;br /&gt;
       var %crlf = $bfind(&amp;amp;bvTemp, 1, 13 10)&lt;br /&gt;
       if (!%crlf) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5)) is in chunked encoding but no chunk size found in data&lt;br /&gt;
         return&lt;br /&gt;
       }&lt;br /&gt;
       var %chunkleft = $base($bvar(&amp;amp;bvTemp, 1, $calc(%crlf - 1)).text,16,10)&lt;br /&gt;
       if (%chunkleft !isnum) {&lt;br /&gt;
         echo -atg HTTP internal error. Handle $qt($right($sockname,-5))  has invalid chunk size: %chunkleft&lt;br /&gt;
       }&lt;br /&gt;
       ; New chunk&lt;br /&gt;
       else {&lt;br /&gt;
         ; Save chunk size and remove it (plus its \r\n) from temp&lt;br /&gt;
         .hadd $sockname chunkleft %chunkleft&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%crlf +2) -1&lt;br /&gt;
         if (%chunkLeft == 0) { &lt;br /&gt;
           ; if we just read chunk size 0, that was the end. &lt;br /&gt;
           .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
           http.onDone $sockname&lt;br /&gt;
           return&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; read %chunkleft bytes&lt;br /&gt;
     var %bytesToRead = $iif(%chunkLeft &amp;lt; $bvar(&amp;amp;bvTemp,0),%chunkLeft, $bvar(&amp;amp;bvTemp,0))&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 %bytesToRead&lt;br /&gt;
     hdec $sockname chunkleft %bytesToRead&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS %bytesToRead&lt;br /&gt;
     if (%bytesToRead == %chunkLeft) {&lt;br /&gt;
       ; If this is the end of the chunk, consume crlf&lt;br /&gt;
       inc %bytesToRead 2&lt;br /&gt;
       ; If there&#039;s still stuff in this var, go back and read the new chunk&lt;br /&gt;
       if (%bytesToRead &amp;lt; $bvar(&amp;amp;bvTemp,0)) {&lt;br /&gt;
         .bcopy -c &amp;amp;bvTemp 1 &amp;amp;bvTemp $calc(%bytesToRead + 1) -1    &lt;br /&gt;
         goto readChunk&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     ; End of this call, save body in hashtable for future calls&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
   }&lt;br /&gt;
   else {&lt;br /&gt;
     ; Copy everything&lt;br /&gt;
     .bcopy &amp;amp;bvBody $calc($bvar(&amp;amp;bvBody,0) +1) &amp;amp;bvTemp 1 -1&lt;br /&gt;
     ; Add read-so-far body to hashtable&lt;br /&gt;
     .hadd -b $sockname body &amp;amp;bvBody&lt;br /&gt;
     .signal http $right($sockname,-5) PROGRESS $bvar(&amp;amp;bvBody,0) $hget($sockname,contentlength) &lt;br /&gt;
     ; Check if we&#039;re done&lt;br /&gt;
     if ($bvar(&amp;amp;bvBody,0) &amp;gt;= $hget($sockname,contentlength)) { &lt;br /&gt;
       ; We&#039;re done&lt;br /&gt;
       http.onDone $sockname&lt;br /&gt;
       if ($bvar(&amp;amp;bvBody,0) &amp;gt; $hget($sockname,contentlength)) { &lt;br /&gt;
         echo -qatg HTTP Warning: Read past content-length.&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when we get the first header response from the server&lt;br /&gt;
 alias -l http.OnHeaders {&lt;br /&gt;
   noop $hget($1,responseheaders, &amp;amp;bvResponseHeaders)&lt;br /&gt;
   ; First header is always the status response&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, 1, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= 1) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server did not respond with HTTP status &lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %status = $bvar(&amp;amp;bvResponseHeaders,1,$calc(%end - 1)).text&lt;br /&gt;
   .hadd $1 responsestatus %status&lt;br /&gt;
   var %version = $gettok(%status,1,32)&lt;br /&gt;
   if (%version != HTTP/1.1 &amp;amp;&amp;amp; %version != HTTP/1.0) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated unknown version: %version&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   var %statuscode = $gettok(%status,2,32)&lt;br /&gt;
   if (%statuscode !isnum) {&lt;br /&gt;
     echo -atg Error: HTTP $qt($right($1,-5)) Server indicated invalid status code: %statuscode&lt;br /&gt;
     goto cleanup&lt;br /&gt;
   }&lt;br /&gt;
   ; Check transfer type&lt;br /&gt;
   if ($http.responseheader($right($1,-5),Content-Length,1) != $null) {&lt;br /&gt;
     hadd $1 contentlength $v1&lt;br /&gt;
   }&lt;br /&gt;
   elseif ($http.responseheader($right($1,-5), Transfer-Encoding, 1) != chunked) {&lt;br /&gt;
     echo -atg HTTP Handle $qt($right($1,-5)) has no content-length header and is not chunked&lt;br /&gt;
     echo -atg $bvar(&amp;amp;bvResponseHeaders,1-).text&lt;br /&gt;
     return&lt;br /&gt;
   } &lt;br /&gt;
   ; Add any cookies that were set&lt;br /&gt;
   var %cookie = 1&lt;br /&gt;
   while ($http.responseheader($right($1,-5), Set-Cookie, %cookie)) {&lt;br /&gt;
     inc %cookie&lt;br /&gt;
     var %cookieValue = $gettok($ifmatch,1,$asc(;))&lt;br /&gt;
     http.addcookie $right($1,-5) %cookieValue&lt;br /&gt;
   }&lt;br /&gt;
   ; Handle redirects&lt;br /&gt;
   if (%statuscode == 303 || %statuscode == 302 || %statuscode == 307) {&lt;br /&gt;
     var %location = $http.responseheader($right($1,-5), Location)&lt;br /&gt;
     .signal http $right($1,-5) REDIRECT %location&lt;br /&gt;
     http.seturl $1 %location&lt;br /&gt;
     hinc $1 redirects&lt;br /&gt;
     ; If the server doesn&#039;t promise us this connection is keep-alive, close it and use a new one&lt;br /&gt;
     if ($http.responseheader($right($1,-5), Connection, 1) != Keep-Alive) {&lt;br /&gt;
       if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
     }&lt;br /&gt;
     if ($hget($1, redirects) &amp;gt; 5) {&lt;br /&gt;
       echo -atg HTTP Handle $qt($right($1,-5)) redirect loop detected&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     http.sockstart $1&lt;br /&gt;
   }&lt;br /&gt;
   return&lt;br /&gt;
   :cleanup&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addcookie {&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.addcookie: No such HTTP handle&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.addcookie: Usage: /http.addcookie &amp;lt;handle&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %cookieNum = 1&lt;br /&gt;
   while ($hget(%hashtable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (cookie-* iswm %item) {&lt;br /&gt;
       var %tmp = $gettok(%item,2,$asc(-))&lt;br /&gt;
       ; If the value is the same, overwrite&lt;br /&gt;
       if ($2- == $hget(%hashtable,%item)) {&lt;br /&gt;
         %cookieNum = %tmp&lt;br /&gt;
         break&lt;br /&gt;
       }&lt;br /&gt;
       ; Otherwise store max cookie num&lt;br /&gt;
       elseif (%tmp &amp;gt;= %cookieNum) { %cookieNum = $calc(%tmp + 1) }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   .hadd %hashtable cookie- $+ %cookieNum $+ -value $2-&lt;br /&gt;
 }&lt;br /&gt;
 ; Gets an HTTP cookie for the given handle&lt;br /&gt;
 ; $http.cookie(&amp;lt;handle&amp;gt;, &amp;lt;index&amp;gt;)&lt;br /&gt;
 alias http.cookie {&lt;br /&gt;
   if ($2 !isnum) {&lt;br /&gt;
     echo -atg * $!http.cookie invalid parameters&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %hashtable = http. $+ $1&lt;br /&gt;
   if (!$hget(%hashtable)) {&lt;br /&gt;
     echo -atg * $!http.cookie no such HTTP handle &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %found = 0&lt;br /&gt;
   while ($hget(%hashtable, %i).item) {&lt;br /&gt;
     inc %i&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     if (cookie-*-value iswm %item) {&lt;br /&gt;
       inc %found&lt;br /&gt;
       var %id = $gettok(%item,2,$asc(-))&lt;br /&gt;
       if (%found == $2) {&lt;br /&gt;
         return $hget(%hashtable, cookie- $+ %id $+ -value)&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if ($2 == 0) { return %found }&lt;br /&gt;
 }&lt;br /&gt;
 ; Returns the value of the given response header&lt;br /&gt;
 ; Case sensitive since it is binary variable operation&lt;br /&gt;
 alias http.responseheader {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg Invalid usage. Use: $http.responseheader(&amp;lt;handle&amp;gt;, &amp;lt;header&amp;gt;[, index])&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; %index is parameter 3 - 1-N or 0 for number&lt;br /&gt;
   var %index = 1&lt;br /&gt;
   if ($3 isnum) {&lt;br /&gt;
     %index = $3&lt;br /&gt;
   }&lt;br /&gt;
   ; Veriy hash table exists&lt;br /&gt;
   var %table = http. $+ $1&lt;br /&gt;
   if (!$hget(%table,responseheaders)) { return $false }&lt;br /&gt;
   noop $hget(%table,responseheaders, &amp;amp;bvResponseHeaders))&lt;br /&gt;
   ; Set up parameters for the header search&lt;br /&gt;
   ; Offset is where we start searching from in the loop&lt;br /&gt;
   ; thisIndex is the number of matches found so far&lt;br /&gt;
   var %offset = 1&lt;br /&gt;
   var %thisIndex = 0&lt;br /&gt;
   ; The main search loop&lt;br /&gt;
   :search&lt;br /&gt;
   var %start = $bfind(&amp;amp;bvResponseHeaders, %offset, $2 $+ :).text&lt;br /&gt;
   ; If there isn&#039;t another match, either the index was too large or they want the count&lt;br /&gt;
   if (!%start) { &lt;br /&gt;
     if (%index == 0) { return %thisIndex }&lt;br /&gt;
     else { return $false  }&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the end of the header&lt;br /&gt;
   var %end = $bfind(&amp;amp;bvResponseHeaders, %start, 13 10)&lt;br /&gt;
   if (%end &amp;lt;= %start) { echo -atg HTTP Internal error ($http.responseheader), header $qt($2) for handle $qt($1) has no CRLF termination | return $false }&lt;br /&gt;
   inc %thisIndex&lt;br /&gt;
   ; If this not the one they asked for, try again starting from the end of this header&lt;br /&gt;
   if (%index != %thisIndex) {&lt;br /&gt;
     var %offset = %end&lt;br /&gt;
     goto search&lt;br /&gt;
   }&lt;br /&gt;
   ; Find the colon. If there isn&#039;t one we&#039;ll return the whole string&lt;br /&gt;
   if ($calc($bfind(&amp;amp;bvResponseHeaders, %start, $asc(:)) +1) &amp;lt; %end) {&lt;br /&gt;
     %start = $v1&lt;br /&gt;
   }&lt;br /&gt;
   return $bvar(&amp;amp;bvResponseHeaders,%start, $calc(%end - %start)).text&lt;br /&gt;
 }&lt;br /&gt;
 ; Internal event called when a request is filled. $1 = internal handle name&lt;br /&gt;
 alias -l http.onDone {&lt;br /&gt;
   if ($hget($1,responseheaders)) {&lt;br /&gt;
     noop $hget($1,responseheaders,&amp;amp;bvResponseHeaders)&lt;br /&gt;
   }&lt;br /&gt;
   ; Write out and close filestream if set&lt;br /&gt;
   if ($hget($1,fstream)) {&lt;br /&gt;
     noop $hget($1,body,&amp;amp;bvBody)&lt;br /&gt;
     if (!$bvar(&amp;amp;bvBody,0)) {&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       .fwrite -b $hget($1,fstream) &amp;amp;bvBody&lt;br /&gt;
       ; Save fname so we can use it in the signal after the close&lt;br /&gt;
       var %fname = $fopen($hget($1,fstream)).fname&lt;br /&gt;
       .fclose $hget($1,fstream)&lt;br /&gt;
       .signal http $right($1,-5) SAVED %fname&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   ; If socket is still around, close it&lt;br /&gt;
   if ($sock($1)) { .sockclose $1 }&lt;br /&gt;
   ; Reset redirects for future calls&lt;br /&gt;
   .hadd $1 redirects 0&lt;br /&gt;
   .signal HTTP $right($1,-5) COMPLETED $hget($1,responsestatus)&lt;br /&gt;
 }&lt;br /&gt;
 alias http.open {&lt;br /&gt;
   var %hashtable&lt;br /&gt;
   ; If two parameters are given, $1 is the name, else create a new name&lt;br /&gt;
   if ($0 &amp;gt; 1) { %hashtable = http. $+ $1 }&lt;br /&gt;
   else { &lt;br /&gt;
     var %h = $calc($ticks % $rand(1,1000000))&lt;br /&gt;
     var %hashtable = http. $+ %h  &lt;br /&gt;
   }&lt;br /&gt;
   if ($hget(%hashtable)) {&lt;br /&gt;
     echo -atg * /http.open: HTTP Request $qt($1) already exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Encode the octects of the URL. e.g.: foo bar,pie --&amp;gt; foo%20bar,%2Cpie&lt;br /&gt;
   var %url = $iif($0 &amp;gt; 1, $2-, $1-)&lt;br /&gt;
   var %urlEncoded = $http.urlencode(%url)&lt;br /&gt;
   ; Check the URL&lt;br /&gt;
   if (!$http.urlparse(%urlEncoded)) {&lt;br /&gt;
     echo -atg * /http.open: Malformed URL. Use /http.open [name] &amp;lt;url&amp;gt; &lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if ($http.urlparse(%urlEncoded).secure &amp;amp;&amp;amp; !$sslready) {&lt;br /&gt;
     echo -atg * /http.open: URL is SSL but $!sslready = false. See http://www.mirc.com/ssl.html&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   ; Everything checks out, let&#039;s store the relevent bits in a hashtable&lt;br /&gt;
   .hmake %hashtable 10&lt;br /&gt;
   http.seturl %hashtable %url&lt;br /&gt;
   echo -qatg * /http.open: Opened HTTP request $qt($right(%hashtable,-5))&lt;br /&gt;
 }&lt;br /&gt;
 alias -l http.seturl {&lt;br /&gt;
   if ($0 &amp;lt; 2) { echo -atg HTTP Internal error, seturl called with no url }&lt;br /&gt;
   var %hashtable = $1&lt;br /&gt;
   if (!$hget(%hashtable)) { echo -atg HTTP Internal error, seturl called with invalid handle }&lt;br /&gt;
   var %urlEncoded = $http.urlencode($2-)&lt;br /&gt;
   .hadd %hashtable host $http.urlparse(%urlEncoded).host&lt;br /&gt;
   .hadd %hashtable port $http.urlparse(%urlEncoded).port&lt;br /&gt;
   .hadd %hashtable secure $http.urlparse(%urlEncoded).secure&lt;br /&gt;
   .hadd %hashtable path $http.urlparse(%urlEncoded).path&lt;br /&gt;
   .hadd %hashtable user-agent mIRC $version&lt;br /&gt;
 }&lt;br /&gt;
 alias http.list {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     inc %i&lt;br /&gt;
     if (http.* iswm %table) {&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -atg * $right(%table,-5) $+ : Host: $hget(%table,host) Port: $hget(%table,port) Path: $hget(%table,path)&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) { echo -atg * No HTTP handles opened }&lt;br /&gt;
 }&lt;br /&gt;
 alias http.close {&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   var %numFound = 0&lt;br /&gt;
   while (%i &amp;lt;= $hget(0)) {&lt;br /&gt;
     var %table = $hget(%i)&lt;br /&gt;
     if (http. $+ $1 iswm %table) {&lt;br /&gt;
       ; Cleanup resources&lt;br /&gt;
       if ($sock(%table)) { .sockclose %table }&lt;br /&gt;
       if ($fopen(%table)) { .fclose %table }&lt;br /&gt;
       if ($hget(%table $+ .postdata)) { .hfree %table $+ .postdata }&lt;br /&gt;
       .hfree %table&lt;br /&gt;
       inc %numFound&lt;br /&gt;
       echo -qatg * HTTP Closed $right(%table,-5) &lt;br /&gt;
     }&lt;br /&gt;
     ; Only inc if we didnt find a match&lt;br /&gt;
     else {&lt;br /&gt;
       inc %i&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
   if (%numFound == 0) {  echo -qatg * No matching HTTP handles } &lt;br /&gt;
 }&lt;br /&gt;
 ;&lt;br /&gt;
 ; Takes an encoded URL and returns based on the property&lt;br /&gt;
 ; .secure - true if https&lt;br /&gt;
 ; .host - the URL host&lt;br /&gt;
 ; .port - the port, 80 by default&lt;br /&gt;
 ; .path - The path to download, / by default&lt;br /&gt;
 ;  Returns $false is URL is malformed&lt;br /&gt;
 ;&lt;br /&gt;
 alias http.urlparse {&lt;br /&gt;
   var %secure&lt;br /&gt;
   var %host&lt;br /&gt;
   var %port&lt;br /&gt;
   var %path&lt;br /&gt;
   var %regex = /^(https?://)?([a-z.0-9\-_]+)(:\d+)?(/.*)?$/i&lt;br /&gt;
   if ($regex($1, %regex)) {&lt;br /&gt;
     if (http*:// iswm $regml(1)) {&lt;br /&gt;
       %protocol = $lower($left($regml(1),-3))&lt;br /&gt;
     }&lt;br /&gt;
     %secure = $false&lt;br /&gt;
     if (%protocol == https) { &lt;br /&gt;
       %secure = $true&lt;br /&gt;
     }&lt;br /&gt;
     if (%protocol) { %host = $regml(2) }&lt;br /&gt;
     else { %host = $regml(1) }  &lt;br /&gt;
     var %portIndex = $iif(%protocol, 3, 2)&lt;br /&gt;
     var %portfound = $false&lt;br /&gt;
     if ($left($regml(%portIndex),1) == : &amp;amp;&amp;amp; $right($regml(%portIndex),-1) isnum) {&lt;br /&gt;
       %port = $right($regml(%portIndex),-1) &lt;br /&gt;
       %portFound = $true&lt;br /&gt;
     }&lt;br /&gt;
     else {&lt;br /&gt;
       if (%secure) { &lt;br /&gt;
         %port = 443&lt;br /&gt;
       }&lt;br /&gt;
       else {&lt;br /&gt;
         %port = 80&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
     %pathIndex = $iif(%portFound, $calc(%portIndex + 1), %portIndex)&lt;br /&gt;
     if ($regml(0) &amp;gt;= %pathIndex) {&lt;br /&gt;
       %path = $regml(%pathIndex)&lt;br /&gt;
     }&lt;br /&gt;
     else { %path = / }&lt;br /&gt;
   }  &lt;br /&gt;
   else { return $false }&lt;br /&gt;
   if ($prop == secure) { return %secure }&lt;br /&gt;
   if ($prop == host) { return %host }&lt;br /&gt;
   if ($prop == path) { return %path }&lt;br /&gt;
   if ($prop == port) { return %port }&lt;br /&gt;
   return $true&lt;br /&gt;
 }&lt;br /&gt;
 alias http.addpost {&lt;br /&gt;
   if ($0 &amp;lt; 3) { &lt;br /&gt;
     echo -atg * /http.addpost: Usage: /http.addpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt; &amp;lt;value&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable)) { .hmake %posttable 10 }&lt;br /&gt;
   hadd %posttable $2 $3-&lt;br /&gt;
   echo -qatg Post variable added: $2 $+ = $+ $3-&lt;br /&gt;
 }&lt;br /&gt;
 alias http.delpost {&lt;br /&gt;
   if ($0 &amp;lt; 2) {&lt;br /&gt;
     echo -atg * /http.delpost: Usage: /http.delpost &amp;lt;handle&amp;gt; &amp;lt;variable&amp;gt;&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   var %maintable = http. $+ $1&lt;br /&gt;
   var %posttable = %maintable $+ .postdata&lt;br /&gt;
   if (!$hget(%maintable)) {&lt;br /&gt;
     echo -atg * /http.addpost: No such HTTP handle found&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   if (!$hget(%posttable,$2)) {&lt;br /&gt;
     echo -qatg * /http.addpost: No post variable with that name exists&lt;br /&gt;
     return&lt;br /&gt;
   }&lt;br /&gt;
   hdel %posttable $2&lt;br /&gt;
   echo -qatg Post variable deleted: $2&lt;br /&gt;
 }&lt;br /&gt;
 ; Usage: $http.postdata(handle)&lt;br /&gt;
 ; Returns a binvar with a post data style query string (x=y&amp;amp;z=a) or $false&lt;br /&gt;
 alias http.postdata {&lt;br /&gt;
   ; Post data is stored in INNAME.postdata&lt;br /&gt;
   var %posttable = http. $+ $1 $+ .postdata&lt;br /&gt;
   if (!$hget(%posttable,0).item) { return $false }&lt;br /&gt;
   var %i = 1&lt;br /&gt;
   while ($hget(%posttable,%i).item) {&lt;br /&gt;
     var %item = $ifmatch&lt;br /&gt;
     bset -t &amp;amp;bvPost $calc($bvar(&amp;amp;bvPost,0) +1) $iif(%i &amp;gt; 1,&amp;amp;,) $+ $http.urlencode(%item) $+ = $+ $http.urlencode($hget(%posttable,%item))&lt;br /&gt;
     inc %i&lt;br /&gt;
   }&lt;br /&gt;
   return &amp;amp;bvPost&lt;br /&gt;
 }&lt;br /&gt;
 ; Replace some commonly touchy URL characters with their hex-octect encoding&lt;br /&gt;
 alias -l http.urlencode {&lt;br /&gt;
   return $replacex($1-,$chr(32),% $+ 20, $chr(44), % $+ 2C,+, % $+ 2B, $chr(37), % $+ 25)&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
{{Author|aca20031}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Script Archive]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Editbox&amp;diff=4869</id>
		<title>Editbox</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Editbox&amp;diff=4869"/>
		<updated>2010-12-03T23:16:08Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: added -c&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This command controls the window edit box, and can be used to manipulate the text inside. (An editbox is the place where text is typed in a window, for example where you would type your message)&lt;br /&gt;
&lt;br /&gt;
 /editbox [-safnopbNeNvqNc] [window] &amp;lt;text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Choosing your window&lt;br /&gt;
* -s specifies status window&lt;br /&gt;
* -a specifies the active window&lt;br /&gt;
* you can use [window] to give a specific window name&lt;br /&gt;
** If the window is a DCC chat, prefix it with =&lt;br /&gt;
&lt;br /&gt;
* -f sets the focus to the editbox of the specified window&lt;br /&gt;
* -p appends a space to the end of the text&lt;br /&gt;
* -n will press enter after the text has been entered&lt;br /&gt;
* -o specifies the second editbox, if the given window has two&lt;br /&gt;
* -bNeN will select text in the edit box, starting at b and ending at e&lt;br /&gt;
** To select characters 5-10, /editbox -b5e10 #help.script This text will be highlighted&lt;br /&gt;
* -v prevents the content from being changed&lt;br /&gt;
* -qN enables, disables, or toggles (on/off) the second edit box.&lt;br /&gt;
* -c clears the editbox &#039;&#039;&#039;Note: This is undocumented functionality and may be removed in future versions&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
[[$editbox]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Commands]][[Category:Custom Windows]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Editbox&amp;diff=2782</id>
		<title>Editbox</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Editbox&amp;diff=2782"/>
		<updated>2010-12-03T23:15:46Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This command controls the window edit box, and can be used to manipulate the text inside. (An editbox is the place where text is typed in a window, for example where you would type your message)&lt;br /&gt;
&lt;br /&gt;
 /editbox [-safnopbNeNvqN] [window] &amp;lt;text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Choosing your window&lt;br /&gt;
* -s specifies status window&lt;br /&gt;
* -a specifies the active window&lt;br /&gt;
* you can use [window] to give a specific window name&lt;br /&gt;
** If the window is a DCC chat, prefix it with =&lt;br /&gt;
&lt;br /&gt;
* -f sets the focus to the editbox of the specified window&lt;br /&gt;
* -p appends a space to the end of the text&lt;br /&gt;
* -n will press enter after the text has been entered&lt;br /&gt;
* -o specifies the second editbox, if the given window has two&lt;br /&gt;
* -bNeN will select text in the edit box, starting at b and ending at e&lt;br /&gt;
** To select characters 5-10, /editbox -b5e10 #help.script This text will be highlighted&lt;br /&gt;
* -v prevents the content from being changed&lt;br /&gt;
* -qN enables, disables, or toggles (on/off) the second edit box.&lt;br /&gt;
* -c clears the editbox &#039;&#039;&#039;Note: This is undocumented functionality and may be removed in future versions&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
[[$editbox]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Commands]][[Category:Custom Windows]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=If-Then-Else&amp;diff=4215</id>
		<title>If-Then-Else</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=If-Then-Else&amp;diff=4215"/>
		<updated>2010-11-17T23:51:42Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Link to Basic control tutorial&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
* Note: This is a reference. For a tutorial please see the [[basic control tutorial]].&lt;br /&gt;
The If-then-else statement allows you to compare values and execute different parts of a script based on that comparison. This is a basic structure for any script that includes anything more than one-liners!&lt;br /&gt;
&lt;br /&gt;
 if (v1 operator v2) { commands1 }&lt;br /&gt;
 elseif (v1 operator v2) { commands2 }&lt;br /&gt;
 else { commands3 }&lt;br /&gt;
&lt;br /&gt;
If the first statement (line) is [[$true]], commands inside the first brackets are executed. If the first if-statement returns [[$false]], script starts looking for an elseif-statement. An elseif-statement is only triggered if the group&#039;s if-statement returned $false before. And at last, if none of the if or elseif statements were triggered, commands in else-statement are executed.&lt;br /&gt;
&lt;br /&gt;
One if structure/group can consist of one main if-statement, after it there can be 0 .. N elseif-statements. There can be only one else, which can be understood as the default statement, if none one before were triggered. There doesn&#039;t need to be else-statement though.&lt;br /&gt;
&lt;br /&gt;
Every if statement is handled separatly and none of others affect in another.&lt;br /&gt;
&lt;br /&gt;
 if (A) { ... }&lt;br /&gt;
 elseif (B) {&lt;br /&gt;
   if (C) { ... }&lt;br /&gt;
   elseif (D) { ... }&lt;br /&gt;
   elseif (E) { ... }&lt;br /&gt;
   else { F }&lt;br /&gt;
 }&lt;br /&gt;
 elseif (G) { ... }&lt;br /&gt;
 else { H }&lt;br /&gt;
&lt;br /&gt;
If structure would be hierarchical represented it would look something like this&lt;br /&gt;
&lt;br /&gt;
 A               If A is true, execute its commands and skip the rest&lt;br /&gt;
 B               If B is true, &lt;br /&gt;
  \&lt;br /&gt;
   C                 check if C, D or E returns true&lt;br /&gt;
   D&lt;br /&gt;
   E&lt;br /&gt;
   F             If none of these got triggered, return F&lt;br /&gt;
 G               If A and B were false and G is true, execute this&lt;br /&gt;
 H               If A, B and G were all false, execute H&lt;br /&gt;
&lt;br /&gt;
== The Operators ==&lt;br /&gt;
&lt;br /&gt;
 [[#==|==]]        equal to&lt;br /&gt;
 [[#==|===]]       equal to (case-sensitive)&lt;br /&gt;
 [[#&amp;lt;|&amp;lt;]]         less than&lt;br /&gt;
 [[#&amp;gt;|&amp;gt;]]         larger than&lt;br /&gt;
 [[#&amp;lt;=|&amp;lt;=]]        less than or equal to&lt;br /&gt;
 [[#&amp;gt;=|&amp;gt;=]]        larger than or equal to&lt;br /&gt;
 [[#//|//]]        v2 is a multiple of v1&lt;br /&gt;
 [[#\\|\\]]        v2 is not a multiple of v1&lt;br /&gt;
 [[#&amp;amp;|&amp;amp;]]         bitwise comparison&lt;br /&gt;
 [[#!=|!]]         negation operator (!= is opposite of == and !isin is &amp;quot;is not in&amp;quot;)&lt;br /&gt;
 &lt;br /&gt;
 [[#isin|isin]]      string v1 is in string v2&lt;br /&gt;
 [[#isin|isincs]]    string v1 is in string v2 (case sensitive)&lt;br /&gt;
 [[#iswm|iswm]]      wildcard string v1 matches string v2&lt;br /&gt;
 [[#iswm|iswmcs]]    wildcard string v1 matches string v2 (case sensitive)&lt;br /&gt;
 [[#isnum|isnum]]     number v1 is a number in the range v2 which is in the form n1-n2 (v2 optional)&lt;br /&gt;
 [[#isletter|isletter]]  letter v1 is a letter in the list of letters in v2 (v2 optional)&lt;br /&gt;
 &lt;br /&gt;
 [[#isalnum|isalnum]]   text contains only letters and numbers&lt;br /&gt;
 [[#isalpha|isalpha]]   text contains only letters&lt;br /&gt;
 [[#islower|islower]]   text contains only lower case letters&lt;br /&gt;
 [[#isupper|isupper]]   text contains only upper case letters&lt;br /&gt;
 &lt;br /&gt;
 [[#ison|ison]]      if v1 is on channel v2&lt;br /&gt;
 [[#isop|isop]]      if v1 is an op on channel v2&lt;br /&gt;
 [[#ishop|ishop]]     if v1 is a halfop on channel v2&lt;br /&gt;
 [[#isvoice|isvoice]]   if v1 has a voice on channel v2&lt;br /&gt;
 [[#isreg|isreg]]     if v1 is a normal nick on channel v2&lt;br /&gt;
 [[#ischan|ischan]]    if v1 is a channel which you are on.&lt;br /&gt;
 [[#isban|isban]]     if v1 is a banned address in the internal ban list for channel v2&lt;br /&gt;
 &lt;br /&gt;
 [[#isaop|isaop]]     if v1 is a user in your auto-op list for channel v2 (v2 optional)&lt;br /&gt;
 [[#isavoice|isavoice]]  if v1 is a user in your auto-voice list for channel v2 (v2 optional)&lt;br /&gt;
 [[#isignore|isignore]]  if v1 is a user in your ignore list with the ignore switch v2 (v2 optional)&lt;br /&gt;
 [[#isprotect|isprotect]] if v1 is a user in your protect list for channel v2 (v2 optional)&lt;br /&gt;
 [[#isnotify|isnotify]]  if v1 is a user in your notify list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note:&#039;&#039; To negate an operator you can prefix it with an exclamation mark (!).&lt;br /&gt;
&lt;br /&gt;
=== == ===&lt;br /&gt;
 if (cat == cAt)  ;Returns true since == is case-insensitive.&lt;br /&gt;
 if (cat === cAt) ;Returns false since === is case-sensitive.&lt;br /&gt;
The above checks that v1 (cat) matches v2 (cAt).&lt;br /&gt;
&lt;br /&gt;
=== != ===&lt;br /&gt;
This isn&#039;t actually an operator, but a combination of two operators ! and =. And actually, = isn&#039;t an operator either, but in mIRC scripting it can be used instead of == and it works just the way == works. In most mIRC scripts you only see !=, which actually shouldn&#039;t be used. Instead, !== should be used to negate ==.&lt;br /&gt;
&lt;br /&gt;
 if (cat != cAt)   ;Returns false since != is case-insensitive and both values v1 and v2 are equal.&lt;br /&gt;
 if (cat !== caT)  ;Returns false since !== is the same as !=&lt;br /&gt;
 if (cat !=== cAt) ;Returns true since !=== is case-sensitive and both values v1 and v2 are not equal when case is taken in to consideration.&lt;br /&gt;
 if (!$away)       ;Returns $true if you are NOT away ($away returns $false and ! negates it to $true).&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt; ===&lt;br /&gt;
 if (6 &amp;lt; 9) ;Returns true because the numerical value 6 is less than 9.&lt;br /&gt;
Checks that v1 (6) has a lower numerical value than v2 (9)&lt;br /&gt;
&lt;br /&gt;
=== &amp;gt; ===&lt;br /&gt;
 if (6 &amp;gt; 9) ;Returns false because the numerical value 6 is not greater than 9.&lt;br /&gt;
Checks that v1 (6) has a higher numerical value than v2 (9)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;= ===&lt;br /&gt;
 if (6 &amp;lt;= 9) ;Returns true because the numerical value 6 is less than or equal to 9.&lt;br /&gt;
Checks that v1 (6) has a lower or equal numerical value than v2 (9).&lt;br /&gt;
&lt;br /&gt;
=== &amp;gt;= ===&lt;br /&gt;
 if (6 &amp;gt;= 9) ;Returns false because the numerical value 6 is not greater than or equal to 9.&lt;br /&gt;
Checks that v1 (6) has a greater or equal numerical value than v2 (9).&lt;br /&gt;
&lt;br /&gt;
=== // ===&lt;br /&gt;
 if (3 // 9) ;Returns true because 3 is a multiple of 9.&lt;br /&gt;
Multiples of 9 are numbers that when devided in to 9 result in an integer.&lt;br /&gt;
You are not limited to positive numbers.&lt;br /&gt;
&lt;br /&gt;
=== \\ ===&lt;br /&gt;
 if (3 \\ 9) ;Returns false because 3 is a multiple of 9.&lt;br /&gt;
\\ is the negate of //&lt;br /&gt;
&lt;br /&gt;
=== &amp;amp; ===&lt;br /&gt;
 if (N1 &amp;amp; N2) ; This will compare the bits that are [[$biton|turned on]] in the decimal N1 AND N2 in the same way [[$and]]() would.&lt;br /&gt;
As long as N1 and N2 share atleast 1 bit the condition is returned as true.&lt;br /&gt;
&lt;br /&gt;
 if (13 &amp;amp; 11) ;Is true, see the table below for explanation.&lt;br /&gt;
 echo -ag $and(13,11) ;returns 9.&lt;br /&gt;
&lt;br /&gt;
If you break down 13 and 11 into their bits as below:&lt;br /&gt;
{|class=&amp;quot;gallery&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width:100px&amp;quot; | &#039;&#039;&#039;&#039;&#039;Binary&#039;&#039;&#039;&#039;&#039; || &#039;&#039;&#039;&#039;&#039;Value&#039;&#039;&#039;&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
| 1101      || 13&lt;br /&gt;
|-&lt;br /&gt;
| 1011      || 11 &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:1px dotted #2f6fab; color: Black;&amp;quot; | 1001      || style=&amp;quot;border:1px dotted #2f6fab; color: Black;&amp;quot; | 9 &lt;br /&gt;
|}&lt;br /&gt;
 if (11 &amp;amp; 4) ;Is false.&lt;br /&gt;
 echo -ag $and(11,4) ;returns 0.&lt;br /&gt;
{|class=&amp;quot;gallery&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width:100px&amp;quot; | &#039;&#039;&#039;&#039;&#039;Binary&#039;&#039;&#039;&#039;&#039; || &#039;&#039;&#039;&#039;&#039;Value&#039;&#039;&#039;&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
| 1011      || 11 &lt;br /&gt;
|-&lt;br /&gt;
| 0100      || 4&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:1px dotted #2f6fab; color: Black;&amp;quot; | 0000      || style=&amp;quot;border:1px dotted #2f6fab; color: Black;&amp;quot; | 0&lt;br /&gt;
|}&lt;br /&gt;
Above you can see, 13 and 11 both have the first bit AND fourth bit on, so the statement is true. The binary 1001 value has a decimal value of 9.&lt;br /&gt;
Also you can see, 11 and 4 do not share any common bits, so the statement is false.&lt;br /&gt;
&lt;br /&gt;
=== isin ===&lt;br /&gt;
 if (cat isin [[$1-]])&lt;br /&gt;
Matches for &amp;quot;a &#039;&#039;cat&#039;&#039;&amp;quot;, &amp;quot;&#039;&#039;cat&#039;&#039;alog&amp;quot;, &amp;quot;impli&#039;&#039;cat&#039;&#039;e &amp;quot; and any string where &amp;quot;cat&amp;quot; &#039;&#039;&#039;is in&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== iswm ===&lt;br /&gt;
iswm stands for &#039;&#039;is wildcard match&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Operators:&lt;br /&gt;
 *  0 or more characters&lt;br /&gt;
 ?  1 character&lt;br /&gt;
 &amp;amp;  1 word (atleast 1 or more non-space characters)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 if (home*away*lost iswm $1-)&lt;br /&gt;
This would match anything starting with home and ending in lost, with away being between home and lost.&lt;br /&gt;
It would match, &amp;quot;homeawaylost&amp;quot;, &amp;quot;home away lost&amp;quot;, &amp;quot;home is missed when you are away or lost&amp;quot;, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 if (ca?* iswm $1-)&lt;br /&gt;
Requires one character after &amp;quot;ca&amp;quot; and allows 0 or more character after it. So it would match for cat, cab, cabin etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 if ([[$(...)|$(]]I &amp;amp; you) iswm $1-)&lt;br /&gt;
Matches any string that consists of &amp;quot;I + one word + you&amp;quot;.&lt;br /&gt;
Examples would be, &amp;quot;I love you&amp;quot;, &amp;quot;I hate you&amp;quot;, &amp;quot;I moo you&amp;quot;, it would not however match, &amp;quot;I don&#039;t love you&amp;quot; since &amp;quot;don&#039;t love&amp;quot; contains a space. Note the $( ), it is needed to prevent mirc treat this statement as bitwise comparison.&lt;br /&gt;
&lt;br /&gt;
=== isnum ===&lt;br /&gt;
&lt;br /&gt;
 if ($1 isnum)  ;Returns true if $1 is of numeric value&lt;br /&gt;
 if ($1 isnum 10-)  ;Returns true if $1 is number higher than or equal to 10&lt;br /&gt;
 if ($1 isnum 20-30)  ;Returns true if $1 is a number between 20 and 30 inclusive&lt;br /&gt;
&lt;br /&gt;
The examples above checks $1, to see;&lt;br /&gt;
&lt;br /&gt;
Is it a number?&amp;lt;br /&amp;gt;&lt;br /&gt;
Is it a number above 10?&amp;lt;br /&amp;gt;&lt;br /&gt;
Is it a number between 20 and 30?&lt;br /&gt;
&lt;br /&gt;
=== isletter ===&lt;br /&gt;
&lt;br /&gt;
 if ($1 isletter)  ;Returns true if &#039;&#039;$1&#039;&#039; is a letter, any letter&lt;br /&gt;
 if ($1 isletter abcdefg)  ;Returns true if &#039;&#039;$1&#039;&#039; is in the string of letters &#039;&#039;abcdefg&#039;&#039;&lt;br /&gt;
 if ($1 isletter HelloWorld)  ;Returns true if &#039;&#039;$1&#039;&#039; is in the string of letters &#039;&#039;HeloWrd&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Note that the checking is case-sensitive, the letter &#039;&#039;h&#039;&#039; will not return true if checked against the string &#039;&#039;HelloWorld&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== isalnum ===&lt;br /&gt;
 if (Dana34 isalnum) ;returns true as &#039;&#039;Dana34&#039;&#039; contains letters and numbers only&lt;br /&gt;
 if (Dana_ isalnum) ;returns false as &#039;&#039;_&#039;&#039; is neither letter nor number&lt;br /&gt;
&lt;br /&gt;
Matches whether the string consists of letters and numbers only.&lt;br /&gt;
&lt;br /&gt;
=== isalpha ===&lt;br /&gt;
 if (Dana isalpha) ; returns true as &#039;&#039;Dana&#039;&#039; consists of letters only&lt;br /&gt;
 if (foo42 isalpha) ; returns false as &#039;&#039;42&#039;&#039; is no letter&lt;br /&gt;
&lt;br /&gt;
Isalpha checks whether the word consists of letters only. It doesn&#039;t matter whether there are upper or lower case.&lt;br /&gt;
&lt;br /&gt;
=== islower ===&lt;br /&gt;
 if (moo islower) ; returns true as all letters in &#039;&#039;moo&#039;&#039; are lower case.&lt;br /&gt;
 if (m0o islower) ; returns true as well as all letters in &#039;&#039;m0o&#039;&#039; are lower case. &lt;br /&gt;
 if (mooO islower) ; retursn false as there is an upper &#039;&#039;O&#039;&#039; in &#039;&#039;mooO&#039;&#039;.&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; that islower checks whether all letters in the string are lower case. There can still be numbers or other characters in the string $1.&lt;br /&gt;
&lt;br /&gt;
=== isupper ===&lt;br /&gt;
 if (MOO isupper) ; returns true as &#039;&#039;MOO&#039;&#039; consists of upper case letters only&lt;br /&gt;
 if (1234 isupper) ; returns true as all letters in &#039;&#039;1234&#039;&#039; are upper case, as there are none.&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; that it checks whether all letters in $1 are upper case. There can still be numbers or other characters in $1.&lt;br /&gt;
&lt;br /&gt;
=== ison ===&lt;br /&gt;
 if (Dana ison #help.script) ; returns true as &#039;&#039;Dana&#039;&#039; is in the channel #help.script&lt;br /&gt;
 if (foo !ison #help.script) ; returns true as &#039;&#039;foo&#039;&#039; is not in #help.script&lt;br /&gt;
&lt;br /&gt;
This checks whether $1 is in the channel $2. Note that you need to be in the same channel to use this.&lt;br /&gt;
&lt;br /&gt;
=== isop ===&lt;br /&gt;
 if (Dana isop #help.script) ; returns true as &#039;&#039;Dana&#039;&#039; is an operator in #help.script&lt;br /&gt;
 if (foo isop #help.script) ; returns false as &#039;&#039;foo&#039;&#039; is no operator in #help.script&lt;br /&gt;
&lt;br /&gt;
Checks whether $1 is an operator in $2. Note that you need to be in the same channel to use this.&lt;br /&gt;
&lt;br /&gt;
=== ishop ===&lt;br /&gt;
 if (moo ishop #mIRC) ; returns true if &#039;&#039;moo&#039;&#039; is an half operator in #mIRC&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; that QuakeNet does not support halfops. Note also that you need to be in the same channel to use this.&lt;br /&gt;
&lt;br /&gt;
=== isvoice ===&lt;br /&gt;
 if (Dana isvoice #help.script) ; returns true as &#039;&#039;Dana&#039;&#039; has voice in #help.script.&lt;br /&gt;
 if (foobar isvoice #help.script) ; returns false as &#039;&#039;foobar&#039;&#039; has no voice in #help.script.&lt;br /&gt;
This checks if $1 has voice in $2 or not. Note that you need to be in the same channel to use this.&lt;br /&gt;
&lt;br /&gt;
=== isreg ===&lt;br /&gt;
 if (Dana isreg #help.script) ; returns false as Dana is no regular user in #help.script.&lt;br /&gt;
 if (foobar isreg #help.script) ; returns true as foobar is a regular user in #help.script.&lt;br /&gt;
This checks whether $1 is a regular user (no voice, no half operator, no operator) in $2. Note that you need to be in the same channel to use this.&lt;br /&gt;
&lt;br /&gt;
=== ischan ===&lt;br /&gt;
 if (#help.script ischan) ; returns true as you are in #help.script.&lt;br /&gt;
 if (#foobar ischan) ; returns false as you are no in #foobar.&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; that it returns true only if you are in this channel.&lt;br /&gt;
&lt;br /&gt;
=== isban ===&lt;br /&gt;
 if (idiot!*@* isban #mychan) ; returns $true if &#039;&#039;idiot!*@*&#039;&#039; is in your [[:Category:IBL|Internal Ban List]] for the channel #mychan.&lt;br /&gt;
 if ($ial($me) isban $chan) ; returns $true if you are banned on the current channel.&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; that isban performs two different checks, depending on whether v1 contains wildcards or not:&lt;br /&gt;
* if v1 contains wildcards, isban will return $true if v1 is an exact ban in the IBL;&lt;br /&gt;
* if v1 does not contain wildcards, isban will return $true if one or more bans match that address.&lt;br /&gt;
&lt;br /&gt;
=== isaop ===&lt;br /&gt;
 if (foobar isaop) ; returns true if &#039;&#039;foobar&#039;&#039; is in your autoop list for any channel.&lt;br /&gt;
 if (foobar isaop #help.script) ; returns true if &#039;&#039;foobar&#039;&#039; is in your autoop list for #help.script.&lt;br /&gt;
To put someone in your autoop-list, take a look at [[Aop|/aop]].&lt;br /&gt;
&lt;br /&gt;
=== isavoice ===&lt;br /&gt;
 if (foobar isavoice) ; returns true if &#039;&#039;foobar&#039;&#039; is in your autovoicelist for any channel.&lt;br /&gt;
 if (foobar isavoice #help.script) ; returns true if &#039;&#039;foobar&#039;&#039; is your autovoicelist for #help.script.&lt;br /&gt;
To put someone in your autovoicelist, take a look at [[Avoice|/avoice]].&lt;br /&gt;
&lt;br /&gt;
=== isignore ===&lt;br /&gt;
 if (foobar isignore) ; returns true if &#039;&#039;foobar&#039;&#039; is in your ignore list.&lt;br /&gt;
 if (foobar isignore c) ; returns true if &#039;&#039;foobar&#039;&#039; is in your ignore list with switch -c.&lt;br /&gt;
To get a more in detail explanation of these switches, take a look at [[Ignore|/ignore]].&lt;br /&gt;
&lt;br /&gt;
=== isprotect ===&lt;br /&gt;
 if (foobar isprotect) ; returns true if &#039;&#039;foobar&#039;&#039; is in your protect list.&lt;br /&gt;
 if (foobar isprotect #help.script) ; returns true if &#039;&#039;foobar&#039;&#039; is in your protect list for #help.script.&lt;br /&gt;
To get more information about protection, see [[Protect|/protect]].&lt;br /&gt;
&lt;br /&gt;
=== isnotify ===&lt;br /&gt;
 if (Dana isnotify) ; returns true if Dana is in your notify list.&lt;br /&gt;
Take a look at [[Notify|/notify]] to add someone to your notifylist. If someone in your notifylist connects (disconnects), the [[On_notify|On notify event]] ([[On_unotify|On Unotify event]]) is triggered.&lt;br /&gt;
&lt;br /&gt;
== Combining comparisons ==&lt;br /&gt;
&lt;br /&gt;
You can combine comparisons by using the &amp;amp;&amp;amp; for AND and || for OR characters.&lt;br /&gt;
&lt;br /&gt;
 [[var]] %c = 5&lt;br /&gt;
 if (%c &amp;lt; 6) &amp;amp;&amp;amp; (%c &amp;gt; 0)      ; returns true because %c is both, smaller than 6 and greater than 0&lt;br /&gt;
 if (%c &amp;lt; 6) || (%c isalpha)  ; returns true because %c is lower than 6.&lt;br /&gt;
                              ; note that %c is not alphapetical and returns false, but || matches for 1 .. N true values.&lt;br /&gt;
 if (%c &amp;lt; 6) &amp;amp;&amp;amp; (%c isalpha)  ; returns false&lt;br /&gt;
 if (%c &amp;lt; 6) &amp;amp;&amp;amp; (%c !isalpha) ; ! negates the operator, this this returns true&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
====&#039;&#039;Not all comparations need two parameters&#039;&#039;====&lt;br /&gt;
 if ($1- isupper)&lt;br /&gt;
Returns true if $1- contains only uppercase letters. So ABCD returns true, but even one lowercase letter makes it return false.&lt;br /&gt;
&lt;br /&gt;
====&#039;&#039;Use parenthesis correctly&#039;&#039;====&lt;br /&gt;
(A) || (B) &amp;amp;&amp;amp; (C) &amp;lt;- logical order for this is to check if either one of A or B is true, and C is true, but if you wish to check that B and C must be true, or A is true, you&#039;ll need to do (A) || ((B) &amp;amp;&amp;amp; (C))&lt;br /&gt;
&lt;br /&gt;
====&#039;&#039;This article covers only if-then-else &#039;&#039;&#039;string&#039;&#039;&#039; operators&#039;&#039;====&lt;br /&gt;
You &#039;&#039;&#039;can&#039;t&#039;&#039;&#039; use&lt;br /&gt;
 if (foo isin test.txt)&lt;br /&gt;
To check the content of a text file, use [[$read]] to search through a file via its search switches.&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[$v1]] &amp;amp; [[$v1|$v2]] - These identifiers allow you to retrieve the values of the last if condition.&lt;br /&gt;
* [[Basic control tutorial]] - An in depth tutorial on how to use these statements&lt;br /&gt;
&lt;br /&gt;
[[Category:Commands]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=4948</id>
		<title>Basic control tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=4948"/>
		<updated>2010-11-17T23:50:12Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &amp;#039;Final&amp;#039; first draft&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Control statements are, very simply, statements which control which parts of your code gets executed.  You should know much about [[aliases]] and [[:Category: Events|events]] (Particularly the [[on text]] event) before reading this.&lt;br /&gt;
&lt;br /&gt;
==Boolean==&lt;br /&gt;
A &#039;boolean&#039; statement is a statement that can have one of two values, &amp;quot;true&amp;quot; or &amp;quot;false&amp;quot;.&lt;br /&gt;
For example: &lt;br /&gt;
 I have a donkey. &lt;br /&gt;
This is a boolean statement because it is either true, or false. Control structures operate under this pretense, &amp;quot;If something is true, do thing1, otherwise do thing2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
 Let a = &amp;quot;It is raining&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This is clearly a boolean statement, it is either &#039;true&#039; or &#039;false&#039;. An example of some program may be&lt;br /&gt;
 ; Here the letter &#039;a&#039; means &amp;quot;it is raining&amp;quot;, like we said up above&lt;br /&gt;
 if (a) { put on poncho } &lt;br /&gt;
 else { take off poncho }&lt;br /&gt;
&lt;br /&gt;
This can be read as&lt;br /&gt;
 if (it is raining) { put on poncho }&lt;br /&gt;
 else { take off poncho }&lt;br /&gt;
&lt;br /&gt;
==If Statements==&lt;br /&gt;
In programming, and particularly mIRC scripts, we use if statements like the example above for our control management.&amp;lt;br /&amp;gt;&lt;br /&gt;
They follow the format if (condition) { commands } &amp;lt;br /&amp;gt;&lt;br /&gt;
For example, consider the following alias: &lt;br /&gt;
&lt;br /&gt;
 alias ifexample {&lt;br /&gt;
    echo -atg Let&#039;s see...&lt;br /&gt;
    if ($true) { echo -atg Yep. This is true. }&lt;br /&gt;
    echo -atg See?&lt;br /&gt;
 }&lt;br /&gt;
In this example, $true is the &#039;condition&#039; and the &#039;command&#039; is &amp;quot;echo -atg Yep. This is true.&amp;quot; &amp;lt;br /&amp;gt;&lt;br /&gt;
If you put this [[alias]] in the remote section of mIRC and type /ifexample in the command line, it will echo to you &amp;quot;Yep. This is true&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
Now try changing [[$true]] to [[$false]] and notice that it will not say this is true. &amp;lt;br /&amp;gt;&lt;br /&gt;
Also notice that no matter what is inside the if statement, you will see &amp;quot;Let&#039;s see...&amp;quot; and &amp;quot;See?&amp;quot;. These things are completely separate from the control statement and will not depend on the value of the stuff in parenthesis.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Well this is all well and good, but how does this help us do anything? The simple answer is [[If-then-else|operators]]. Operators are special words you put inside control statements which will tell you if something is true or false. &amp;lt;br /&amp;gt;&lt;br /&gt;
What if you don&#039;t know if it&#039;s raining, but you still want to put a poncho on if it is? You may wish to have &lt;br /&gt;
 Let a = Is it raining?&lt;br /&gt;
 if (a) { put on poncho }&lt;br /&gt;
&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;&lt;br /&gt;
Let&#039;s make an alias that tells us what to do under different weather conditions named &amp;quot;weather.&amp;quot; We will make it so that we can type /weather rain, and it will tell us to put a poncho on. Remember that if you do this, [[$1]] will be &amp;quot;rain&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
To do this we will need to learn the &amp;quot;==&amp;quot; operator. There are two equals here and you MUST use them both. This checks to see if two things are equal. For example:&lt;br /&gt;
 a == b // This is $false&lt;br /&gt;
 a == a // This is $true&lt;br /&gt;
So in our weather alias we will need to check to see if $1 is equal to &amp;quot;rain&amp;quot;, and if it is, we echo &amp;quot;Put on a poncho.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 alias weather {&lt;br /&gt;
   if ($1 == rain) { echo -atg Put on a poncho. } &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We use some special terms to refer to the condition, condition = v1 operator v2. In this example, v1 is $1, and v2 is weather. operator is ==. This is how the [[If-then-else|operator list]] is formatted so you will need to know this. We can also use special operators [[$v1]] and [[$v2]] to return what was in those positions. For example we could do this: &lt;br /&gt;
 alias weather {&lt;br /&gt;
   if ($1 == rain) { echo -atg Due to $v1, you should put on a poncho. } &lt;br /&gt;
 }&lt;br /&gt;
 And it would tell us &amp;quot;Due to rain, you should...&amp;quot; because $v1 is $1 which is &amp;quot;rain&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Some operators do not have a v2, for example &amp;quot;isupper&amp;quot; is $true if v1 is all upper case, and $false if it is not. For this you only need the text to check, v1.&lt;br /&gt;
 on *:TEXT:*:#: {&lt;br /&gt;
     if ($1- isupper) { msg [[$chan]] STOP YELLING AT ME... }&lt;br /&gt;
     if ($1- islower) { msg $chan Speak up.... }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This is an example of an event, and of using two control statements.&lt;br /&gt;
&lt;br /&gt;
===Negation===&lt;br /&gt;
One more thing about operators. &amp;lt;br /&amp;gt;&lt;br /&gt;
Now we know how to check if text is all upper case, and if something equals something, but what if we want to check the opposite? What if we want to know if all the text is NOT upper case, or if something does NOT equal something. For this, we use the &#039;negation&#039; or &#039;not&#039; operator !. &amp;lt;br /&amp;gt;&lt;br /&gt;
The negative version of isupper is !isupper&lt;br /&gt;
 on *:TEXT:*:#YellHouse: {&lt;br /&gt;
   if ($1- !isupper) { msg $chan YOU HAVE TO YELL IN #YELLHOUSE!!! }&lt;br /&gt;
 }&lt;br /&gt;
This detects if $1- is NOT all upper case. This is the same for most operators (!islower, !isvoice, !isin) the only exception is the equality operators. The opposite of == is !=, the opposite of &amp;gt; is &amp;lt;= and of &amp;lt; is &amp;gt;=, etc. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Combining Statements==&lt;br /&gt;
Now what if you wish to check to things? Something like &lt;br /&gt;
 if (it is raining AND I do not have a poncho on) { put on poncho }&lt;br /&gt;
This combines two statements &amp;quot;it is raining&amp;quot;, &amp;quot;I do not have a poncho on&amp;quot; using AND. We call AND a &amp;quot;conditional operator&amp;quot;. There is also OR as you might imagine.&amp;lt;br /&amp;gt;&lt;br /&gt;
In mIRC, we use &amp;amp;&amp;amp; for and, and || for or.&lt;br /&gt;
 if (it is raining &amp;amp;&amp;amp; I do not have a poncho on) { put on poncho }&lt;br /&gt;
So let&#039;s say we now want to modify our weather alias to remember what the last weather was, and not tell us to do something we&#039;ve already done. For example /weather rain would tell us to put on a poncho, but if we did it again it wouldn&#039;t say that, because we already have one on.&lt;br /&gt;
 alias weather {&lt;br /&gt;
    ; Check to make sure we actually told us of a weather, if not &#039;return&#039; (which means STOP!)&lt;br /&gt;
   if ($1 != [[$null]]) { [[return]] } &lt;br /&gt;
   ; If it is now rain AND we didnt already know that&lt;br /&gt;
   if ($1 == rain &amp;amp;&amp;amp; %weather != rain) {&lt;br /&gt;
      set %weather rain&lt;br /&gt;
      echo -atg Put on a poncho.&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
In this example we show that you can do two things in one control statement by putting them on different lines. Our if statement now checks to see if %weather is not already &#039;rain&#039;, that way we don&#039;t put the poncho on twice. We might also do this:&lt;br /&gt;
 alias weather {&lt;br /&gt;
    ; Check to make sure we actually told us of a weather, if not &#039;return&#039; (which means STOP!)&lt;br /&gt;
   if ($1 != [[$null]]) { [[return]] } &lt;br /&gt;
   ; If it is now rain AND we didnt already know that&lt;br /&gt;
   if ($1 == rain &amp;amp;&amp;amp; %weather != rain) {&lt;br /&gt;
      set %weather rain&lt;br /&gt;
      echo -atg Put on a poncho.&lt;br /&gt;
   }&lt;br /&gt;
   if ($1 == clear &amp;amp;&amp;amp; %weather != clear) {&lt;br /&gt;
     echo -atg Take off poncho&lt;br /&gt;
     set %weather clear&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Nesting==&lt;br /&gt;
This is a bit obvious, but warrants saying aloud. Since &#039;if&#039; is technically a command, we can &#039;nest&#039; it. That means we can have if (condition1) { command | if (condition2) { command2 } } &amp;lt;br /&amp;gt;&lt;br /&gt;
For example, now we want /weather clear to say &amp;quot;Take off poncho&amp;quot; if %weather is rain.&lt;br /&gt;
&lt;br /&gt;
 alias weather {&lt;br /&gt;
    ; Check to make sure we actually told us of a weather, if not &#039;return&#039; (which means STOP!)&lt;br /&gt;
   if ($1 != [[$null]]) { [[return]] } &lt;br /&gt;
   ; If it is now rain AND we didnt already know that&lt;br /&gt;
   if ($1 == rain &amp;amp;&amp;amp; %weather != rain) {&lt;br /&gt;
      set %weather rain&lt;br /&gt;
      echo -atg Put on a poncho.&lt;br /&gt;
   }&lt;br /&gt;
   if ($1 == clear &amp;amp;&amp;amp; %weather != clear) {&lt;br /&gt;
     ; Here we NEST another if statement!&lt;br /&gt;
     if (%weather == rain) {&lt;br /&gt;
        echo -atg Take off poncho&lt;br /&gt;
     }&lt;br /&gt;
     set %weather clear&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Another example of this being used more constructively, and in an event:&lt;br /&gt;
 on *:TEXT:*:#YellHouse: {&lt;br /&gt;
   if ($1- !isupper) {&lt;br /&gt;
      msg # YOU HAVE TO YELL IN #YELLHOUSE!!!!&lt;br /&gt;
      if ($right($1-,1) == !) {&lt;br /&gt;
         msg # PUNCTUATION IS NOT GOOD ENOUGH!!!&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Else and Elseif==&lt;br /&gt;
Elseif and Else are two very useful commands. These commands have to be used with an &#039;if&#039; statement. Elseif is only considered if the if statement before it was $false, and else is only considered if everything before it was $false.&lt;br /&gt;
&lt;br /&gt;
 if (it is raining) { put on poncho }&lt;br /&gt;
 elseif (it is snowing) { put on jacket }&lt;br /&gt;
 else { take off poncho and jacket and enjoy the sun }&lt;br /&gt;
&lt;br /&gt;
Notice, else does not take a condition! Else statements run if none of the elseif statements above it have run and if the closest if has not run. In this example, if it were somehow both raining and snowing, we would only put on a poncho, not a jacket, because the first if would run, and the elseif would not.&lt;br /&gt;
 alias Sign {&lt;br /&gt;
  var %a = $1&lt;br /&gt;
  if (%a &amp;gt; 0) { echo -atg Positive! }&lt;br /&gt;
  if (%a &amp;lt; 0) { echo -atg Negative! }&lt;br /&gt;
  else { echo -atg Zero! }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
There is an error in this alias, and that is that if $1 is positive, it will echo &amp;quot;Positive!&amp;quot; and &amp;quot;Zero!&amp;quot;. This is because else only looks at the first if above it. The proper way to do this is:&lt;br /&gt;
alias Sign {&lt;br /&gt;
 var %a = $1&lt;br /&gt;
 if (%a &amp;gt; 0) { echo -atg Positive! }&lt;br /&gt;
 elseif (%a &amp;lt; 0) { echo -atg Negative! }&lt;br /&gt;
 else { echo -atg Zero! }&lt;br /&gt;
&lt;br /&gt;
Modification of the weather alias to make it faster by using elseifs. This increases speed because it doesn&#039;t have to check $1 if it already knows it is rain&lt;br /&gt;
 alias weather {&lt;br /&gt;
    ; Check to make sure we actually told us of a weather, if not &#039;return&#039; (which means STOP!)&lt;br /&gt;
   if ($1 != [[$null]]) { [[return]] } &lt;br /&gt;
   ; If it is now rain AND we didnt already know that&lt;br /&gt;
   if ($1 == rain &amp;amp;&amp;amp; %weather != rain) {&lt;br /&gt;
      set %weather rain&lt;br /&gt;
      echo -atg Put on a poncho.&lt;br /&gt;
   }&lt;br /&gt;
   ; We use an elseif here, so that it doesnt bother checking if $1 == rain.&lt;br /&gt;
   elseif ($1 == clear &amp;amp;&amp;amp; %weather != clear) {&lt;br /&gt;
     ; Here we NEST another if statement!&lt;br /&gt;
     if (%weather == rain) {&lt;br /&gt;
        echo -atg Take off poncho&lt;br /&gt;
     }&lt;br /&gt;
     set %weather clear&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
==Other Boolean Values==&lt;br /&gt;
There are three important notes about boolean values. Besides $true and $false, three other things are interpreted&lt;br /&gt;
* 0 is ALWAYS interpreted as $false [if (0) { this will never happen }]&lt;br /&gt;
* $null is ALWAYS interpreted as $false [if (!$1) { this is like saying if $1 is not null, because its !$false if $1 is $null }]&lt;br /&gt;
* Anything else is ALWAYS interpreted as $true&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Consider this minor change to the weather alias where we change if ($1 == $null) to something a bit shorter. This uses the fact that $null is $false&lt;br /&gt;
 alias weather {&lt;br /&gt;
    ; Check to make sure we actually told us of a weather, if not &#039;return&#039; (which means STOP!)&lt;br /&gt;
   if (!$1) { [[return]] } &lt;br /&gt;
   ; If it is now rain AND we didnt already know that&lt;br /&gt;
   if ($1 == rain &amp;amp;&amp;amp; %weather != rain) {&lt;br /&gt;
      set %weather rain&lt;br /&gt;
      echo -atg Put on a poncho.&lt;br /&gt;
   }&lt;br /&gt;
   if ($1 == clear &amp;amp;&amp;amp; %weather != clear) {&lt;br /&gt;
     ; Here we NEST another if statement!&lt;br /&gt;
     if (%weather == rain) {&lt;br /&gt;
        echo -atg Take off poncho&lt;br /&gt;
     }&lt;br /&gt;
     set %weather clear&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
==Examples==&lt;br /&gt;
Completed weather alias:&lt;br /&gt;
 alias weather {&lt;br /&gt;
   ; Check to see if they gave us a weather condition, and if it isnt the same as before&lt;br /&gt;
   if ($1 &amp;amp;&amp;amp; $1 != %weather) {&lt;br /&gt;
     if ($1 == rain) {&lt;br /&gt;
        echo -atg Put on a poncho!&lt;br /&gt;
     }&lt;br /&gt;
     elseif ($1 == snow) {&lt;br /&gt;
        echo -atg Put on a jacket!&lt;br /&gt;
     }&lt;br /&gt;
     elseif ($1 == clear) {&lt;br /&gt;
        echo -atg Enjoy the sun!&lt;br /&gt;
     }&lt;br /&gt;
     ; If this is some nonsense weather&lt;br /&gt;
     else {&lt;br /&gt;
        echo -atg I don&#039;t care about $1 $+ , I only advise on rain snow and clear.&lt;br /&gt;
       return&lt;br /&gt;
     }&lt;br /&gt;
     ; This chain of control is for checking what to remove&lt;br /&gt;
     if (%weather == rain) { echo -atg Take off poncho. }&lt;br /&gt;
     elseif (%weather == snow) { echo -atg Take off jacket. }&lt;br /&gt;
     set %weather $1&lt;br /&gt;
    &lt;br /&gt;
  }&lt;br /&gt;
  ; If they didnt give us a condition...&lt;br /&gt;
  elseif (!$1) { echo -atg What is the weather like? }&lt;br /&gt;
  ; If its the same as before, tell them we know!&lt;br /&gt;
  else {&lt;br /&gt;
    echo -atg I already told you what to do about the $1 $+ ...&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[If-Then-Else]] - &#039;Full&#039; list of operators&lt;br /&gt;
* [[While]] - Another control structure for repeating things as long as something is true&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=2779</id>
		<title>Basic control tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=2779"/>
		<updated>2010-11-17T23:21:57Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Control statements are, very simply, statements which control which parts of your code gets executed.  You should know much about [[aliases]] and [[:Category: Events|events]] (Particularly the [[on text]] event) before reading this.&lt;br /&gt;
&lt;br /&gt;
==Boolean==&lt;br /&gt;
A &#039;boolean&#039; statement is a statement that can have one of two values, &amp;quot;true&amp;quot; or &amp;quot;false&amp;quot;.&lt;br /&gt;
For example: &lt;br /&gt;
 I have a donkey. &lt;br /&gt;
This is a boolean statement because it is either true, or false. Control structures operate under this pretense, &amp;quot;If something is true, do thing1, otherwise do thing2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
 Let a = &amp;quot;It is raining&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This is clearly a boolean statement, it is either &#039;true&#039; or &#039;false&#039;. An example of some program may be&lt;br /&gt;
 ; Here the letter &#039;a&#039; means &amp;quot;it is raining&amp;quot;, like we said up above&lt;br /&gt;
 if (a) { put on poncho } &lt;br /&gt;
 else { take off poncho }&lt;br /&gt;
&lt;br /&gt;
This can be read as&lt;br /&gt;
 if (it is raining) { put on poncho }&lt;br /&gt;
 else { take off poncho }&lt;br /&gt;
&lt;br /&gt;
==If Statements==&lt;br /&gt;
In programming, and particularly mIRC scripts, we use if statements like the example above for our control management.&amp;lt;br /&amp;gt;&lt;br /&gt;
They follow the format if (condition) { commands } &amp;lt;br /&amp;gt;&lt;br /&gt;
For example, consider the following alias: &lt;br /&gt;
&lt;br /&gt;
 alias ifexample {&lt;br /&gt;
    echo -atg Let&#039;s see...&lt;br /&gt;
    if ($true) { echo -atg Yep. This is true. }&lt;br /&gt;
    echo -atg See?&lt;br /&gt;
 }&lt;br /&gt;
In this example, $true is the &#039;condition&#039; and the &#039;command&#039; is &amp;quot;echo -atg Yep. This is true.&amp;quot; &amp;lt;br /&amp;gt;&lt;br /&gt;
If you put this [[alias]] in the remote section of mIRC and type /ifexample in the command line, it will echo to you &amp;quot;Yep. This is true&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
Now try changing [[$true]] to [[$false]] and notice that it will not say this is true. &amp;lt;br /&amp;gt;&lt;br /&gt;
Also notice that no matter what is inside the if statement, you will see &amp;quot;Let&#039;s see...&amp;quot; and &amp;quot;See?&amp;quot;. These things are completely separate from the control statement and will not depend on the value of the stuff in parenthesis.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Well this is all well and good, but how does this help us do anything? The simple answer is [[If-then-else|operators]]. Operators are special words you put inside control statements which will tell you if something is true or false. &amp;lt;br /&amp;gt;&lt;br /&gt;
What if you don&#039;t know if it&#039;s raining, but you still want to put a poncho on if it is? You may wish to have &lt;br /&gt;
 Let a = Is it raining?&lt;br /&gt;
 if (a) { put on poncho }&lt;br /&gt;
&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;&lt;br /&gt;
Let&#039;s make an alias that tells us what to do under different weather conditions named &amp;quot;weather.&amp;quot; We will make it so that we can type /weather rain, and it will tell us to put a poncho on. Remember that if you do this, [[$1]] will be &amp;quot;rain&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
To do this we will need to learn the &amp;quot;==&amp;quot; operator. There are two equals here and you MUST use them both. This checks to see if two things are equal. For example:&lt;br /&gt;
 a == b // This is $false&lt;br /&gt;
 a == a // This is $true&lt;br /&gt;
So in our weather alias we will need to check to see if $1 is equal to &amp;quot;rain&amp;quot;, and if it is, we echo &amp;quot;Put on a poncho.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 alias weather {&lt;br /&gt;
   if ($1 == rain) { echo -atg Put on a poncho. } &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We use some special terms to refer to the condition, condition = v1 operator v2. In this example, v1 is $1, and v2 is weather. operator is ==. This is how the [[If-then-else|operator list]] is formatted so you will need to know this. We can also use special operators [[$v1]] and [[$v2]] to return what was in those positions. For example we could do this: &lt;br /&gt;
 alias weather {&lt;br /&gt;
   if ($1 == rain) { echo -atg Due to $v1, you should put on a poncho. } &lt;br /&gt;
 }&lt;br /&gt;
 And it would tell us &amp;quot;Due to rain, you should...&amp;quot; because $v1 is $1 which is &amp;quot;rain&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Some operators do not have a v2, for example &amp;quot;isupper&amp;quot; is $true if v1 is all upper case, and $false if it is not. For this you only need the text to check, v1.&lt;br /&gt;
 on *:TEXT:*:#: {&lt;br /&gt;
     if ($1- isupper) { msg [[$chan]] STOP YELLING AT ME... }&lt;br /&gt;
     if ($1- islower) { msg $chan Speak up.... }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This is an example of an event, and of using two control statements.&lt;br /&gt;
&lt;br /&gt;
===Negation===&lt;br /&gt;
One more thing about operators. &amp;lt;br /&amp;gt;&lt;br /&gt;
Now we know how to check if text is all upper case, and if something equals something, but what if we want to check the opposite? What if we want to know if all the text is NOT upper case, or if something does NOT equal something. For this, we use the &#039;negation&#039; or &#039;not&#039; operator !. &amp;lt;br /&amp;gt;&lt;br /&gt;
The negative version of isupper is !isupper&lt;br /&gt;
 on *:TEXT:*:#YellHouse: {&lt;br /&gt;
   if ($1- !isupper) { msg $chan YOU HAVE TO YELL IN #YELLHOUSE!!! }&lt;br /&gt;
 }&lt;br /&gt;
This detects if $1- is NOT all upper case. This is the same for most operators (!islower, !isvoice, !isin) the only exception is the equality operators. The opposite of == is !=, the opposite of &amp;gt; is &amp;lt;= and of &amp;lt; is &amp;gt;=, etc. &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Combining Statements==&lt;br /&gt;
Now what if you wish to check to things? Something like &lt;br /&gt;
 if (it is raining AND I do not have a poncho on) { put on poncho }&lt;br /&gt;
This combines tow statements &amp;quot;it is raining&amp;quot;, &amp;quot;I do not have a poncho on&amp;quot; using AND. We call AND a &amp;quot;conditional operator&amp;quot;. There is also OR as you might imagine.&amp;lt;br /&amp;gt;&lt;br /&gt;
In mIRC, we use &amp;amp;&amp;amp; for and, and || for or.&lt;br /&gt;
 if (it is raining &amp;amp;&amp;amp; I do not have a poncho on) { put on poncho }&lt;br /&gt;
So let&#039;s say we now want to modify our weather alias to remember what the last weather was, and not tell us to do something we&#039;ve already done. For example /weather rain would tell us to put on a poncho, but if we did it again it wouldn&#039;t say that, because we already have one on.&lt;br /&gt;
 alias weather {&lt;br /&gt;
    ; Check to make sure we actually told us of a weather, if not &#039;return&#039; (which means STOP!)&lt;br /&gt;
   if ($1 != [[$null]]) { [[return]] } &lt;br /&gt;
   ; If it is now rain AND we didnt already know that&lt;br /&gt;
   if ($1 == rain &amp;amp;&amp;amp; %weather != rain) {&lt;br /&gt;
      set %weather rain&lt;br /&gt;
      echo -atg Put on a poncho.&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
In this example we show that you can do two things in one control statement by putting them on different lines. Our if statement now checks to see if %weather is not already &#039;rain&#039;, that way we don&#039;t put the poncho on twice. We might also do this:&lt;br /&gt;
 alias weather {&lt;br /&gt;
    ; Check to make sure we actually told us of a weather, if not &#039;return&#039; (which means STOP!)&lt;br /&gt;
   if ($1 != [[$null]]) { [[return]] } &lt;br /&gt;
   ; If it is now rain AND we didnt already know that&lt;br /&gt;
   if ($1 == rain &amp;amp;&amp;amp; %weather != rain) {&lt;br /&gt;
      set %weather rain&lt;br /&gt;
      echo -atg Put on a poncho.&lt;br /&gt;
   }&lt;br /&gt;
   if ($1 == clear &amp;amp;&amp;amp; %weather != clear) {&lt;br /&gt;
     echo -atg Take off poncho&lt;br /&gt;
     set %weather clear&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[If-Then-Else]] - &#039;Full&#039; list of operators&lt;br /&gt;
* [[While]] - Another control structure for repeating things as long as something is true&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=2778</id>
		<title>Basic control tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=2778"/>
		<updated>2010-11-17T23:00:41Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Control statements are, very simply, statements which control which parts of your code gets executed.  You should know much about [[aliases]] and [[:Category: Events|events]] (Particularly the [[on text]] event) before reading this.&lt;br /&gt;
&lt;br /&gt;
==Boolean==&lt;br /&gt;
A &#039;boolean&#039; statement is a statement that can have one of two values, &amp;quot;true&amp;quot; or &amp;quot;false&amp;quot;.&lt;br /&gt;
For example: &lt;br /&gt;
 I have a donkey. &lt;br /&gt;
This is a boolean statement because it is either true, or false. Control structures operate under this pretense, &amp;quot;If something is true, do thing1, otherwise do thing2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
 Let a = &amp;quot;It is raining&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This is clearly a boolean statement, it is either &#039;true&#039; or &#039;false&#039;. An example of some program may be&lt;br /&gt;
 if (a) { put on poncho }&lt;br /&gt;
 else { take off poncho }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==If Statements==&lt;br /&gt;
In programming, and particularly mIRC scripts, we use if statements like the example above for our control management.&amp;lt;br /&amp;gt;&lt;br /&gt;
They follow the format if (condition) { commands } &amp;lt;br /&amp;gt;&lt;br /&gt;
For example, consider the following alias: &lt;br /&gt;
&lt;br /&gt;
 alias ifexample {&lt;br /&gt;
    echo -atg Let&#039;s see...&lt;br /&gt;
    if ($true) { echo -atg Yep. This is true. }&lt;br /&gt;
    echo -atg See?&lt;br /&gt;
 }&lt;br /&gt;
In this example, $true is the &#039;condition&#039; and the &#039;command&#039; is &amp;quot;echo -atg Yep. This is true.&amp;quot; &amp;lt;br /&amp;gt;&lt;br /&gt;
If you put this [[alias]] in the remote section of mIRC and type /ifexample in the command line, it will echo to you &amp;quot;Yep. This is true&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
Now try changing [[$true]] to [[$false]] and notice that it will not say this is true. &amp;lt;br /&amp;gt;&lt;br /&gt;
Also notice that no matter what is inside the if statement, you will see &amp;quot;Let&#039;s see...&amp;quot; and &amp;quot;See?&amp;quot;. These things are completely separate from the control statement and will not depend on the value of the stuff in parenthesis.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Well this is all well and good, but how does this help us do anything? The simple answer is [[If-then-else|operators]]. Operators are special words you put inside control statements which will tell you if something is true or false. &amp;lt;br /&amp;gt;&lt;br /&gt;
What if you don&#039;t know if it&#039;s raining, but you still want to put a poncho on if it is? You may wish to have &lt;br /&gt;
 Let a = Is it raining?&lt;br /&gt;
 if (a) { put on poncho }&lt;br /&gt;
&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;&lt;br /&gt;
Let&#039;s make an alias that tells us what to do under different weather conditions named &amp;quot;weather.&amp;quot; We will make it so that we can type /weather rain, and it will tell us to put a poncho on. Remember that if you do this, [[$1]] will be &amp;quot;rain&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
To do this we will need to learn the &amp;quot;==&amp;quot; operator. There are two equals here and you MUST use them both. This checks to see if two things are equal. For example:&lt;br /&gt;
 a == b // This is $false&lt;br /&gt;
 a == a // This is $true&lt;br /&gt;
So in our weather alias we will need to check to see if $1 is equal to &amp;quot;rain&amp;quot;, and if it is, we echo &amp;quot;Put on a poncho.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 alias weather {&lt;br /&gt;
   if ($1 == rain) { echo -atg Put on a poncho. } &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We use some special terms to refer to the condition, condition = v1 operator v2. In this example, v1 is $1, and v2 is weather. operator is ==. This is how the [[If-then-else|operator list]] is formatted so you will need to know this. We can also use special operators [[$v1]] and [[$v2]] to return what was in those positions. For example we could do this: &lt;br /&gt;
 alias weather {&lt;br /&gt;
   if ($1 == rain) { echo -atg Due to $v1, you should put on a poncho. } &lt;br /&gt;
 }&lt;br /&gt;
 And it would tell us &amp;quot;Due to rain, you should...&amp;quot; because $v1 is $1 which is &amp;quot;rain&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Some operators do not have a v2, for example &amp;quot;isupper&amp;quot; is $true if v1 is all upper case, and $false if it is not. For this you only need the text to check, v1.&lt;br /&gt;
 on *:TEXT:*:#: {&lt;br /&gt;
     if ($1- isupper) { msg [[$chan]] STOP YELLING AT ME... }&lt;br /&gt;
     if ($1- islower) { msg $chan Speak up.... }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This is an example of an event, and of using two control statements.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[If-Then-Else]] - &#039;Full&#039; list of operators&lt;br /&gt;
* [[While]] - Another control structure for repeating things as long as something is true&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=2777</id>
		<title>Basic control tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Basic_control_tutorial&amp;diff=2777"/>
		<updated>2010-11-17T22:55:19Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Control statements are, very simply, statements which control which parts of your code gets executed.  You should know much about [[aliases]] and [[:Category: Events|events]] (Particularly the [[on text]] event) before reading this.&lt;br /&gt;
&lt;br /&gt;
==Boolean==&lt;br /&gt;
A &#039;boolean&#039; statement is a statement that can have one of two values, &amp;quot;true&amp;quot; or &amp;quot;false&amp;quot;.&lt;br /&gt;
For example: &lt;br /&gt;
 I have a donkey. &lt;br /&gt;
This is a boolean statement because it is either true, or false. Control structures operate under this pretense, &amp;quot;If something is true, do thing1, otherwise do thing2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
 Let a = &amp;quot;It is raining&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This is clearly a boolean statement, it is either &#039;true&#039; or &#039;false&#039;. An example of some program may be&lt;br /&gt;
 if (a) { put on poncho }&lt;br /&gt;
 else { take off poncho }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==If Statements==&lt;br /&gt;
In programming, and particularly mIRC scripts, we use if statements like the example above for our control management.&amp;lt;br /&amp;gt;&lt;br /&gt;
They follow the format if (condition) { commands } &amp;lt;br /&amp;gt;&lt;br /&gt;
For example, consider the following alias: &lt;br /&gt;
&lt;br /&gt;
 alias ifexample {&lt;br /&gt;
    echo -atg Let&#039;s see...&lt;br /&gt;
    if ($true) { echo -atg Yep. This is true. }&lt;br /&gt;
    echo -atg See?&lt;br /&gt;
 }&lt;br /&gt;
In this example, $true is the &#039;condition&#039; and the &#039;command&#039; is &amp;quot;echo -atg Yep. This is true.&amp;quot; &amp;lt;br /&amp;gt;&lt;br /&gt;
If you put this [[alias]] in the remote section of mIRC and type /ifexample in the command line, it will echo to you &amp;quot;Yep. This is true&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
Now try changing [[$true]] to [[$false]] and notice that it will not say this is true. &amp;lt;br /&amp;gt;&lt;br /&gt;
Also notice that no matter what is inside the if statement, you will see &amp;quot;Let&#039;s see...&amp;quot; and &amp;quot;See?&amp;quot;. These things are completely separate from the control statement and will not depend on the value of the stuff in parenthesis.&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Well this is all well and good, but how does this help us do anything? The simple answer is [[If-then-else|operators]]. Operators are special words you put inside control statements which will tell you if something is true or false. &amp;lt;br /&amp;gt;&lt;br /&gt;
What if you don&#039;t know if it&#039;s raining, but you still want to put a poncho on if it is? You may wish to have &lt;br /&gt;
 Let a = Is it raining?&lt;br /&gt;
 if (a) { put on poncho }&lt;br /&gt;
&amp;lt;br /&amp;gt; &amp;lt;br /&amp;gt;&lt;br /&gt;
Let&#039;s make an alias that tells us what to do under different weather conditions named &amp;quot;weather.&amp;quot; We will make it so that we can type /weather rain, and it will tell us to put a poncho on. Remember that if you do this, [[$1]] will be &amp;quot;rain&amp;quot;. &amp;lt;br /&amp;gt;&lt;br /&gt;
To do this we will need to learn the &amp;quot;==&amp;quot; operator. There are two equals here and you MUST use them both. This checks to see if two things are equal. For example:&lt;br /&gt;
 a == b // This is $false&lt;br /&gt;
 a == a // This is $true&lt;br /&gt;
So in our weather alias we will need to check to see if $1 is equal to &amp;quot;rain&amp;quot;, and if it is, we echo &amp;quot;Put on a poncho.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 alias weather {&lt;br /&gt;
   if ($1 == rain) { echo -atg Put on a poncho. } &lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[If-Then-Else]] - &#039;Full&#039; list of operators&lt;br /&gt;
* [[While]] - Another control structure for repeating things as long as something is true&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Big_sockets_tutorial&amp;diff=4946</id>
		<title>Big sockets tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Big_sockets_tutorial&amp;diff=4946"/>
		<updated>2010-06-13T21:14:44Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is an advanced socket tutorial covering TCP sockets, and using as an example an http connection.  It provides you with an explanation of everything going on, and assumes you have no clue as to what a socket really is.  In addition, this tutorial uses [[:Category:Binary_Files|binary variables]] to ensure this works correctly. You may use normal [[variables]] if you know what you are doing an understand what is going on.&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will create a script that goes to youtube.com and finds the title of any link pasted in the channel.&lt;br /&gt;
&lt;br /&gt;
==What Are Sockets, How Do Networks Work?==&lt;br /&gt;
&lt;br /&gt;
First let&#039;s de-romanticize sockets.  The idea is rather simple but most scripters go into them thinking they are magical and difficult.  Sockets (Read: TCP Sockets) are the method used by programs to communicate over a network, a network such as the internet. &lt;br /&gt;
&lt;br /&gt;
A TCP Socket must connect to a computer using two things: an [http://en.wikipedia.org/wiki/IP_address IP Address] which specifies where the computer is in the world, and a port, which is a number between 1 and 65535 that contains the program you wish to communicate with on the computer.&lt;br /&gt;
&lt;br /&gt;
Once a connection is established, you can read (receive data) and write (send data) through it, until it is disconnected.&lt;br /&gt;
&lt;br /&gt;
==Script Setup==&lt;br /&gt;
&lt;br /&gt;
First let us set up our script.  We will catch all youtube links in any window and call an [[alias]] &amp;quot;youtubelookup&amp;quot; with the location of the file.&lt;br /&gt;
Note: http://www.youtube.com/watch?v=dF184_T_eWw&amp;amp;feature=sub&lt;br /&gt;
The HOST (which counts as the IP Address, in a manner of speaking) is www.youtube.com&lt;br /&gt;
The LOCATION is /watch?v=dF184_T_eWw&amp;amp;feature=sub&lt;br /&gt;
The PROTOCOL is http&lt;br /&gt;
&lt;br /&gt;
 on *:TEXT:*youtube.com/*:*: {&lt;br /&gt;
  ; Get the first youtube.com link from the text the users say. We use the $mid just so we don;t have to worry about the http://www.you... crap in our $pos&lt;br /&gt;
  [[var]] %link = $mid([[$wildtok]]([[$1-]], *youtube.com/*,1,32),10)&lt;br /&gt;
  ; Get the location by returning everything after and including the /&lt;br /&gt;
  var %location = $mid(%link,$pos(%link,/))&lt;br /&gt;
  ; Call alias youtubelookup&lt;br /&gt;
  youtubelookup %location&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now that the dirty business there is done, lets get to the sockets.&lt;br /&gt;
&lt;br /&gt;
==Making the Connection==&lt;br /&gt;
&lt;br /&gt;
Now we must establish a connection to youtube&#039;s servers.  I said before that you need an IP and a port, well theres a bit of a catch.  The internet has a more practical way than remembering complex IP addresses such as 74.125.95.93, they are called host names.  www.youtube.com is a host name.  mIRC makes things easy for us, we can give it a host name and a port, or an a ip and a port.&lt;br /&gt;
&lt;br /&gt;
So clearly our host name is www.youtube.com, but what is our port? Some ports are reserved, here is a list of reserved ports: [http://www.iana.org/assignments/port-numbers Reserved Port Numbers].  What you need to know now is that the reserved port for the HTTP protocol, which is what the web is based on in general, is 80.  There are others, but 80 is the most common, and the default port for all browsers.&lt;br /&gt;
&lt;br /&gt;
We will use the [[sockopen]] command to connect. In the interest of time I will not explain every command or event, but will instead provide a link, please use them to learn more.&lt;br /&gt;
&lt;br /&gt;
In mIRC sockets are referred to by their names, so we will need to give this connection a unique name.  We could use a name such as &#039;youtubecheck&#039; but then we would only be able to do one at a time. Instead, let us use a randomly made name created by using this: youtubecheck $+ [[$ticks]] &lt;br /&gt;
&lt;br /&gt;
 alias youtubelookup {&lt;br /&gt;
   ; We make sure the location was given to us, otherwise we echo an error and stop doing things&lt;br /&gt;
   if ($0 != 1) { echo -a * /youtubelookup: invalid parameters | [[halt]] }&lt;br /&gt;
   ; We generate our random name and make absolutely certain this name isn&#039;t taken.  Note since our on text can work for both channels and queries, we use an iif&lt;br /&gt;
   var %name = youtubelookup $+ $ticks&lt;br /&gt;
   if ($sock(%name)) { msg [[$iif]](#,#,$nick) Youtube lookup error, name was in use ( $+ %name $+ ) | halt }&lt;br /&gt;
   ;&lt;br /&gt;
   sockopen %name www.youtube.com 80&lt;br /&gt;
   ; We will need a way to tell which channel or nick to respond to once we get our reply from youtube&#039;s servers. We use sockmark for this. &lt;br /&gt;
   ;Sockmark is a simple way to store data related to a socket in text form. Please click the link for more information&lt;br /&gt;
   [[sockmark]] %name $iif(#,#,$nick) $1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Sending Data==&lt;br /&gt;
Now we have opened a connection to youtube&#039;s servers.&lt;br /&gt;
A [http://en.wikipedia.org/wiki/Protocol_(computing) Protocol] is the rules by which two things communicate. In this case, it tells us what we (the client) and what youtube (the server) should say in response to events.  The [http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol HTTP protocol] says that once connected, the client should send headers, a list of information about what we want the server to send us.&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
[http://en.wikipedia.org/wiki/List_of_HTTP_headers Headers] look like this:   &amp;lt;br /&amp;gt;&lt;br /&gt;
GET /mypage.html HTTP/1.1&amp;lt;br /&amp;gt;&lt;br /&gt;
Host: mysite.com&amp;lt;br /&amp;gt;&lt;br /&gt;
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)&amp;lt;br /&amp;gt;&lt;br /&gt;
Connection: close&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
You can look them up at the link, by the basic gist of this is that we want to get the location /mypage.html from mysite.com. Once we get it, we want you to close the connection. We are browsing with Mozilla Firefox 5.0 for Windows.  [https://addons.mozilla.org/en-US/firefox/addon/3829/ Live HTTP Headers] is a plugin for firefox which allows you to see the headers firefox sends.  If you have trouble viewing a site with your sockets, use every header that firefox sends just in case.&lt;br /&gt;
&lt;br /&gt;
Let us now send our request to youtube once we connect:&lt;br /&gt;
&lt;br /&gt;
 on *:SOCKOPEN:youtubelookup*: {&lt;br /&gt;
  ; We set an variable as an alias to write the data to the socket so we don&#039;t have to type it every time&lt;br /&gt;
  var %n = [[sockwrite]] -n [[$sockname]]&lt;br /&gt;
  ; We GET the location we parsed out in our on text event. If you recall, we set this as the second word in our sockmark in the alias above.&lt;br /&gt;
  %n GET [[$gettok]]($sock($sockname).mark,2,32) HTTP/1.1&lt;br /&gt;
  ; Some IPs have many web sites on them.  We need to tell the web server that www.youtube.com is the host we are using.&lt;br /&gt;
  %n Host: www.youtube.com&lt;br /&gt;
  ; Some web sites only allow certain web browsers, so we lie a bit here and tell youtube we are firefox 5.0.&lt;br /&gt;
  %n User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)&lt;br /&gt;
  ; We make sure youtube closes its connection with us as soon as its sent everything we asked for&lt;br /&gt;
  %n Connection: close&lt;br /&gt;
  ; This empty line is the HTTP protocol way to tell it that we are done sending our data.&lt;br /&gt;
  %n&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Using the Reply==&lt;br /&gt;
&lt;br /&gt;
Youtube&#039;s server will reply to what we asked for.  There are many possible replies depending on what the deal is: The page may not be found, and then it will reply to tell us as much.  The page may be moved, or the page may be just fine.  No matter what the deal is, the server will explain it to us in headers.  If the page exists, it will send us the page&#039;s HTML (javascript css etc, the stuff the web site is written in) after the header telling us it exists.&lt;br /&gt;
&lt;br /&gt;
Now, we do not receive this whole web page at once! Our internet speeds aren&#039;t unlimited, and data must travel, for that reason we can only receive data at most as fast as our connection is, and so we must deal with the data as we receive it.  In mIRC the easiest way to do this is to add it all to the end of a binary variable as we receive the data then write it to a file for storage and once we are done receiving data (Once the server closes the connection) then we can work with all of it at once. Let us do this now.&lt;br /&gt;
&lt;br /&gt;
Sockread is triggered every time more data is received.&lt;br /&gt;
&lt;br /&gt;
 on *:SOCKREAD:youtubelookup*: {&lt;br /&gt;
  ; If there was an error, $sockerr will contain the error number. We will message the user and tell them about the error, then stop.&lt;br /&gt;
  if ($sockerr) { msg $gettok($sock($sockname).mark,1,32) There was an error while verifying youtube link: $sock($sockname).wsmsg | [[sockclose]] $sockname | halt }&lt;br /&gt;
  ;&lt;br /&gt;
  :read&lt;br /&gt;
  ; We will read the data we received&lt;br /&gt;
  [[sockread]] -f &amp;amp;data &lt;br /&gt;
  ; We will write the data to the end of the file with the same name as the socket. The -1 means the end of the file.&lt;br /&gt;
   if ($sockbr &amp;gt; 0) { [[bwrite]] $sockname -1 &amp;amp;data }&lt;br /&gt;
  ; $sockbr (sock bytes read) contains the number of bytes (read: ascii letters) that we received this time.  We want to keep reading until we can&#039;t read anymore!&lt;br /&gt;
  ; Note: Once $sockbr is 0, this doesn&#039;t mean the server doesn&#039;t have more to send! It just means we haven&#039;t gotten any more.&lt;br /&gt;
  while ($sockbr &amp;gt; 0) { [[goto]] read } &lt;br /&gt;
  ;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Using the Data==&lt;br /&gt;
Congratulations! You are done with sockets.  We have now asked youtube for information, and received our information. Now, we need to do something with it.  This isn&#039;t related to sockets, but since I took you this far I&#039;ll take you to the end. Remember: We have written all the data we received constantly to a file which has the same name as the socket, now we will read this data, first lets add this bit of practice that will just open the file so we can see it all in all its glory :)&lt;br /&gt;
&lt;br /&gt;
Recall: When the server is done, we told it to close the socket.&lt;br /&gt;
 on *:SOCKCLOSE:youtubelookup*: { run notepad $sockname }&lt;br /&gt;
&lt;br /&gt;
I used the same link as I did from above to test it, saying &amp;quot;http://www.youtube.com/watch?v=dF184_T_eWw&amp;amp;feature=sub&amp;quot; in a channel I am in.  If you did too, you probably saw a few headers setting some cookies, other things, and  HTTP/1.1 200 OK telling us this page is fine and it gave it to us.&lt;br /&gt;
&lt;br /&gt;
Then you will see an empty line followed by what may be gibberish to you.  This is the web page. It is HTML, CSS, and Javascript.  I&#039;m certainly not going to teach you HTML in this tutorial, if you&#039;re interested see w3schools.com - what&#039;s important is that everything on the web page is represented here in one form or another.  What I want my script to do is tell the person who pasted the link the title of the page. Press ctrl+f in notepad and search the document for &amp;lt;title&amp;gt;: You should see this:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;title&amp;gt;YouTube - Congressmen Submit Emergency 3 AM Bill Demanding IHOP Stay Open All Night&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Although it will not be as pretty. Youtube and most mass sites have BAD code, they do not care as long as it works.  There will be line breaks and spaces everywhere, we are going to fix that. This part is somewhat advanced and is not related to sockets, if you wish to continue you may, but otherwise I encourage you to use what you learned here and try several things; Try web pages that dont exists or redirect and see what they say, once you have what you need read from the file you downloaded and paste the data to a channel or user, just do not forget to delete the file when you are done reading from it!&lt;br /&gt;
&lt;br /&gt;
==Parsing The Data==&lt;br /&gt;
 on *:SOCKCLOSE:youtubelookup*: { &lt;br /&gt;
  ; The place to send the data&lt;br /&gt;
  var %target = $gettok($sock($sockname).mark,1,32)&lt;br /&gt;
  ; Read the file into the binary file &amp;amp;data&lt;br /&gt;
  bread $sockname 1 $file($sockname).size &amp;amp;data&lt;br /&gt;
  ; Gets the position of &amp;lt;title&amp;gt; in the document&lt;br /&gt;
  var %start = $bfind(&amp;amp;data,1,&amp;lt;title&amp;gt;).text&lt;br /&gt;
  ; Gets the position of &amp;lt;/title&amp;gt; in the document&lt;br /&gt;
  var %end = $bfind(&amp;amp;data,%start,&amp;lt;/title&amp;gt;).text &lt;br /&gt;
  ; If we could not find these tags, the file likely is not valid.&lt;br /&gt;
  if (!%start || !%end) { msg %target Invalid youtube link. | halt }&lt;br /&gt;
  ; get the data in between %start and %end, excluding the 7 letters for &amp;lt;title&amp;gt;&lt;br /&gt;
  echo -atg  Test: $bvar(&amp;amp;data,$calc(%start +7),$calc(%end - %start - 7))&lt;br /&gt;
  ; Remove new lines&lt;br /&gt;
  breplace &amp;amp;data 10 32&lt;br /&gt;
  breplace &amp;amp;data 13 32&lt;br /&gt;
  var %title = $bvar(&amp;amp;data, $calc(%start + 7), $calc(%end - %start - 7)).text&lt;br /&gt;
  ; Tell target&lt;br /&gt;
  msg %target Youtube Link: %title&lt;br /&gt;
  ; Delete the file&lt;br /&gt;
  .remove $sockname&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]][[Category:Socket]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Big_sockets_tutorial&amp;diff=2763</id>
		<title>Big sockets tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Big_sockets_tutorial&amp;diff=2763"/>
		<updated>2010-06-10T19:44:01Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is an advanced socket tutorial covering TCP sockets, and using as an example an http connection.  It provides you with an explanation of everything going on, and assumes you have no clue as to what a socket really is.  In addition, this tutorial uses [[:Category:Binary_Files|binary variables]] to ensure this works correctly. You may use normal [[variables]] if you know what you are doing an understand what is going on.&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will create a script that goes to youtube.com and finds the title of any link pasted in the channel.&lt;br /&gt;
&lt;br /&gt;
==What Are Sockets, How Do Networks Work?==&lt;br /&gt;
&lt;br /&gt;
First let&#039;s de-romanticize sockets.  The idea is rather simple but most scripters go into them thinking they are magical and difficult.  Sockets (Read: TCP Sockets) are the method used by programs to communicate over a network, a network such as the internet. &lt;br /&gt;
&lt;br /&gt;
 A TCP Socket must connect to a computer using two things: an [http://en.wikipedia.org/wiki/IP_address IP Address] which specifies where the computer is in the world, and a port, which is a number between 1 and 65535 that contains the program you wish to communicate with on the computer.&lt;br /&gt;
&lt;br /&gt;
 Once a connection is established, you can read (receive data) and write (send data) through it, until it is disconnected.&lt;br /&gt;
&lt;br /&gt;
==Script Setup==&lt;br /&gt;
&lt;br /&gt;
First let us set up our script.  We will catch all youtube links in any window and call an [[alias]] &amp;quot;youtubelookup&amp;quot; with the location of the file.&lt;br /&gt;
Note: http://www.youtube.com/watch?v=dF184_T_eWw&amp;amp;feature=sub&lt;br /&gt;
The HOST (which counts as the IP Address, in a manner of speaking) is www.youtube.com&lt;br /&gt;
The LOCATION is /watch?v=dF184_T_eWw&amp;amp;feature=sub&lt;br /&gt;
The PROTOCOL is http&lt;br /&gt;
&lt;br /&gt;
 on *:TEXT:*youtube.com/*:*: {&lt;br /&gt;
  ; Get the first youtube.com link from the text the users say. We use the $mid just so we don;t have to worry about the http://www.you... crap in our $pos&lt;br /&gt;
  [[var]] %link = $mid([[$wildtok]]([[$1-]], *youtube.com/*,1,32),10)&lt;br /&gt;
  ; Get the location by returning everything after and including the /&lt;br /&gt;
  var %location = $mid(%link,$pos(%link,/))&lt;br /&gt;
  ; Call alias youtubelookup&lt;br /&gt;
  youtubelookup %location&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now that the dirty business there is done, lets get to the sockets.&lt;br /&gt;
&lt;br /&gt;
==Making the Connection==&lt;br /&gt;
&lt;br /&gt;
Now we must establish a connection to youtube&#039;s servers.  I said before that you need an IP and a port, well theres a bit of a catch.  The internet has a more practical way than remembering complex IP addresses such as 74.125.95.93, they are called host names.  www.youtube.com is a host name.  mIRC makes things easy for us, we can give it a host name and a port, or an a ip and a port.&lt;br /&gt;
&lt;br /&gt;
So clearly our host name is www.youtube.com, but what is our port? Some ports are reserved, here is a list of reserved ports: [http://www.iana.org/assignments/port-numbers Reserved Port Numbers].  What you need to know now is that the reserved port for the HTTP protocol, which is what the web is based on in general, is 80.  There are others, but 80 is the most common, and the default port for all browsers.&lt;br /&gt;
&lt;br /&gt;
We will use the [[sockopen]] command to connect. In the interest of time I will not explain every command or event, but will instead provide a link, please use them to learn more.&lt;br /&gt;
&lt;br /&gt;
In mIRC sockets are referred to by their names, so we will need to give this connection a unique name.  We could use a name such as &#039;youtubecheck&#039; but then we would only be able to do one at a time. Instead, let us use a randomly made name created by using this: youtubecheck $+ [[$ticks]] &lt;br /&gt;
&lt;br /&gt;
 alias youtubelookup {&lt;br /&gt;
   ; We make sure the location was given to us, otherwise we echo an error and stop doing things&lt;br /&gt;
   if ($0 != 1) { echo -a * /youtubelookup: invalid parameters | [[halt]] }&lt;br /&gt;
   ; We generate our random name and make absolutely certain this name isn&#039;t taken.  Note since our on text can work for both channels and queries, we use an iif&lt;br /&gt;
   var %name = youtubelookup $+ $ticks&lt;br /&gt;
   if ($sock(%name)) { msg [[$iif]](#,#,$nick) Youtube lookup error, name was in use ( $+ %name $+ ) | halt }&lt;br /&gt;
   ;&lt;br /&gt;
   sockopen %name www.youtube.com 80&lt;br /&gt;
   ; We will need a way to tell which channel or nick to respond to once we get our reply from youtube&#039;s servers. We use sockmark for this. &lt;br /&gt;
   ;Sockmark is a simple way to store data related to a socket in text form. Please click the link for more information&lt;br /&gt;
   [[sockmark]] %name $iif(#,#,$nick) $1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Sending Data==&lt;br /&gt;
Now we have opened a connection to youtube&#039;s servers.&lt;br /&gt;
A [http://en.wikipedia.org/wiki/Protocol_(computing) Protocol] is the rules by which two things communicate. In this case, it tells us what we (the client) and what youtube (the server) should say in response to events.  The [http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol HTTP protocol] says that once connected, the client should send headers, a list of information about what we want the server to send us.&lt;br /&gt;
&lt;br /&gt;
[http://en.wikipedia.org/wiki/List_of_HTTP_headers Headers] look like this:  &lt;br /&gt;
GET /mypage.html HTTP/1.1&lt;br /&gt;
Host: mysite.com&lt;br /&gt;
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)&lt;br /&gt;
Connection: close&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can look them up at the link, by the basic gist of this is that we want to get the location /mypage.html from mysite.com. Once we get it, we want you to close the connection. We are browsing with Mozilla Firefox 5.0 for Windows.  [https://addons.mozilla.org/en-US/firefox/addon/3829/ Live HTTP Headers] is a plugin for firefox which allows you to see the headers firefox sends.  If you have trouble viewing a site with your sockets, use every header that firefox sends just in case.&lt;br /&gt;
&lt;br /&gt;
Let us now send our request to youtube once we connect:&lt;br /&gt;
&lt;br /&gt;
 on *:SOCKOPEN:youtubelookup*: {&lt;br /&gt;
  ; We set an variable as an alias to write the data to the socket so we don&#039;t have to type it every time&lt;br /&gt;
  var %n = [[sockwrite]] -n [[$sockname]]&lt;br /&gt;
  ; We GET the location we parsed out in our on text event. If you recall, we set this as the second word in our sockmark in the alias above.&lt;br /&gt;
  %n GET [[$gettok]]($sock($sockname).mark,2,32) HTTP/1.1&lt;br /&gt;
  ; Some IPs have many web sites on them.  We need to tell the web server that www.youtube.com is the host we are using.&lt;br /&gt;
  %n Host: www.youtube.com&lt;br /&gt;
  ; Some web sites only allow certain web browsers, so we lie a bit here and tell youtube we are firefox 5.0.&lt;br /&gt;
  %n User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)&lt;br /&gt;
  ; We make sure youtube closes its connection with us as soon as its sent everything we asked for&lt;br /&gt;
  %n Connection: close&lt;br /&gt;
  ; This empty line is the HTTP protocol way to tell it that we are done sending our data.&lt;br /&gt;
  %n&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Using the Reply==&lt;br /&gt;
&lt;br /&gt;
Youtube&#039;s server will reply to what we asked for.  There are many possible replies depending on what the deal is: The page may not be found, and then it will reply to tell us as much.  The page may be moved, or the page may be just fine.  No matter what the deal is, the server will explain it to us in headers.  If the page exists, it will send us the page&#039;s HTML (javascript css etc, the stuff the web site is written in) after the header telling us it exists.&lt;br /&gt;
&lt;br /&gt;
Now, we do not receive this whole web page at once! Our internet speeds aren&#039;t unlimited, and data must travel, for that reason we can only receive data at most as fast as our connection is, and so we must deal with the data as we receive it.  In mIRC the easiest way to do this is to add it all to the end of a binary variable as we receive the data then write it to a file for storage and once we are done receiving data (Once the server closes the connection) then we can work with all of it at once. Let us do this now.&lt;br /&gt;
&lt;br /&gt;
Sockread is triggered every time more data is received.&lt;br /&gt;
&lt;br /&gt;
 on *:SOCKREAD:youtubelookup*: {&lt;br /&gt;
  ; If there was an error, $sockerr will contain the error number. We will message the user and tell them about the error, then stop.&lt;br /&gt;
  if ($sockerr) { msg $gettok($sock($sockname).mark,1,32) There was an error while verifying youtube link: $sock($sockname).wsmsg | [[sockclose]] $sockname | halt }&lt;br /&gt;
  ;&lt;br /&gt;
  :read&lt;br /&gt;
  ; We will read the data we received&lt;br /&gt;
  [[sockread]] -f &amp;amp;data &lt;br /&gt;
  ; We will write the data to the end of the file with the same name as the socket. The -1 means the end of the file.&lt;br /&gt;
   if ($sockbr &amp;gt; 0) { [[bwrite]] $sockname -1 &amp;amp;data }&lt;br /&gt;
  ; $sockbr (sock bytes read) contains the number of bytes (read: ascii letters) that we received this time.  We want to keep reading until we can&#039;t read anymore!&lt;br /&gt;
  ; Note: Once $sockbr is 0, this doesn&#039;t mean the server doesn&#039;t have more to send! It just means we haven&#039;t gotten any more.&lt;br /&gt;
  while ($sockbr &amp;gt; 0) { [[goto]] read } &lt;br /&gt;
  ;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Using the Data==&lt;br /&gt;
Congratulations! You are done with sockets.  We have now asked youtube for information, and received our information. Now, we need to do something with it.  This isn&#039;t related to sockets, but since I took you this far I&#039;ll take you to the end. Remember: We have written all the data we received constantly to a file which has the same name as the socket, now we will read this data, first lets add this bit of practice that will just open the file so we can see it all in all its glory :)&lt;br /&gt;
&lt;br /&gt;
Recall: When the server is done, we told it to close the socket.&lt;br /&gt;
 on *:SOCKCLOSE:youtubelookup*: { run notepad $sockname }&lt;br /&gt;
&lt;br /&gt;
I used the same link as I did from above to test it, saying &amp;quot;http://www.youtube.com/watch?v=dF184_T_eWw&amp;amp;feature=sub&amp;quot; in a channel I am in.  If you did too, you probably saw a few headers setting some cookies, other things, and  HTTP/1.1 200 OK telling us this page is fine and it gave it to us.&lt;br /&gt;
&lt;br /&gt;
Then you will see an empty line followed by what may be gibberish to you.  This is the web page. It is HTML, CSS, and Javascript.  I&#039;m certainly not going to teach you HTML in this tutorial, if you&#039;re interested see w3schools.com - what&#039;s important is that everything on the web page is represented here in one form or another.  What I want my script to do is tell the person who pasted the link the title of the page. Press ctrl+f in notepad and search the document for &amp;lt;title&amp;gt;: You should see this:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;title&amp;gt;YouTube - Congressmen Submit Emergency 3 AM Bill Demanding IHOP Stay Open All Night&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Although it will not be as pretty. Youtube and most mass sites have BAD code, they do not care as long as it works.  There will be line breaks and spaces everywhere, we are going to fix that. This part is somewhat advanced and is not related to sockets, if you wish to continue you may, but otherwise I encourage you to use what you learned here and try several things; Try web pages that dont exists or redirect and see what they say, once you have what you need read from the file you downloaded and paste the data to a channel or user, just do not forget to delete the file when you are done reading from it!&lt;br /&gt;
&lt;br /&gt;
==Parsing The Data==&lt;br /&gt;
 on *:SOCKCLOSE:youtubelookup*: { &lt;br /&gt;
  ; The place to send the data&lt;br /&gt;
  var %target = $gettok($sock($sockname).mark,1,32)&lt;br /&gt;
  ; Read the file into the binary file &amp;amp;data&lt;br /&gt;
  bread $sockname 1 $file($sockname).size &amp;amp;data&lt;br /&gt;
  ; Gets the position of &amp;lt;title&amp;gt; in the document&lt;br /&gt;
  var %start = $bfind(&amp;amp;data,1,&amp;lt;title&amp;gt;).text&lt;br /&gt;
  ; Gets the position of &amp;lt;/title&amp;gt; in the document&lt;br /&gt;
  var %end = $bfind(&amp;amp;data,%start,&amp;lt;/title&amp;gt;).text &lt;br /&gt;
  ; If we could not find these tags, the file likely is not valid.&lt;br /&gt;
  if (!%start || !%end) { msg %target Invalid youtube link. | halt }&lt;br /&gt;
  ; get the data in between %start and %end, excluding the 7 letters for &amp;lt;title&amp;gt;&lt;br /&gt;
  echo -atg  Test: $bvar(&amp;amp;data,$calc(%start +7),$calc(%end - %start - 7))&lt;br /&gt;
  ; Remove new lines&lt;br /&gt;
  breplace &amp;amp;data 10 32&lt;br /&gt;
  breplace &amp;amp;data 13 32&lt;br /&gt;
  var %title = $bvar(&amp;amp;data, $calc(%start + 7), $calc(%end - %start - 7)).text&lt;br /&gt;
  ; Tell target&lt;br /&gt;
  msg %target Youtube Link: %title&lt;br /&gt;
  ; Delete the file&lt;br /&gt;
  .remove $sockname&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]][[Category:Socket]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Little_sockets_tutorial&amp;diff=3793</id>
		<title>Little sockets tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Little_sockets_tutorial&amp;diff=3793"/>
		<updated>2010-06-10T19:43:52Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This tutorial shows how to use [[:Category:Socket|sockets]] to get info from http://checkip.dyndns.org/index.php and how to use that info to set the ip in mirc correct. This can be usefull if you&#039;ve a router and a bnc.&lt;br /&gt;
&lt;br /&gt;
 ;this is a little tutorial about sockets&lt;br /&gt;
 ;&lt;br /&gt;
 ;it&#039;s about how to connect to http://checkip.dyndns.org/index.php with mirc, then take your ip out&lt;br /&gt;
 ;&lt;br /&gt;
 ;and use that ip to set [[$ip]]&lt;br /&gt;
 ;&lt;br /&gt;
 ;this can be useful if you&#039;ve a router and a bnc, depending on your mirc settings, mirc will normally&lt;br /&gt;
 ;make $ip your bnc serverip, or your local ip, like 192.168.0.2&lt;br /&gt;
 ;&lt;br /&gt;
 ;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 ;on connect event, with timer, so if you connect to 3 different servers on start up, it&#039;ll only be triggered once :)&lt;br /&gt;
 [[On_connect|on *:connect]]:{ .[[timer]]myip 1 5 myip }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 [[Aliases|alias]] myip {&lt;br /&gt;
  ;if the socket already is open, close it, you can only have one connection with the same name at the same time&lt;br /&gt;
  [[If-Then-Else|if]] ([[$sock]](myip)) { [[sockclose]] myip }&lt;br /&gt;
  ;to open a socket with name myip to address checkip.dyndns.org on port 80&lt;br /&gt;
  [[sockopen]] myip checkip.dyndns.org 80&lt;br /&gt;
 }&lt;br /&gt;
 ;this event is triggered on the moment the sockets is opened&lt;br /&gt;
 [[On_sockopen|on *:sockopen]]:myip:{&lt;br /&gt;
  ;to tell the server which file you want to receive&lt;br /&gt;
  [[sockwrite]] -n [[$sockname]] GET /index.php HTTP/1.0&lt;br /&gt;
  sockwrite -n $sockname Host: checkip.dyndns.org&lt;br /&gt;
  ;this is only needed for a few websites, but it&#039;s just more complete to have it ;)&lt;br /&gt;
  sockwrite -n $sockname user-agent: Mozilla/??&lt;br /&gt;
  sockwrite -n $sockname Connection: Keep-Alive&lt;br /&gt;
  sockwrite -n $sockname [[$crlf]]&lt;br /&gt;
 }&lt;br /&gt;
 [[On_sockread|on *:sockread]]:myip:{&lt;br /&gt;
  ;same way as in mirc helpfile with the :nextread&lt;br /&gt;
  if ([[$sockerr]] &amp;gt; 0) [[return]]&lt;br /&gt;
  :nextread&lt;br /&gt;
  [[sockread]] %temp&lt;br /&gt;
  if ([[$sockbr]] == 0) return&lt;br /&gt;
  ;to check if we got the right line, it&#039;s pretty easy for this file, but usually more complicated,&lt;br /&gt;
  ;use $gettok or variables to get the right line.&lt;br /&gt;
  if (&amp;lt;html&amp;gt; [[If-Then-Else#isin|isin]] %temp) {&lt;br /&gt;
    ;%temp will contain something like:&lt;br /&gt;
    ;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Current IP Check&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;Current IP Address: 217.20.116.184&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;
    ;an easy way to &#039;strip&#039; it is with $remove&lt;br /&gt;
    [[var]] %z = [[$remove]](%temp,&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Current IP Check&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;Current IP Address:,&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;)&lt;br /&gt;
 &lt;br /&gt;
    ;scon -a is to send a command to all servers mirc is connected to&lt;br /&gt;
    [[scon]] -a localinfo [[$chr]](10) %z&lt;br /&gt;
    scon -a [[echo]] 4 -s ip is set to $ip&lt;br /&gt;
  }  &lt;br /&gt;
  [[Goto_loops|goto]] nextread&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 ;If your knowledge about sockets is still [[$null]] or just not enough yet ;), maybe read this tutorial:&lt;br /&gt;
 ; http://www.mircscripts.org/showdoc.php?type=tutorial&amp;amp;id=1128 or check /help Sockets&lt;br /&gt;
== See Also ==&lt;br /&gt;
* [[Big sockets tutorial]]&lt;br /&gt;
[[Category:Tutorials]][[Category:Socket]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Big_sockets_tutorial&amp;diff=2762</id>
		<title>Big sockets tutorial</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Big_sockets_tutorial&amp;diff=2762"/>
		<updated>2010-06-10T19:42:02Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This is an advanced socket tutorial covering TCP sockets, and using as an example an http connection.  It provides you with an explanation of everything going on, and assumes you have no clue as to what a socket really is.  In addition, this tutorial uses [[:Category:Binary_Files|binary variables]] to ensure this works correctly. You may use normal [[variables]] if you know what you are doing an understand what is going on.&lt;br /&gt;
&lt;br /&gt;
In this tutorial we will create a script that goes to youtube.com and finds the title of any link pasted in the channel.&lt;br /&gt;
&lt;br /&gt;
==What Are Sockets, How Do Networks Work?==&lt;br /&gt;
&lt;br /&gt;
First let&#039;s de-romanticize sockets.  The idea is rather simple but most scripters go into them thinking they are magical and difficult.  Sockets (Read: TCP Sockets) are the method used by programs to communicate over a network, a network such as the internet. &lt;br /&gt;
&lt;br /&gt;
 A TCP Socket must connect to a computer using two things: an [http://en.wikipedia.org/wiki/IP_address IP Address] which specifies where the computer is in the world, and a port, which is a number between 1 and 65535 that contains the program you wish to communicate with on the computer.&lt;br /&gt;
&lt;br /&gt;
 Once a connection is established, you can read (receive data) and write (send data) through it, until it is disconnected.&lt;br /&gt;
&lt;br /&gt;
==Script Setup==&lt;br /&gt;
&lt;br /&gt;
First let us set up our script.  We will catch all youtube links in any window and call an [[alias]] &amp;quot;youtubelookup&amp;quot; with the location of the file.&lt;br /&gt;
Note: http://www.youtube.com/watch?v=dF184_T_eWw&amp;amp;feature=sub&lt;br /&gt;
The HOST (which counts as the IP Address, in a manner of speaking) is www.youtube.com&lt;br /&gt;
The LOCATION is /watch?v=dF184_T_eWw&amp;amp;feature=sub&lt;br /&gt;
The PROTOCOL is http&lt;br /&gt;
&lt;br /&gt;
 on *:TEXT:*youtube.com/*:*: {&lt;br /&gt;
  ; Get the first youtube.com link from the text the users say. We use the $mid just so we don;t have to worry about the http://www.you... crap in our $pos&lt;br /&gt;
  [[var]] %link = $mid([[$wildtok]]([[$1-]], *youtube.com/*,1,32),10)&lt;br /&gt;
  ; Get the location by returning everything after and including the /&lt;br /&gt;
  var %location = $mid(%link,$pos(%link,/))&lt;br /&gt;
  ; Call alias youtubelookup&lt;br /&gt;
  youtubelookup %location&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now that the dirty business there is done, lets get to the sockets.&lt;br /&gt;
&lt;br /&gt;
==Making the Connection==&lt;br /&gt;
&lt;br /&gt;
Now we must establish a connection to youtube&#039;s servers.  I said before that you need an IP and a port, well theres a bit of a catch.  The internet has a more practical way than remembering complex IP addresses such as 74.125.95.93, they are called host names.  www.youtube.com is a host name.  mIRC makes things easy for us, we can give it a host name and a port, or an a ip and a port.&lt;br /&gt;
&lt;br /&gt;
So clearly our host name is www.youtube.com, but what is our port? Some ports are reserved, here is a list of reserved ports: [http://www.iana.org/assignments/port-numbers Reserved Port Numbers].  What you need to know now is that the reserved port for the HTTP protocol, which is what the web is based on in general, is 80.  There are others, but 80 is the most common, and the default port for all browsers.&lt;br /&gt;
&lt;br /&gt;
We will use the [[sockopen]] command to connect. In the interest of time I will not explain every command or event, but will instead provide a link, please use them to learn more.&lt;br /&gt;
&lt;br /&gt;
In mIRC sockets are referred to by their names, so we will need to give this connection a unique name.  We could use a name such as &#039;youtubecheck&#039; but then we would only be able to do one at a time. Instead, let us use a randomly made name created by using this: youtubecheck $+ [[$ticks]] &lt;br /&gt;
&lt;br /&gt;
 alias youtubelookup {&lt;br /&gt;
   ; We make sure the location was given to us, otherwise we echo an error and stop doing things&lt;br /&gt;
   if ($0 != 1) { echo -a * /youtubelookup: invalid parameters | [[halt]] }&lt;br /&gt;
   ; We generate our random name and make absolutely certain this name isn&#039;t taken.  Note since our on text can work for both channels and queries, we use an iif&lt;br /&gt;
   var %name = youtubelookup $+ $ticks&lt;br /&gt;
   if ($sock(%name)) { msg [[$iif]](#,#,$nick) Youtube lookup error, name was in use ( $+ %name $+ ) | halt }&lt;br /&gt;
   ;&lt;br /&gt;
   sockopen %name www.youtube.com 80&lt;br /&gt;
   ; We will need a way to tell which channel or nick to respond to once we get our reply from youtube&#039;s servers. We use sockmark for this. &lt;br /&gt;
   ;Sockmark is a simple way to store data related to a socket in text form. Please click the link for more information&lt;br /&gt;
   [[sockmark]] %name $iif(#,#,$nick) $1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Sending Data==&lt;br /&gt;
Now we have opened a connection to youtube&#039;s servers.&lt;br /&gt;
A [http://en.wikipedia.org/wiki/Protocol_(computing) Protocol] is the rules by which two things communicate. In this case, it tells us what we (the client) and what youtube (the server) should say in response to events.  The [http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol HTTP protocol] says that once connected, the client should send headers, a list of information about what we want the server to send us.&lt;br /&gt;
&lt;br /&gt;
[http://en.wikipedia.org/wiki/List_of_HTTP_headers Headers] look like this:  &lt;br /&gt;
GET /mypage.html HTTP/1.1&lt;br /&gt;
Host: mysite.com&lt;br /&gt;
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)&lt;br /&gt;
Connection: close&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can look them up at the link, by the basic gist of this is that we want to get the location /mypage.html from mysite.com. Once we get it, we want you to close the connection. We are browsing with Mozilla Firefox 5.0 for Windows.  [https://addons.mozilla.org/en-US/firefox/addon/3829/ Live HTTP Headers] is a plugin for firefox which allows you to see the headers firefox sends.  If you have trouble viewing a site with your sockets, use every header that firefox sends just in case.&lt;br /&gt;
&lt;br /&gt;
Let us now send our request to youtube once we connect:&lt;br /&gt;
&lt;br /&gt;
 on *:SOCKOPEN:youtubelookup*: {&lt;br /&gt;
  ; We set an variable as an alias to write the data to the socket so we don&#039;t have to type it every time&lt;br /&gt;
  var %n = [[sockwrite]] -n [[$sockname]]&lt;br /&gt;
  ; We GET the location we parsed out in our on text event. If you recall, we set this as the second word in our sockmark in the alias above.&lt;br /&gt;
  %n GET [[$gettok]]($sock($sockname).mark,2,32) HTTP/1.1&lt;br /&gt;
  ; Some IPs have many web sites on them.  We need to tell the web server that www.youtube.com is the host we are using.&lt;br /&gt;
  %n Host: www.youtube.com&lt;br /&gt;
  ; Some web sites only allow certain web browsers, so we lie a bit here and tell youtube we are firefox 5.0.&lt;br /&gt;
  %n User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)&lt;br /&gt;
  ; We make sure youtube closes its connection with us as soon as its sent everything we asked for&lt;br /&gt;
  %n Connection: close&lt;br /&gt;
  ; This empty line is the HTTP protocol way to tell it that we are done sending our data.&lt;br /&gt;
  %n&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Using the Reply==&lt;br /&gt;
&lt;br /&gt;
Youtube&#039;s server will reply to what we asked for.  There are many possible replies depending on what the deal is: The page may not be found, and then it will reply to tell us as much.  The page may be moved, or the page may be just fine.  No matter what the deal is, the server will explain it to us in headers.  If the page exists, it will send us the page&#039;s HTML (javascript css etc, the stuff the web site is written in) after the header telling us it exists.&lt;br /&gt;
&lt;br /&gt;
Now, we do not receive this whole web page at once! Our internet speeds aren&#039;t unlimited, and data must travel, for that reason we can only receive data at most as fast as our connection is, and so we must deal with the data as we receive it.  In mIRC the easiest way to do this is to add it all to the end of a binary variable as we receive the data then write it to a file for storage and once we are done receiving data (Once the server closes the connection) then we can work with all of it at once. Let us do this now.&lt;br /&gt;
&lt;br /&gt;
Sockread is triggered every time more data is received.&lt;br /&gt;
&lt;br /&gt;
 on *:SOCKREAD:youtubelookup*: {&lt;br /&gt;
  ; If there was an error, $sockerr will contain the error number. We will message the user and tell them about the error, then stop.&lt;br /&gt;
  if ($sockerr) { msg $gettok($sock($sockname).mark,1,32) There was an error while verifying youtube link: $sock($sockname).wsmsg | [[sockclose]] $sockname | halt }&lt;br /&gt;
  ;&lt;br /&gt;
  :read&lt;br /&gt;
  ; We will read the data we received&lt;br /&gt;
  [[sockread]] -f &amp;amp;data &lt;br /&gt;
  ; We will write the data to the end of the file with the same name as the socket. The -1 means the end of the file.&lt;br /&gt;
   if ($sockbr &amp;gt; 0) { [[bwrite]] $sockname -1 &amp;amp;data }&lt;br /&gt;
  ; $sockbr (sock bytes read) contains the number of bytes (read: ascii letters) that we received this time.  We want to keep reading until we can&#039;t read anymore!&lt;br /&gt;
  ; Note: Once $sockbr is 0, this doesn&#039;t mean the server doesn&#039;t have more to send! It just means we haven&#039;t gotten any more.&lt;br /&gt;
  while ($sockbr &amp;gt; 0) { [[goto]] read } &lt;br /&gt;
  ;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==Using the Data==&lt;br /&gt;
Congratulations! You are done with sockets.  We have now asked youtube for information, and received our information. Now, we need to do something with it.  This isn&#039;t related to sockets, but since I took you this far I&#039;ll take you to the end. Remember: We have written all the data we received constantly to a file which has the same name as the socket, now we will read this data, first lets add this bit of practice that will just open the file so we can see it all in all its glory :)&lt;br /&gt;
&lt;br /&gt;
Recall: When the server is done, we told it to close the socket.&lt;br /&gt;
 on *:SOCKCLOSE:youtubelookup*: { run notepad $sockname }&lt;br /&gt;
&lt;br /&gt;
I used the same link as I did from above to test it, saying &amp;quot;http://www.youtube.com/watch?v=dF184_T_eWw&amp;amp;feature=sub&amp;quot; in a channel I am in.  If you did too, you probably saw a few headers setting some cookies, other things, and  HTTP/1.1 200 OK telling us this page is fine and it gave it to us.&lt;br /&gt;
&lt;br /&gt;
Then you will see an empty line followed by what may be gibberish to you.  This is the web page. It is HTML, CSS, and Javascript.  I&#039;m certainly not going to teach you HTML in this tutorial, if you&#039;re interested see w3schools.com - what&#039;s important is that everything on the web page is represented here in one form or another.  What I want my script to do is tell the person who pasted the link the title of the page. Press ctrl+f in notepad and search the document for &amp;lt;title&amp;gt;: You should see this:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;title&amp;gt;YouTube - Congressmen Submit Emergency 3 AM Bill Demanding IHOP Stay Open All Night&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Although it will not be as pretty. Youtube and most mass sites have BAD code, they do not care as long as it works.  There will be line breaks and spaces everywhere, we are going to fix that. This part is somewhat advanced and is not related to sockets, if you wish to continue you may, but otherwise I encourage you to use what you learned here and try several things; Try web pages that dont exists or redirect and see what they say, once you have what you need read from the file you downloaded and paste the data to a channel or user, just do not forget to delete the file when you are done reading from it!&lt;br /&gt;
&lt;br /&gt;
==Parsing The Data==&lt;br /&gt;
 on *:SOCKCLOSE:youtubelookup*: { &lt;br /&gt;
  ; The place to send the data&lt;br /&gt;
  var %target = $gettok($sock($sockname).mark,1,32)&lt;br /&gt;
  ; Read the file into the binary file &amp;amp;data&lt;br /&gt;
  bread $sockname 1 $file($sockname).size &amp;amp;data&lt;br /&gt;
  ; Gets the position of &amp;lt;title&amp;gt; in the document&lt;br /&gt;
  var %start = $bfind(&amp;amp;data,1,&amp;lt;title&amp;gt;).text&lt;br /&gt;
  ; Gets the position of &amp;lt;/title&amp;gt; in the document&lt;br /&gt;
  var %end = $bfind(&amp;amp;data,%start,&amp;lt;/title&amp;gt;).text &lt;br /&gt;
  ; If we could not find these tags, the file likely is not valid.&lt;br /&gt;
  if (!%start || !%end) { msg %target Invalid youtube link. | halt }&lt;br /&gt;
  ; get the data in between %start and %end, excluding the 7 letters for &amp;lt;title&amp;gt;&lt;br /&gt;
  echo -atg  Test: $bvar(&amp;amp;data,$calc(%start +7),$calc(%end - %start - 7))&lt;br /&gt;
  ; Remove new lines&lt;br /&gt;
  breplace &amp;amp;data 10 32&lt;br /&gt;
  breplace &amp;amp;data 13 32&lt;br /&gt;
  var %title = $bvar(&amp;amp;data, $calc(%start + 7), $calc(%end - %start - 7)).text&lt;br /&gt;
  ; Tell target&lt;br /&gt;
  msg %target Youtube Link: %title&lt;br /&gt;
  ; Delete the file&lt;br /&gt;
  .remove $sockname&lt;br /&gt;
 }&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$asin&amp;diff=4772</id>
		<title>$asin</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$asin&amp;diff=4772"/>
		<updated>2010-03-14T02:01:21Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Trigonometric function to return the arcsine (1/y) of an angle.&lt;br /&gt;
&lt;br /&gt;
 $asin(N)[.deg]&lt;br /&gt;
* N is an angle in radians.&lt;br /&gt;
* The .deg property allows N to be given in degrees&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[$sin]] for getting the sine of an angle.&lt;br /&gt;
&lt;br /&gt;
[[Category:Text and Number Identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$sin&amp;diff=4770</id>
		<title>$sin</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$sin&amp;diff=4770"/>
		<updated>2010-03-14T02:01:00Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Trigonometric function to return the sine (y) of an angle.&lt;br /&gt;
&lt;br /&gt;
 $sin(N)[.deg]&lt;br /&gt;
* N is an angle in radians.&lt;br /&gt;
* The .deg property allows N to be given in degrees&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[$asin]] for getting the arcsine of an angle.&lt;br /&gt;
&lt;br /&gt;
[[Category:Text and Number Identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$acos&amp;diff=4773</id>
		<title>$acos</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$acos&amp;diff=4773"/>
		<updated>2010-03-14T02:00:26Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Trigonometric function to return the arccosine (1/x) of an angle.&lt;br /&gt;
&lt;br /&gt;
 $acos(N)[.deg]&lt;br /&gt;
* N is an angle in radians.&lt;br /&gt;
* The .deg property allows N to be given in degrees&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[$cos]] for getting the cosine of an angle.&lt;br /&gt;
&lt;br /&gt;
[[Category:Text and Number Identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$cos&amp;diff=4769</id>
		<title>$cos</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$cos&amp;diff=4769"/>
		<updated>2010-03-14T02:00:04Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Trigonometric function to return the cosine (x) of an angle.&lt;br /&gt;
&lt;br /&gt;
 $cos(N)[.deg]&lt;br /&gt;
* N is an angle in radians.&lt;br /&gt;
* The .deg property allows N to be given in degrees&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[$acos]] for getting the arccosine of an angle.&lt;br /&gt;
&lt;br /&gt;
[[Category:Text and Number Identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$atan&amp;diff=4771</id>
		<title>$atan</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$atan&amp;diff=4771"/>
		<updated>2010-03-14T01:59:09Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Trigonometric function to return the arctangent (y/x) of an angle.&lt;br /&gt;
&lt;br /&gt;
 $atan(N)[.deg]&lt;br /&gt;
* N is an angle in radians.&lt;br /&gt;
* The .deg property allows N to be given in degrees&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[$tan]] for getting the tangent of an angle.&lt;br /&gt;
&lt;br /&gt;
[[Category:Text and Number Identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$tan&amp;diff=4768</id>
		<title>$tan</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$tan&amp;diff=4768"/>
		<updated>2010-03-14T01:58:51Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Trigonometric function to return the tangent (x/y) of an angle.&lt;br /&gt;
&lt;br /&gt;
 $tan(N)[.deg]&lt;br /&gt;
* N is an angle in radians.&lt;br /&gt;
* The .deg property allows N to be given in degrees&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
[[$atan]] for getting the arctangent of an angle&lt;br /&gt;
&lt;br /&gt;
[[Category:Text and Number Identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2736</id>
		<title>Scope</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2736"/>
		<updated>2009-10-03T19:11:17Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Scope Precedence&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Scope is the term used to refer to a [[:Category:Variables|variable&#039;s]] &amp;quot;life-span&amp;quot;.  mIRC variables have only two scopes.&lt;br /&gt;
&lt;br /&gt;
==Global Scope==&lt;br /&gt;
A variable with a global scope is accessable everywhere in mIRC where a variable can be used. This means in the command line, in every script file, alias file, popup, etc.  These variables are saved in the Variables tab of the mIRC Scripts editor (alt+r) which are saved to a file (usually vars.ini).&lt;br /&gt;
&lt;br /&gt;
* A global scoped variable is created with [[set]] and destroyed with [[unset]]&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called, /test and then /test2.&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
  set %foo bar.&lt;br /&gt;
 ; %foo is available here&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  set %bar foo&lt;br /&gt;
  ; %foo is available here after being set with /test&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
%foo will now return bar EVERYWHERE (unless overridden by a local variable)&lt;br /&gt;
&lt;br /&gt;
==Local Scope==&lt;br /&gt;
&lt;br /&gt;
A variable with a local scope is only available within the event, alias, or menu (popup) script in which it is used.&lt;br /&gt;
&lt;br /&gt;
* A local variable is created with [[var]] and is automatically destroyed after leaving scope (exiting the current alias, event or menu). It can also be destroyed by [[unset]].&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called using /test &lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
   [[var]] %foo = bar.&lt;br /&gt;
   ; %foo is available here&lt;br /&gt;
   test2 %foo&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  [[var]] %bar = foo&lt;br /&gt;
  ; %foo is NOT available here. We can, however, use $1 to refer to %foo because it was passed as a parameter to the alias.&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Scope Precedence==&lt;br /&gt;
&lt;br /&gt;
A final note on scope: A local scope will take Precedence over a global scope. This means that if there is a global variable and a local variable with the same name, the local variable&#039;s value will be used.&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called using /test and /test2&lt;br /&gt;
&lt;br /&gt;
 alias test {&lt;br /&gt;
   set %foo bar.&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 alias test2{&lt;br /&gt;
   var %foo = Vliedel Smells&lt;br /&gt;
   [[echo]] -atg %foo&lt;br /&gt;
   ; This will echo &amp;quot;Vliedel Smells&amp;quot; even though %foo is still a global variable with value &amp;quot;bar&amp;quot;&lt;br /&gt;
   [[unset]] %foo&lt;br /&gt;
   [[echo]] -atg %foo&lt;br /&gt;
   ; This will echo &amp;quot;bar&amp;quot; because the first unset unset the local-scope variable %foo.&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[var]] - Command used to create a local variables&lt;br /&gt;
* [[Local Variables]] - Variables with limited scope&lt;br /&gt;
* [[set]] - Command used to create global variables&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Variables]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2735</id>
		<title>Scope</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2735"/>
		<updated>2009-10-03T18:54:21Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Scope is the term used to refer to a [[:Category:Variables|variable&#039;s]] &amp;quot;life-span&amp;quot;.  mIRC variables have only two scopes.&lt;br /&gt;
&lt;br /&gt;
==Global Scope==&lt;br /&gt;
A variable with a global scope is accessable everywhere in mIRC where a variable can be used. This means in the command line, in every script file, alias file, popup, etc.  These variables are saved in the Variables tab of the mIRC Scripts editor (alt+r) which are saved to a file (usually vars.ini).&lt;br /&gt;
&lt;br /&gt;
* A global scoped variable is created with [[set]] and destroyed with [[unset]]&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called, /test and then /test2.&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
  set %foo bar.&lt;br /&gt;
 ; %foo is available here&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  set %bar foo&lt;br /&gt;
  ; %foo is available here after being set with /test&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
%foo will now return bar EVERYWHERE.&lt;br /&gt;
&lt;br /&gt;
==Local Scope==&lt;br /&gt;
&lt;br /&gt;
A variable with a local scope is only available within the event, alias, or menu (popup) script in which it is used.&lt;br /&gt;
&lt;br /&gt;
* A local variable is created with [[var]] and is automatically destroyed after leaving scope (exiting the current alias, event or menu). It can also be destroyed by [[unset]].&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called using /test &lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
   [[var]] %foo = bar.&lt;br /&gt;
   ; %foo is available here&lt;br /&gt;
   test2 %foo&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  [[var]] %bar = foo&lt;br /&gt;
  ; %foo is NOT available here. We can, however, use $1 to refer to %foo because it was passed as a parameter to the alias.&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[var]] - Command used to create a local variables&lt;br /&gt;
* [[Local Variables]] - Variables with limited scope&lt;br /&gt;
* [[set]] - Command used to create global variables&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Variables]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2734</id>
		<title>Scope</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2734"/>
		<updated>2009-10-03T18:52:13Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: copy paste error...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Scope is the term used to refer to a [[:Category:Variables|variable&#039;s]] &amp;quot;life-span&amp;quot;.  mIRC variables have only two scopes.&lt;br /&gt;
&lt;br /&gt;
==Global Scope==&lt;br /&gt;
A variable with a global scope is accessable everywhere in mIRC where a variable can be used. This means in the command line, in every script file, alias file, popup, etc.  These variables are saved in the Variables tab of the mIRC Scripts editor (alt+r) which are saved to a file (usually vars.ini).&lt;br /&gt;
&lt;br /&gt;
* A global scoped variable is created with [[set]] and destroyed with [[unset]]&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called, /test and then /test2.&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
  set %foo bar.&lt;br /&gt;
 ; %foo is available here&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  set %bar foo&lt;br /&gt;
  ; %foo is available here after being set with /test&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
%foo will now return bar EVERYWHERE.&lt;br /&gt;
&lt;br /&gt;
==Local Scope==&lt;br /&gt;
&lt;br /&gt;
A variable with a local scope is only available within the event, alias, or menu (popup) script in which it is used.&lt;br /&gt;
&lt;br /&gt;
* A local variable is created with [[var]] and is automatically destroyed after leaving scope (exiting the current alias, event or menu).&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called using /test &lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
  var %foo = bar.&lt;br /&gt;
 ; %foo is available here&lt;br /&gt;
 test2 %foo&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  set %bar foo&lt;br /&gt;
  ; %foo is NOT available here. We can, however, use $1 to refer to %foo because it was passed as a parameter to the alias.&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[var]] - Command used to create a local variables&lt;br /&gt;
* [[Local Variables]] - Variables with limited scope&lt;br /&gt;
* [[set]] - Command used to create global variables&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Variables]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2733</id>
		<title>Scope</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Scope&amp;diff=2733"/>
		<updated>2009-10-03T18:48:36Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Scope is the term used to refer to a [[:Category:Variables|variable&#039;s]] &amp;quot;life-span&amp;quot;.  mIRC variables have only two scopes.&lt;br /&gt;
&lt;br /&gt;
==Global Scope==&lt;br /&gt;
A variable with a global scope is accessable everywhere in mIRC where a variable can be used. This means in the command line, in every script file, alias file, popup, etc.  These variables are saved in the Variables tab of the mIRC Scripts editor (alt+r) which are saved to a file (usually vars.ini).&lt;br /&gt;
&lt;br /&gt;
* A global scoped variable is created with [[set]] and destroyed with [[unset]]&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called, /test and then /test2.&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
  set %foo bar.&lt;br /&gt;
 ; %foo is available here&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  set %bar foo&lt;br /&gt;
  ; %foo is available here after being set with /test&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
%foo will now return bar EVERYWHERE.&lt;br /&gt;
&lt;br /&gt;
==Local Scope==&lt;br /&gt;
&lt;br /&gt;
A variable with a local scope is only available within the event, alias, or menu (popup) script in which it is used.&lt;br /&gt;
&lt;br /&gt;
* A local variable is created with [[var]] and is automatically destroyed after leaving scope (exiting the current alias, event or menu).&lt;br /&gt;
&lt;br /&gt;
===Example===&lt;br /&gt;
&lt;br /&gt;
Suppose the following was called using /test &lt;br /&gt;
 [[alias]] test {&lt;br /&gt;
  set %foo bar.&lt;br /&gt;
 ; %foo is available here&lt;br /&gt;
 test2 %foo&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 [[alias]] test2 {&lt;br /&gt;
  set %bar foo&lt;br /&gt;
  ; %foo is NOT available here. We can, however, use $1 to refer to %foo because it was passed as a parameter to the alias.&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[var]] - Command used to create a local variables&lt;br /&gt;
* [[Local Variables]] - Variables with limited scope&lt;br /&gt;
* [[set]] - Command used to create global variables&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Variables]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=DollarStar&amp;diff=4937</id>
		<title>DollarStar</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=DollarStar&amp;diff=4937"/>
		<updated>2009-07-31T18:31:52Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[$*]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$*&amp;diff=2723</id>
		<title>$*</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$*&amp;diff=2723"/>
		<updated>2009-07-31T18:29:52Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: Typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;$* is a hidden shortcut to &amp;quot;looping&amp;quot; [[tokenize|tokenized]] ([[$1-|$N]]) data&lt;br /&gt;
&lt;br /&gt;
 $*&lt;br /&gt;
* $* only works in one command per tokenization&lt;br /&gt;
* $* is very quirky, and should not be used in most cases.  Instead, [[While loops]] should be employed.  &lt;br /&gt;
&lt;br /&gt;
==Notes==&lt;br /&gt;
When using $*, mIRC replaces it with &amp;quot;`~$*&amp;quot;  and passes it to all identifiers used. Once it is done, and all identifers have returned, and the command is ready to be executed, mIRC loops and does the command $0 times replacing EVERY instance of &amp;quot;`~$*&amp;quot; with one of the tokens.&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
(Assume $1 = a, $2 = b, $3 = c)&lt;br /&gt;
 ; Echos &amp;quot;a&amp;quot; then &amp;quot;b&amp;quot; then &amp;quot;c&amp;quot;&lt;br /&gt;
 echo -a $*&lt;br /&gt;
&lt;br /&gt;
 ; Echos `. Remember, &amp;quot;`~$*&amp;quot; is ONLY replaced once it is back in the command. You cannot pass it to an identifier, or &amp;quot;`~$*&amp;quot; is used.&lt;br /&gt;
 echo -a $left($*,1)&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[tokenize]]&lt;br /&gt;
* [[$1-]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Undocumented identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=$*&amp;diff=2719</id>
		<title>$*</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=$*&amp;diff=2719"/>
		<updated>2009-07-31T18:29:35Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;$* is a hidden shortcut to &amp;quot;looping&amp;quot; [[tokenize|tokenized]] ([[$1-|$N]]) dat&lt;br /&gt;
&lt;br /&gt;
 $*&lt;br /&gt;
* $* only works in one command per tokenization&lt;br /&gt;
* $* is very quirky, and should not be used in most cases.  Instead, [[While loops]] should be employed.  &lt;br /&gt;
&lt;br /&gt;
==Notes==&lt;br /&gt;
When using $*, mIRC replaces it with &amp;quot;`~$*&amp;quot;  and passes it to all identifiers used. Once it is done, and all identifers have returned, and the command is ready to be executed, mIRC loops and does the command $0 times replacing EVERY instance of &amp;quot;`~$*&amp;quot; with one of the tokens.&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
(Assume $1 = a, $2 = b, $3 = c)&lt;br /&gt;
 ; Echos &amp;quot;a&amp;quot; then &amp;quot;b&amp;quot; then &amp;quot;c&amp;quot;&lt;br /&gt;
 echo -a $*&lt;br /&gt;
&lt;br /&gt;
 ; Echos `. Remember, &amp;quot;`~$*&amp;quot; is ONLY replaced once it is back in the command. You cannot pass it to an identifier, or &amp;quot;`~$*&amp;quot; is used.&lt;br /&gt;
 echo -a $left($*,1)&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[tokenize]]&lt;br /&gt;
* [[$1-]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Undocumented identifiers]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
	<entry>
		<id>https://script.quakenet.org/wiki/index.php?title=Wildcards&amp;diff=4935</id>
		<title>Wildcards</title>
		<link rel="alternate" type="text/html" href="https://script.quakenet.org/wiki/index.php?title=Wildcards&amp;diff=4935"/>
		<updated>2009-07-31T16:51:43Z</updated>

		<summary type="html">&lt;p&gt;Aca20031: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wildcard characters is the name given to the special meta characters *, &amp;amp;, and ?. These characters are used to &amp;quot;match&amp;quot; strings of text without knowing exactly what will be said.&lt;br /&gt;
&lt;br /&gt;
The characters are as follows:&lt;br /&gt;
* * - Matches anything. From nothing at all, to one letter, to one word, to several words.&lt;br /&gt;
* &amp;amp; - Matches one word. Does not include the space&lt;br /&gt;
* ? - Matches one letter (or other character such as a number or a special symbol)&lt;br /&gt;
&lt;br /&gt;
==Using Wild Cards in Events==&lt;br /&gt;
&lt;br /&gt;
Wild cards in mIRC are used most often in events, such as the [[on text]] event.  The [[on text]] event&#039;s syntax is as follows:&lt;br /&gt;
 on &amp;lt;level&amp;gt;:TEXT:&amp;lt;matchtext&amp;gt;:&amp;lt;*&amp;gt;&amp;lt;?&amp;gt;&amp;lt;#[,#]&amp;gt;:&amp;lt;commands&amp;gt;&lt;br /&gt;
(For more information on this event, see the [[on text|on text event page]], as it will not be covered in detail here.)&lt;br /&gt;
&lt;br /&gt;
This event &#039;triggers&#039; when someone types a line that matches &amp;quot;matchtext&amp;quot;. By default, matchtext is a wildcard string. Common uses would be:&lt;br /&gt;
&lt;br /&gt;
* !help - No wildcard characters. Matches ONLY the word &amp;quot;!help.&amp;quot; will NOT match &amp;quot;!help topics&amp;quot;&lt;br /&gt;
* !help &amp;amp; - The &amp;amp; (word) matching character. Matches !help &amp;lt;anyword&amp;gt;. The word is required. Will NOT match &amp;quot;!help topics please&amp;quot;&lt;br /&gt;
* !help* - The * operator matches anything (and nothing). This format will match !help by itself, since * can match nothing. It will also match !helpmeplease or !help me please. It will NOT match ...!help because the * operator only follows !help. &lt;br /&gt;
* ?help - The ? operator here matches any character. So it will match &amp;quot;!help&amp;quot; &amp;quot;^help&amp;quot; &amp;quot;&amp;amp;help&amp;quot; etc etc. It will NOT match &amp;quot;help&amp;quot; by itself, or &amp;quot;!help please&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==More Examples==&lt;br /&gt;
 ; Matches !join #anything, for joining channels. Note this will also match !join #channel somethingelse&lt;br /&gt;
 !join #*&lt;br /&gt;
&lt;br /&gt;
 ; Matches !join &amp;lt;anything&amp;gt;. Note while this will match !join #foo, it will also match !join notachannel. It will not match !join #channel somethingelse&lt;br /&gt;
 !join &amp;amp;&lt;br /&gt;
&lt;br /&gt;
 ; Matches !join #anything. Unlike the use of the * operator, this will NOT match !join #channel somethingelse. It will also match @join #anything, *join #anything, etc.&lt;br /&gt;
 ?join #&amp;amp;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [[If#iswm|iswm]] - using wild cards in [[If]] statements&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Aca20031</name></author>
	</entry>
</feed>