gettext.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 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. | Author: Alex Plotnick <alex@wgate.com> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. #include "config.h"
  20. #endif
  21. #include "php.h"
  22. #if HAVE_LIBINTL
  23. #include <stdio.h>
  24. #include "ext/standard/info.h"
  25. #include "php_gettext.h"
  26. /* {{{ arginfo */
  27. ZEND_BEGIN_ARG_INFO(arginfo_textdomain, 0)
  28. ZEND_ARG_INFO(0, domain)
  29. ZEND_END_ARG_INFO()
  30. ZEND_BEGIN_ARG_INFO(arginfo_gettext, 0)
  31. ZEND_ARG_INFO(0, msgid)
  32. ZEND_END_ARG_INFO()
  33. ZEND_BEGIN_ARG_INFO(arginfo_dgettext, 0)
  34. ZEND_ARG_INFO(0, domain_name)
  35. ZEND_ARG_INFO(0, msgid)
  36. ZEND_END_ARG_INFO()
  37. ZEND_BEGIN_ARG_INFO(arginfo_dcgettext, 0)
  38. ZEND_ARG_INFO(0, domain_name)
  39. ZEND_ARG_INFO(0, msgid)
  40. ZEND_ARG_INFO(0, category)
  41. ZEND_END_ARG_INFO()
  42. ZEND_BEGIN_ARG_INFO(arginfo_bindtextdomain, 0)
  43. ZEND_ARG_INFO(0, domain_name)
  44. ZEND_ARG_INFO(0, dir)
  45. ZEND_END_ARG_INFO()
  46. #if HAVE_NGETTEXT
  47. ZEND_BEGIN_ARG_INFO(arginfo_ngettext, 0)
  48. ZEND_ARG_INFO(0, msgid1)
  49. ZEND_ARG_INFO(0, msgid2)
  50. ZEND_ARG_INFO(0, count)
  51. ZEND_END_ARG_INFO()
  52. #endif
  53. #if HAVE_DNGETTEXT
  54. ZEND_BEGIN_ARG_INFO(arginfo_dngettext, 0)
  55. ZEND_ARG_INFO(0, domain)
  56. ZEND_ARG_INFO(0, msgid1)
  57. ZEND_ARG_INFO(0, msgid2)
  58. ZEND_ARG_INFO(0, count)
  59. ZEND_END_ARG_INFO()
  60. #endif
  61. #if HAVE_DCNGETTEXT
  62. ZEND_BEGIN_ARG_INFO(arginfo_dcngettext, 0)
  63. ZEND_ARG_INFO(0, domain)
  64. ZEND_ARG_INFO(0, msgid1)
  65. ZEND_ARG_INFO(0, msgid2)
  66. ZEND_ARG_INFO(0, count)
  67. ZEND_ARG_INFO(0, category)
  68. ZEND_END_ARG_INFO()
  69. #endif
  70. #if HAVE_BIND_TEXTDOMAIN_CODESET
  71. ZEND_BEGIN_ARG_INFO(arginfo_bind_textdomain_codeset, 0)
  72. ZEND_ARG_INFO(0, domain)
  73. ZEND_ARG_INFO(0, codeset)
  74. ZEND_END_ARG_INFO()
  75. #endif
  76. /* }}} */
  77. /* {{{ php_gettext_functions[]
  78. */
  79. static const zend_function_entry php_gettext_functions[] = {
  80. PHP_NAMED_FE(textdomain, zif_textdomain, arginfo_textdomain)
  81. PHP_NAMED_FE(gettext, zif_gettext, arginfo_gettext)
  82. /* Alias for gettext() */
  83. PHP_NAMED_FE(_, zif_gettext, arginfo_gettext)
  84. PHP_NAMED_FE(dgettext, zif_dgettext, arginfo_dgettext)
  85. PHP_NAMED_FE(dcgettext, zif_dcgettext, arginfo_dcgettext)
  86. PHP_NAMED_FE(bindtextdomain, zif_bindtextdomain, arginfo_bindtextdomain)
  87. #if HAVE_NGETTEXT
  88. PHP_NAMED_FE(ngettext, zif_ngettext, arginfo_ngettext)
  89. #endif
  90. #if HAVE_DNGETTEXT
  91. PHP_NAMED_FE(dngettext, zif_dngettext, arginfo_dngettext)
  92. #endif
  93. #if HAVE_DCNGETTEXT
  94. PHP_NAMED_FE(dcngettext, zif_dcngettext, arginfo_dcngettext)
  95. #endif
  96. #if HAVE_BIND_TEXTDOMAIN_CODESET
  97. PHP_NAMED_FE(bind_textdomain_codeset, zif_bind_textdomain_codeset, arginfo_bind_textdomain_codeset)
  98. #endif
  99. PHP_FE_END
  100. };
  101. /* }}} */
  102. #include <libintl.h>
  103. zend_module_entry php_gettext_module_entry = {
  104. STANDARD_MODULE_HEADER,
  105. "gettext",
  106. php_gettext_functions,
  107. NULL,
  108. NULL,
  109. NULL,
  110. NULL,
  111. PHP_MINFO(php_gettext),
  112. PHP_GETTEXT_VERSION,
  113. STANDARD_MODULE_PROPERTIES
  114. };
  115. #ifdef COMPILE_DL_GETTEXT
  116. ZEND_GET_MODULE(php_gettext)
  117. #endif
  118. #define PHP_GETTEXT_MAX_DOMAIN_LENGTH 1024
  119. #define PHP_GETTEXT_MAX_MSGID_LENGTH 4096
  120. #define PHP_GETTEXT_DOMAIN_LENGTH_CHECK(domain_len) \
  121. if (UNEXPECTED(domain_len > PHP_GETTEXT_MAX_DOMAIN_LENGTH)) { \
  122. php_error_docref(NULL, E_WARNING, "domain passed too long"); \
  123. RETURN_FALSE; \
  124. }
  125. #define PHP_GETTEXT_LENGTH_CHECK(check_name, check_len) \
  126. if (UNEXPECTED(check_len > PHP_GETTEXT_MAX_MSGID_LENGTH)) { \
  127. php_error_docref(NULL, E_WARNING, "%s passed too long", check_name); \
  128. RETURN_FALSE; \
  129. }
  130. PHP_MINFO_FUNCTION(php_gettext)
  131. {
  132. php_info_print_table_start();
  133. php_info_print_table_row(2, "GetText Support", "enabled");
  134. php_info_print_table_end();
  135. }
  136. /* {{{ proto string textdomain(string domain)
  137. Set the textdomain to "domain". Returns the current domain */
  138. PHP_NAMED_FUNCTION(zif_textdomain)
  139. {
  140. char *domain = NULL, *domain_name, *retval;
  141. size_t domain_len = 0;
  142. if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &domain, &domain_len) == FAILURE) {
  143. return;
  144. }
  145. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(domain_len)
  146. if (domain != NULL && strcmp(domain, "") && strcmp(domain, "0")) {
  147. domain_name = domain;
  148. } else {
  149. domain_name = NULL;
  150. }
  151. retval = textdomain(domain_name);
  152. RETURN_STRING(retval);
  153. }
  154. /* }}} */
  155. /* {{{ proto string gettext(string msgid)
  156. Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist */
  157. PHP_NAMED_FUNCTION(zif_gettext)
  158. {
  159. char *msgstr;
  160. zend_string *msgid;
  161. ZEND_PARSE_PARAMETERS_START(1, 1)
  162. Z_PARAM_STR(msgid)
  163. ZEND_PARSE_PARAMETERS_END();
  164. PHP_GETTEXT_LENGTH_CHECK("msgid", ZSTR_LEN(msgid))
  165. msgstr = gettext(ZSTR_VAL(msgid));
  166. if (msgstr != ZSTR_VAL(msgid)) {
  167. RETURN_STRING(msgstr);
  168. } else {
  169. RETURN_STR_COPY(msgid);
  170. }
  171. }
  172. /* }}} */
  173. /* {{{ proto string dgettext(string domain_name, string msgid)
  174. Return the translation of msgid for domain_name, or msgid unaltered if a translation does not exist */
  175. PHP_NAMED_FUNCTION(zif_dgettext)
  176. {
  177. char *msgstr;
  178. zend_string *domain, *msgid;
  179. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &domain, &msgid) == FAILURE) {
  180. return;
  181. }
  182. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(ZSTR_LEN(domain))
  183. PHP_GETTEXT_LENGTH_CHECK("msgid", ZSTR_LEN(msgid))
  184. msgstr = dgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid));
  185. if (msgstr != ZSTR_VAL(msgid)) {
  186. RETURN_STRING(msgstr);
  187. } else {
  188. RETURN_STR_COPY(msgid);
  189. }
  190. }
  191. /* }}} */
  192. /* {{{ proto string dcgettext(string domain_name, string msgid, int category)
  193. Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist */
  194. PHP_NAMED_FUNCTION(zif_dcgettext)
  195. {
  196. char *msgstr;
  197. zend_string *domain, *msgid;
  198. zend_long category;
  199. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl", &domain, &msgid, &category) == FAILURE) {
  200. return;
  201. }
  202. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(ZSTR_LEN(domain))
  203. PHP_GETTEXT_LENGTH_CHECK("msgid", ZSTR_LEN(msgid))
  204. msgstr = dcgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid), category);
  205. if (msgstr != ZSTR_VAL(msgid)) {
  206. RETURN_STRING(msgstr);
  207. } else {
  208. RETURN_STR_COPY(msgid);
  209. }
  210. }
  211. /* }}} */
  212. /* {{{ proto string bindtextdomain(string domain_name, string dir)
  213. Bind to the text domain domain_name, looking for translations in dir. Returns the current domain */
  214. PHP_NAMED_FUNCTION(zif_bindtextdomain)
  215. {
  216. char *domain, *dir;
  217. size_t domain_len, dir_len;
  218. char *retval, dir_name[MAXPATHLEN];
  219. if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &domain, &domain_len, &dir, &dir_len) == FAILURE) {
  220. return;
  221. }
  222. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(domain_len)
  223. if (domain[0] == '\0') {
  224. php_error(E_WARNING, "The first parameter of bindtextdomain must not be empty");
  225. RETURN_FALSE;
  226. }
  227. if (dir[0] != '\0' && strcmp(dir, "0")) {
  228. if (!VCWD_REALPATH(dir, dir_name)) {
  229. RETURN_FALSE;
  230. }
  231. } else if (!VCWD_GETCWD(dir_name, MAXPATHLEN)) {
  232. RETURN_FALSE;
  233. }
  234. retval = bindtextdomain(domain, dir_name);
  235. RETURN_STRING(retval);
  236. }
  237. /* }}} */
  238. #if HAVE_NGETTEXT
  239. /* {{{ proto string ngettext(string MSGID1, string MSGID2, int N)
  240. Plural version of gettext() */
  241. PHP_NAMED_FUNCTION(zif_ngettext)
  242. {
  243. char *msgid1, *msgid2, *msgstr;
  244. size_t msgid1_len, msgid2_len;
  245. zend_long count;
  246. if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
  247. return;
  248. }
  249. PHP_GETTEXT_LENGTH_CHECK("msgid1", msgid1_len)
  250. PHP_GETTEXT_LENGTH_CHECK("msgid2", msgid2_len)
  251. msgstr = ngettext(msgid1, msgid2, count);
  252. ZEND_ASSERT(msgstr);
  253. RETURN_STRING(msgstr);
  254. }
  255. /* }}} */
  256. #endif
  257. #if HAVE_DNGETTEXT
  258. /* {{{ proto string dngettext(string domain, string msgid1, string msgid2, int count)
  259. Plural version of dgettext() */
  260. PHP_NAMED_FUNCTION(zif_dngettext)
  261. {
  262. char *domain, *msgid1, *msgid2, *msgstr = NULL;
  263. size_t domain_len, msgid1_len, msgid2_len;
  264. zend_long count;
  265. if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssl", &domain, &domain_len,
  266. &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
  267. return;
  268. }
  269. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(domain_len)
  270. PHP_GETTEXT_LENGTH_CHECK("msgid1", msgid1_len)
  271. PHP_GETTEXT_LENGTH_CHECK("msgid2", msgid2_len)
  272. msgstr = dngettext(domain, msgid1, msgid2, count);
  273. ZEND_ASSERT(msgstr);
  274. RETURN_STRING(msgstr);
  275. }
  276. /* }}} */
  277. #endif
  278. #if HAVE_DCNGETTEXT
  279. /* {{{ proto string dcngettext(string domain, string msgid1, string msgid2, int n, int category)
  280. Plural version of dcgettext() */
  281. PHP_NAMED_FUNCTION(zif_dcngettext)
  282. {
  283. char *domain, *msgid1, *msgid2, *msgstr = NULL;
  284. size_t domain_len, msgid1_len, msgid2_len;
  285. zend_long count, category;
  286. RETVAL_FALSE;
  287. if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssll", &domain, &domain_len,
  288. &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count, &category) == FAILURE) {
  289. return;
  290. }
  291. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(domain_len)
  292. PHP_GETTEXT_LENGTH_CHECK("msgid1", msgid1_len)
  293. PHP_GETTEXT_LENGTH_CHECK("msgid2", msgid2_len)
  294. msgstr = dcngettext(domain, msgid1, msgid2, count, category);
  295. ZEND_ASSERT(msgstr);
  296. RETURN_STRING(msgstr);
  297. }
  298. /* }}} */
  299. #endif
  300. #if HAVE_BIND_TEXTDOMAIN_CODESET
  301. /* {{{ proto string bind_textdomain_codeset(string domain, string codeset)
  302. Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. */
  303. PHP_NAMED_FUNCTION(zif_bind_textdomain_codeset)
  304. {
  305. char *domain, *codeset, *retval = NULL;
  306. size_t domain_len, codeset_len;
  307. if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &domain, &domain_len, &codeset, &codeset_len) == FAILURE) {
  308. return;
  309. }
  310. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(domain_len)
  311. retval = bind_textdomain_codeset(domain, codeset);
  312. if (!retval) {
  313. RETURN_FALSE;
  314. }
  315. RETURN_STRING(retval);
  316. }
  317. /* }}} */
  318. #endif
  319. #endif /* HAVE_LIBINTL */
  320. /*
  321. * Local variables:
  322. * tab-width: 4
  323. * c-basic-offset: 4
  324. * End:
  325. * vim600: sw=4 ts=4 fdm=marker
  326. * vim<600: sw=4 ts=4
  327. */