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