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
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "php.h"
22
23 #ifdef HAVE_LIBINTL
24
25 #include <stdio.h>
26 #include "ext/standard/info.h"
27 #include "php_gettext.h"
28 #include "gettext_arginfo.h"
29
30 #include <libintl.h>
31
32 zend_module_entry php_gettext_module_entry = {
33 STANDARD_MODULE_HEADER,
34 "gettext",
35 ext_functions,
36 NULL,
37 NULL,
38 NULL,
39 NULL,
40 PHP_MINFO(php_gettext),
41 PHP_GETTEXT_VERSION,
42 STANDARD_MODULE_PROPERTIES
43 };
44
45 #ifdef COMPILE_DL_GETTEXT
46 ZEND_GET_MODULE(php_gettext)
47 #endif
48
49 #define PHP_GETTEXT_MAX_DOMAIN_LENGTH 1024
50 #define PHP_GETTEXT_MAX_MSGID_LENGTH 4096
51
52 #define PHP_GETTEXT_DOMAIN_LENGTH_CHECK(_arg_num, domain_len) \
53 if (UNEXPECTED(domain_len > PHP_GETTEXT_MAX_DOMAIN_LENGTH)) { \
54 zend_argument_value_error(_arg_num, "is too long"); \
55 RETURN_THROWS(); \
56 }
57
58 #define PHP_GETTEXT_LENGTH_CHECK(_arg_num, check_len) \
59 if (UNEXPECTED(check_len > PHP_GETTEXT_MAX_MSGID_LENGTH)) { \
60 zend_argument_value_error(_arg_num, "is too long"); \
61 RETURN_THROWS(); \
62 }
63
PHP_MINFO_FUNCTION(php_gettext)64 PHP_MINFO_FUNCTION(php_gettext)
65 {
66 php_info_print_table_start();
67 php_info_print_table_row(2, "GetText Support", "enabled");
68 php_info_print_table_end();
69 }
70
71 /* {{{ Set the textdomain to "domain". Returns the current domain */
PHP_FUNCTION(textdomain)72 PHP_FUNCTION(textdomain)
73 {
74 char *domain_name = NULL, *retval;
75 zend_string *domain = NULL;
76
77 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!", &domain) == FAILURE) {
78 RETURN_THROWS();
79 }
80
81 if (domain != NULL && ZSTR_LEN(domain) != 0 && !zend_string_equals_literal(domain, "0")) {
82 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
83 domain_name = ZSTR_VAL(domain);
84 }
85
86 retval = textdomain(domain_name);
87
88 RETURN_STRING(retval);
89 }
90 /* }}} */
91
92 /* {{{ Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist */
PHP_FUNCTION(gettext)93 PHP_FUNCTION(gettext)
94 {
95 char *msgstr;
96 zend_string *msgid;
97
98 ZEND_PARSE_PARAMETERS_START(1, 1)
99 Z_PARAM_STR(msgid)
100 ZEND_PARSE_PARAMETERS_END();
101
102 PHP_GETTEXT_LENGTH_CHECK(1, ZSTR_LEN(msgid))
103 msgstr = gettext(ZSTR_VAL(msgid));
104
105 if (msgstr != ZSTR_VAL(msgid)) {
106 RETURN_STRING(msgstr);
107 } else {
108 RETURN_STR_COPY(msgid);
109 }
110 }
111 /* }}} */
112
113 /* {{{ Return the translation of msgid for domain_name, or msgid unaltered if a translation does not exist */
PHP_FUNCTION(dgettext)114 PHP_FUNCTION(dgettext)
115 {
116 char *msgstr;
117 zend_string *domain, *msgid;
118
119 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &domain, &msgid) == FAILURE) {
120 RETURN_THROWS();
121 }
122
123 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
124 PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid))
125
126 msgstr = dgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid));
127
128 if (msgstr != ZSTR_VAL(msgid)) {
129 RETURN_STRING(msgstr);
130 } else {
131 RETURN_STR_COPY(msgid);
132 }
133 }
134 /* }}} */
135
136 /* {{{ Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist */
PHP_FUNCTION(dcgettext)137 PHP_FUNCTION(dcgettext)
138 {
139 char *msgstr;
140 zend_string *domain, *msgid;
141 zend_long category;
142
143 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl", &domain, &msgid, &category) == FAILURE) {
144 RETURN_THROWS();
145 }
146
147 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
148 PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid))
149
150 msgstr = dcgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid), category);
151
152 if (msgstr != ZSTR_VAL(msgid)) {
153 RETURN_STRING(msgstr);
154 } else {
155 RETURN_STR_COPY(msgid);
156 }
157 }
158 /* }}} */
159
160 /* {{{ Bind to the text domain domain_name, looking for translations in dir. Returns the current domain */
PHP_FUNCTION(bindtextdomain)161 PHP_FUNCTION(bindtextdomain)
162 {
163 char *domain;
164 size_t domain_len;
165 zend_string *dir = NULL;
166 char *retval, dir_name[MAXPATHLEN];
167
168 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS!", &domain, &domain_len, &dir) == FAILURE) {
169 RETURN_THROWS();
170 }
171
172 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
173
174 if (domain[0] == '\0') {
175 zend_argument_value_error(1, "cannot be empty");
176 RETURN_THROWS();
177 }
178
179 if (dir == NULL) {
180 RETURN_STRING(bindtextdomain(domain, NULL));
181 }
182
183 if (ZSTR_LEN(dir) != 0 && !zend_string_equals_literal(dir, "0")) {
184 if (!VCWD_REALPATH(ZSTR_VAL(dir), dir_name)) {
185 RETURN_FALSE;
186 }
187 } else if (!VCWD_GETCWD(dir_name, MAXPATHLEN)) {
188 RETURN_FALSE;
189 }
190
191 retval = bindtextdomain(domain, dir_name);
192
193 RETURN_STRING(retval);
194 }
195 /* }}} */
196
197 #ifdef HAVE_NGETTEXT
198 /* {{{ Plural version of gettext() */
PHP_FUNCTION(ngettext)199 PHP_FUNCTION(ngettext)
200 {
201 char *msgid1, *msgid2, *msgstr;
202 size_t msgid1_len, msgid2_len;
203 zend_long count;
204
205 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
206 RETURN_THROWS();
207 }
208
209 PHP_GETTEXT_LENGTH_CHECK(1, msgid1_len)
210 PHP_GETTEXT_LENGTH_CHECK(2, msgid2_len)
211
212 msgstr = ngettext(msgid1, msgid2, count);
213
214 ZEND_ASSERT(msgstr);
215 RETURN_STRING(msgstr);
216 }
217 /* }}} */
218 #endif
219
220 #ifdef HAVE_DNGETTEXT
221 /* {{{ Plural version of dgettext() */
PHP_FUNCTION(dngettext)222 PHP_FUNCTION(dngettext)
223 {
224 char *domain, *msgid1, *msgid2, *msgstr = NULL;
225 size_t domain_len, msgid1_len, msgid2_len;
226 zend_long count;
227
228 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssl", &domain, &domain_len,
229 &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
230 RETURN_THROWS();
231 }
232
233 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
234 PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len)
235 PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len)
236
237 msgstr = dngettext(domain, msgid1, msgid2, count);
238
239 ZEND_ASSERT(msgstr);
240 RETURN_STRING(msgstr);
241 }
242 /* }}} */
243 #endif
244
245 #ifdef HAVE_DCNGETTEXT
246 /* {{{ Plural version of dcgettext() */
PHP_FUNCTION(dcngettext)247 PHP_FUNCTION(dcngettext)
248 {
249 char *domain, *msgid1, *msgid2, *msgstr = NULL;
250 size_t domain_len, msgid1_len, msgid2_len;
251 zend_long count, category;
252
253 RETVAL_FALSE;
254
255 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssll", &domain, &domain_len,
256 &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count, &category) == FAILURE) {
257 RETURN_THROWS();
258 }
259
260 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
261 PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len)
262 PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len)
263
264 msgstr = dcngettext(domain, msgid1, msgid2, count, category);
265
266 ZEND_ASSERT(msgstr);
267 RETURN_STRING(msgstr);
268 }
269 /* }}} */
270 #endif
271
272 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
273
274 /* {{{ Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. */
PHP_FUNCTION(bind_textdomain_codeset)275 PHP_FUNCTION(bind_textdomain_codeset)
276 {
277 char *domain, *codeset = NULL, *retval = NULL;
278 size_t domain_len, codeset_len;
279
280 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss!", &domain, &domain_len, &codeset, &codeset_len) == FAILURE) {
281 RETURN_THROWS();
282 }
283
284 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
285
286 retval = bind_textdomain_codeset(domain, codeset);
287
288 if (!retval) {
289 RETURN_FALSE;
290 }
291 RETURN_STRING(retval);
292 }
293 /* }}} */
294 #endif
295
296
297 #endif /* HAVE_LIBINTL */
298