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