PageRenderTime 59ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/standard/html.c

http://github.com/infusion/PHP
C | 1502 lines | 1288 code | 88 blank | 126 comment | 116 complexity | f6e540c24befc7f60b8b45016771f6a5 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2011 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Rasmus Lerdorf <rasmus@php.net> |
  16. | Jaakko Hyv�tti <jaakko.hyvatti@iki.fi> |
  17. | Wez Furlong <wez@thebrainroom.com> |
  18. +----------------------------------------------------------------------+
  19. */
  20. /* $Id: html.c 306939 2011-01-01 02:19:59Z felipe $ */
  21. /*
  22. * HTML entity resources:
  23. *
  24. * http://msdn.microsoft.com/workshop/author/dhtml/reference/charsets/charset2.asp
  25. * http://msdn.microsoft.com/workshop/author/dhtml/reference/charsets/charset3.asp
  26. * http://www.unicode.org/Public/MAPPINGS/OBSOLETE/UNI2SGML.TXT
  27. *
  28. * http://www.w3.org/TR/2002/REC-xhtml1-20020801/dtds.html#h-A2
  29. *
  30. */
  31. #include "php.h"
  32. #if PHP_WIN32
  33. #include "config.w32.h"
  34. #else
  35. #include <php_config.h>
  36. #endif
  37. #include "html.h"
  38. #include "php_string.h"
  39. #include "SAPI.h"
  40. #if HAVE_LOCALE_H
  41. #include <locale.h>
  42. #endif
  43. #if HAVE_LANGINFO_H
  44. #include <langinfo.h>
  45. #endif
  46. #if HAVE_MBSTRING
  47. # include "ext/mbstring/mbstring.h"
  48. ZEND_EXTERN_MODULE_GLOBALS(mbstring)
  49. #endif
  50. enum entity_charset { cs_terminator, cs_8859_1, cs_cp1252,
  51. cs_8859_15, cs_utf_8, cs_big5, cs_gb2312,
  52. cs_big5hkscs, cs_sjis, cs_eucjp, cs_koi8r,
  53. cs_cp1251, cs_8859_5, cs_cp866, cs_macroman
  54. };
  55. typedef const char *const entity_table_t;
  56. /* codepage 1252 is a Windows extension to iso-8859-1. */
  57. static entity_table_t ent_cp_1252[] = {
  58. "euro", NULL, "sbquo", "fnof", "bdquo", "hellip", "dagger",
  59. "Dagger", "circ", "permil", "Scaron", "lsaquo", "OElig",
  60. NULL, NULL, NULL, NULL, "lsquo", "rsquo", "ldquo", "rdquo",
  61. "bull", "ndash", "mdash", "tilde", "trade", "scaron", "rsaquo",
  62. "oelig", NULL, NULL, "Yuml"
  63. };
  64. static entity_table_t ent_iso_8859_1[] = {
  65. "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar",
  66. "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg",
  67. "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro",
  68. "para", "middot", "cedil", "sup1", "ordm", "raquo", "frac14",
  69. "frac12", "frac34", "iquest", "Agrave", "Aacute", "Acirc",
  70. "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave",
  71. "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc",
  72. "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde",
  73. "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml",
  74. "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc",
  75. "atilde", "auml", "aring", "aelig", "ccedil", "egrave",
  76. "eacute", "ecirc", "euml", "igrave", "iacute", "icirc",
  77. "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde",
  78. "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc",
  79. "uuml", "yacute", "thorn", "yuml"
  80. };
  81. static entity_table_t ent_iso_8859_15[] = {
  82. "nbsp", "iexcl", "cent", "pound", "euro", "yen", "Scaron",
  83. "sect", "scaron", "copy", "ordf", "laquo", "not", "shy", "reg",
  84. "macr", "deg", "plusmn", "sup2", "sup3", NULL, /* Zcaron */
  85. "micro", "para", "middot", NULL, /* zcaron */ "sup1", "ordm",
  86. "raquo", "OElig", "oelig", "Yuml", "iquest", "Agrave", "Aacute",
  87. "Acirc", "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave",
  88. "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc",
  89. "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde",
  90. "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml",
  91. "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc",
  92. "atilde", "auml", "aring", "aelig", "ccedil", "egrave",
  93. "eacute", "ecirc", "euml", "igrave", "iacute", "icirc",
  94. "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde",
  95. "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc",
  96. "uuml", "yacute", "thorn", "yuml"
  97. };
  98. static entity_table_t ent_uni_338_402[] = {
  99. /* 338 (0x0152) */
  100. "OElig", "oelig", NULL, NULL, NULL, NULL,
  101. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  102. /* 352 (0x0160) */
  103. "Scaron", "scaron", NULL, NULL, NULL, NULL, NULL, NULL,
  104. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  105. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  106. /* 376 (0x0178) */
  107. "Yuml", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  108. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  109. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  110. /* 400 (0x0190) */
  111. NULL, NULL, "fnof"
  112. };
  113. static entity_table_t ent_uni_spacing[] = {
  114. /* 710 */
  115. "circ",
  116. /* 711 - 730 */
  117. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  118. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  119. /* 731 - 732 */
  120. NULL, "tilde"
  121. };
  122. static entity_table_t ent_uni_greek[] = {
  123. /* 913 */
  124. "Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta",
  125. "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho",
  126. NULL, "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega",
  127. /* 938 - 944 are not mapped */
  128. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  129. "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta",
  130. "iota", "kappa", "lambda", "mu", "nu", "xi", "omicron", "pi", "rho",
  131. "sigmaf", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega",
  132. /* 970 - 976 are not mapped */
  133. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  134. "thetasym", "upsih",
  135. NULL, NULL, NULL,
  136. "piv"
  137. };
  138. static entity_table_t ent_uni_punct[] = {
  139. /* 8194 */
  140. "ensp", "emsp", NULL, NULL, NULL, NULL, NULL,
  141. "thinsp", NULL, NULL, "zwnj", "zwj", "lrm", "rlm",
  142. NULL, NULL, NULL, "ndash", "mdash", NULL, NULL, NULL,
  143. /* 8216 */
  144. "lsquo", "rsquo", "sbquo", NULL, "ldquo", "rdquo", "bdquo", NULL,
  145. "dagger", "Dagger", "bull", NULL, NULL, NULL, "hellip",
  146. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "permil", NULL,
  147. /* 8242 */
  148. "prime", "Prime", NULL, NULL, NULL, NULL, NULL, "lsaquo", "rsaquo", NULL,
  149. NULL, NULL, "oline", NULL, NULL, NULL, NULL, NULL,
  150. "frasl"
  151. };
  152. static entity_table_t ent_uni_euro[] = {
  153. "euro"
  154. };
  155. static entity_table_t ent_uni_8465_8501[] = {
  156. /* 8465 */
  157. "image", NULL, NULL, NULL, NULL, NULL, NULL,
  158. /* 8472 */
  159. "weierp", NULL, NULL, NULL,
  160. /* 8476 */
  161. "real", NULL, NULL, NULL, NULL, NULL,
  162. /* 8482 */
  163. "trade", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  164. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  165. /* 8501 */
  166. "alefsym",
  167. };
  168. static entity_table_t ent_uni_8592_9002[] = {
  169. /* 8592 (0x2190) */
  170. "larr", "uarr", "rarr", "darr", "harr", NULL, NULL, NULL,
  171. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  172. /* 8608 (0x21a0) */
  173. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  174. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  175. /* 8624 (0x21b0) */
  176. NULL, NULL, NULL, NULL, NULL, "crarr", NULL, NULL,
  177. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  178. /* 8640 (0x21c0) */
  179. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  180. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  181. /* 8656 (0x21d0) */
  182. "lArr", "uArr", "rArr", "dArr", "hArr", NULL, NULL, NULL,
  183. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  184. /* 8672 (0x21e0) */
  185. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  186. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  187. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  188. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  189. /* 8704 (0x2200) */
  190. "forall", NULL, "part", "exist", NULL, "empty", NULL, "nabla",
  191. "isin", "notin", NULL, "ni", NULL, NULL, NULL, "prod",
  192. /* 8720 (0x2210) */
  193. NULL, "sum", "minus", NULL, NULL, NULL, NULL, "lowast",
  194. NULL, NULL, "radic", NULL, NULL, "prop", "infin", NULL,
  195. /* 8736 (0x2220) */
  196. "ang", NULL, NULL, NULL, NULL, NULL, NULL, "and",
  197. "or", "cap", "cup", "int", NULL, NULL, NULL, NULL,
  198. /* 8752 (0x2230) */
  199. NULL, NULL, NULL, NULL, "there4", NULL, NULL, NULL,
  200. NULL, NULL, NULL, NULL, "sim", NULL, NULL, NULL,
  201. /* 8768 (0x2240) */
  202. NULL, NULL, NULL, NULL, NULL, "cong", NULL, NULL,
  203. "asymp", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  204. /* 8784 (0x2250) */
  205. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  206. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  207. /* 8800 (0x2260) */
  208. "ne", "equiv", NULL, NULL, "le", "ge", NULL, NULL,
  209. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  210. /* 8816 (0x2270) */
  211. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  212. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  213. /* 8832 (0x2280) */
  214. NULL, NULL, "sub", "sup", "nsub", NULL, "sube", "supe",
  215. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  216. /* 8848 (0x2290) */
  217. NULL, NULL, NULL, NULL, NULL, "oplus", NULL, "otimes",
  218. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  219. /* 8864 (0x22a0) */
  220. NULL, NULL, NULL, NULL, NULL, "perp", NULL, NULL,
  221. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  222. /* 8880 (0x22b0) */
  223. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  224. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  225. /* 8896 (0x22c0) */
  226. NULL, NULL, NULL, NULL, NULL, "sdot", NULL, NULL,
  227. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  228. /* 8912 (0x22d0) */
  229. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  230. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  231. /* 8928 (0x22e0) */
  232. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  233. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  234. /* 8944 (0x22f0) */
  235. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  236. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  237. /* 8960 (0x2300) */
  238. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  239. "lceil", "rceil", "lfloor", "rfloor", NULL, NULL, NULL, NULL,
  240. /* 8976 (0x2310) */
  241. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  242. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  243. /* 8992 (0x2320) */
  244. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  245. NULL, "lang", "rang"
  246. };
  247. static entity_table_t ent_uni_9674[] = {
  248. /* 9674 */
  249. "loz"
  250. };
  251. static entity_table_t ent_uni_9824_9830[] = {
  252. /* 9824 */
  253. "spades", NULL, NULL, "clubs", NULL, "hearts", "diams"
  254. };
  255. static entity_table_t ent_koi8r[] = {
  256. "#1105", /* "jo "*/
  257. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  258. NULL, NULL, NULL, NULL, NULL, "#1025", /* "JO" */
  259. NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  260. "#1102", "#1072", "#1073", "#1094", "#1076", "#1077", "#1092",
  261. "#1075", "#1093", "#1080", "#1081", "#1082", "#1083", "#1084",
  262. "#1085", "#1086", "#1087", "#1103", "#1088", "#1089", "#1090",
  263. "#1091", "#1078", "#1074", "#1100", "#1099", "#1079", "#1096",
  264. "#1101", "#1097", "#1095", "#1098", "#1070", "#1040", "#1041",
  265. "#1062", "#1044", "#1045", "#1060", "#1043", "#1061", "#1048",
  266. "#1049", "#1050", "#1051", "#1052", "#1053", "#1054", "#1055",
  267. "#1071", "#1056", "#1057", "#1058", "#1059", "#1046", "#1042",
  268. "#1068", "#1067", "#1047", "#1064", "#1069", "#1065", "#1063",
  269. "#1066"
  270. };
  271. static entity_table_t ent_cp_1251[] = {
  272. "#1026", "#1027", "#8218", "#1107", "#8222", "hellip", "dagger",
  273. "Dagger", "euro", "permil", "#1033", "#8249", "#1034", "#1036",
  274. "#1035", "#1039", "#1106", "#8216", "#8217", "#8219", "#8220",
  275. "bull", "ndash", "mdash", NULL, "trade", "#1113", "#8250",
  276. "#1114", "#1116", "#1115", "#1119", "nbsp", "#1038", "#1118",
  277. "#1032", "curren", "#1168", "brvbar", "sect", "#1025", "copy",
  278. "#1028", "laquo", "not", "shy", "reg", "#1031", "deg", "plusmn",
  279. "#1030", "#1110", "#1169", "micro", "para", "middot", "#1105",
  280. "#8470", "#1108", "raquo", "#1112", "#1029", "#1109", "#1111",
  281. "#1040", "#1041", "#1042", "#1043", "#1044", "#1045", "#1046",
  282. "#1047", "#1048", "#1049", "#1050", "#1051", "#1052", "#1053",
  283. "#1054", "#1055", "#1056", "#1057", "#1058", "#1059", "#1060",
  284. "#1061", "#1062", "#1063", "#1064", "#1065", "#1066", "#1067",
  285. "#1068", "#1069", "#1070", "#1071", "#1072", "#1073", "#1074",
  286. "#1075", "#1076", "#1077", "#1078", "#1079", "#1080", "#1081",
  287. "#1082", "#1083", "#1084", "#1085", "#1086", "#1087", "#1088",
  288. "#1089", "#1090", "#1091", "#1092", "#1093", "#1094", "#1095",
  289. "#1096", "#1097", "#1098", "#1099", "#1100", "#1101", "#1102",
  290. "#1103"
  291. };
  292. static entity_table_t ent_iso_8859_5[] = {
  293. "#1056", "#1057", "#1058", "#1059", "#1060", "#1061", "#1062",
  294. "#1063", "#1064", "#1065", "#1066", "#1067", "#1068", "#1069",
  295. "#1070", "#1071", "#1072", "#1073", "#1074", "#1075", "#1076",
  296. "#1077", "#1078", "#1079", "#1080", "#1081", "#1082", "#1083",
  297. "#1084", "#1085", "#1086", "#1087", "#1088", "#1089", "#1090",
  298. "#1091", "#1092", "#1093", "#1094", "#1095", "#1096", "#1097",
  299. "#1098", "#1099", "#1100", "#1101", "#1102", "#1103", "#1104",
  300. "#1105", "#1106", "#1107", "#1108", "#1109", "#1110", "#1111",
  301. "#1112", "#1113", "#1114", "#1115", "#1116", "#1117", "#1118",
  302. "#1119"
  303. };
  304. static entity_table_t ent_cp_866[] = {
  305. "#9492", "#9524", "#9516", "#9500", "#9472", "#9532", "#9566",
  306. "#9567", "#9562", "#9556", "#9577", "#9574", "#9568", "#9552",
  307. "#9580", "#9575", "#9576", "#9572", "#9573", "#9561", "#9560",
  308. "#9554", "#9555", "#9579", "#9578", "#9496", "#9484", "#9608",
  309. "#9604", "#9612", "#9616", "#9600", "#1088", "#1089", "#1090",
  310. "#1091", "#1092", "#1093", "#1094", "#1095", "#1096", "#1097",
  311. "#1098", "#1099", "#1100", "#1101", "#1102", "#1103", "#1025",
  312. "#1105", "#1028", "#1108", "#1031", "#1111", "#1038", "#1118",
  313. "#176", "#8729", "#183", "#8730", "#8470", "#164", "#9632",
  314. "#160"
  315. };
  316. /* MacRoman has a couple of low-ascii chars that need mapping too */
  317. /* Vertical tab (ASCII 11) is often used to store line breaks inside */
  318. /* DB exports, this mapping changes it to a space */
  319. static entity_table_t ent_macroman[] = {
  320. "sp", NULL, NULL, NULL,
  321. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  322. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  323. NULL, NULL, NULL, NULL, NULL, "quot", NULL,
  324. NULL, NULL, "amp", NULL, NULL, NULL, NULL,
  325. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  326. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  327. NULL, NULL, NULL, "lt", NULL, "gt", NULL,
  328. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  329. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  330. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  331. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  332. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  333. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  334. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  335. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  336. NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  337. NULL, "Auml", "Aring", "Ccedil", "Eacute", "Ntilde", "Ouml",
  338. "Uuml", "aacute", "agrave", "acirc", "auml", "atilde", "aring",
  339. "ccedil", "eacute", "egrave", "ecirc", "euml", "iacute", "igrave",
  340. "icirc", "iuml", "ntilde", "oacute", "ograve", "ocirc", "ouml",
  341. "otilde", "uacute", "ugrave", "ucirc", "uuml", "dagger", "deg",
  342. "cent", "pound", "sect", "bull", "para", "szlig", "reg",
  343. "copy", "trade", "acute", "uml", "ne", "AElig", "Oslash",
  344. "infin", "plusmn", "le", "ge", "yen", "micro", "part",
  345. "sum", "prod", "pi", "int", "ordf", "ordm", "Omega",
  346. "aelig", "oslash", "iquest", "iexcl", "not", "radic", "fnof",
  347. "asymp", "#8710", "laquo", "raquo", "hellip", "nbsp", "Agrave",
  348. "Atilde", "Otilde", "OElig", "oelig", "ndash", "mdash", "ldquo",
  349. "rdquo", "lsquo", "rsquo", "divide", "loz", "yuml", "Yuml",
  350. "frasl", "euro", "lsaquo", "rsaquo", "#xFB01", "#xFB02", "Dagger",
  351. "middot", "sbquo", "bdquo", "permil", "Acirc", "Ecirc", "Aacute",
  352. "Euml", "Egrave", "Iacute", "Icirc", "Iuml", "Igrave", "Oacute",
  353. "Ocirc", "#xF8FF", "Ograve", "Uacute", "Ucirc", "Ugrave", "#305",
  354. "circ", "tilde", "macr", "#728", "#729", "#730", "cedil",
  355. "#733", "#731", "#711"
  356. };
  357. struct html_entity_map {
  358. enum entity_charset charset; /* charset identifier */
  359. unsigned int basechar; /* char code at start of table */
  360. unsigned int endchar; /* last char code in the table */
  361. entity_table_t *table; /* the table of mappings */
  362. };
  363. static const struct html_entity_map entity_map[] = {
  364. { cs_cp1252, 0x80, 0x9f, ent_cp_1252 },
  365. { cs_cp1252, 0xa0, 0xff, ent_iso_8859_1 },
  366. { cs_8859_1, 0xa0, 0xff, ent_iso_8859_1 },
  367. { cs_8859_15, 0xa0, 0xff, ent_iso_8859_15 },
  368. { cs_utf_8, 0xa0, 0xff, ent_iso_8859_1 },
  369. { cs_utf_8, 338, 402, ent_uni_338_402 },
  370. { cs_utf_8, 710, 732, ent_uni_spacing },
  371. { cs_utf_8, 913, 982, ent_uni_greek },
  372. { cs_utf_8, 8194, 8260, ent_uni_punct },
  373. { cs_utf_8, 8364, 8364, ent_uni_euro },
  374. { cs_utf_8, 8465, 8501, ent_uni_8465_8501 },
  375. { cs_utf_8, 8592, 9002, ent_uni_8592_9002 },
  376. { cs_utf_8, 9674, 9674, ent_uni_9674 },
  377. { cs_utf_8, 9824, 9830, ent_uni_9824_9830 },
  378. { cs_big5, 0xa0, 0xff, ent_iso_8859_1 },
  379. { cs_gb2312, 0xa0, 0xff, ent_iso_8859_1 },
  380. { cs_big5hkscs, 0xa0, 0xff, ent_iso_8859_1 },
  381. { cs_sjis, 0xa0, 0xff, ent_iso_8859_1 },
  382. { cs_eucjp, 0xa0, 0xff, ent_iso_8859_1 },
  383. { cs_koi8r, 0xa3, 0xff, ent_koi8r },
  384. { cs_cp1251, 0x80, 0xff, ent_cp_1251 },
  385. { cs_8859_5, 0xc0, 0xff, ent_iso_8859_5 },
  386. { cs_cp866, 0xc0, 0xff, ent_cp_866 },
  387. { cs_macroman, 0x0b, 0xff, ent_macroman },
  388. { cs_terminator }
  389. };
  390. static const struct {
  391. const char *codeset;
  392. enum entity_charset charset;
  393. } charset_map[] = {
  394. { "ISO-8859-1", cs_8859_1 },
  395. { "ISO8859-1", cs_8859_1 },
  396. { "ISO-8859-15", cs_8859_15 },
  397. { "ISO8859-15", cs_8859_15 },
  398. { "utf-8", cs_utf_8 },
  399. { "cp1252", cs_cp1252 },
  400. { "Windows-1252", cs_cp1252 },
  401. { "1252", cs_cp1252 },
  402. { "BIG5", cs_big5 },
  403. { "950", cs_big5 },
  404. { "GB2312", cs_gb2312 },
  405. { "936", cs_gb2312 },
  406. { "BIG5-HKSCS", cs_big5hkscs },
  407. { "Shift_JIS", cs_sjis },
  408. { "SJIS", cs_sjis },
  409. { "932", cs_sjis },
  410. { "EUCJP", cs_eucjp },
  411. { "EUC-JP", cs_eucjp },
  412. { "KOI8-R", cs_koi8r },
  413. { "koi8-ru", cs_koi8r },
  414. { "koi8r", cs_koi8r },
  415. { "cp1251", cs_cp1251 },
  416. { "Windows-1251", cs_cp1251 },
  417. { "win-1251", cs_cp1251 },
  418. { "iso8859-5", cs_8859_5 },
  419. { "iso-8859-5", cs_8859_5 },
  420. { "cp866", cs_cp866 },
  421. { "866", cs_cp866 },
  422. { "ibm866", cs_cp866 },
  423. { "MacRoman", cs_macroman },
  424. { NULL }
  425. };
  426. static const struct {
  427. unsigned short charcode;
  428. char *entity;
  429. int entitylen;
  430. int flags;
  431. } basic_entities[] = {
  432. { '"', "&quot;", 6, ENT_HTML_QUOTE_DOUBLE },
  433. { '\'', "&#039;", 6, ENT_HTML_QUOTE_SINGLE },
  434. { '\'', "&#39;", 5, ENT_HTML_QUOTE_SINGLE },
  435. { '<', "&lt;", 4, 0 },
  436. { '>', "&gt;", 4, 0 },
  437. { 0, NULL, 0, 0 }
  438. };
  439. struct basic_entities_dec {
  440. unsigned short charcode;
  441. char entity[8];
  442. int entitylen;
  443. };
  444. #define MB_RETURN { \
  445. *newpos = pos; \
  446. mbseq[mbpos] = '\0'; \
  447. *mbseqlen = mbpos; \
  448. return this_char; }
  449. #define MB_WRITE(mbchar) { \
  450. mbspace--; \
  451. if (mbspace == 0) { \
  452. MB_RETURN; \
  453. } \
  454. mbseq[mbpos++] = (mbchar); }
  455. /* skip one byte and return */
  456. #define MB_FAILURE(pos) do { \
  457. *newpos = pos + 1; \
  458. *status = FAILURE; \
  459. return 0; \
  460. } while (0)
  461. #define CHECK_LEN(pos, chars_need) \
  462. if (chars_need < 1) { \
  463. if((str_len - (pos)) < chars_need) { \
  464. *newpos = pos; \
  465. *status = FAILURE; \
  466. return 0; \
  467. } \
  468. } else { \
  469. if((str_len - (pos)) < chars_need) { \
  470. *newpos = pos + 1; \
  471. *status = FAILURE; \
  472. return 0; \
  473. } \
  474. }
  475. /* {{{ get_next_char
  476. */
  477. inline static unsigned int get_next_char(enum entity_charset charset,
  478. unsigned char * str,
  479. int str_len,
  480. int * newpos,
  481. unsigned char * mbseq,
  482. int * mbseqlen,
  483. int *status)
  484. {
  485. int pos = *newpos;
  486. int mbpos = 0;
  487. int mbspace = *mbseqlen;
  488. unsigned int this_char = 0;
  489. unsigned char next_char;
  490. *status = SUCCESS;
  491. if (mbspace <= 0) {
  492. *mbseqlen = 0;
  493. CHECK_LEN(pos, 1);
  494. *newpos = pos + 1;
  495. return str[pos];
  496. }
  497. switch (charset) {
  498. case cs_utf_8:
  499. {
  500. unsigned char c;
  501. CHECK_LEN(pos, 1);
  502. c = str[pos];
  503. if (c < 0x80) {
  504. MB_WRITE(c);
  505. this_char = c;
  506. pos++;
  507. } else if (c < 0xc2) {
  508. MB_FAILURE(pos);
  509. } else if (c < 0xe0) {
  510. CHECK_LEN(pos, 2);
  511. if (str[pos + 1] < 0x80 || str[pos + 1] > 0xbf) {
  512. MB_FAILURE(pos);
  513. }
  514. this_char = ((c & 0x1f) << 6) | (str[pos + 1] & 0x3f);
  515. if (this_char < 0x80) {
  516. MB_FAILURE(pos);
  517. }
  518. MB_WRITE((unsigned char)c);
  519. MB_WRITE((unsigned char)str[pos + 1]);
  520. pos += 2;
  521. } else if (c < 0xf0) {
  522. CHECK_LEN(pos, 3);
  523. if (str[pos + 1] < 0x80 || str[pos + 1] > 0xbf) {
  524. MB_FAILURE(pos);
  525. }
  526. if (str[pos + 2] < 0x80 || str[pos + 2] > 0xbf) {
  527. MB_FAILURE(pos);
  528. }
  529. this_char = ((c & 0x0f) << 12) | ((str[pos + 1] & 0x3f) << 6) | (str[pos + 2] & 0x3f);
  530. if (this_char < 0x800) {
  531. MB_FAILURE(pos);
  532. } else if (this_char >= 0xd800 && this_char <= 0xdfff) {
  533. MB_FAILURE(pos);
  534. }
  535. MB_WRITE((unsigned char)c);
  536. MB_WRITE((unsigned char)str[pos + 1]);
  537. MB_WRITE((unsigned char)str[pos + 2]);
  538. pos += 3;
  539. } else if (c < 0xf5) {
  540. CHECK_LEN(pos, 4);
  541. if (str[pos + 1] < 0x80 || str[pos + 1] > 0xbf) {
  542. MB_FAILURE(pos);
  543. }
  544. if (str[pos + 2] < 0x80 || str[pos + 2] > 0xbf) {
  545. MB_FAILURE(pos);
  546. }
  547. if (str[pos + 3] < 0x80 || str[pos + 3] > 0xbf) {
  548. MB_FAILURE(pos);
  549. }
  550. this_char = ((c & 0x07) << 18) | ((str[pos + 1] & 0x3f) << 12) | ((str[pos + 2] & 0x3f) << 6) | (str[pos + 3] & 0x3f);
  551. if (this_char < 0x10000 || this_char > 0x10FFFF) {
  552. MB_FAILURE(pos);
  553. }
  554. MB_WRITE((unsigned char)c);
  555. MB_WRITE((unsigned char)str[pos + 1]);
  556. MB_WRITE((unsigned char)str[pos + 2]);
  557. MB_WRITE((unsigned char)str[pos + 3]);
  558. pos += 4;
  559. } else {
  560. MB_FAILURE(pos);
  561. }
  562. }
  563. break;
  564. case cs_big5:
  565. case cs_gb2312:
  566. case cs_big5hkscs:
  567. {
  568. CHECK_LEN(pos, 1);
  569. this_char = str[pos++];
  570. /* check if this is the first of a 2-byte sequence */
  571. if (this_char >= 0x81 && this_char <= 0xfe) {
  572. /* peek at the next char */
  573. CHECK_LEN(pos, 1);
  574. next_char = str[pos++];
  575. if ((next_char >= 0x40 && next_char <= 0x7e) ||
  576. (next_char >= 0xa1 && next_char <= 0xfe)) {
  577. /* yes, this a wide char */
  578. MB_WRITE(this_char);
  579. MB_WRITE(next_char);
  580. this_char = (this_char << 8) | next_char;
  581. } else {
  582. MB_FAILURE(pos);
  583. }
  584. } else {
  585. MB_WRITE(this_char);
  586. }
  587. }
  588. break;
  589. case cs_sjis:
  590. {
  591. CHECK_LEN(pos, 1);
  592. this_char = str[pos++];
  593. /* check if this is the first of a 2-byte sequence */
  594. if ((this_char >= 0x81 && this_char <= 0x9f) ||
  595. (this_char >= 0xe0 && this_char <= 0xfc)) {
  596. /* peek at the next char */
  597. CHECK_LEN(pos, 1);
  598. next_char = str[pos++];
  599. if ((next_char >= 0x40 && next_char <= 0x7e) ||
  600. (next_char >= 0x80 && next_char <= 0xfc))
  601. {
  602. /* yes, this a wide char */
  603. MB_WRITE(this_char);
  604. MB_WRITE(next_char);
  605. this_char = (this_char << 8) | next_char;
  606. } else {
  607. MB_FAILURE(pos);
  608. }
  609. } else {
  610. MB_WRITE(this_char);
  611. }
  612. break;
  613. }
  614. case cs_eucjp:
  615. {
  616. CHECK_LEN(pos, 1);
  617. this_char = str[pos++];
  618. /* check if this is the first of a multi-byte sequence */
  619. if (this_char >= 0xa1 && this_char <= 0xfe) {
  620. /* peek at the next char */
  621. CHECK_LEN(pos, 1);
  622. next_char = str[pos++];
  623. if (next_char >= 0xa1 && next_char <= 0xfe) {
  624. /* yes, this a jis kanji char */
  625. MB_WRITE(this_char);
  626. MB_WRITE(next_char);
  627. this_char = (this_char << 8) | next_char;
  628. } else {
  629. MB_FAILURE(pos);
  630. }
  631. } else if (this_char == 0x8e) {
  632. /* peek at the next char */
  633. CHECK_LEN(pos, 1);
  634. next_char = str[pos++];
  635. if (next_char >= 0xa1 && next_char <= 0xdf) {
  636. /* JIS X 0201 kana */
  637. MB_WRITE(this_char);
  638. MB_WRITE(next_char);
  639. this_char = (this_char << 8) | next_char;
  640. } else {
  641. MB_FAILURE(pos);
  642. }
  643. } else if (this_char == 0x8f) {
  644. /* peek at the next two char */
  645. unsigned char next2_char;
  646. CHECK_LEN(pos, 2);
  647. next_char = str[pos];
  648. next2_char = str[pos + 1];
  649. pos += 2;
  650. if ((next_char >= 0xa1 && next_char <= 0xfe) &&
  651. (next2_char >= 0xa1 && next2_char <= 0xfe)) {
  652. /* JIS X 0212 hojo-kanji */
  653. MB_WRITE(this_char);
  654. MB_WRITE(next_char);
  655. MB_WRITE(next2_char);
  656. this_char = (this_char << 16) | (next_char << 8) | next2_char;
  657. } else {
  658. MB_FAILURE(pos);
  659. }
  660. } else {
  661. MB_WRITE(this_char);
  662. }
  663. break;
  664. }
  665. default:
  666. /* single-byte charsets */
  667. CHECK_LEN(pos, 1);
  668. this_char = str[pos++];
  669. MB_WRITE(this_char);
  670. break;
  671. }
  672. MB_RETURN;
  673. }
  674. /* }}} */
  675. /* {{{ entity_charset determine_charset
  676. * returns the charset identifier based on current locale or a hint.
  677. * defaults to iso-8859-1 */
  678. static enum entity_charset determine_charset(char *charset_hint TSRMLS_DC)
  679. {
  680. int i;
  681. enum entity_charset charset = cs_utf_8;
  682. int len = 0;
  683. zval *uf_result = NULL;
  684. /* Guarantee default behaviour for backwards compatibility */
  685. if (charset_hint == NULL)
  686. return cs_utf_8;
  687. if ((len = strlen(charset_hint)) != 0) {
  688. goto det_charset;
  689. }
  690. #if HAVE_MBSTRING
  691. #if !defined(COMPILE_DL_MBSTRING)
  692. /* XXX: Ugly things. Why don't we look for a more sophisticated way? */
  693. switch (MBSTRG(current_internal_encoding)) {
  694. case mbfl_no_encoding_8859_1:
  695. return cs_8859_1;
  696. case mbfl_no_encoding_utf8:
  697. return cs_utf_8;
  698. case mbfl_no_encoding_euc_jp:
  699. case mbfl_no_encoding_eucjp_win:
  700. return cs_eucjp;
  701. case mbfl_no_encoding_sjis:
  702. case mbfl_no_encoding_sjis_open:
  703. case mbfl_no_encoding_cp932:
  704. return cs_sjis;
  705. case mbfl_no_encoding_cp1252:
  706. return cs_cp1252;
  707. case mbfl_no_encoding_8859_15:
  708. return cs_8859_15;
  709. case mbfl_no_encoding_big5:
  710. return cs_big5;
  711. case mbfl_no_encoding_euc_cn:
  712. case mbfl_no_encoding_hz:
  713. case mbfl_no_encoding_cp936:
  714. return cs_gb2312;
  715. case mbfl_no_encoding_koi8r:
  716. return cs_koi8r;
  717. case mbfl_no_encoding_cp866:
  718. return cs_cp866;
  719. case mbfl_no_encoding_cp1251:
  720. return cs_cp1251;
  721. case mbfl_no_encoding_8859_5:
  722. return cs_8859_5;
  723. default:
  724. ;
  725. }
  726. #else
  727. {
  728. zval nm_mb_internal_encoding;
  729. ZVAL_STRING(&nm_mb_internal_encoding, "mb_internal_encoding", 0);
  730. if (call_user_function_ex(CG(function_table), NULL, &nm_mb_internal_encoding, &uf_result, 0, NULL, 1, NULL TSRMLS_CC) != FAILURE) {
  731. charset_hint = Z_STRVAL_P(uf_result);
  732. len = Z_STRLEN_P(uf_result);
  733. if (len == 4) { /* sizeof(none|auto|pass)-1 */
  734. if (!memcmp("pass", charset_hint, sizeof("pass") - 1) ||
  735. !memcmp("auto", charset_hint, sizeof("auto") - 1) ||
  736. !memcmp("none", charset_hint, sizeof("none") - 1)) {
  737. charset_hint = NULL;
  738. len = 0;
  739. }
  740. }
  741. goto det_charset;
  742. }
  743. }
  744. #endif
  745. #endif
  746. charset_hint = SG(default_charset);
  747. if (charset_hint != NULL && (len=strlen(charset_hint)) != 0) {
  748. goto det_charset;
  749. }
  750. /* try to detect the charset for the locale */
  751. #if HAVE_NL_LANGINFO && HAVE_LOCALE_H && defined(CODESET)
  752. charset_hint = nl_langinfo(CODESET);
  753. if (charset_hint != NULL && (len=strlen(charset_hint)) != 0) {
  754. goto det_charset;
  755. }
  756. #endif
  757. #if HAVE_LOCALE_H
  758. /* try to figure out the charset from the locale */
  759. {
  760. char *localename;
  761. char *dot, *at;
  762. /* lang[_territory][.codeset][@modifier] */
  763. localename = setlocale(LC_CTYPE, NULL);
  764. dot = strchr(localename, '.');
  765. if (dot) {
  766. dot++;
  767. /* locale specifies a codeset */
  768. at = strchr(dot, '@');
  769. if (at)
  770. len = at - dot;
  771. else
  772. len = strlen(dot);
  773. charset_hint = dot;
  774. } else {
  775. /* no explicit name; see if the name itself
  776. * is the charset */
  777. charset_hint = localename;
  778. len = strlen(charset_hint);
  779. }
  780. }
  781. #endif
  782. det_charset:
  783. if (charset_hint) {
  784. int found = 0;
  785. /* now walk the charset map and look for the codeset */
  786. for (i = 0; charset_map[i].codeset; i++) {
  787. if (len == strlen(charset_map[i].codeset) && strncasecmp(charset_hint, charset_map[i].codeset, len) == 0) {
  788. charset = charset_map[i].charset;
  789. found = 1;
  790. break;
  791. }
  792. }
  793. if (!found) {
  794. php_error_docref(NULL TSRMLS_CC, E_WARNING, "charset `%s' not supported, assuming iso-8859-1",
  795. charset_hint);
  796. }
  797. }
  798. if (uf_result != NULL) {
  799. zval_ptr_dtor(&uf_result);
  800. }
  801. return charset;
  802. }
  803. /* }}} */
  804. /* {{{ php_utf32_utf8 */
  805. size_t php_utf32_utf8(unsigned char *buf, unsigned k)
  806. {
  807. size_t retval = 0;
  808. if (k < 0x80) {
  809. buf[0] = k;
  810. retval = 1;
  811. } else if (k < 0x800) {
  812. buf[0] = 0xc0 | (k >> 6);
  813. buf[1] = 0x80 | (k & 0x3f);
  814. retval = 2;
  815. } else if (k < 0x10000) {
  816. buf[0] = 0xe0 | (k >> 12);
  817. buf[1] = 0x80 | ((k >> 6) & 0x3f);
  818. buf[2] = 0x80 | (k & 0x3f);
  819. retval = 3;
  820. } else if (k < 0x200000) {
  821. buf[0] = 0xf0 | (k >> 18);
  822. buf[1] = 0x80 | ((k >> 12) & 0x3f);
  823. buf[2] = 0x80 | ((k >> 6) & 0x3f);
  824. buf[3] = 0x80 | (k & 0x3f);
  825. retval = 4;
  826. } else if (k < 0x4000000) {
  827. buf[0] = 0xf8 | (k >> 24);
  828. buf[1] = 0x80 | ((k >> 18) & 0x3f);
  829. buf[2] = 0x80 | ((k >> 12) & 0x3f);
  830. buf[3] = 0x80 | ((k >> 6) & 0x3f);
  831. buf[4] = 0x80 | (k & 0x3f);
  832. retval = 5;
  833. } else {
  834. buf[0] = 0xfc | (k >> 30);
  835. buf[1] = 0x80 | ((k >> 24) & 0x3f);
  836. buf[2] = 0x80 | ((k >> 18) & 0x3f);
  837. buf[3] = 0x80 | ((k >> 12) & 0x3f);
  838. buf[4] = 0x80 | ((k >> 6) & 0x3f);
  839. buf[5] = 0x80 | (k & 0x3f);
  840. retval = 6;
  841. }
  842. buf[retval] = '\0';
  843. return retval;
  844. }
  845. /* }}} */
  846. /* {{{ php_unescape_html_entities
  847. */
  848. PHPAPI char *php_unescape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset TSRMLS_DC)
  849. {
  850. int retlen;
  851. int j, k;
  852. char *replaced, *ret, *p, *q, *lim, *next;
  853. enum entity_charset charset = determine_charset(hint_charset TSRMLS_CC);
  854. unsigned char replacement[15];
  855. int replacement_len;
  856. ret = estrndup(old, oldlen);
  857. retlen = oldlen;
  858. if (!retlen) {
  859. goto empty_source;
  860. }
  861. if (all) {
  862. /* look for a match in the maps for this charset */
  863. for (j = 0; entity_map[j].charset != cs_terminator; j++) {
  864. if (entity_map[j].charset != charset)
  865. continue;
  866. for (k = entity_map[j].basechar; k <= entity_map[j].endchar; k++) {
  867. unsigned char entity[32];
  868. int entity_length = 0;
  869. if (entity_map[j].table[k - entity_map[j].basechar] == NULL)
  870. continue;
  871. entity_length = slprintf(entity, sizeof(entity), "&%s;", entity_map[j].table[k - entity_map[j].basechar]);
  872. if (entity_length >= sizeof(entity)) {
  873. continue;
  874. }
  875. /* When we have MBCS entities in the tables above, this will need to handle it */
  876. replacement_len = 0;
  877. switch (charset) {
  878. case cs_8859_1:
  879. case cs_cp1252:
  880. case cs_8859_15:
  881. case cs_cp1251:
  882. case cs_8859_5:
  883. case cs_cp866:
  884. case cs_koi8r:
  885. replacement[0] = k;
  886. replacement[1] = '\0';
  887. replacement_len = 1;
  888. break;
  889. case cs_big5:
  890. case cs_gb2312:
  891. case cs_big5hkscs:
  892. case cs_sjis:
  893. case cs_eucjp:
  894. /* we cannot properly handle those multibyte encodings
  895. * with php_str_to_str. skip it. */
  896. continue;
  897. case cs_utf_8:
  898. replacement_len = php_utf32_utf8(replacement, k);
  899. break;
  900. default:
  901. php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot yet handle MBCS!");
  902. efree(ret);
  903. return NULL;
  904. }
  905. if (php_memnstr(ret, entity, entity_length, ret+retlen)) {
  906. replaced = php_str_to_str(ret, retlen, entity, entity_length, replacement, replacement_len, &retlen);
  907. efree(ret);
  908. ret = replaced;
  909. }
  910. }
  911. }
  912. }
  913. for (j = 0; basic_entities[j].charcode != 0; j++) {
  914. if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0)
  915. continue;
  916. replacement[0] = (unsigned char)basic_entities[j].charcode;
  917. replacement[1] = '\0';
  918. if (php_memnstr(ret, basic_entities[j].entity, basic_entities[j].entitylen, ret+retlen)) {
  919. replaced = php_str_to_str(ret, retlen, basic_entities[j].entity, basic_entities[j].entitylen, replacement, 1, &retlen);
  920. efree(ret);
  921. ret = replaced;
  922. }
  923. }
  924. /* replace numeric entities & "&amp;" */
  925. lim = ret + retlen;
  926. for (p = ret, q = ret; p < lim;) {
  927. int code;
  928. if (p[0] == '&') {
  929. if (p + 2 < lim) {
  930. if (p[1] == '#') {
  931. int invalid_code = 0;
  932. if (p[2] == 'x' || p[2] == 'X') {
  933. code = strtol(p + 3, &next, 16);
  934. } else {
  935. code = strtol(p + 2, &next, 10);
  936. }
  937. if (code == '\'' && !(quote_style & ENT_HTML_QUOTE_SINGLE) ||
  938. code == '"' && !(quote_style & ENT_HTML_QUOTE_DOUBLE)) {
  939. invalid_code = 1;
  940. }
  941. if (next != NULL && *next == ';' && !invalid_code) {
  942. switch (charset) {
  943. case cs_utf_8:
  944. q += php_utf32_utf8(q, code);
  945. break;
  946. case cs_8859_1:
  947. case cs_8859_5:
  948. case cs_8859_15:
  949. if ((code >= 0x80 && code < 0xa0) || code > 0xff) {
  950. invalid_code = 1;
  951. } else {
  952. *(q++) = code;
  953. }
  954. break;
  955. case cs_cp1252:
  956. if (code > 0xff) {
  957. invalid_code = 1;
  958. } else {
  959. *(q++) = code;
  960. }
  961. break;
  962. case cs_cp1251:
  963. case cs_cp866:
  964. case cs_big5:
  965. case cs_big5hkscs:
  966. case cs_sjis:
  967. case cs_eucjp:
  968. if (code >= 0x80) {
  969. invalid_code = 1;
  970. } else {
  971. *(q++) = code;
  972. }
  973. break;
  974. case cs_gb2312:
  975. if (code >= 0x81) {
  976. invalid_code = 1;
  977. } else {
  978. *(q++) = code;
  979. }
  980. break;
  981. default:
  982. /* for backwards compatilibity */
  983. invalid_code = 1;
  984. break;
  985. }
  986. if (invalid_code) {
  987. for (; p <= next; p++) {
  988. *(q++) = *p;
  989. }
  990. }
  991. p = next + 1;
  992. } else {
  993. *(q++) = *(p++);
  994. *(q++) = *(p++);
  995. }
  996. } else if (p + 4 < lim &&
  997. p[1] == 'a' && p[2] == 'm' &&p[3] == 'p' &&
  998. p[4] == ';') {
  999. *(q++) = '&';
  1000. p += 5;
  1001. } else {
  1002. *(q++) = *(p++);
  1003. *(q++) = *(p++);
  1004. }
  1005. } else {
  1006. *(q++) = *(p++);
  1007. }
  1008. } else {
  1009. *(q++) = *(p++);
  1010. }
  1011. }
  1012. *q = '\0';
  1013. retlen = (size_t)(q - ret);
  1014. empty_source:
  1015. *newlen = retlen;
  1016. return ret;
  1017. }
  1018. /* }}} */
  1019. PHPAPI char *php_escape_html_entities(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset TSRMLS_DC)
  1020. {
  1021. return php_escape_html_entities_ex(old, oldlen, newlen, all, quote_style, hint_charset, 1 TSRMLS_CC);
  1022. }
  1023. /* {{{ php_escape_html_entities
  1024. */
  1025. PHPAPI char *php_escape_html_entities_ex(unsigned char *old, int oldlen, int *newlen, int all, int quote_style, char *hint_charset, zend_bool double_encode TSRMLS_DC)
  1026. {
  1027. int i, j, maxlen, len;
  1028. char *replaced;
  1029. enum entity_charset charset = determine_charset(hint_charset TSRMLS_CC);
  1030. int matches_map;
  1031. maxlen = 2 * oldlen;
  1032. if (maxlen < 128)
  1033. maxlen = 128;
  1034. replaced = emalloc (maxlen);
  1035. len = 0;
  1036. i = 0;
  1037. while (i < oldlen) {
  1038. unsigned char mbsequence[16]; /* allow up to 15 characters in a multibyte sequence */
  1039. int mbseqlen = sizeof(mbsequence);
  1040. int status = SUCCESS;
  1041. unsigned int this_char = get_next_char(charset, old, oldlen, &i, mbsequence, &mbseqlen, &status);
  1042. if(status == FAILURE) {
  1043. /* invalid MB sequence */
  1044. if (quote_style & ENT_HTML_IGNORE_ERRORS) {
  1045. continue;
  1046. }
  1047. efree(replaced);
  1048. if(!PG(display_errors)) {
  1049. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid multibyte sequence in argument");
  1050. }
  1051. *newlen = 0;
  1052. return STR_EMPTY_ALLOC();
  1053. }
  1054. matches_map = 0;
  1055. if (len + 16 > maxlen)
  1056. replaced = erealloc (replaced, maxlen += 128);
  1057. if (all) {
  1058. /* look for a match in the maps for this charset */
  1059. unsigned char *rep = NULL;
  1060. for (j = 0; entity_map[j].charset != cs_terminator; j++) {
  1061. if (entity_map[j].charset == charset
  1062. && this_char >= entity_map[j].basechar
  1063. && this_char <= entity_map[j].endchar) {
  1064. rep = (unsigned char*)entity_map[j].table[this_char - entity_map[j].basechar];
  1065. if (rep == NULL) {
  1066. /* there is no entity for this position; fall through and
  1067. * just output the character itself */
  1068. break;
  1069. }
  1070. matches_map = 1;
  1071. break;
  1072. }
  1073. }
  1074. if (matches_map) {
  1075. int l = strlen(rep);
  1076. /* increase the buffer size */
  1077. if (len + 2 + l >= maxlen) {
  1078. replaced = erealloc(replaced, maxlen += 128);
  1079. }
  1080. replaced[len++] = '&';
  1081. strlcpy(replaced + len, rep, maxlen);
  1082. len += l;
  1083. replaced[len++] = ';';
  1084. }
  1085. }
  1086. if (!matches_map) {
  1087. int is_basic = 0;
  1088. if (this_char == '&') {
  1089. if (double_encode) {
  1090. encode_amp:
  1091. memcpy(replaced + len, "&amp;", sizeof("&amp;") - 1);
  1092. len += sizeof("&amp;") - 1;
  1093. } else {
  1094. char *e = memchr(old + i, ';', oldlen - i);
  1095. char *s = old + i;
  1096. if (!e || (e - s) > 10) { /* minor optimization to avoid "entities" over 10 chars in length */
  1097. goto encode_amp;
  1098. } else {
  1099. if (*s == '#') { /* numeric entities */
  1100. s++;
  1101. /* Hex (&#x5A;) */
  1102. if (*s == 'x' || *s == 'X') {
  1103. s++;
  1104. while (s < e) {
  1105. if (!isxdigit((int)*(unsigned char *)s++)) {
  1106. goto encode_amp;
  1107. }
  1108. }
  1109. /* Dec (&#90;)*/
  1110. } else {
  1111. while (s < e) {
  1112. if (!isdigit((int)*(unsigned char *)s++)) {
  1113. goto encode_amp;
  1114. }
  1115. }
  1116. }
  1117. } else { /* text entities */
  1118. while (s < e) {
  1119. if (!isalnum((int)*(unsigned char *)s++)) {
  1120. goto encode_amp;
  1121. }
  1122. }
  1123. }
  1124. replaced[len++] = '&';
  1125. }
  1126. }
  1127. is_basic = 1;
  1128. } else {
  1129. for (j = 0; basic_entities[j].charcode != 0; j++) {
  1130. if ((basic_entities[j].charcode != this_char) ||
  1131. (basic_entities[j].flags &&
  1132. (quote_style & basic_entities[j].flags) == 0)) {
  1133. continue;
  1134. }
  1135. memcpy(replaced + len, basic_entities[j].entity, basic_entities[j].entitylen);
  1136. len += basic_entities[j].entitylen;
  1137. is_basic = 1;
  1138. break;
  1139. }
  1140. }
  1141. if (!is_basic) {
  1142. /* a wide char without a named entity; pass through the original sequence */
  1143. if (mbseqlen > 1) {
  1144. memcpy(replaced + len, mbsequence, mbseqlen);
  1145. len += mbseqlen;
  1146. } else {
  1147. replaced[len++] = (unsigned char)this_char;
  1148. }
  1149. }
  1150. }
  1151. }
  1152. replaced[len] = '\0';
  1153. *newlen = len;
  1154. return replaced;
  1155. }
  1156. /* }}} */
  1157. /* {{{ php_html_entities
  1158. */
  1159. static void php_html_entities(INTERNAL_FUNCTION_PARAMETERS, int all)
  1160. {
  1161. char *str, *hint_charset = NULL;
  1162. int str_len, hint_charset_len = 0;
  1163. int len;
  1164. long quote_style = ENT_QUOTES;
  1165. char *replaced;
  1166. zend_bool double_encode = 1;
  1167. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls!b", &str, &str_len, &quote_style, &hint_charset, &hint_charset_len, &double_encode) == FAILURE) {
  1168. return;
  1169. }
  1170. replaced = php_escape_html_entities_ex(str, str_len, &len, all, quote_style, hint_charset, double_encode TSRMLS_CC);
  1171. RETVAL_STRINGL(replaced, len, 0);
  1172. }
  1173. /* }}} */
  1174. #define HTML_SPECIALCHARS 0
  1175. #define HTML_ENTITIES 1
  1176. /* {{{ register_html_constants
  1177. */
  1178. void register_html_constants(INIT_FUNC_ARGS)
  1179. {
  1180. REGISTER_LONG_CONSTANT("HTML_SPECIALCHARS", HTML_SPECIALCHARS, CONST_PERSISTENT|CONST_CS);
  1181. REGISTER_LONG_CONSTANT("HTML_ENTITIES", HTML_ENTITIES, CONST_PERSISTENT|CONST_CS);
  1182. REGISTER_LONG_CONSTANT("ENT_COMPAT", ENT_COMPAT, CONST_PERSISTENT|CONST_CS);
  1183. REGISTER_LONG_CONSTANT("ENT_QUOTES", ENT_QUOTES, CONST_PERSISTENT|CONST_CS);
  1184. REGISTER_LONG_CONSTANT("ENT_NOQUOTES", ENT_NOQUOTES, CONST_PERSISTENT|CONST_CS);
  1185. REGISTER_LONG_CONSTANT("ENT_IGNORE", ENT_IGNORE, CONST_PERSISTENT|CONST_CS);
  1186. }
  1187. /* }}} */
  1188. /* {{{ proto string htmlspecialchars(string string [, int quote_style[, string charset[, bool double_encode]]])
  1189. Convert special characters to HTML entities */
  1190. PHP_FUNCTION(htmlspecialchars)
  1191. {
  1192. php_html_entities(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  1193. }
  1194. /* }}} */
  1195. /* {{{ proto string htmlspecialchars_decode(string string [, int quote_style])
  1196. Convert special HTML entities back to characters */
  1197. PHP_FUNCTION(htmlspecialchars_decode)
  1198. {
  1199. char *str, *new_str, *e, *p;
  1200. int len, j, i, new_len;
  1201. long quote_style = ENT_QUOTES;
  1202. struct basic_entities_dec basic_entities_dec[8];
  1203. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &quote_style) == FAILURE) {
  1204. return;
  1205. }
  1206. new_str = estrndup(str, len);
  1207. new_len = len;
  1208. e = new_str + new_len;
  1209. if (!(p = memchr(new_str, '&', new_len))) {
  1210. RETURN_STRINGL(new_str, new_len, 0);
  1211. }
  1212. for (j = 0, i = 0; basic_entities[i].charcode != 0; i++) {
  1213. if (basic_entities[i].flags && !(quote_style & basic_entities[i].flags)) {
  1214. continue;
  1215. }
  1216. basic_entities_dec[j].charcode = basic_entities[i].charcode;
  1217. memcpy(basic_entities_dec[j].entity, basic_entities[i].entity, basic_entities[i].entitylen + 1);
  1218. basic_entities_dec[j].entitylen = basic_entities[i].entitylen;
  1219. j++;
  1220. }
  1221. basic_entities_dec[j].charcode = '&';
  1222. basic_entities_dec[j].entitylen = sizeof("&amp;") - 1;
  1223. memcpy(basic_entities_dec[j].entity, "&amp;", sizeof("&amp;"));
  1224. i = j + 1;
  1225. do {
  1226. int l = e - p;
  1227. for (j = 0; j < i; j++) {
  1228. if (basic_entities_dec[j].entitylen > l) {
  1229. continue;
  1230. }
  1231. if (!memcmp(p, basic_entities_dec[j].entity, basic_entities_dec[j].entitylen)) {
  1232. int e_len = basic_entities_dec[j].entitylen - 1;
  1233. *p++ = basic_entities_dec[j].charcode;
  1234. memmove(p, p + e_len, (e - p - e_len));
  1235. e -= e_len;
  1236. goto done;
  1237. }
  1238. }
  1239. p++;
  1240. done:
  1241. if (p >= e) {
  1242. break;
  1243. }
  1244. } while ((p = memchr(p, '&', (e - p))));
  1245. new_len = e - new_str;
  1246. new_str[new_len] = '\0';
  1247. RETURN_STRINGL(new_str, new_len, 0);
  1248. }
  1249. /* }}} */
  1250. /* {{{ proto string html_entity_decode(string string [, int quote_style][, string charset])
  1251. Convert all HTML entities to their applicable characters */
  1252. PHP_FUNCTION(html_entity_decode)
  1253. {
  1254. char *str, *hint_charset = NULL;
  1255. int str_len, hint_charset_len = 0, len;
  1256. long quote_style = ENT_QUOTES;
  1257. char *replaced;
  1258. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", &str, &str_len,
  1259. &quote_style, &hint_charset, &hint_charset_len) == FAILURE) {
  1260. return;
  1261. }
  1262. replaced = php_unescape_html_entities(str, str_len, &len, 1, quote_style, hint_charset TSRMLS_CC);
  1263. if (replaced) {
  1264. RETURN_STRINGL(replaced, len, 0);
  1265. }
  1266. RETURN_FALSE;
  1267. }
  1268. /* }}} */
  1269. /* {{{ proto string htmlentities(string string [, int quote_style[, string charset[, bool double_encode]]])
  1270. Convert all applicable characters to HTML entities */
  1271. PHP_FUNCTION(htmlentities)
  1272. {
  1273. php_html_entities(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  1274. }
  1275. /* }}} */
  1276. /* {{{ proto array get_html_translation_table([int table [, int quote_style [, string charset_hint]]])
  1277. Returns the internal translation table used by htmlspecialchars and htmlentities */
  1278. PHP_FUNCTION(get_html_translation_table)
  1279. {
  1280. long which = HTML_SPECIALCHARS, quote_style = ENT_QUOTES;
  1281. unsigned int i;
  1282. int j;
  1283. unsigned char ind[5]; /* max # of 8-bit code units (4; for UTF-8) + 1 for \0 */
  1284. void *dummy;
  1285. char *charset_hint = NULL;
  1286. int charset_hint_len;
  1287. enum entity_charset charset;
  1288. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lls",
  1289. &which, &quote_style, &charset_hint, &charset_hint_len) == FAILURE) {
  1290. return;
  1291. }
  1292. charset = determine_charset(charset_hint TSRMLS_CC);
  1293. array_init(return_value);
  1294. switch (which) {
  1295. case HTML_ENTITIES:
  1296. for (j = 0; entity_map[j].charset != cs_terminator; j++) {
  1297. if (entity_map[j].charset != charset)
  1298. continue;
  1299. for (i = 0; i <= entity_map[j].endchar - entity_map[j].basechar; i++) {
  1300. char buffer[16];
  1301. unsigned k;
  1302. size_t written;
  1303. if (entity_map[j].table[i] == NULL)
  1304. continue;
  1305. k = i + entity_map[j].basechar;
  1306. switch (charset) {
  1307. case cs_utf_8:
  1308. written = php_utf32_utf8(ind, k);
  1309. ind[written] = '\0';
  1310. break;
  1311. case cs_big5:
  1312. case cs_gb2312:
  1313. case cs_big5hkscs:
  1314. case cs_sjis:
  1315. /* we have no mappings for these, but if we had... */
  1316. /* break through */
  1317. default: /* one byte */
  1318. written = 1;
  1319. ind[0] = (unsigned char)k;
  1320. ind[1] = '\0';
  1321. break;
  1322. }
  1323. snprintf(buffer, sizeof(buffer), "&%s;", entity_map[j].table[i]);
  1324. if (zend_hash_find(Z_ARRVAL_P(return_value), (const char*)ind, written+1, &dummy) == FAILURE) {
  1325. /* in case of the single quote, which is repeated, the first one wins,
  1326. * so don't replace the existint mapping */
  1327. add_assoc_string(return_value, (const char*)ind, buffer, 1);
  1328. }
  1329. }
  1330. }
  1331. /* break thru */
  1332. case HTML_SPECIALCHARS:
  1333. add_assoc_stringl(return_value, "&", "&amp;", sizeof("&amp;") - 1, 1);
  1334. for (j = 0; basic_entities[j].charcode != 0; j++) {
  1335. if (basic_entities[j].flags && (quote_style & basic_entities[j].flags) == 0)
  1336. continue;
  1337. ind[0] = (unsigned char)basic_entities[j].charcode;
  1338. ind[1] = '\0';
  1339. if (zend_hash_find(Z_ARRVAL_P(return_value), (const char*)ind, 2, &dummy) == FAILURE) {
  1340. add_assoc_stringl(return_value, ind, basic_entities[j].entity,
  1341. basic_entities[j].entitylen, 1);
  1342. }
  1343. }
  1344. break;
  1345. }
  1346. }
  1347. /* }}} */
  1348. /*
  1349. * Local variables:
  1350. * tab-width: 4
  1351. * c-basic-offset: 4
  1352. * End:
  1353. * vim600: sw=4 ts=4 fdm=marker
  1354. * vim<600: sw=4 ts=4
  1355. */