Merge upstream branch
diff --git a/application/config/mimes.php b/application/config/mimes.php
index 8c34fd2..2a68ce9 100644
--- a/application/config/mimes.php
+++ b/application/config/mimes.php
@@ -121,8 +121,8 @@
 				'avi'	=>	array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
 				'movie'	=>	'video/x-sgi-movie',
 				'doc'	=>	'application/msword',
-				'docx'	=>	'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-				'xlsx'	=>	'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+				'docx'	=>	array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip'),
+				'xlsx'	=>	array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip'),
 				'word'	=>	array('application/msword', 'application/octet-stream'),
 				'xl'	=>	'application/excel',
 				'eml'	=>	'message/rfc822',
diff --git a/system/database/DB_driver.php b/system/database/DB_driver.php
index 271a70e..6352c73 100644
--- a/system/database/DB_driver.php
+++ b/system/database/DB_driver.php
@@ -1424,7 +1424,23 @@
 
 		return $item.$alias;
 	}
+	
+	// --------------------------------------------------------------------
+
+	/**
+	 * Dummy method that allows Active Record class to be disabled
+	 *
+	 * This function is used extensively by every db driver.
+	 *
+	 * @access	private
+	 * @return	void
+	 */
+	protected function _reset_select()
+	{
+	
+	}
+
 }
 
 /* End of file DB_driver.php */
-/* Location: ./system/database/DB_driver.php */
+/* Location: ./system/database/DB_driver.php */
\ No newline at end of file
diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php
index 2069a19..02c425b 100644
--- a/system/helpers/inflector_helper.php
+++ b/system/helpers/inflector_helper.php
@@ -34,7 +34,7 @@
  * @subpackage	Helpers
  * @category	Helpers
  * @author		EllisLab Dev Team
- * @link		http://codeigniter.com/user_guide/helpers/directory_helper.html
+ * @link		http://codeigniter.com/user_guide/helpers/inflector_helper.html
  */
 
 
@@ -45,7 +45,6 @@
  *
  * Takes a plural word and makes it singular
  *
- * @access	public
  * @param	string
  * @return	str
  */
@@ -55,37 +54,51 @@
 	{
 		$result = strval($str);
 
+		if ( ! is_countable($result))
+		{
+			return $result;
+		}
+		
 		$singular_rules = array(
-			'/(matr)ices$/'			=> '\1ix',
-			'/(vert|ind)ices$/'		=> '\1ex',
-			'/^(ox)en/'				=> '\1',
-			'/(alias)es$/'			=> '\1',
-			'/([octop|vir])i$/'		=> '\1us',
-			'/(cris|ax|test)es$/'	=> '\1is',
-			'/(shoe)s$/'			=> '\1',
-			'/(o)es$/'				=> '\1',
-			'/(bus|campus)es$/'		=> '\1',
-			'/([m|l])ice$/'			=> '\1ouse',
-			'/(x|ch|ss|sh)es$/'		=> '\1',
-			'/(m)ovies$/'			=> '\1\2ovie',
-			'/(s)eries$/'			=> '\1\2eries',
-			'/([^aeiouy]|qu)ies$/'	=> '\1y',
-			'/([lr])ves$/'			=> '\1f',
-			'/(tive)s$/'			=> '\1',
-			'/(hive)s$/'			=> '\1',
-			'/([^f])ves$/'			=> '\1fe',
-			'/(^analy)ses$/'		=> '\1sis',
+			'/(matr)ices$/'         => '\1ix',
+			'/(vert|ind)ices$/'     => '\1ex',
+			'/^(ox)en/'             => '\1',
+			'/(alias)es$/'          => '\1',
+			'/([octop|vir])i$/'     => '\1us',
+			'/(cris|ax|test)es$/'   => '\1is',
+			'/(shoe)s$/'            => '\1',
+			'/(o)es$/'              => '\1',
+			'/(bus|campus)es$/'     => '\1',
+			'/([m|l])ice$/'         => '\1ouse',
+			'/(x|ch|ss|sh)es$/'     => '\1',
+			'/(m)ovies$/'           => '\1\2ovie',
+			'/(s)eries$/'           => '\1\2eries',
+			'/([^aeiouy]|qu)ies$/'  => '\1y',
+			'/([lr])ves$/'          => '\1f',
+			'/(tive)s$/'            => '\1',
+			'/(hive)s$/'            => '\1',
+			'/([^f])ves$/'          => '\1fe',
+			'/(^analy)ses$/'        => '\1sis',
 			'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\1\2sis',
-			'/([ti])a$/'			=> '\1um',
-			'/(p)eople$/'			=> '\1\2erson',
-			'/(m)en$/'				=> '\1an',
-			'/(s)tatuses$/'			=> '\1\2tatus',
-			'/(c)hildren$/'			=> '\1\2hild',
-			'/(n)ews$/'				=> '\1\2ews',
-			'/([^u])s$/'			=> '\1',
+			'/([ti])a$/'            => '\1um',
+			'/(p)eople$/'           => '\1\2erson',
+			'/(m)en$/'              => '\1an',
+			'/(s)tatuses$/'         => '\1\2tatus',
+			'/(c)hildren$/'         => '\1\2hild',
+			'/(n)ews$/'             => '\1\2ews',
+			'/([^us])s$/'           => '\1',
 		);
 
-		return preg_replace(array_keys($singular_rules), $singular_rules, $result);
+		foreach ($singular_rules as $rule => $replacement)
+		{
+			if (preg_match($rule, $result))
+			{
+				$result = preg_replace($rule, $replacement, $result);
+				break;
+			}
+		}
+
+		return $result;
 	}
 }
 
