Fix issue #1953 (form values being escaped twice)

Re-instaing an improved form_prep() function, reverting most of the changes from 74ffd17ab06327ca62ddfe28a186cae7ba6bd459.
diff --git a/system/helpers/form_helper.php b/system/helpers/form_helper.php
index 622622c..9c4c4da 100644
--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -124,9 +124,9 @@
 	 * Generates hidden fields. You can pass a simple key/value string or
 	 * an associative array with multiple values.
 	 *
-	 * @param	mixed
-	 * @param	string
-	 * @param	bool
+	 * @param	mixed	$name		Field name
+	 * @param	string	$value		Field value
+	 * @param	bool	$recursing
 	 * @return	string
 	 */
 	function form_hidden($name, $value = '', $recursing = FALSE)
@@ -149,7 +149,7 @@
 
 		if ( ! is_array($value))
 		{
-			$form .= '<input type="hidden" name="'.$name.'" value="'.html_escape($value)."\" />\n";
+			$form .= '<input type="hidden" name="'.$name.'" value="'.form_prep($value)."\" />\n";
 		}
 		else
 		{
@@ -243,9 +243,9 @@
 	/**
 	 * Textarea field
 	 *
-	 * @param	mixed
-	 * @param	string
-	 * @param	string
+	 * @param	mixed	$data
+	 * @param	string	$value
+	 * @param	string	$extra
 	 * @return	string
 	 */
 	function form_textarea($data = '', $value = '', $extra = '')
@@ -263,7 +263,7 @@
 		}
 
 		$name = is_array($data) ? $data['name'] : $data;
-		return '<textarea '._parse_form_attributes($data, $defaults).$extra.'>'.html_escape($val)."</textarea>\n";
+		return '<textarea '._parse_form_attributes($data, $defaults).$extra.'>'.form_prep($val, TRUE)."</textarea>\n";
 	}
 }
 
@@ -298,10 +298,10 @@
 	/**
 	 * Drop-down Menu
 	 *
-	 * @param	mixed	$name = ''
-	 * @param	mixed	$options = array()
-	 * @param	mixed	$selected = array()
-	 * @param	mixed	$extra = array()
+	 * @param	mixed	$name
+	 * @param	mixed	$options
+	 * @param	mixed	$selected
+	 * @param	mixed	$extra
 	 * @return	string
 	 */
 	function form_dropdown($name = '', $options = array(), $selected = array(), $extra = '')
@@ -349,7 +349,7 @@
 				foreach ($val as $optgroup_key => $optgroup_val)
 				{
 					$sel = in_array($optgroup_key, $selected) ? ' selected="selected"' : '';
-					$form .= '<option value="'.html_escape($optgroup_key).'"'.$sel.'>'
+					$form .= '<option value="'.form_prep($optgroup_key).'"'.$sel.'>'
 						.(string) $optgroup_val."</option>\n";
 				}
 
@@ -357,7 +357,7 @@
 			}
 			else
 			{
-				$form .= '<option value="'.html_escape($key).'"'
+				$form .= '<option value="'.form_prep($key).'"'
 					.(in_array($key, $selected) ? ' selected="selected"' : '').'>'
 					.(string) $val."</option>\n";
 			}
@@ -600,17 +600,28 @@
 	 *
 	 * Formats text so that it can be safely placed in a form field in the event it has HTML tags.
 	 *
-	 * @todo	Remove in version 3.1+.
-	 * @deprecated	3.0.0	This function has been broken for a long time
-	 *			and is now just an alias for html_escape(). It's
-	 *			second argument is ignored.
-	 * @param	string	$str = ''
-	 * @param	string	$field_name = ''
-	 * @return	string
+	 * @param	string|string[]	$str		Value to escape
+	 * @param	bool		$is_textarea	Whether we're escaping for a textarea element
+	 * @return	string|string[]	Escaped values
 	 */
