xref: /PHP-7.2/ext/gettext/gettext.c (revision 60a69dae)
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