@@ -96,7 +109,6 @@
  *
  * Takes a singular word and makes it plural
  *
- * @access	public
  * @param	string
  * @param	bool
  * @return	str
@@ -104,32 +116,46 @@
 if ( ! function_exists('plural'))
 {
 	function plural($str, $force = FALSE)
-	{
+	{	
 		$result = strval($str);
 
-		$plural_rules = array(
-			'/^(ox)$/'					=> '\1\2en',	 // ox
-			'/([m|l])ouse$/'			=> '\1ice',	  // mouse, louse
-			'/(matr|vert|ind)ix|ex$/'	=> '\1ices',	 // matrix, vertex, index
-			'/(x|ch|ss|sh)$/'			=> '\1es',	   // search, switch, fix, box, process, address
-			'/([^aeiouy]|qu)y$/'		=> '\1ies',	  // query, ability, agency
-			'/(hive)$/'					=> '\1s',		// archive, hive
-			'/(?:([^f])fe|([lr])f)$/'	=> '\1\2ves',	// half, safe, wife
-			'/sis$/'					=> 'ses',		// basis, diagnosis
-			'/([ti])um$/'				=> '\1a',		// datum, medium
-			'/(p)erson$/'				=> '\1eople',	// person, salesperson
-			'/(m)an$/'					=> '\1en',	   // man, woman, spokesman
-			'/(c)hild$/'				=> '\1hildren',  // child
-			'/(buffal|tomat)o$/'		=> '\1\2oes',	// buffalo, tomato
-			'/(bu|campu)s$/'			=> '\1\2ses',	// bus, campus
-			'/(alias|status|virus)/'	=> '\1es',	   // alias
-			'/(octop)us$/'				=> '\1i',		// octopus
-			'/(ax|cris|test)is$/'		=> '\1es',	   // axis, crisis
-			'/s$/'						=> 's',		  // no change (compatibility)
-			'/$/'						=> 's',
-		);
+		if ( ! is_countable($result))
+		{
+			return $result;
+		}
 
-		return preg_replace(array_keys($plural_rules), $plural_rules, $result);
+		$plural_rules = array(
+			'/^(ox)$/'                 => '\1\2en',     // ox
+			'/([m|l])ouse$/'           => '\1ice',      // mouse, louse
+			'/(matr|vert|ind)ix|ex$/'  => '\1ices',     // matrix, vertex, index
+			'/(x|ch|ss|sh)$/'          => '\1es',       // search, switch, fix, box, process, address
+			'/([^aeiouy]|qu)y$/'       => '\1ies',      // query, ability, agency
+			'/(hive)$/'                => '\1s',        // archive, hive
+			'/(?:([^f])fe|([lr])f)$/'  => '\1\2ves',    // half, safe, wife
+			'/sis$/'                   => 'ses',        // basis, diagnosis
+			'/([ti])um$/'              => '\1a',        // datum, medium
+			'/(p)erson$/'              => '\1eople',    // person, salesperson
+			'/(m)an$/'                 => '\1en',       // man, woman, spokesman
+			'/(c)hild$/'               => '\1hildren',  // child
+			'/(buffal|tomat)o$/'       => '\1\2oes',    // buffalo, tomato
+			'/(bu|campu)s$/'           => '\1\2ses',    // bus, campus
+			'/(alias|status|virus)$/'  => '\1es',       // alias
+			'/(octop)us$/'             => '\1i',        // octopus
+			'/(ax|cris|test)is$/'      => '\1es',       // axis, crisis
+			'/s$/'                     => 's',          // no change (compatibility)
+			'/$/'                      => 's',
+		);
+		
+		foreach ($plural_rules as $rule => $replacement)
+		{
+			if (preg_match($rule, $result))
+			{
+				$result = preg_replace($rule, $replacement, $result);
+				break;
+			}
+		}
+
+		return $result;
 	}
 }
 
@@ -140,7 +166,6 @@
  *
  * Takes multiple words separated by spaces or underscores and camelizes them
  *
- * @access	public
  * @param	string
  * @return	str
  */
@@ -159,7 +184,6 @@
  *
  * Takes multiple words separated by spaces and underscores them
  *
- * @access	public
  * @param	string
  * @return	str
  */
@@ -178,7 +202,6 @@
  *
  * Takes multiple words separated by the separator and changes them to spaces
  *
- * @access	public
  * @param	string $str
  * @param 	string $separator
  * @return	str
@@ -191,5 +214,22 @@
 	}
 }
 
