gettext.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Alex Plotnick <alex@wgate.com> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #ifdef HAVE_CONFIG_H
  17. #include "config.h"
  18. #endif
  19. #include "php.h"
  20. #ifdef HAVE_LIBINTL
  21. #include <stdio.h>
  22. #include "ext/standard/info.h"
  23. #include "php_gettext.h"
  24. #include "gettext_arginfo.h"
  25. #include <libintl.h>
  26. zend_module_entry php_gettext_module_entry = {
  27. STANDARD_MODULE_HEADER,
  28. "gettext",
  29. ext_functions,
  30. NULL,
  31. NULL,
  32. NULL,
  33. NULL,
  34. PHP_MINFO(php_gettext),
  35. PHP_GETTEXT_VERSION,
  36. STANDARD_MODULE_PROPERTIES
  37. };
  38. #ifdef COMPILE_DL_GETTEXT
  39. ZEND_GET_MODULE(php_gettext)
  40. #endif
  41. #define PHP_GETTEXT_MAX_DOMAIN_LENGTH 1024
  42. #define PHP_GETTEXT_MAX_MSGID_LENGTH 4096
  43. #define PHP_GETTEXT_DOMAIN_LENGTH_CHECK(_arg_num, domain_len) \
  44. if (UNEXPECTED(domain_len > PHP_GETTEXT_MAX_DOMAIN_LENGTH)) { \
  45. zend_argument_value_error(_arg_num, "is too long"); \
  46. RETURN_THROWS(); \
  47. }
  48. #define PHP_GETTEXT_LENGTH_CHECK(_arg_num, check_len) \
  49. if (UNEXPECTED(check_len > PHP_GETTEXT_MAX_MSGID_LENGTH)) { \
  50. zend_argument_value_error(_arg_num, "is too long"); \
  51. RETURN_THROWS(); \
  52. }
  53. PHP_MINFO_FUNCTION(php_gettext)
  54. {
  55. php_info_print_table_start();
  56. php_info_print_table_row(2, "GetText Support", "enabled");
  57. php_info_print_table_end();
  58. }
  59. /* {{{ Set the textdomain to "domain". Returns the current domain */
  60. PHP_FUNCTION(textdomain)
  61. {
  62. char *domain_name = NULL, *retval;
  63. zend_string *domain = NULL;
  64. if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!", &domain) == FAILURE) {
  65. RETURN_THROWS();
  66. }
  67. if (domain != NULL && ZSTR_LEN(domain) != 0 && !zend_string_equals_literal(domain, "0")) {
  68. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
  69. domain_name = ZSTR_VAL(domain);
  70. }
  71. retval = textdomain(domain_name);
  72. RETURN_STRING(retval);
  73. }
  74. /* }}} */
  75. /* {{{ Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist */
  76. PHP_FUNCTION(gettext)
  77. {
  78. char *msgstr;
  79. zend_string *msgid;
  80. ZEND_PARSE_PARAMETERS_START(1, 1)
  81. Z_PARAM_STR(msgid)
  82. ZEND_PARSE_PARAMETERS_END();
  83. PHP_GETTEXT_LENGTH_CHECK(1, ZSTR_LEN(msgid))
  84. msgstr = gettext(ZSTR_VAL(msgid));
  85. if (msgstr != ZSTR_VAL(msgid)) {
  86. RETURN_STRING(msgstr);
  87. } else {
  88. RETURN_STR_COPY(msgid);
  89. }
  90. }
  91. /* }}} */
  92. /* {{{ Return the translation of msgid for domain_name, or msgid unaltered if a translation does not exist */
  93. PHP_FUNCTION(dgettext)
  94. {
  95. char *msgstr;
  96. zend_string *domain, *msgid;
  97. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &domain, &msgid) == FAILURE) {
  98. RETURN_THROWS();
  99. }
  100. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
  101. PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid))
  102. msgstr = dgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid));
  103. if (msgstr != ZSTR_VAL(msgid)) {
  104. RETURN_STRING(msgstr);
  105. } else {
  106. RETURN_STR_COPY(msgid);
  107. }
  108. }
  109. /* }}} */
  110. /* {{{ Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist */
  111. PHP_FUNCTION(dcgettext)
  112. {
  113. char *msgstr;
  114. zend_string *domain, *msgid;
  115. zend_long category;
  116. if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl", &domain, &msgid, &category) == FAILURE) {
  117. RETURN_THROWS();
  118. }
  119. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
  120. PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid))
  121. msgstr = dcgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid), category);
  122. if (msgstr != ZSTR_VAL(msgid)) {
  123. RETURN_STRING(msgstr);
  124. } else {
  125. RETURN_STR_COPY(msgid);
  126. }
  127. }
  128. /* }}} */
  129. /* {{{ Bind to the text domain domain_name, looking for translations in dir. Returns the current domain */
  130. PHP_FUNCTION(bindtextdomain)
  131. {
  132. char *domain;
  133. size_t domain_len;
  134. zend_string *dir = NULL;
  135. char *retval, dir_name[MAXPATHLEN];
  136. if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS!", &domain, &domain_len, &dir) == FAILURE) {
  137. RETURN_THROWS();
  138. }
  139. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
  140. if (domain[0] == '\0') {
  141. zend_argument_value_error(1, "cannot be empty");
  142. RETURN_THROWS();
  143. }
  144. if (dir == NULL) {
  145. RETURN_STRING(bindtextdomain(domain, NULL));
  146. }
  147. if (ZSTR_LEN(dir) != 0 && !zend_string_equals_literal(dir, "0")) {
  148. if (!VCWD_REALPATH(ZSTR_VAL(dir), dir_name)) {
  149. RETURN_FALSE;
  150. }
  151. } else if (!VCWD_GETCWD(dir_name, MAXPATHLEN)) {
  152. RETURN_FALSE;
  153. }
  154. retval = bindtextdomain(domain, dir_name);
  155. RETURN_STRING(retval);
  156. }
  157. /* }}} */
  158. #ifdef HAVE_NGETTEXT
  159. /* {{{ Plural version of gettext() */
  160. PHP_FUNCTION(ngettext)
  161. {
  162. char *msgid1, *msgid2, *msgstr;
  163. size_t msgid1_len, msgid2_len;
  164. zend_long count;
  165. if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
  166. RETURN_THROWS();
  167. }
  168. PHP_GETTEXT_LENGTH_CHECK(1, msgid1_len)
  169. PHP_GETTEXT_LENGTH_CHECK(2, msgid2_len)
  170. msgstr = ngettext(msgid1, msgid2, count);
  171. ZEND_ASSERT(msgstr);
  172. RETURN_STRING(msgstr);
  173. }
  174. /* }}} */
  175. #endif
  176. #ifdef HAVE_DNGETTEXT
  177. /* {{{ Plural version of dgettext() */
  178. PHP_FUNCTION(dngettext)
  179. {
  180. char *domain, *msgid1, *msgid2, *msgstr = NULL;
  181. size_t domain_len, msgid1_len, msgid2_len;
  182. zend_long count;
  183. if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssl", &domain, &domain_len,
  184. &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
  185. RETURN_THROWS();
  186. }
  187. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
  188. PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len)
  189. PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len)
  190. msgstr = dngettext(domain, msgid1, msgid2, count);
  191. ZEND_ASSERT(msgstr);
  192. RETURN_STRING(msgstr);
  193. }
  194. /* }}} */
  195. #endif
  196. #ifdef HAVE_DCNGETTEXT
  197. /* {{{ Plural version of dcgettext() */
  198. PHP_FUNCTION(dcngettext)
  199. {
  200. char *domain, *msgid1, *msgid2, *msgstr = NULL;
  201. size_t domain_len, msgid1_len, msgid2_len;
  202. zend_long count, category;
  203. RETVAL_FALSE;
  204. if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssll", &domain, &domain_len,
  205. &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count, &category) == FAILURE) {
  206. RETURN_THROWS();
  207. }
  208. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
  209. PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len)
  210. PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len)
  211. msgstr = dcngettext(domain, msgid1, msgid2, count, category);
  212. ZEND_ASSERT(msgstr);
  213. RETURN_STRING(msgstr);
  214. }
  215. /* }}} */
  216. #endif
  217. #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
  218. /* {{{ Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. */
  219. PHP_FUNCTION(bind_textdomain_codeset)
  220. {
  221. char *domain, *codeset = NULL, *retval = NULL;
  222. size_t domain_len, codeset_len;
  223. if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss!", &domain, &domain_len, &codeset, &codeset_len) == FAILURE) {
  224. RETURN_THROWS();
  225. }
  226. PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
  227. retval = bind_textdomain_codeset(domain, codeset);
  228. if (!retval) {
  229. RETURN_FALSE;
  230. }
  231. RETURN_STRING(retval);
  232. }
  233. /* }}} */
  234. #endif
  235. #endif /* HAVE_LIBINTL */