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 | http://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 = NULL, *domain_name, *retval;
75 size_t domain_len = 0;
76
77 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!", &domain, &domain_len) == FAILURE) {
78 RETURN_THROWS();
79 }
80
81 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
82
83 if (domain != NULL && strcmp(domain, "") && strcmp(domain, "0")) {
84 domain_name = domain;
85 } else {
86 domain_name = NULL;
87 }
88
89 retval = textdomain(domain_name);
90
91 RETURN_STRING(retval);
92 }
93 /* }}} */
94
95 /* {{{ Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist */
PHP_FUNCTION(gettext)96 PHP_FUNCTION(gettext)
97 {
98 char *msgstr;
99 zend_string *msgid;
100
101 ZEND_PARSE_PARAMETERS_START(1, 1)
102 Z_PARAM_STR(msgid)
103 ZEND_PARSE_PARAMETERS_END();
104
105 PHP_GETTEXT_LENGTH_CHECK(1, ZSTR_LEN(msgid))
106 msgstr = gettext(ZSTR_VAL(msgid));
107
108 if (msgstr != ZSTR_VAL(msgid)) {
109 RETURN_STRING(msgstr);
110 } else {
111 RETURN_STR_COPY(msgid);
112 }
113 }
114 /* }}} */
115
116 /* {{{ Return the translation of msgid for domain_name, or msgid unaltered if a translation does not exist */
PHP_FUNCTION(dgettext)117 PHP_FUNCTION(dgettext)
118 {
119 char *msgstr;
120 zend_string *domain, *msgid;
121
122 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &domain, &msgid) == FAILURE) {
123 RETURN_THROWS();
124 }
125
126 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
127 PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid))
128
129 msgstr = dgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid));
130
131 if (msgstr != ZSTR_VAL(msgid)) {
132 RETURN_STRING(msgstr);
133 } else {
134 RETURN_STR_COPY(msgid);
135 }
136 }
137 /* }}} */
138
139 /* {{{ Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist */
PHP_FUNCTION(dcgettext)140 PHP_FUNCTION(dcgettext)
141 {
142 char *msgstr;
143 zend_string *domain, *msgid;
144 zend_long category;
145
146 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl", &domain, &msgid, &category) == FAILURE) {
147 RETURN_THROWS();
148 }
149
150 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain))
151 PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid))
152
153 msgstr = dcgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid), category);
154
155 if (msgstr != ZSTR_VAL(msgid)) {
156 RETURN_STRING(msgstr);
157 } else {
158 RETURN_STR_COPY(msgid);
159 }
160 }
161 /* }}} */
162
163 /* {{{ Bind to the text domain domain_name, looking for translations in dir. Returns the current domain */
PHP_FUNCTION(bindtextdomain)164 PHP_FUNCTION(bindtextdomain)
165 {
166 char *domain, *dir = NULL;
167 size_t domain_len, dir_len;
168 char *retval, dir_name[MAXPATHLEN];
169
170 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss!", &domain, &domain_len, &dir, &dir_len) == FAILURE) {
171 RETURN_THROWS();
172 }
173
174 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
175
176 if (domain[0] == '\0') {
177 zend_argument_value_error(1, "cannot be empty");
178 RETURN_THROWS();
179 }
180
181 if (dir == NULL) {
182 RETURN_STRING(bindtextdomain(domain, NULL));
183 }
184
185 if (dir[0] != '\0' && strcmp(dir, "0")) {
186 if (!VCWD_REALPATH(dir, dir_name)) {
187 RETURN_FALSE;
188 }
189 } else if (!VCWD_GETCWD(dir_name, MAXPATHLEN)) {
190 RETURN_FALSE;
191 }
192
193 retval = bindtextdomain(domain, dir_name);
194
195 RETURN_STRING(retval);
196 }
197 /* }}} */
198
199 #ifdef HAVE_NGETTEXT
200 /* {{{ Plural version of gettext() */
PHP_FUNCTION(ngettext)201 PHP_FUNCTION(ngettext)
202 {
203 char *msgid1, *msgid2, *msgstr;
204 size_t msgid1_len, msgid2_len;
205 zend_long count;
206
207 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
208 RETURN_THROWS();
209 }
210
211 PHP_GETTEXT_LENGTH_CHECK(1, msgid1_len)
212 PHP_GETTEXT_LENGTH_CHECK(2, msgid2_len)
213
214 msgstr = ngettext(msgid1, msgid2, count);
215
216 ZEND_ASSERT(msgstr);
217 RETURN_STRING(msgstr);
218 }
219 /* }}} */
220 #endif
221
222 #ifdef HAVE_DNGETTEXT
223 /* {{{ Plural version of dgettext() */
PHP_FUNCTION(dngettext)224 PHP_FUNCTION(dngettext)
225 {
226 char *domain, *msgid1, *msgid2, *msgstr = NULL;
227 size_t domain_len, msgid1_len, msgid2_len;
228 zend_long count;
229
230 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssl", &domain, &domain_len,
231 &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) {
232 RETURN_THROWS();
233 }
234
235 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
236 PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len)
237 PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len)
238
239 msgstr = dngettext(domain, msgid1, msgid2, count);
240
241 ZEND_ASSERT(msgstr);
242 RETURN_STRING(msgstr);
243 }
244 /* }}} */
245 #endif
246
247 #ifdef HAVE_DCNGETTEXT
248 /* {{{ Plural version of dcgettext() */
PHP_FUNCTION(dcngettext)249 PHP_FUNCTION(dcngettext)
250 {
251 char *domain, *msgid1, *msgid2, *msgstr = NULL;
252 size_t domain_len, msgid1_len, msgid2_len;
253 zend_long count, category;
254
255 RETVAL_FALSE;
256
257 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssll", &domain, &domain_len,
258 &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count, &category) == FAILURE) {
259 RETURN_THROWS();
260 }
261
262 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
263 PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len)
264 PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len)
265
266 msgstr = dcngettext(domain, msgid1, msgid2, count, category);
267
268 ZEND_ASSERT(msgstr);
269 RETURN_STRING(msgstr);
270 }
271 /* }}} */
272 #endif
273
274 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
275
276 /* {{{ Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. */
PHP_FUNCTION(bind_textdomain_codeset)277 PHP_FUNCTION(bind_textdomain_codeset)
278 {
279 char *domain, *codeset = NULL, *retval = NULL;
280 size_t domain_len, codeset_len;
281
282 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss!", &domain, &domain_len, &codeset, &codeset_len) == FAILURE) {
283 RETURN_THROWS();
284 }
285
286 PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len)
287
288 retval = bind_textdomain_codeset(domain, codeset);
289
290 if (!retval) {
291 RETURN_FALSE;
292 }
293 RETURN_STRING(retval);
294 }
295 /* }}} */
296 #endif
297
298
299 #endif /* HAVE_LIBINTL */
300