-	function form_prep($str = '', $field_name = '')
+	function form_prep($str = '', $is_textarea = FALSE)
 	{
-		return html_escape($str);
+		if (is_array($str))
+		{
+			foreach (array_keys($str) as $key)
+			{
+				$str[$key] = form_prep($str[$key], $is_textarea);
+			}
+
+			return $str;
+		}
+
+		if ($is_textarea === TRUE)
+		{
+			return str_replace(array('<', '>'), array('&lt;', '&gt;'), stripslashes($str));
+		}
+
+		return str_replace(array("'", '"'), array('&#39;', '&quot;'), stripslashes($data));
 	}
 }
 
@@ -625,23 +636,21 @@
 	 * re-populate an input field or textarea. If Form Validation
 	 * is active it retrieves the info from the validation class
 	 *
-	 * @param	string
-	 * @param	string
-	 * @return	mixed
+	 * @param	string	$field		Field name
+	 * @param	string	$default	Default value
+	 * @param	bool	$is_textarea	Whether the field is a textarea element
+	 * @return	string
 	 */
-	function set_value($field = '', $default = '')
+	function set_value($field = '', $default = '', $is_textarea = FALSE)
 	{
 		if (FALSE === ($OBJ =& _get_validation_object()))
 		{
-			if ( ! isset($_POST[$field]))
-			{
-				return html_escape($default);
-			}
-
-			return html_escape($_POST[$field]);
+			return isset($_POST[$field])
+				? form_prep($_POST[$field], $is_textarea)
+				: form_prep($default, $is_textarea);
 		}
 
-		return html_escape($OBJ->set_value($field, $default));
+		return form_prep($OBJ->set_value($field, $default), $is_textarea);
 	}
 }
 
@@ -862,8 +871,8 @@
 	 *
 	 * Helper function used by some of the form helpers
 	 *
-	 * @param	array
-	 * @param	array
+	 * @param	array	$attributes	List of attributes
+	 * @param	array	$default	Default values
 	 * @return	string
 	 */
 	function _parse_form_attributes($attributes, $default)
@@ -891,7 +900,7 @@
 		{
 			if ($key === 'value')
 			{
-				$val = html_escape($val);
+				$val = form_prep($val);
 			}
 			elseif ($key === 'name' && ! strlen($default['name']))
 			{
diff --git a/system/libraries/Form_validation.php b/system/libraries/Form_validation.php
index c1bf519..74dac7d 100644
--- a/system/libraries/Form_validation.php
+++ b/system/libraries/Form_validation.php
@@ -1323,6 +1323,11 @@
 	 */
 	public function prep_for_form($data = '')
 	{
+		if ($this->_safe_form_data === FALSE OR empty($data))
+		{
+			return $data;
+		}
+
 		if (is_array($data))
 		{
 			foreach ($data as $key => $val)
@@ -1333,11 +1338,6 @@
 			return $data;
 		}
 
-		if ($this->_safe_form_data === FALSE OR $data === '')
-		{
-			return $data;
-		}
-
 		return str_replace(array("'", '"', '<', '>'), array('&#39;', '&quot;', '&lt;', '&gt;'), stripslashes($data));
 	}
 
diff --git a/tests/codeigniter/helpers/form_helper_test.php b/tests/codeigniter/helpers/form_helper_test.php
index 0327858..8916527 100644
--- a/tests/codeigniter/helpers/form_helper_test.php
+++ b/tests/codeigniter/helpers/form_helper_test.php
@@ -272,6 +272,21 @@
 		$this->assertEquals($expected, form_close('</div></div>'));
 	}
 
+	// ------------------------------------------------------------------------
+
+	public function test_form_prep()
+	{
+		$this->assertEquals(
+			'Here is a string containing &quot;quoted&quot; text.',
+			form_prep('Here is a string containing "quoted" text.')
+		);
+
+		$this->assertEquals(
+			'Here is a string containing a &lt;tag&gt;.',
+			form_prep('Here is a string containing a <tag>.', TRUE)
+		);
+	}
+
 }
 
 /* End of file form_helper_test.php */
