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