+/**
+ * Checks if the given word has a plural version.
+ *
+ * @param   string  the word to check
+ * @return  bool    if the word is countable
+ */
+if ( ! function_exists('is_countable'))
+{
+	function is_countable($word)
+	{
+		return ! (in_array(strtolower(strval($word)), array(
+			'equipment', 'information', 'rice', 'money',
+			'species', 'series', 'fish', 'meta'
+		)));
+	}
+}
+
 /* End of file inflector_helper.php */
-/* Location: ./system/helpers/inflector_helper.php */
+/* Location: ./system/helpers/inflector_helper.php */
\ No newline at end of file
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 5ea830f..9826eab 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -251,7 +251,7 @@
 		}
 		else
 		{
-			if (function_exists('realpath') && @realpath($this->new_image) !== FALSE)
+			if (strpos($this->new_image, '/') === FALSE AND strpos($this->new_image, '\\') === FALSE)
 			{
 				$full_dest_path = str_replace('\\', '/', realpath($this->new_image));
 			}
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 0c63886..82383f6 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -1026,47 +1026,104 @@
 	 */
 	protected function _file_mime_type($file)
 	{
-		// Use if the Fileinfo extension, if available (only versions above 5.3 support the FILEINFO_MIME_TYPE flag)
-		if ( (float) substr(phpversion(), 0, 3) >= 5.3 && function_exists('finfo_file'))
+		// We'll need this to validate the MIME info string (e.g. text/plain; charset=us-ascii)
+		$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
+
+		/* Fileinfo extension - most reliable method
+		 *
+		 * Unfortunately, prior to PHP 5.3 - it's only available as a PECL extension and the
+		 * more convenient FILEINFO_MIME_TYPE flag doesn't exist.
+		 */
+		if (function_exists('finfo_file'))
 		{
-			$finfo = new finfo(FILEINFO_MIME_TYPE);
-			if ($finfo !== FALSE) // This is possible, if there is no magic MIME database file found on the system
+			$finfo = finfo_open(FILEINFO_MIME);
+			if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system
 			{
-				$file_type = $finfo->file($file['tmp_name']);
+				$mime = @finfo_file($finfo, $file['tmp_name']);
+				finfo_close($finfo);
 
 				/* According to the comments section of the PHP manual page,
 				 * it is possible that this function returns an empty string
 				 * for some files (e.g. if they don't exist in the magic MIME database)
 				 */
-				if (strlen($file_type) > 1)
+				if (is_string($mime) && preg_match($regexp, $mime, $matches))
 				{
-					$this->file_type = $file_type;
+					$this->file_type = $matches[1];
 					return;
 				}
 			}
 		}
 
-		// Fall back to the deprecated mime_content_type(), if available
+		/* This is an ugly hack, but UNIX-type systems provide a "native" way to detect the file type,
+		 * which is still more secure than depending on the value of $_FILES[$field]['type'], and as it
+		 * was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750) - it's better
+		 * than mime_content_type() as well, hence the attempts to try calling the command line with
+		 * three different functions.
+		 *
+		 * Notes:
+		 *	- the DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
+		 *	- many system admins would disable the exec(), shell_exec(), popen() and similar functions
+		 *	  due to security concerns, hence the function_exists() checks
+		 */
+		if (DIRECTORY_SEPARATOR !== '\\')
+		{
+			$cmd = 'file --brief --mime ' . escapeshellarg($file['tmp_name']) . ' 2>&1';
+
+			if (function_exists('exec'))
+			{
+				/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
+				 * However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
+				 * anything that could already be set for $mime previously. This effectively makes the second parameter a dummy
+				 * value, which is only put to allow us to get the return status code.
+				 */
+				$mime = @exec($cmd, $mime, $return_status);
+				if ($return_status === 0 && is_string($mime) && preg_match($regexp, $mime, $matches))
+				{
+					$this->file_type = $matches[1];
+					return;
+				}
+			}
+
+			if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
+			{
+				$mime = @shell_exec($cmd);
+				if (strlen($mime) > 0)
+				{
+					$mime = explode("\n", trim($mime));
+					if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
+					{
+						$this->file_type = $matches[1];
+						return;
+					}
+				}
+			}
+
+			if (function_exists('popen'))
+			{
+				$proc = @popen($cmd, 'r');
+				if (is_resource($proc))
+				{
+					$mime = @fread($test, 512);
+					@pclose($proc);
+					if ($mime !== FALSE)
+					{
+						$mime = explode("\n", trim($mime));
+						if (preg_match($regexp, $mime[(count($mime) - 1)], $matches))
+						{
+							$this->file_type = $matches[1];
+							return;
+						}
+					}
+				}
+			}
+		}
+
+		// Fall back to the deprecated mime_content_type(), if available (still better than $_FILES[$field]['type'])
 		if (function_exists('mime_content_type'))
 		{
 			$this->file_type = @mime_content_type($file['tmp_name']);
-			return;
-		}
-
-		/* This is an ugly hack, but UNIX-type systems provide a native way to detect the file type,
-		 * which is still more secure than depending on the value of $_FILES[$field]['type'].
-		 *
-		 * Notes:
-		 *	- a 'W' in the substr() expression bellow, would mean that we're using Windows
-		 *	- many system admins would disable the exec() function due to security concerns, hence the function_exists() check
-		 */
-		if (DIRECTORY_SEPARATOR !== '\\' && function_exists('exec'))
-		{
-			$output = array();
-			@exec('file --brief --mime-type ' . escapeshellarg($file['tmp_path']), $output, $return_code);
-			if ($return_code === 0 && strlen($output[0]) > 0) // A return status code != 0 would mean failed execution
+			if (strlen($this->file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string
 			{
-				$this->file_type = rtrim($output[0]);
 				return;
 			}
 		}
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index dc281f4..1297a16 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -39,7 +39,8 @@
    -  url_title() will now trim extra dashes from beginning and end.
    -  Added XHTML Basic 1.1 doctype to :doc:`HTML Helper <helpers/html_helper>`.
    -  Changed humanize to include a second param for the separator.
-   -  Added an optional third parameter to force_download() that enables/disables sending the actual file MIME type in the Content-Type header (disabled by default).
+   -  Refactored ``plural()`` and ``singular()`` to avoid double pluralization and support more words.
+   -  Added an optional third parameter to ``force_download()`` that enables/disables sending the actual file MIME type in the Content-Type header (disabled by default).
 
 -  Database
 
@@ -111,12 +112,34 @@
 -  Fixed a bug (#1070) - CI_DB_driver::initialize() didn't set a character set if a database is not selected.
 -  Fixed a bug (#177) - CI_Form_validation::set_value() didn't set the default value if POST data is NULL.
 
-Version 2.1.0
+
+Version 2.1.1
 =============
 
 Release Date: Not Released
 
 -  General Changes
+   -  Fixed support for docx, xlsx files in mimes.php.
+
+-  Libraries
+   -  Further improved MIME type detection in the :doc:`File Uploading Library <libraries/file_uploading>`.
+
+
+Bug fixes for 2.1.1
+-------------------
+
+-  Fixed a bug (#697) - A wrong array key was used in the Upload library to check for mime-types.
+-  Fixed a bug - form_open() compared $action against site_url() instead of base_url().
+-  Fixed a bug - CI_Upload::_file_mime_type() could've failed if mime_content_type() is used for the detection and returns FALSE.
+-  Fixed a bug (#538) - Windows paths were ignored when using the :doc:`Image Manipulation Library <libraries/image_lib>` to create a new file.
+
+
+Version 2.1.0
+=============
+
+Release Date: November 14, 2011
+
+-  General Changes
 
    -  Callback validation rules can now accept parameters like any other
       validation rule.