Implemented GET string support from Dan Horrigan and modified it slightly. Also tweaked his regex_match changes.
diff --git a/application/config/config.php b/application/config/config.php
index 251f1aa..a844f71 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -137,6 +137,9 @@
 | By default CodeIgniter uses search-engine friendly segment based URLs:
 | example.com/who/what/where/
 |
+| By default CodeIgniter enables access to the $_GET array.  If for some
+| reason you would like to disable it, set 'allow_get_array' to FALSE.
+|
 | You can optionally enable standard query string based URLs:
 | example.com?who=me&what=something&where=here
 |
@@ -151,6 +154,7 @@
 | use segment based URLs.
 |
 */
+$config['allow_get_array']		= TRUE;
 $config['enable_query_strings'] = FALSE;
 $config['controller_trigger']	= 'c';
 $config['function_trigger']		= 'm';
diff --git a/system/core/Input.php b/system/core/Input.php
index 5a22733..eb2048e 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -30,7 +30,7 @@
 
 	var $ip_address				= FALSE;
 	var $user_agent				= FALSE;
-	var $_allow_get_array		= FALSE;
+	var $_allow_get_array		= TRUE;
 	var $_standardize_newlines	= TRUE;
 	var $_enable_xss			= FALSE; // Set automatically based on config setting
 	var $_enable_csrf			= FALSE; // Set automatically based on config setting
@@ -49,9 +49,9 @@
 	{
 		log_message('debug', "Input Class Initialized");
 
-		$this->_allow_get_array	= (config_item('enable_query_strings') === TRUE) ? TRUE : FALSE;
-		$this->_enable_xss		= (config_item('global_xss_filtering') === TRUE) ? TRUE : FALSE;
-		$this->_enable_csrf		= (config_item('csrf_protection') === TRUE) ? TRUE : FALSE;
+		$this->_allow_get_array	= (config_item('allow_get_array') === TRUE);
+		$this->_enable_xss		= (config_item('global_xss_filtering') === TRUE);
+		$this->_enable_csrf		= (config_item('csrf_protection') === TRUE);
 
 		// Do we need to load the security class?
 		if ($this->_enable_xss == TRUE OR $this->_enable_csrf == TRUE)
@@ -216,14 +216,7 @@
 		}
 		else
 		{
-			if ($expire > 0)
-			{
-				$expire = time() + $expire;
-			}
-			else
-			{
-				$expire = 0;
-			}
+			$expire = ($expire > 0) ? time() + $expire : 0;
 		}
 
 		setcookie($prefix.$name, $value, $expire, $path, $domain, 0);
@@ -514,7 +507,7 @@
 		{
 			if (strpos($str, "\r") !== FALSE)
 			{
-				$str = str_replace(array("\r\n", "\r"), "\n", $str);
+				$str = str_replace(array("\r\n", "\r"), PHP_EOL, $str);
 			}
 		}
 
