xref: /PHP-8.0/ext/gettext/gettext.c (revision d319098b)
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