\ No newline at end of file
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 4aef2a1..511ee00 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -77,7 +77,7 @@
    -  Added a work-around in ``force_download()`` for a bug Android <= 2.1, where the filename extension needs to be in uppercase.
    -  :doc:`Form Helper <helpers/form_helper>` changes include:
 	 - ``form_dropdown()`` will now also take an array for unity with other form helpers.
-	 - ``form_prep()`` is now **DEPRECATED** and only acts as an alias for :doc:`common function <general/common_functions>` ``html_escape()``.
+	 - ``form_prep()``'s second argument now only accepts a boolean value, which determines whether the value is escaped for a *textarea* or a regular *input* element.
    -  ``do_hash()`` now uses PHP's native ``hash()`` function (supporting more algorithms) and is deprecated.
    -  Removed previously deprecated helper function ``js_insert_smiley()`` from :doc:`Smiley Helper <helpers/smiley_helper>`.
    -  :doc:`File Helper <helpers/file_helper>` changes include:
diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst
index 015bf11..02a7586 100644
--- a/user_guide_src/source/helpers/form_helper.rst
+++ b/user_guide_src/source/helpers/form_helper.rst
@@ -463,6 +463,26 @@
 	echo form_close($string);
 	// Would produce:  </form> </div></div>
 
+form_prep()
+===========
+
+Allows you to safely use HTML and characters such as quotes within form
+elements without breaking out of the form. Consider this example
+::
+
+	$string = 'Here is a string containing "quoted" text.';
+	<input type="text" name="myform" value="$string" />
+
+Since the above string contains a set of quotes it will cause the form
+to break. The ``form_prep()`` function converts HTML so that it can be used
+safely::
+
+	<input type="text" name="myform" value="<?php echo form_prep($string); ?>" />
+
+.. note:: If you use any of the form helper functions listed in this page the form
+	values will be prepped automatically, so there is no need to call this
+	function. Use it only if you are creating your own form elements.
+
 set_value()
 ===========
 
@@ -523,26 +543,4 @@
 .. note:: If you are using the Form Validation class, you must always specify a rule for your field,
 	even if empty, in order for the set_*() functions to work. This is because if a Form Validation object
 	is defined, the control for set_*() is handed over to a method of the class instead of the generic helper
-	function.
-
-Escaping field values
-=====================
-
-You may need to use HTML and characters such as quotes within form
-elements. In order to do that safely, you'll need to use
-:doc:`common function <../general/common_functions>` ``html_escape()``.
-
-Consider the following example::
-
-	$string = 'Here is a string containing "quoted" text.';
-	<input type="text" name="myform" value="$string" />
-
-Since the above string contains a set of quotes it will cause the form
-to break. The ``html_escape()`` function converts HTML so that it can be
-used safely::
-
-	<input type="text" name="myform" value="<?php echo html_escape($string); ?>" />
-
-.. note:: If you use any of the form helper functions listed in this page, the form
-	values will be prepped automatically, so there is no need to call this
-	function. Use it only if you are creating your own form elements.
\ No newline at end of file
+	function.
\ No newline at end of file
diff --git a/user_guide_src/source/installation/upgrade_300.rst b/user_guide_src/source/installation/upgrade_300.rst
index 6d99f46..fd5eea4 100644
--- a/user_guide_src/source/installation/upgrade_300.rst
+++ b/user_guide_src/source/installation/upgrade_300.rst
@@ -166,16 +166,6 @@
 .. note:: This function is still available, but you're strongly encouraged to remove it's usage sooner
 	rather than later.
 
-Form helper form_prep()
-=======================
-
-:doc:`Form Helper <../helpers/form_helper>` function ``form_prep()`` is now just an alias for
-:doc:`common function <../general/common_functions>` ``html_escape()`` and it's second argument
-is ignored. It is deprecated and scheduled for removal in CodeIgniter 3.1+.
-
-.. note:: This function is still available, but you're strongly encouraged to remove it's usage sooner
-	rather than later.
-
 Date helper standard_date()
 ===========================