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