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