Index: trunk/include/smarty/libs/plugins/modifier.escape.php
===================================================================
--- trunk/include/smarty/libs/plugins/modifier.escape.php	(revision 3282)
+++ trunk/include/smarty/libs/plugins/modifier.escape.php	(revision 23384)
@@ -2,8 +2,8 @@
 /**
  * Smarty plugin
+ *
  * @package Smarty
- * @subpackage plugins
+ * @subpackage PluginsModifier
  */
-
 
 /**
@@ -12,20 +12,79 @@
  * Type:     modifier<br>
  * Name:     escape<br>
- * Purpose:  Escape the string according to escapement type
- * @link http://smarty.php.net/manual/en/language.modifier.escape.php
- *          escape (Smarty online manual)
- * @author   Monte Ohrt <monte at ohrt dot com>
- * @param string
- * @param html|htmlall|url|quotes|hex|hexentity|javascript
- * @return string
+ * Purpose:  escape string for output
+ *
+ * @link http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual)
+ * @author Monte Ohrt <monte at ohrt dot com>
+ * @param string  $string        input string
+ * @param string  $esc_type      escape type
+ * @param string  $char_set      character set, used for htmlspecialchars() or htmlentities()
+ * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities()
+ * @return string escaped input string
  */
-function smarty_modifier_escape($string, $esc_type = 'html', $char_set = 'ISO-8859-1')
+function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true)
 {
+    static $_double_encode = null;
+    if ($_double_encode === null) {
+        $_double_encode = version_compare(PHP_VERSION, '5.2.3', '>=');
+    }
+    
+    if (!$char_set) {
+        $char_set = Smarty::$_CHARSET;
+    }
+
     switch ($esc_type) {
         case 'html':
-            return htmlspecialchars($string, ENT_QUOTES, $char_set);
+            if ($_double_encode) {
+                // php >=5.3.2 - go native
+                return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
+            } else {
+                if ($double_encode) {
+                    // php <5.2.3 - only handle double encoding
+                    return htmlspecialchars($string, ENT_QUOTES, $char_set);
+                } else {
+                    // php <5.2.3 - prevent double encoding
+                    $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+                    $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
+                    $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string);
+                    return $string;
+                }
+            }
 
         case 'htmlall':
-            return htmlentities($string, ENT_QUOTES, $char_set);
+            if (Smarty::$_MBSTRING) {
+                // mb_convert_encoding ignores htmlspecialchars()
+                if ($_double_encode) {
+                    // php >=5.3.2 - go native
+                    $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
+                } else {
+                    if ($double_encode) {
+                        // php <5.2.3 - only handle double encoding
+                        $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
+                    } else {
+                        // php <5.2.3 - prevent double encoding
+                        $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+                        $string = htmlspecialchars($string, ENT_QUOTES, $char_set);
+                        $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string);
+                        return $string;
+                    }
+                }
+                
+                // htmlentities() won't convert everything, so use mb_convert_encoding
+                return mb_convert_encoding($string, 'HTML-ENTITIES', $char_set);
+            }
+
+            // no MBString fallback
+            if ($_double_encode) {
+                return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
+            } else {
+                if ($double_encode) {
+                    return htmlentities($string, ENT_QUOTES, $char_set);
+                } else {
+                    $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
+                    $string = htmlentities($string, ENT_QUOTES, $char_set);
+                    $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string);
+                    return $string;
+                }
+            }
 
         case 'url':
@@ -33,6 +92,6 @@
 
         case 'urlpathinfo':
-            return str_replace('%2F','/',rawurlencode($string));
-            
+            return str_replace('%2F', '/', rawurlencode($string));
+
         case 'quotes':
             // escape unescaped single quotes
@@ -40,14 +99,26 @@
 
         case 'hex':
-            // escape every character into hex
+            // escape every byte into hex
+            // Note that the UTF-8 encoded character ä will be represented as %c3%a4
             $return = '';
-            for ($x=0; $x < strlen($string); $x++) {
+            $_length = strlen($string);
+            for ($x = 0; $x < $_length; $x++) {
                 $return .= '%' . bin2hex($string[$x]);
             }
             return $return;
-            
+
         case 'hexentity':
             $return = '';
-            for ($x=0; $x < strlen($string); $x++) {
+            if (Smarty::$_MBSTRING) {
+                require_once(SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php');
+                $return = '';
+                foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
+                    $return .= '&#x' . strtoupper(dechex($unicode)) . ';';
+                }
+                return $return;
+            }
+            // no MBString fallback
+            $_length = strlen($string);
+            for ($x = 0; $x < $_length; $x++) {
                 $return .= '&#x' . bin2hex($string[$x]) . ';';
             }
@@ -56,5 +127,15 @@
         case 'decentity':
             $return = '';
-            for ($x=0; $x < strlen($string); $x++) {
+            if (Smarty::$_MBSTRING) {
+                require_once(SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php');
+                $return = '';
+                foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
+                    $return .= '&#' . $unicode . ';';
+                }
+                return $return;
+            }
+            // no MBString fallback
+            $_length = strlen($string);
+            for ($x = 0; $x < $_length; $x++) {
                 $return .= '&#' . ord($string[$x]) . ';';
             }
@@ -63,24 +144,40 @@
         case 'javascript':
             // escape quotes and backslashes, newlines, etc.
-            return strtr($string, array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n','</'=>'<\/'));
-            
+            return strtr($string, array('\\' => '\\\\', "'" => "\\'", '"' => '\\"', "\r" => '\\r', "\n" => '\\n', '</' => '<\/'));
+
         case 'mail':
-            // safe way to display e-mail address on a web page
-            return str_replace(array('@', '.'),array(' [AT] ', ' [DOT] '), $string);
-            
+            if (Smarty::$_MBSTRING) {
+                require_once(SMARTY_PLUGINS_DIR . 'shared.mb_str_replace.php');
+                return smarty_mb_str_replace(array('@', '.'), array(' [AT] ', ' [DOT] '), $string);
+            }
+            // no MBString fallback
+            return str_replace(array('@', '.'), array(' [AT] ', ' [DOT] '), $string);
+
         case 'nonstd':
-           // escape non-standard chars, such as ms document quotes
-           $_res = '';
-           for($_i = 0, $_len = strlen($string); $_i < $_len; $_i++) {
-               $_ord = ord(substr($string, $_i, 1));
-               // non-standard char, escape it
-               if($_ord >= 126){
-                   $_res .= '&#' . $_ord . ';';
-               }
-               else {
-                   $_res .= substr($string, $_i, 1);
-               }
-           }
-           return $_res;
+            // escape non-standard chars, such as ms document quotes
+            $return = '';
+            if (Smarty::$_MBSTRING) {
+                require_once(SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php');
+                foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) {
+                    if ($unicode >= 126) {
+                        $return .= '&#' . $unicode . ';';
+                    } else {
+                        $return .= chr($unicode);
+                    }
+                }
+                return $return;
+            }
+
+            $_length = strlen($string);
+            for ($_i = 0; $_i < $_length; $_i++) {
+                $_ord = ord(substr($string, $_i, 1));
+                // non-standard char, escape it
+                if ($_ord >= 126) {
+                    $return .= '&#' . $_ord . ';';
+                } else {
+                    $return .= substr($string, $_i, 1);
+                }
+            }
+            return $return;
 
         default:
@@ -89,5 +186,3 @@
 }
 
-/* vim: set expandtab: */
-
 ?>
