xref: /PHP-8.0/ext/enchant/enchant.c (revision b63ea104)
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: Pierre-Alain Joye <paj@pearfr.org>                           |
14   |         Ilia Alshanetsky <ilia@prohost.org>                          |
15   +----------------------------------------------------------------------+
16 */
17 
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include "php.h"
23 #include "php_ini.h"
24 #include "ext/standard/info.h"
25 #include "Zend/zend_interfaces.h"
26 #include "Zend/zend_exceptions.h"
27 #include "../spl/spl_exceptions.h"
28 #include <enchant.h>
29 #include "php_enchant.h"
30 #include "enchant_arginfo.h"
31 
32 typedef struct _broker_struct {
33 	EnchantBroker  *pbroker;
34 	int             nb_dict;
35 	zend_object     std;
36 } enchant_broker;
37 
38 typedef struct _dict_struct {
39 	EnchantDict	   *pdict;
40 	zval            zbroker;
41 	zend_object     std;
42 } enchant_dict;
43 
44 zend_class_entry *enchant_broker_ce;
45 static zend_object_handlers enchant_broker_handlers;
46 
enchant_broker_from_obj(zend_object * obj)47 static inline enchant_broker *enchant_broker_from_obj(zend_object *obj) {
48 	return (enchant_broker *)((char *)(obj) - XtOffsetOf(enchant_broker, std));
49 }
50 
51 #define Z_ENCHANT_BROKER_P(zv) enchant_broker_from_obj(Z_OBJ_P(zv))
52 
enchant_broker_create_object(zend_class_entry * class_type)53 static zend_object *enchant_broker_create_object(zend_class_entry *class_type) {
54 	enchant_broker *intern = zend_object_alloc(sizeof(enchant_broker), class_type);
55 
56 	zend_object_std_init(&intern->std, class_type);
57 	object_properties_init(&intern->std, class_type);
58 	intern->std.handlers = &enchant_broker_handlers;
59 
60 	return &intern->std;
61 }
62 
63 zend_class_entry *enchant_dict_ce;
64 static zend_object_handlers enchant_dict_handlers;
65 
enchant_dict_from_obj(zend_object * obj)66 static inline enchant_dict *enchant_dict_from_obj(zend_object *obj) {
67 	return (enchant_dict *)((char *)(obj) - XtOffsetOf(enchant_dict, std));
68 }
69 
70 #define Z_ENCHANT_DICT_P(zv) enchant_dict_from_obj(Z_OBJ_P(zv))
71 
enchant_dict_create_object(zend_class_entry * class_type)72 static zend_object *enchant_dict_create_object(zend_class_entry *class_type) {
73 	enchant_dict *intern = zend_object_alloc(sizeof(enchant_dict), class_type);
74 
75 	zend_object_std_init(&intern->std, class_type);
76 	object_properties_init(&intern->std, class_type);
77 	intern->std.handlers = &enchant_dict_handlers;
78 
79 	return &intern->std;
80 }
81 
82 #define PHP_ENCHANT_MYSPELL 1
83 #define PHP_ENCHANT_ISPELL 2
84 
85 /* {{{ enchant_module_entry */
86 zend_module_entry enchant_module_entry = {
87 	STANDARD_MODULE_HEADER,
88 	"enchant",
89 	ext_functions,
90 	PHP_MINIT(enchant),
91 	PHP_MSHUTDOWN(enchant),
92 	NULL,	/* Replace with NULL if there's nothing to do at request start */
93 	NULL,	/* Replace with NULL if there's nothing to do at request end */
94 	PHP_MINFO(enchant),
95 	PHP_ENCHANT_VERSION,
96 	STANDARD_MODULE_PROPERTIES
97 };
98 /* }}} */
99 
100 #ifdef COMPILE_DL_ENCHANT
ZEND_GET_MODULE(enchant)101 ZEND_GET_MODULE(enchant)
102 #endif
103 
104 static void
105 enumerate_providers_fn (const char * const name,
106                         const char * const desc,
107                         const char * const file,
108                         void * ud) /* {{{ */
109 {
110 	zval *zdesc = (zval *) ud;
111 	zval tmp_array;
112 
113 	array_init(&tmp_array);
114 
115 	add_assoc_string(&tmp_array, "name", (char *)name);
116 	add_assoc_string(&tmp_array, "desc", (char *)desc);
117 	add_assoc_string(&tmp_array, "file", (char *)file);
118 	add_next_index_zval(zdesc, &tmp_array);
119 }
120 /* }}} */
121 
122 static void
describe_dict_fn(const char * const lang,const char * const name,const char * const desc,const char * const file,void * ud)123 describe_dict_fn (const char * const lang,
124                   const char * const name,
125                   const char * const desc,
126                   const char * const file,
127                   void * ud) /* {{{ */
128 {
129 	zval *zdesc = (zval *) ud;
130 	array_init(zdesc);
131 	add_assoc_string(zdesc, "lang", (char *)lang);
132 	add_assoc_string(zdesc, "name", (char *)name);
133 	add_assoc_string(zdesc, "desc", (char *)desc);
134 	add_assoc_string(zdesc, "file", (char *)file);
135 }
136 /* }}} */
137 
php_enchant_list_dicts_fn(const char * const lang_tag,const char * const provider_name,const char * const provider_desc,const char * const provider_file,void * ud)138 static void php_enchant_list_dicts_fn( const char * const lang_tag,
139 	   	const char * const provider_name, const char * const provider_desc,
140 		const char * const provider_file, void * ud) /* {{{ */
141 {
142 	zval *zdesc = (zval *) ud;
143 	zval tmp_array;
144 
145 	array_init(&tmp_array);
146 	add_assoc_string(&tmp_array, "lang_tag", (char *)lang_tag);
147 	add_assoc_string(&tmp_array, "provider_name", (char *)provider_name);
148 	add_assoc_string(&tmp_array, "provider_desc", (char *)provider_desc);
149 	add_assoc_string(&tmp_array, "provider_file", (char *)provider_file);
150 	add_next_index_zval(zdesc, &tmp_array);
151 
152 }
153 /* }}} */
154 
php_enchant_broker_free(zend_object * object)155 static void php_enchant_broker_free(zend_object *object) /* {{{ */
156 {
157 	enchant_broker *broker = enchant_broker_from_obj(object);
158 
159 	if (broker->pbroker) {  /* may have been freed by enchant_broker_free */
160 		enchant_broker_free(broker->pbroker);
161 		broker->pbroker = NULL;
162 	}
163 	zend_object_std_dtor(object);
164 }
165 /* }}} */
166 
php_enchant_dict_free(zend_object * object)167 static void php_enchant_dict_free(zend_object *object) /* {{{ */
168 
169 {
170 	enchant_dict *dict = enchant_dict_from_obj(object);
171 
172 	if (dict->pdict) { /* may have been freed by enchant_broker_free_dict */
173 		enchant_broker *broker = Z_ENCHANT_BROKER_P(&dict->zbroker);
174 
175 		if (broker && broker->pbroker) {
176 			enchant_broker_free_dict(broker->pbroker, dict->pdict);
177 			broker->nb_dict--;
178 			zval_ptr_dtor(&dict->zbroker);
179 		}
180 		dict->pdict = NULL;
181 	}
182 	zend_object_std_dtor(object);
183 }
184 /* }}} */
185 
186 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(enchant)187 PHP_MINIT_FUNCTION(enchant)
188 {
189 	zend_class_entry bce, dce;
190 
191 	INIT_CLASS_ENTRY(bce, "EnchantBroker", class_EnchantBroker_methods);
192 	enchant_broker_ce = zend_register_internal_class(&bce);
193 	enchant_broker_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
194 	enchant_broker_ce->create_object = enchant_broker_create_object;
195 	enchant_broker_ce->serialize = zend_class_serialize_deny;
196 	enchant_broker_ce->unserialize = zend_class_unserialize_deny;
197 
198 	memcpy(&enchant_broker_handlers, &std_object_handlers, sizeof(zend_object_handlers));
199 	enchant_broker_handlers.offset = XtOffsetOf(enchant_broker, std);
200 	enchant_broker_handlers.free_obj = php_enchant_broker_free;
201 	enchant_broker_handlers.clone_obj = NULL;
202 	enchant_broker_handlers.compare = zend_objects_not_comparable;
203 
204 	INIT_CLASS_ENTRY(dce, "EnchantDictionary", class_EnchantDictionary_methods);
205 	enchant_dict_ce = zend_register_internal_class(&dce);
206 	enchant_dict_ce->ce_flags |= ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES;
207 	enchant_dict_ce->create_object = enchant_dict_create_object;
208 	enchant_dict_ce->serialize = zend_class_serialize_deny;
209 	enchant_dict_ce->unserialize = zend_class_unserialize_deny;
210 
211 	memcpy(&enchant_dict_handlers, &std_object_handlers, sizeof(zend_object_handlers));
212 	enchant_dict_handlers.offset = XtOffsetOf(enchant_dict, std);
213 	enchant_dict_handlers.free_obj = php_enchant_dict_free;
214 	enchant_dict_handlers.clone_obj = NULL;
215 	enchant_dict_handlers.compare = zend_objects_not_comparable;
216 
217 	REGISTER_LONG_CONSTANT("ENCHANT_MYSPELL", PHP_ENCHANT_MYSPELL, CONST_CS | CONST_PERSISTENT | CONST_DEPRECATED);
218 	REGISTER_LONG_CONSTANT("ENCHANT_ISPELL",  PHP_ENCHANT_ISPELL,  CONST_CS | CONST_PERSISTENT | CONST_DEPRECATED);
219 #ifdef HAVE_ENCHANT_GET_VERSION
220 	REGISTER_STRING_CONSTANT("LIBENCHANT_VERSION", enchant_get_version(), CONST_CS | CONST_PERSISTENT);
221 #endif
222 	return SUCCESS;
223 }
224 /* }}} */
225 
226 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(enchant)227 PHP_MSHUTDOWN_FUNCTION(enchant)
228 {
229 	return SUCCESS;
230 }
231 /* }}} */
232 
__enumerate_providers_fn(const char * const name,const char * const desc,const char * const file,void * ud)233 static void __enumerate_providers_fn (const char * const name,
234                         const char * const desc,
235                         const char * const file,
236                         void * ud) /* {{{ */
237 {
238 	php_info_print_table_row(3, name, desc, file);
239 }
240 /* }}} */
241 
242 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(enchant)243 PHP_MINFO_FUNCTION(enchant)
244 {
245 	EnchantBroker *pbroker;
246 
247 	pbroker = enchant_broker_init();
248 	php_info_print_table_start();
249 	php_info_print_table_row(2, "enchant support", "enabled");
250 #ifdef HAVE_ENCHANT_GET_VERSION
251 	php_info_print_table_row(2, "Libenchant Version", enchant_get_version());
252 #elif defined(HAVE_ENCHANT_BROKER_SET_PARAM)
253 	php_info_print_table_row(2, "Libenchant Version", "1.5.x");
254 #endif
255 	php_info_print_table_end();
256 
257 	php_info_print_table_start();
258 	enchant_broker_describe(pbroker, __enumerate_providers_fn, NULL);
259 	php_info_print_table_end();
260 	enchant_broker_free(pbroker);
261 }
262 /* }}} */
263 
264 #define PHP_ENCHANT_GET_BROKER	\
265 	pbroker = Z_ENCHANT_BROKER_P(broker); \
266 	if (!pbroker->pbroker) {	\
267 		zend_value_error("Invalid or uninitialized EnchantBroker object"); \
268 		RETURN_THROWS(); \
269 	}
270 
271 #define PHP_ENCHANT_GET_DICT	\
272 	pdict = Z_ENCHANT_DICT_P(dict); \
273 	if (!pdict->pdict) {	\
274 		zend_value_error("Invalid or uninitialized EnchantDictionary object"); \
275 		RETURN_THROWS(); \
276 	}
277 
278 /* {{{ create a new broker object capable of requesting */
PHP_FUNCTION(enchant_broker_init)279 PHP_FUNCTION(enchant_broker_init)
280 {
281 	enchant_broker *broker;
282 	EnchantBroker *pbroker;
283 
284 	if (zend_parse_parameters_none() == FAILURE) {
285 		RETURN_THROWS();
286 	}
287 
288 	pbroker = enchant_broker_init();
289 	if (pbroker) {
290 		object_init_ex(return_value, enchant_broker_ce);
291 		broker = Z_ENCHANT_BROKER_P(return_value);
292 		broker->pbroker = pbroker;
293 		broker->nb_dict = 0;
294 	} else {
295 		RETURN_FALSE;
296 	}
297 }
298 /* }}} */
299 
300 /* {{{ Destroys the broker object and its dictionaries */
PHP_FUNCTION(enchant_broker_free)301 PHP_FUNCTION(enchant_broker_free)
302 {
303 	zval *broker;
304 	enchant_broker *pbroker;
305 
306 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &broker, enchant_broker_ce) == FAILURE) {
307 		RETURN_THROWS();
308 	}
309 	PHP_ENCHANT_GET_BROKER;
310 
311 	if (pbroker->nb_dict > 0) {
312 		zend_throw_error(NULL, "Cannot free EnchantBroker object with open EnchantDictionary objects");
313 		RETURN_THROWS();
314 	}
315 	if (pbroker->pbroker) {
316 		enchant_broker_free(pbroker->pbroker);
317 		pbroker->pbroker = NULL;
318 	}
319 	RETURN_TRUE;
320 }
321 /* }}} */
322 
323 /* {{{ Returns the last error of the broker */
PHP_FUNCTION(enchant_broker_get_error)324 PHP_FUNCTION(enchant_broker_get_error)
325 {
326 	zval *broker;
327 	enchant_broker *pbroker;
328 	const char *msg;
329 
330 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &broker, enchant_broker_ce) == FAILURE) {
331 		RETURN_THROWS();
332 	}
333 
334 	PHP_ENCHANT_GET_BROKER;
335 
336 	msg = enchant_broker_get_error(pbroker->pbroker);
337 	if (msg) {
338 		RETURN_STRING((char *)msg);
339 	}
340 	RETURN_FALSE;
341 }
342 /* }}} */
343 
344 /* {{{ Set the directory path for a given backend, works with ispell and myspell */
PHP_FUNCTION(enchant_broker_set_dict_path)345 PHP_FUNCTION(enchant_broker_set_dict_path)
346 {
347 	zval *broker;
348 	zend_long dict_type;
349 	char *value;
350 	size_t value_len;
351 
352 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ols", &broker, enchant_broker_ce, &dict_type, &value, &value_len) == FAILURE) {
353 		RETURN_THROWS();
354 	}
355 
356 #if HAVE_ENCHANT_BROKER_SET_PARAM
357 	enchant_broker *pbroker;
358 	if (!value_len) {
359 		RETURN_FALSE;
360 	}
361 
362 	PHP_ENCHANT_GET_BROKER;
363 
364 	switch (dict_type) {
365 		case PHP_ENCHANT_MYSPELL:
366 			PHP_ENCHANT_GET_BROKER;
367 			enchant_broker_set_param(pbroker->pbroker, "enchant.myspell.dictionary.path", (const char *)value);
368 			RETURN_TRUE;
369 			break;
370 
371 		case PHP_ENCHANT_ISPELL:
372 			PHP_ENCHANT_GET_BROKER;
373 			enchant_broker_set_param(pbroker->pbroker, "enchant.ispell.dictionary.path", (const char *)value);
374 			RETURN_TRUE;
375 			break;
376 
377 		default:
378 			RETURN_FALSE;
379 	}
380 #endif
381 }
382 /* }}} */
383 
384 
385 /* {{{ Get the directory path for a given backend, works with ispell and myspell */
PHP_FUNCTION(enchant_broker_get_dict_path)386 PHP_FUNCTION(enchant_broker_get_dict_path)
387 {
388 	zval *broker;
389 	zend_long dict_type;
390 
391 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &broker, enchant_broker_ce, &dict_type) == FAILURE) {
392 		RETURN_THROWS();
393 	}
394 
395 #if HAVE_ENCHANT_BROKER_SET_PARAM
396 	enchant_broker *pbroker;
397 	char *value;
398 	PHP_ENCHANT_GET_BROKER;
399 
400 	switch (dict_type) {
401 		case PHP_ENCHANT_MYSPELL:
402 			PHP_ENCHANT_GET_BROKER;
403 			value = enchant_broker_get_param(pbroker->pbroker, "enchant.myspell.dictionary.path");
404 			break;
405 
406 		case PHP_ENCHANT_ISPELL:
407 			PHP_ENCHANT_GET_BROKER;
408 			value = enchant_broker_get_param(pbroker->pbroker, "enchant.ispell.dictionary.path");
409 			break;
410 
411 		default:
412 			RETURN_FALSE;
413 	}
414 
415 	if (value == NULL) {
416 		php_error_docref(NULL, E_WARNING, "dict_path not set");
417 		RETURN_FALSE;
418 	}
419 
420 	RETURN_STRING(value);
421 #endif
422 }
423 /* }}} */
424 
425 /* {{{ Lists the dictionaries available for the given broker */
PHP_FUNCTION(enchant_broker_list_dicts)426 PHP_FUNCTION(enchant_broker_list_dicts)
427 {
428 	zval *broker;
429 	enchant_broker *pbroker;
430 
431 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &broker, enchant_broker_ce) == FAILURE) {
432 		RETURN_THROWS();
433 	}
434 
435 	PHP_ENCHANT_GET_BROKER;
436 
437 	array_init(return_value);
438 	enchant_broker_list_dicts(pbroker->pbroker, php_enchant_list_dicts_fn, (void *)return_value);
439 }
440 /* }}} */
441 
442 /* {{{ create a new dictionary using tag, the non-empty language tag you wish to request
443 	a dictionary for ("en_US", "de_DE", ...) */
PHP_FUNCTION(enchant_broker_request_dict)444 PHP_FUNCTION(enchant_broker_request_dict)
445 {
446 	zval *broker;
447 	enchant_broker *pbroker;
448 	enchant_dict *dict;
449 	EnchantDict *pdict;
450 	char *tag;
451 	size_t taglen;
452 
453 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &broker, enchant_broker_ce, &tag, &taglen) == FAILURE) {
454 		RETURN_THROWS();
455 	}
456 
457 	PHP_ENCHANT_GET_BROKER;
458 
459 	if (taglen == 0) {
460 		zend_argument_value_error(2, "cannot be empty");
461 		RETURN_THROWS();
462 	}
463 
464 	pdict = enchant_broker_request_dict(pbroker->pbroker, (const char *)tag);
465 	if (pdict) {
466 		pbroker->nb_dict++;
467 
468 		object_init_ex(return_value, enchant_dict_ce);
469 		dict = Z_ENCHANT_DICT_P(return_value);
470 		dict->pdict = pdict;
471 		ZVAL_COPY(&dict->zbroker, broker);
472 	} else {
473 		RETURN_FALSE;
474 	}
475 }
476 /* }}} */
477 
478 /* {{{ creates a dictionary using a PWL file. A PWL file is personal word file one word per line. It must exist before the call.*/
PHP_FUNCTION(enchant_broker_request_pwl_dict)479 PHP_FUNCTION(enchant_broker_request_pwl_dict)
480 {
481 	zval *broker;
482 	enchant_broker *pbroker;
483 	enchant_dict *dict;
484 	EnchantDict *pdict;
485 	const char *pwl;
486 	size_t pwllen;
487 
488 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Op", &broker, enchant_broker_ce, &pwl, &pwllen) == FAILURE) {
489 		RETURN_THROWS();
490 	}
491 
492 	if (php_check_open_basedir(pwl)) {
493 		RETURN_FALSE;
494 	}
495 
496 	PHP_ENCHANT_GET_BROKER;
497 
498 	pdict = enchant_broker_request_pwl_dict(pbroker->pbroker, pwl);
499 	if (pdict) {
500 		pbroker->nb_dict++;
501 
502 		object_init_ex(return_value, enchant_dict_ce);
503 		dict = Z_ENCHANT_DICT_P(return_value);
504 		dict->pdict = pdict;
505 		ZVAL_COPY(&dict->zbroker, broker);
506 	} else {
507 		RETURN_FALSE;
508 	}
509 }
510 /* }}} */
511 
512 /* {{{ Free the dictionary resource */
PHP_FUNCTION(enchant_broker_free_dict)513 PHP_FUNCTION(enchant_broker_free_dict)
514 {
515 	zval *dict;
516 	enchant_dict *pdict;
517 
518 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &dict, enchant_dict_ce) == FAILURE) {
519 		RETURN_THROWS();
520 	}
521 
522 	PHP_ENCHANT_GET_DICT;
523 
524 	if (pdict->pdict) {
525 		enchant_broker *broker = Z_ENCHANT_BROKER_P(&pdict->zbroker);
526 
527 		if (broker && broker->pbroker) {
528 			enchant_broker_free_dict(broker->pbroker, pdict->pdict);
529 			broker->nb_dict--;
530 			zval_ptr_dtor(&pdict->zbroker);
531 		}
532 		pdict->pdict = NULL;
533 	}
534 
535 	RETURN_TRUE;
536 }
537 /* }}} */
538 
539 /* {{{ Whether a dictionary exists or not. Using non-empty tag */
PHP_FUNCTION(enchant_broker_dict_exists)540 PHP_FUNCTION(enchant_broker_dict_exists)
541 {
542 	zval *broker;
543 	char *tag;
544 	size_t taglen;
545 	enchant_broker * pbroker;
546 
547 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &broker, enchant_broker_ce,  &tag, &taglen) == FAILURE) {
548 		RETURN_THROWS();
549 	}
550 
551 	PHP_ENCHANT_GET_BROKER;
552 
553 	RETURN_BOOL(enchant_broker_dict_exists(pbroker->pbroker, tag));
554 }
555 /* }}} */
556 
557 /* {{{ Declares a preference of dictionaries to use for the language
558 	described/referred to by 'tag'. The ordering is a comma delimited
559 	list of provider names. As a special exception, the "*" tag can
560 	be used as a language tag to declare a default ordering for any
561 	language that does not explicitly declare an ordering. */
562 
PHP_FUNCTION(enchant_broker_set_ordering)563 PHP_FUNCTION(enchant_broker_set_ordering)
564 {
565 	zval *broker;
566 	char *pordering;
567 	size_t porderinglen;
568 	char *ptag;
569 	size_t ptaglen;
570 	enchant_broker * pbroker;
571 
572 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oss", &broker, enchant_broker_ce, &ptag, &ptaglen, &pordering, &porderinglen) == FAILURE) {
573 		RETURN_THROWS();
574 	}
575 
576 	PHP_ENCHANT_GET_BROKER;
577 
578 	enchant_broker_set_ordering(pbroker->pbroker, ptag, pordering);
579 	RETURN_TRUE;
580 }
581 /* }}} */
582 
583 /* {{{ Enumerates the Enchant providers and tells you some rudimentary information about them. The same info is provided through phpinfo() */
PHP_FUNCTION(enchant_broker_describe)584 PHP_FUNCTION(enchant_broker_describe)
585 {
586 	EnchantBrokerDescribeFn describetozval = enumerate_providers_fn;
587 	zval *broker;
588 	enchant_broker * pbroker;
589 
590 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &broker, enchant_broker_ce) == FAILURE) {
591 		RETURN_THROWS();
592 	}
593 
594 	PHP_ENCHANT_GET_BROKER;
595 
596 	array_init(return_value);
597 	enchant_broker_describe(pbroker->pbroker, describetozval, (void *)return_value);
598 }
599 /* }}} */
600 
601 /* {{{ If the word is correctly spelled return true, otherwise return false, if suggestions variable
602     is provided, fill it with spelling alternatives. */
PHP_FUNCTION(enchant_dict_quick_check)603 PHP_FUNCTION(enchant_dict_quick_check)
604 {
605 	zval *dict, *sugg = NULL;
606 	char *word;
607 	size_t wordlen;
608 	enchant_dict *pdict;
609 
610 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os|z", &dict, enchant_dict_ce, &word, &wordlen, &sugg) == FAILURE) {
611 		RETURN_THROWS();
612 	}
613 
614 	if (sugg) {
615 		sugg = zend_try_array_init(sugg);
616 		if (!sugg) {
617 			RETURN_THROWS();
618 		}
619 	}
620 
621 	PHP_ENCHANT_GET_DICT;
622 
623 	if (enchant_dict_check(pdict->pdict, word, wordlen) > 0) {
624 		size_t n_sugg;
625 		char **suggs;
626 
627 		if (!sugg && ZEND_NUM_ARGS() == 2) {
628 			RETURN_FALSE;
629 		}
630 
631 		suggs = enchant_dict_suggest(pdict->pdict, word, wordlen, &n_sugg);
632 		if (suggs && n_sugg) {
633 			size_t i;
634 			for (i = 0; i < n_sugg; i++) {
635 				add_next_index_string(sugg, suggs[i]);
636 			}
637 			enchant_dict_free_string_list(pdict->pdict, suggs);
638 		}
639 
640 		RETURN_FALSE;
641 	}
642 
643 	RETURN_TRUE;
644 }
645 /* }}} */
646 
647 /* {{{ If the word is correctly spelled return true, otherwise return false */
PHP_FUNCTION(enchant_dict_check)648 PHP_FUNCTION(enchant_dict_check)
649 {
650 	zval *dict;
651 	char *word;
652 	size_t wordlen;
653 	enchant_dict *pdict;
654 
655 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &dict, enchant_dict_ce, &word, &wordlen) == FAILURE) {
656 		RETURN_THROWS();
657 	}
658 
659 	PHP_ENCHANT_GET_DICT;
660 
661 	RETURN_BOOL(!enchant_dict_check(pdict->pdict, word, wordlen));
662 }
663 /* }}} */
664 
665 /* {{{ Will return a list of values if any of those pre-conditions are not met.*/
PHP_FUNCTION(enchant_dict_suggest)666 PHP_FUNCTION(enchant_dict_suggest)
667 {
668 	zval *dict;
669 	char *word;
670 	size_t wordlen;
671 	char **suggs;
672 	enchant_dict *pdict;
673 	size_t n_sugg;
674 
675 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &dict, enchant_dict_ce, &word, &wordlen) == FAILURE) {
676 		RETURN_THROWS();
677 	}
678 
679 	PHP_ENCHANT_GET_DICT;
680 	array_init(return_value);
681 
682 	suggs = enchant_dict_suggest(pdict->pdict, word, wordlen, &n_sugg);
683 	if (suggs && n_sugg) {
684 		size_t i;
685 
686 		for (i = 0; i < n_sugg; i++) {
687 			add_next_index_string(return_value, suggs[i]);
688 		}
689 
690 		enchant_dict_free_string_list(pdict->pdict, suggs);
691 	}
692 }
693 /* }}} */
694 
695 /* {{{ add 'word' to personal word list */
PHP_FUNCTION(enchant_dict_add)696 PHP_FUNCTION(enchant_dict_add)
697 {
698 	zval *dict;
699 	char *word;
700 	size_t wordlen;
701 	enchant_dict *pdict;
702 
703 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &dict, enchant_dict_ce, &word, &wordlen) == FAILURE) {
704 		RETURN_THROWS();
705 	}
706 
707 	PHP_ENCHANT_GET_DICT;
708 
709 	enchant_dict_add(pdict->pdict, word, wordlen);
710 }
711 /* }}} */
712 
713 /* {{{ add 'word' to this spell-checking session */
PHP_FUNCTION(enchant_dict_add_to_session)714 PHP_FUNCTION(enchant_dict_add_to_session)
715 {
716 	zval *dict;
717 	char *word;
718 	size_t wordlen;
719 	enchant_dict *pdict;
720 
721 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &dict, enchant_dict_ce, &word, &wordlen) == FAILURE) {
722 		RETURN_THROWS();
723 	}
724 
725 	PHP_ENCHANT_GET_DICT;
726 
727 	enchant_dict_add_to_session(pdict->pdict, word, wordlen);
728 }
729 /* }}} */
730 
731 /* {{{ whether or not 'word' exists in this spelling-session */
PHP_FUNCTION(enchant_dict_is_added)732 PHP_FUNCTION(enchant_dict_is_added)
733 {
734 	zval *dict;
735 	char *word;
736 	size_t wordlen;
737 	enchant_dict *pdict;
738 
739 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &dict, enchant_dict_ce, &word, &wordlen) == FAILURE) {
740 		RETURN_THROWS();
741 	}
742 
743 	PHP_ENCHANT_GET_DICT;
744 
745 	RETURN_BOOL(enchant_dict_is_added(pdict->pdict, word, wordlen));
746 }
747 /* }}} */
748 
749 /* {{{ add a correction for 'mis' using 'cor'.
750 	Notes that you replaced @mis with @cor, so it's possibly more likely
751 	that future occurrences of @mis will be replaced with @cor. So it might
752 	bump @cor up in the suggestion list.*/
PHP_FUNCTION(enchant_dict_store_replacement)753 PHP_FUNCTION(enchant_dict_store_replacement)
754 {
755 	zval *dict;
756 	char *mis, *cor;
757 	size_t mislen, corlen;
758 
759 	enchant_dict *pdict;
760 
761 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oss", &dict, enchant_dict_ce, &mis, &mislen, &cor, &corlen) == FAILURE) {
762 		RETURN_THROWS();
763 	}
764 
765 	PHP_ENCHANT_GET_DICT;
766 
767 	enchant_dict_store_replacement(pdict->pdict, mis, mislen, cor, corlen);
768 }
769 /* }}} */
770 
771 /* {{{ Returns the last error of the current spelling-session */
PHP_FUNCTION(enchant_dict_get_error)772 PHP_FUNCTION(enchant_dict_get_error)
773 {
774 	zval *dict;
775 	enchant_dict *pdict;
776 	const char *msg;
777 
778 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &dict, enchant_dict_ce) == FAILURE) {
779 		RETURN_THROWS();
780 	}
781 
782 	PHP_ENCHANT_GET_DICT;
783 
784 	msg = enchant_dict_get_error(pdict->pdict);
785 	if (msg) {
786 		RETURN_STRING((char *)msg);
787 	}
788 
789 	RETURN_FALSE;
790 }
791 /* }}} */
792 
793 /* {{{ Describes an individual dictionary 'dict' */
PHP_FUNCTION(enchant_dict_describe)794 PHP_FUNCTION(enchant_dict_describe)
795 {
796 	zval *dict;
797 	enchant_dict *pdict;
798 
799 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &dict, enchant_dict_ce) == FAILURE) {
800 		RETURN_THROWS();
801 	}
802 
803 	PHP_ENCHANT_GET_DICT;
804 
805 	enchant_dict_describe(pdict->pdict, describe_dict_fn, (void *)return_value);
806 }
807 /* }}} */
808