diff --git a/system/core/URI.php b/system/core/URI.php
index 5a5a37c..047e3c9 100644
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -61,13 +61,10 @@
 	{
 		if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
 		{
-			// If the URL has a question mark then it's simplest to just
-			// build the URI string from the zero index of the $_GET array.
-			// This avoids having to deal with $_SERVER variables, which
-			// can be unreliable in some environments
-			if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
+			// Let's try the REQUEST_URI first, this will work in most situations
+			if ($uri = $this->_get_request_uri())
 			{
-				$this->uri_string = key($_GET);
+				$this->uri_string = $this->_parse_request_uri($uri);
 				return;
 			}
 
@@ -88,12 +85,10 @@
 				return;
 			}
 
-			// No QUERY_STRING?... Maybe the ORIG_PATH_INFO variable exists?
-			$path = str_replace($_SERVER['SCRIPT_NAME'], '', (isset($_SERVER['ORIG_PATH_INFO'])) ? $_SERVER['ORIG_PATH_INFO'] : @getenv('ORIG_PATH_INFO'));
-			if (trim($path, '/') != '' && $path != "/".SELF)
+			// As a last ditch effort lets try using the $_GET array
+			if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
 			{
-				// remove path and script information so we have good URI data
-				$this->uri_string = $path;
+				$this->uri_string = key($_GET);
 				return;
 			}
 
@@ -106,7 +101,7 @@
 
 			if ($uri == 'REQUEST_URI')
 			{
-				$this->uri_string = $this->_parse_request_uri();
+				$this->uri_string = $this->_parse_request_uri($this->_get_request_uri());
 				return;
 			}
 
@@ -123,39 +118,73 @@
 	// --------------------------------------------------------------------
 
 	/**
-	 * Parse the REQUEST_URI
+	 * Get REQUEST_URI
+	 *
+	 * Retrieves the REQUEST_URI, or equivelent for IIS.
+	 *
+	 * @access	private
+	 * @return	string
+	 */
+	function _get_request_uri()
+	{
+		$uri = FALSE;
+
+		// Let's check for standard servers first
+		if (isset($_SERVER['REQUEST_URI']))
+		{
+			$uri = $_SERVER['REQUEST_URI'];
+			if (strpos($uri, $_SERVER['SERVER_NAME']) !== FALSE)
+			{
+				$uri = preg_replace('/^\w+:\/\/[^\/]+/', '', $uri);
+			}
+		}
+
+		// Now lets check for IIS
+		elseif (isset($_SERVER['HTTP_X_REWRITE_URL']))
+		{
+			$uri = $_SERVER['HTTP_X_REWRITE_URL'];
+		}
+		
+		// Last ditch effort (for older CGI servers, like IIS 5)
+		elseif (isset($_SERVER['ORIG_PATH_INFO']))
+		{
+			$uri = $_SERVER['ORIG_PATH_INFO'];
+			if ( ! empty($_SERVER['QUERY_STRING']))
+			{
+				$uri .= '?' . $_SERVER['QUERY_STRING'];
+			}
+		}
+
+		return $uri;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
+	 * Parse REQUEST_URI
 	 *
 	 * Due to the way REQUEST_URI works it usually contains path info
 	 * that makes it unusable as URI data.  We'll trim off the unnecessary
 	 * data, hopefully arriving at a valid URI that we can use.
 	 *
 	 * @access	private
+	 * @param	string
 	 * @return	string
 	 */
-	function _parse_request_uri()
+	function _parse_request_uri($uri)
 	{
-		if ( ! isset($_SERVER['REQUEST_URI']) OR $_SERVER['REQUEST_URI'] == '')
-		{
-			return '';
-		}
-
-		$request_uri = preg_replace("|/(.*)|", "\\1", str_replace("\\", "/", $_SERVER['REQUEST_URI']));
-
-		if ($request_uri == '' OR $request_uri == SELF)
-		{
-			return '';
-		}
-
-		$fc_path = FCPATH.SELF;
-		if (strpos($request_uri, '?') !== FALSE)
+		// Some server's require URL's like index.php?/whatever If that is the case,
+		// then we need to add that to our parsing.
+		$fc_path = ltrim(FCPATH . SELF, '/');
+		if (strpos($uri, SELF . '?') !== FALSE)
 		{
 			$fc_path .= '?';
 		}
 
-		$parsed_uri = explode("/", $request_uri);
+		$parsed_uri = explode('/', ltrim($uri, '/'));
 
 		$i = 0;
-		foreach(explode("/", $fc_path) as $segment)
+		foreach (explode("/", $fc_path) as $segment)
 		{
 			if (isset($parsed_uri[$i]) && $segment == $parsed_uri[$i])
 			{
@@ -163,14 +192,25 @@
 			}
 		}
 
-		$parsed_uri = implode("/", array_slice($parsed_uri, $i));
+		$uri = implode("/", array_slice($parsed_uri, $i));
 
-		if ($parsed_uri != '')
+		// Let's take off any query string and re-assign $_SERVER['QUERY_STRING'] and $_GET.
+		// This is only needed on some servers.  However, we are forced to use it to accomodate
+		// them.
+		if (($qs_pos = strpos($uri, '?')) !== FALSE)
 		{
-			$parsed_uri = '/'.$parsed_uri;
+			$_SERVER['QUERY_STRING'] = substr($uri, $qs_pos + 1);
+			parse_str($_SERVER['QUERY_STRING'], $_GET);
+			$uri = substr($uri, 0, $qs_pos);
 		}
 
-		return $parsed_uri;
+		// If it is just a / or index.php then just empty it.
+		if ($uri == '/' OR $uri == SELF)
+		{
+			$uri = '';
+		}
+
+		return $uri;
 	}
 
 	// --------------------------------------------------------------------
@@ -228,7 +268,7 @@
 	 */
 	function _explode_segments()
 	{
-		foreach(explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
+		foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
 		{
 			// Filter segments for security
 			$val = trim($this->_filter_uri($val));
diff --git a/system/language/english/form_validation_lang.php b/system/language/english/form_validation_lang.php
index 99ed70a..b018850 100644
--- a/system/language/english/form_validation_lang.php
+++ b/system/language/english/form_validation_lang.php
@@ -15,6 +15,7 @@
 $lang['numeric']			= "The %s field must contain only numbers.";
 $lang['is_numeric']			= "The %s field must contain only numeric characters.";
 $lang['integer']			= "The %s field must contain an integer.";
+$lang['regex_match']		= "The %s field is not in the correct format.";
 $lang['matches']			= "The %s field does not match the %s field.";
 $lang['is_natural']			= "The %s field must contain only positive numbers.";
 $lang['is_natural_no_zero']	= "The %s field must contain a number greater than zero.";
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index bf36890..38f50fa 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -339,7 +339,13 @@
 				}
 			}
 
-			$this->_execute($row, explode('|', $row['rules']), $this->_field_data[$field]['postdata']);
+			preg_match_all('/([a-zA-Z_-]*(\[.*\])?)\|?/i', $row['rules'], $matches);
+
+			$rules = $matches[1];
+			array_pop($rules);
+			unset($matches);
+
+			$this->_execute($row, $rules, $this->_field_data[$field]['postdata']);
 		}
 
 		// Did we end up with any errors?
@@ -576,7 +582,7 @@
 			// Strip the parameter (if exists) from the rule
 			// Rules can contain a parameter: max_length[5]
 			$param = FALSE;
-			if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match))
+			if (preg_match("/(.*?)\[(.*)\]/", $rule, $match))
 			{
 				$rule	= $match[1];
 				$param	= $match[2];
@@ -889,6 +895,26 @@
 	// --------------------------------------------------------------------
 
 	/**
+	 * Performs a Regular Expression match test.
+	 *
+	 * @access	public
+	 * @param	string
+	 * @param	regex
+	 * @return	bool
+	 */
+	function regex_match($str, $regex)
+	{
+		if ( ! preg_match($regex, $str))
+		{
+			return FALSE;
+		}
+
+		return  TRUE;
+	}
+
+	// --------------------------------------------------------------------
+
+	/**
 	 * Match one field to another
 	 *
 	 * @access	public