xref: /PHP-8.1/ext/pspell/pspell.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: Vlad Krupin <phpdevel@echospace.com>                         |
14    +----------------------------------------------------------------------+
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include "php.h"
22 
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 
27 #ifdef HAVE_PSPELL
28 
29 /* this will enforce compatibility in .12 version (broken after .11.2) */
30 #define USE_ORIGINAL_MANAGER_FUNCS
31 
32 #include "php_pspell.h"
33 #include <pspell.h>
34 #include "ext/standard/info.h"
35 #include "pspell_arginfo.h"
36 
37 #define PSPELL_FAST 1L
38 #define PSPELL_NORMAL 2L
39 #define PSPELL_BAD_SPELLERS 3L
40 #define PSPELL_SPEED_MASK_INTERNAL 3L
41 #define PSPELL_RUN_TOGETHER 8L
42 
43 /* Largest ignored word can be 999 characters (this seems sane enough),
44  * and it takes 3 bytes to represent that (see pspell_config_ignore)
45  */
46 #define PSPELL_LARGEST_WORD 3
47 
48 static PHP_MINIT_FUNCTION(pspell);
49 static PHP_MINFO_FUNCTION(pspell);
50 
51 static zend_class_entry *php_pspell_ce = NULL;
52 static zend_object_handlers php_pspell_handlers;
53 static zend_class_entry *php_pspell_config_ce = NULL;
54 static zend_object_handlers php_pspell_config_handlers;
55 
56 zend_module_entry pspell_module_entry = {
57 	STANDARD_MODULE_HEADER,
58 	"pspell",
59 	ext_functions,
60 	PHP_MINIT(pspell),
61 	NULL,
62 	NULL,
63 	NULL,
64 	PHP_MINFO(pspell),
65 	PHP_PSPELL_VERSION,
66 	STANDARD_MODULE_PROPERTIES,
67 };
68 
69 #ifdef COMPILE_DL_PSPELL
70 ZEND_GET_MODULE(pspell)
71 #endif
72 
73 /* class PSpell */
74 
75 typedef struct _php_pspell_object {
76 	PspellManager *mgr;
77 	zend_object std;
78 } php_pspell_object;
79 
php_pspell_object_from_zend_object(zend_object * zobj)80 static php_pspell_object *php_pspell_object_from_zend_object(zend_object *zobj) {
81 	return ((php_pspell_object*)(zobj + 1)) - 1;
82 }
83 
php_pspell_object_to_zend_object(php_pspell_object * obj)84 static zend_object *php_pspell_object_to_zend_object(php_pspell_object *obj) {
85 	return ((zend_object*)(obj + 1)) - 1;
86 }
87 
php_pspell_object_get_constructor(zend_object * object)88 static zend_function *php_pspell_object_get_constructor(zend_object *object)
89 {
90 	zend_throw_error(NULL, "You cannot initialize a PSpell\\Dictionary object except through helper functions");
91 	return NULL;
92 }
93 
php_pspell_object_create(zend_class_entry * ce)94 static zend_object *php_pspell_object_create(zend_class_entry *ce)
95 {
96 	php_pspell_object *obj = zend_object_alloc(sizeof(php_pspell_object), ce);
97 	zend_object *zobj = php_pspell_object_to_zend_object(obj);
98 
99 	obj->mgr = NULL;
100 	zend_object_std_init(zobj, ce);
101 	object_properties_init(zobj, ce);
102 	zobj->handlers = &php_pspell_handlers;
103 
104 	return zobj;
105 }
106 
php_pspell_object_free(zend_object * zobj)107 static void php_pspell_object_free(zend_object *zobj) {
108 	delete_pspell_manager(php_pspell_object_from_zend_object(zobj)->mgr);
109 }
110 
111 /* class PSpellConfig */
112 
113 typedef struct _php_pspell_config_object {
114 	PspellConfig *cfg;
115 	zend_object std;
116 } php_pspell_config_object;
117 
php_pspell_config_object_from_zend_object(zend_object * zobj)118 static php_pspell_config_object *php_pspell_config_object_from_zend_object(zend_object *zobj) {
119 	return ((php_pspell_config_object*)(zobj + 1)) - 1;
120 }
121 
php_pspell_config_object_to_zend_object(php_pspell_config_object * obj)122 static zend_object *php_pspell_config_object_to_zend_object(php_pspell_config_object *obj) {
123 	return ((zend_object*)(obj + 1)) - 1;
124 }
125 
php_pspell_config_object_get_constructor(zend_object * object)126 static zend_function *php_pspell_config_object_get_constructor(zend_object *object)
127 {
128 	zend_throw_error(NULL, "You cannot initialize a PSpell\\Config object except through helper functions");
129 	return NULL;
130 }
131 
php_pspell_config_object_create(zend_class_entry * ce)132 static zend_object *php_pspell_config_object_create(zend_class_entry *ce)
133 {
134 	php_pspell_config_object *obj = zend_object_alloc(sizeof(php_pspell_config_object), ce);
135 	zend_object *zobj = php_pspell_config_object_to_zend_object(obj);
136 
137 	obj->cfg = NULL;
138 	zend_object_std_init(zobj, ce);
139 	object_properties_init(zobj, ce);
140 	zobj->handlers = &php_pspell_config_handlers;
141 
142 	return zobj;
143 }
144 
php_pspell_config_object_free(zend_object * zobj)145 static void php_pspell_config_object_free(zend_object *zobj) {
146 	delete_pspell_config(php_pspell_config_object_from_zend_object(zobj)->cfg);
147 }
148 
149 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(pspell)150 static PHP_MINIT_FUNCTION(pspell)
151 {
152 	php_pspell_ce = register_class_PSpell_Dictionary();
153 	php_pspell_ce->create_object = php_pspell_object_create;
154 
155 	memcpy(&php_pspell_handlers, &std_object_handlers, sizeof(zend_object_handlers));
156 	php_pspell_handlers.clone_obj = NULL;
157 	php_pspell_handlers.free_obj = php_pspell_object_free;
158 	php_pspell_handlers.get_constructor = php_pspell_object_get_constructor;
159 	php_pspell_handlers.offset = XtOffsetOf(php_pspell_object, std);
160 
161 	php_pspell_config_ce = register_class_PSpell_Config();
162 	php_pspell_config_ce->create_object = php_pspell_config_object_create;
163 
164 	memcpy(&php_pspell_config_handlers, &std_object_handlers, sizeof(zend_object_handlers));
165 	php_pspell_config_handlers.clone_obj = NULL;
166 	php_pspell_config_handlers.free_obj = php_pspell_config_object_free;
167 	php_pspell_config_handlers.get_constructor = php_pspell_config_object_get_constructor;
168 	php_pspell_config_handlers.offset = XtOffsetOf(php_pspell_config_object, std);
169 
170 	REGISTER_LONG_CONSTANT("PSPELL_FAST", PSPELL_FAST, CONST_PERSISTENT | CONST_CS);
171 	REGISTER_LONG_CONSTANT("PSPELL_NORMAL", PSPELL_NORMAL, CONST_PERSISTENT | CONST_CS);
172 	REGISTER_LONG_CONSTANT("PSPELL_BAD_SPELLERS", PSPELL_BAD_SPELLERS, CONST_PERSISTENT | CONST_CS);
173 	REGISTER_LONG_CONSTANT("PSPELL_RUN_TOGETHER", PSPELL_RUN_TOGETHER, CONST_PERSISTENT | CONST_CS);
174 
175 	return SUCCESS;
176 }
177 /* }}} */
178 
179 /* {{{ Load a dictionary */
PHP_FUNCTION(pspell_new)180 PHP_FUNCTION(pspell_new)
181 {
182 	char *language, *spelling = NULL, *jargon = NULL, *encoding = NULL;
183 	size_t language_len, spelling_len = 0, jargon_len = 0, encoding_len = 0;
184 	zend_long mode = Z_L(0), speed = Z_L(0);
185 	int argc = ZEND_NUM_ARGS();
186 
187 #ifdef PHP_WIN32
188 	TCHAR aspell_dir[200];
189 	TCHAR data_dir[220];
190 	TCHAR dict_dir[220];
191 	HKEY hkey;
192 	DWORD dwType,dwLen;
193 #endif
194 
195 	PspellCanHaveError *ret;
196 	PspellConfig *config;
197 
198 	if (zend_parse_parameters(argc, "s|sssl", &language, &language_len, &spelling, &spelling_len,
199 		&jargon, &jargon_len, &encoding, &encoding_len, &mode) == FAILURE) {
200 		RETURN_THROWS();
201 	}
202 
203 	config = new_pspell_config();
204 
205 #ifdef PHP_WIN32
206 	/* If aspell was installed using installer, we should have a key
207 	 * pointing to the location of the dictionaries
208 	 */
209 	if (0 == RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Aspell", &hkey)) {
210 		LONG result;
211 		dwLen = sizeof(aspell_dir) - 1;
212 		result = RegQueryValueEx(hkey, "", NULL, &dwType, (LPBYTE)&aspell_dir, &dwLen);
213 		RegCloseKey(hkey);
214 		if (result == ERROR_SUCCESS) {
215 			strlcpy(data_dir, aspell_dir, sizeof(data_dir));
216 			strlcat(data_dir, "\\data", sizeof(data_dir));
217 			strlcpy(dict_dir, aspell_dir, sizeof(dict_dir));
218 			strlcat(dict_dir, "\\dict", sizeof(dict_dir));
219 
220 			pspell_config_replace(config, "data-dir", data_dir);
221 			pspell_config_replace(config, "dict-dir", dict_dir);
222 		}
223 	}
224 #endif
225 
226 	pspell_config_replace(config, "language-tag", language);
227 
228 	if (spelling_len) {
229 		pspell_config_replace(config, "spelling", spelling);
230 	}
231 
232 	if (jargon_len) {
233 		pspell_config_replace(config, "jargon", jargon);
234 	}
235 
236 	if (encoding_len) {
237 		pspell_config_replace(config, "encoding", encoding);
238 	}
239 
240 	if (mode) {
241 		speed = mode & PSPELL_SPEED_MASK_INTERNAL;
242 
243 		/* First check what mode we want (how many suggestions) */
244 		if (speed == PSPELL_FAST) {
245 			pspell_config_replace(config, "sug-mode", "fast");
246 		} else if (speed == PSPELL_NORMAL) {
247 			pspell_config_replace(config, "sug-mode", "normal");
248 		} else if (speed == PSPELL_BAD_SPELLERS) {
249 			pspell_config_replace(config, "sug-mode", "bad-spellers");
250 		}
251 
252 		/* Then we see if run-together words should be treated as valid components */
253 		if (mode & PSPELL_RUN_TOGETHER) {
254 			pspell_config_replace(config, "run-together", "true");
255 		}
256 	}
257 
258 	ret = new_pspell_manager(config);
259 	delete_pspell_config(config);
260 
261 	if (pspell_error_number(ret) != 0) {
262 		php_error_docref(NULL, E_WARNING, "PSPELL couldn't open the dictionary. reason: %s", pspell_error_message(ret));
263 		delete_pspell_can_have_error(ret);
264 		RETURN_FALSE;
265 	}
266 
267 	object_init_ex(return_value, php_pspell_ce);
268 	php_pspell_object_from_zend_object(Z_OBJ_P(return_value))->mgr = to_pspell_manager(ret);
269 }
270 /* }}} */
271 
272 /* {{{ Load a dictionary with a personal wordlist*/
PHP_FUNCTION(pspell_new_personal)273 PHP_FUNCTION(pspell_new_personal)
274 {
275 	char *personal, *language, *spelling = NULL, *jargon = NULL, *encoding = NULL;
276 	size_t personal_len, language_len, spelling_len = 0, jargon_len = 0, encoding_len = 0;
277 	zend_long mode = Z_L(0), speed = Z_L(0);
278 	int argc = ZEND_NUM_ARGS();
279 
280 #ifdef PHP_WIN32
281 	TCHAR aspell_dir[200];
282 	TCHAR data_dir[220];
283 	TCHAR dict_dir[220];
284 	HKEY hkey;
285 	DWORD dwType,dwLen;
286 #endif
287 
288 	PspellCanHaveError *ret;
289 	PspellConfig *config;
290 
291 	if (zend_parse_parameters(argc, "ps|sssl", &personal, &personal_len, &language, &language_len,
292 		&spelling, &spelling_len, &jargon, &jargon_len, &encoding, &encoding_len, &mode) == FAILURE) {
293 		RETURN_THROWS();
294 	}
295 
296 	config = new_pspell_config();
297 
298 #ifdef PHP_WIN32
299 	/* If aspell was installed using installer, we should have a key
300 	 * pointing to the location of the dictionaries
301 	 */
302 	if (0 == RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Aspell", &hkey)) {
303 		LONG result;
304 		dwLen = sizeof(aspell_dir) - 1;
305 		result = RegQueryValueEx(hkey, "", NULL, &dwType, (LPBYTE)&aspell_dir, &dwLen);
306 		RegCloseKey(hkey);
307 		if (result == ERROR_SUCCESS) {
308 			strlcpy(data_dir, aspell_dir, sizeof(data_dir));
309 			strlcat(data_dir, "\\data", sizeof(data_dir));
310 			strlcpy(dict_dir, aspell_dir, sizeof(dict_dir));
311 			strlcat(dict_dir, "\\dict", sizeof(dict_dir));
312 
313 			pspell_config_replace(config, "data-dir", data_dir);
314 			pspell_config_replace(config, "dict-dir", dict_dir);
315 		}
316 	}
317 #endif
318 
319 	if (php_check_open_basedir(personal)) {
320 		delete_pspell_config(config);
321 		RETURN_FALSE;
322 	}
323 
324 	pspell_config_replace(config, "personal", personal);
325 	pspell_config_replace(config, "save-repl", "false");
326 
327 	pspell_config_replace(config, "language-tag", language);
328 
329 	if (spelling_len) {
330 		pspell_config_replace(config, "spelling", spelling);
331 	}
332 
333 	if (jargon_len) {
334 		pspell_config_replace(config, "jargon", jargon);
335 	}
336 
337 	if (encoding_len) {
338 		pspell_config_replace(config, "encoding", encoding);
339 	}
340 
341 	if (mode) {
342 		speed = mode & PSPELL_SPEED_MASK_INTERNAL;
343 
344 		/* First check what mode we want (how many suggestions) */
345 		if (speed == PSPELL_FAST) {
346 			pspell_config_replace(config, "sug-mode", "fast");
347 		} else if (speed == PSPELL_NORMAL) {
348 			pspell_config_replace(config, "sug-mode", "normal");
349 		} else if (speed == PSPELL_BAD_SPELLERS) {
350 			pspell_config_replace(config, "sug-mode", "bad-spellers");
351 		}
352 
353 		/* Then we see if run-together words should be treated as valid components */
354 		if (mode & PSPELL_RUN_TOGETHER) {
355 			pspell_config_replace(config, "run-together", "true");
356 		}
357 	}
358 
359 	ret = new_pspell_manager(config);
360 	delete_pspell_config(config);
361 
362 	if (pspell_error_number(ret) != 0) {
363 		php_error_docref(NULL, E_WARNING, "PSPELL couldn't open the dictionary. reason: %s", pspell_error_message(ret));
364 		delete_pspell_can_have_error(ret);
365 		RETURN_FALSE;
366 	}
367 
368 	object_init_ex(return_value, php_pspell_ce);
369 	php_pspell_object_from_zend_object(Z_OBJ_P(return_value))->mgr = to_pspell_manager(ret);
370 }
371 /* }}} */
372 
373 /* {{{ Load a dictionary based on the given config */
PHP_FUNCTION(pspell_new_config)374 PHP_FUNCTION(pspell_new_config)
375 {
376 	zval *zcfg;
377 	PspellCanHaveError *ret;
378 	PspellConfig *config;
379 
380 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zcfg, php_pspell_config_ce) == FAILURE) {
381 		RETURN_THROWS();
382 	}
383 	config = php_pspell_config_object_from_zend_object(Z_OBJ_P(zcfg))->cfg;
384 
385 	ret = new_pspell_manager(config);
386 
387 	if (pspell_error_number(ret) != 0) {
388 		php_error_docref(NULL, E_WARNING, "PSPELL couldn't open the dictionary. reason: %s", pspell_error_message(ret));
389 		delete_pspell_can_have_error(ret);
390 		RETURN_FALSE;
391 	}
392 
393 	object_init_ex(return_value, php_pspell_ce);
394 	php_pspell_object_from_zend_object(Z_OBJ_P(return_value))->mgr = to_pspell_manager(ret);
395 }
396 /* }}} */
397 
398 /* {{{ Returns true if word is valid */
PHP_FUNCTION(pspell_check)399 PHP_FUNCTION(pspell_check)
400 {
401 	zval *zmgr;
402 	zend_string *word;
403 	PspellManager *manager;
404 
405 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &zmgr, php_pspell_ce, &word) == FAILURE) {
406 		RETURN_THROWS();
407 	}
408 	manager = php_pspell_object_from_zend_object(Z_OBJ_P(zmgr))->mgr;
409 
410 	if (pspell_manager_check(manager, ZSTR_VAL(word))) {
411 		RETURN_TRUE;
412 	} else {
413 		RETURN_FALSE;
414 	}
415 }
416 /* }}} */
417 
418 /* {{{ Returns array of suggestions */
PHP_FUNCTION(pspell_suggest)419 PHP_FUNCTION(pspell_suggest)
420 {
421 	zval *zmgr;
422 	zend_string *word;
423 	PspellManager *manager;
424 	const PspellWordList *wl;
425 	const char *sug;
426 
427 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &zmgr, php_pspell_ce, &word) == FAILURE) {
428 		RETURN_THROWS();
429 	}
430 	manager = php_pspell_object_from_zend_object(Z_OBJ_P(zmgr))->mgr;
431 
432 	array_init(return_value);
433 
434 	wl = pspell_manager_suggest(manager, ZSTR_VAL(word));
435 	if (wl) {
436 		PspellStringEmulation *els = pspell_word_list_elements(wl);
437 		while ((sug = pspell_string_emulation_next(els)) != 0) {
438 			add_next_index_string(return_value,(char *)sug);
439 		}
440 		delete_pspell_string_emulation(els);
441 	} else {
442 		php_error_docref(NULL, E_WARNING, "PSPELL had a problem. details: %s", pspell_manager_error_message(manager));
443 		RETURN_FALSE;
444 	}
445 }
446 /* }}} */
447 
448 /* {{{ Notify the dictionary of a user-selected replacement */
PHP_FUNCTION(pspell_store_replacement)449 PHP_FUNCTION(pspell_store_replacement)
450 {
451 	zval *zmgr;
452 	zend_string *miss, *corr;
453 	PspellManager *manager;
454 
455 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS", &zmgr, php_pspell_ce, &miss, &corr) == FAILURE) {
456 		RETURN_THROWS();
457 	}
458 	manager = php_pspell_object_from_zend_object(Z_OBJ_P(zmgr))->mgr;
459 
460 	pspell_manager_store_replacement(manager, ZSTR_VAL(miss), ZSTR_VAL(corr));
461 	if (pspell_manager_error_number(manager) == 0) {
462 		RETURN_TRUE;
463 	} else {
464 		php_error_docref(NULL, E_WARNING, "pspell_store_replacement() gave error: %s", pspell_manager_error_message(manager));
465 		RETURN_FALSE;
466 	}
467 }
468 /* }}} */
469 
470 /* {{{ Adds a word to a personal list */
PHP_FUNCTION(pspell_add_to_personal)471 PHP_FUNCTION(pspell_add_to_personal)
472 {
473 	zval *zmgr;
474 	zend_string *word;
475 	PspellManager *manager;
476 
477 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &zmgr, php_pspell_ce, &word) == FAILURE) {
478 		RETURN_THROWS();
479 	}
480 	manager = php_pspell_object_from_zend_object(Z_OBJ_P(zmgr))->mgr;
481 
482 	/*If the word is empty, we have to return; otherwise we'll segfault! ouch!*/
483 	if (ZSTR_LEN(word) == 0) {
484 		RETURN_FALSE;
485 	}
486 
487 	pspell_manager_add_to_personal(manager, ZSTR_VAL(word));
488 	if (pspell_manager_error_number(manager) == 0) {
489 		RETURN_TRUE;
490 	} else {
491 		php_error_docref(NULL, E_WARNING, "pspell_add_to_personal() gave error: %s", pspell_manager_error_message(manager));
492 		RETURN_FALSE;
493 	}
494 }
495 /* }}} */
496 
497 /* {{{ Adds a word to the current session */
PHP_FUNCTION(pspell_add_to_session)498 PHP_FUNCTION(pspell_add_to_session)
499 {
500 	zval *zmgr;
501 	zend_string *word;
502 	PspellManager *manager;
503 
504 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &zmgr, php_pspell_ce, &word) == FAILURE) {
505 		RETURN_THROWS();
506 	}
507 	manager = php_pspell_object_from_zend_object(Z_OBJ_P(zmgr))->mgr;
508 
509 	/*If the word is empty, we have to return; otherwise we'll segfault! ouch!*/
510 	if (ZSTR_LEN(word) == 0) {
511 		RETURN_FALSE;
512 	}
513 
514 	pspell_manager_add_to_session(manager, ZSTR_VAL(word));
515 	if (pspell_manager_error_number(manager) == 0) {
516 		RETURN_TRUE;
517 	} else {
518 		php_error_docref(NULL, E_WARNING, "pspell_add_to_session() gave error: %s", pspell_manager_error_message(manager));
519 		RETURN_FALSE;
520 	}
521 }
522 /* }}} */
523 
524 /* {{{ Clears the current session */
PHP_FUNCTION(pspell_clear_session)525 PHP_FUNCTION(pspell_clear_session)
526 {
527 	zval *zmgr;
528 	PspellManager *manager;
529 
530 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zmgr, php_pspell_ce) == FAILURE) {
531 		RETURN_THROWS();
532 	}
533 	manager = php_pspell_object_from_zend_object(Z_OBJ_P(zmgr))->mgr;
534 
535 	pspell_manager_clear_session(manager);
536 	if (pspell_manager_error_number(manager) == 0) {
537 		RETURN_TRUE;
538 	} else {
539 		php_error_docref(NULL, E_WARNING, "pspell_clear_session() gave error: %s", pspell_manager_error_message(manager));
540 		RETURN_FALSE;
541 	}
542 }
543 /* }}} */
544 
545 /* {{{ Saves the current (personal) wordlist */
PHP_FUNCTION(pspell_save_wordlist)546 PHP_FUNCTION(pspell_save_wordlist)
547 {
548 	zval *zmgr;
549 	PspellManager *manager;
550 
551 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zmgr, php_pspell_ce) == FAILURE) {
552 		RETURN_THROWS();
553 	}
554 	manager = php_pspell_object_from_zend_object(Z_OBJ_P(zmgr))->mgr;
555 
556 	pspell_manager_save_all_word_lists(manager);
557 
558 	if (pspell_manager_error_number(manager) == 0) {
559 		RETURN_TRUE;
560 	} else {
561 		php_error_docref(NULL, E_WARNING, "pspell_save_wordlist() gave error: %s", pspell_manager_error_message(manager));
562 		RETURN_FALSE;
563 	}
564 
565 }
566 /* }}} */
567 
568 /* {{{ Create a new config to be used later to create a manager */
PHP_FUNCTION(pspell_config_create)569 PHP_FUNCTION(pspell_config_create)
570 {
571 	char *language, *spelling = NULL, *jargon = NULL, *encoding = NULL;
572 	size_t language_len, spelling_len = 0, jargon_len = 0, encoding_len = 0;
573 	PspellConfig *config;
574 
575 #ifdef PHP_WIN32
576 	TCHAR aspell_dir[200];
577 	TCHAR data_dir[220];
578 	TCHAR dict_dir[220];
579 	HKEY hkey;
580 	DWORD dwType,dwLen;
581 #endif
582 
583 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sss", &language, &language_len, &spelling, &spelling_len,
584 		&jargon, &jargon_len, &encoding, &encoding_len) == FAILURE) {
585 		RETURN_THROWS();
586 	}
587 
588 	config = new_pspell_config();
589 
590 #ifdef PHP_WIN32
591     /* If aspell was installed using installer, we should have a key
592      * pointing to the location of the dictionaries
593      */
594 	if (0 == RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Aspell", &hkey)) {
595 		LONG result;
596 		dwLen = sizeof(aspell_dir) - 1;
597 		result = RegQueryValueEx(hkey, "", NULL, &dwType, (LPBYTE)&aspell_dir, &dwLen);
598 		RegCloseKey(hkey);
599 		if (result == ERROR_SUCCESS) {
600 			strlcpy(data_dir, aspell_dir, sizeof(data_dir));
601 			strlcat(data_dir, "\\data", sizeof(data_dir));
602 			strlcpy(dict_dir, aspell_dir, sizeof(dict_dir));
603 			strlcat(dict_dir, "\\dict", sizeof(dict_dir));
604 
605 			pspell_config_replace(config, "data-dir", data_dir);
606 			pspell_config_replace(config, "dict-dir", dict_dir);
607 		}
608 	}
609 #endif
610 
611 	pspell_config_replace(config, "language-tag", language);
612 
613 	if (spelling_len) {
614 		pspell_config_replace(config, "spelling", spelling);
615 	}
616 
617 	if (jargon_len) {
618 		pspell_config_replace(config, "jargon", jargon);
619 	}
620 
621 	if (encoding_len) {
622 		pspell_config_replace(config, "encoding", encoding);
623 	}
624 
625 	/* By default I do not want to write anything anywhere because it'll try to write to $HOME
626 	which is not what we want */
627 	pspell_config_replace(config, "save-repl", "false");
628 
629 	object_init_ex(return_value, php_pspell_config_ce);
630 	php_pspell_config_object_from_zend_object(Z_OBJ_P(return_value))->cfg = config;
631 }
632 /* }}} */
633 
634 /* {{{ Consider run-together words as valid components */
PHP_FUNCTION(pspell_config_runtogether)635 PHP_FUNCTION(pspell_config_runtogether)
636 {
637 	zval *zcfg;
638 	bool runtogether;
639 	PspellConfig *config;
640 
641 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &zcfg, php_pspell_config_ce, &runtogether) == FAILURE) {
642 		RETURN_THROWS();
643 	}
644 	config = php_pspell_config_object_from_zend_object(Z_OBJ_P(zcfg))->cfg;
645 
646 	pspell_config_replace(config, "run-together", runtogether ? "true" : "false");
647 
648 	RETURN_TRUE;
649 }
650 /* }}} */
651 
652 /* {{{ Select mode for config (PSPELL_FAST, PSPELL_NORMAL or PSPELL_BAD_SPELLERS) */
PHP_FUNCTION(pspell_config_mode)653 PHP_FUNCTION(pspell_config_mode)
654 {
655 	zval *zcfg;
656 	zend_long mode;
657 	PspellConfig *config;
658 
659 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &zcfg, php_pspell_config_ce, &mode) == FAILURE) {
660 		RETURN_THROWS();
661 	}
662 	config = php_pspell_config_object_from_zend_object(Z_OBJ_P(zcfg))->cfg;
663 
664 	/* First check what mode we want (how many suggestions) */
665 	if (mode == PSPELL_FAST) {
666 		pspell_config_replace(config, "sug-mode", "fast");
667 	} else if (mode == PSPELL_NORMAL) {
668 		pspell_config_replace(config, "sug-mode", "normal");
669 	} else if (mode == PSPELL_BAD_SPELLERS) {
670 		pspell_config_replace(config, "sug-mode", "bad-spellers");
671 	}
672 
673 	RETURN_TRUE;
674 }
675 /* }}} */
676 
677 /* {{{ Ignore words <= n chars */
PHP_FUNCTION(pspell_config_ignore)678 PHP_FUNCTION(pspell_config_ignore)
679 {
680 	char ignore_str[MAX_LENGTH_OF_LONG + 1];
681 	zval *zcfg;
682 	zend_long ignore = 0L;
683 	PspellConfig *config;
684 
685 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &zcfg, php_pspell_config_ce, &ignore) == FAILURE) {
686 		RETURN_THROWS();
687 	}
688 	config = php_pspell_config_object_from_zend_object(Z_OBJ_P(zcfg))->cfg;
689 
690 	snprintf(ignore_str, sizeof(ignore_str), ZEND_LONG_FMT, ignore);
691 
692 	pspell_config_replace(config, "ignore", ignore_str);
693 	RETURN_TRUE;
694 }
695 /* }}} */
696 
pspell_config_path(INTERNAL_FUNCTION_PARAMETERS,char * option)697 static void pspell_config_path(INTERNAL_FUNCTION_PARAMETERS, char *option)
698 {
699 	zval *zcfg;
700 	zend_string *value;
701 	PspellConfig *config;
702 
703 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OP", &zcfg, php_pspell_config_ce, &value) == FAILURE) {
704 		RETURN_THROWS();
705 	}
706 	config = php_pspell_config_object_from_zend_object(Z_OBJ_P(zcfg))->cfg;
707 
708 	if (php_check_open_basedir(ZSTR_VAL(value))) {
709 		RETURN_FALSE;
710 	}
711 
712 	pspell_config_replace(config, option, ZSTR_VAL(value));
713 
714 	RETURN_TRUE;
715 }
716 
717 /* {{{ Use a personal dictionary for this config */
PHP_FUNCTION(pspell_config_personal)718 PHP_FUNCTION(pspell_config_personal)
719 {
720 	pspell_config_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, "personal");
721 }
722 /* }}} */
723 
724 /* {{{ location of the main word list */
PHP_FUNCTION(pspell_config_dict_dir)725 PHP_FUNCTION(pspell_config_dict_dir)
726 {
727 	pspell_config_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, "dict-dir");
728 }
729 /* }}} */
730 
731 /* {{{ location of language data files */
PHP_FUNCTION(pspell_config_data_dir)732 PHP_FUNCTION(pspell_config_data_dir)
733 {
734 	pspell_config_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, "data-dir");
735 }
736 /* }}} */
737 
738 /* {{{ Use a personal dictionary with replacement pairs for this config */
PHP_FUNCTION(pspell_config_repl)739 PHP_FUNCTION(pspell_config_repl)
740 {
741 	zval *zcfg;
742 	zend_string *repl;
743 	PspellConfig *config;
744 
745 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OP", &zcfg, php_pspell_config_ce, &repl) == FAILURE) {
746 		RETURN_THROWS();
747 	}
748 	config = php_pspell_config_object_from_zend_object(Z_OBJ_P(zcfg))->cfg;
749 
750 	pspell_config_replace(config, "save-repl", "true");
751 
752 	if (php_check_open_basedir(ZSTR_VAL(repl))) {
753 		RETURN_FALSE;
754 	}
755 
756 	pspell_config_replace(config, "repl", ZSTR_VAL(repl));
757 
758 	RETURN_TRUE;
759 }
760 /* }}} */
761 
762 /* {{{ Save replacement pairs when personal list is saved for this config */
PHP_FUNCTION(pspell_config_save_repl)763 PHP_FUNCTION(pspell_config_save_repl)
764 {
765 	zval *zcfg;
766 	bool save;
767 	PspellConfig *config;
768 
769 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &zcfg, php_pspell_config_ce, &save) == FAILURE) {
770 		RETURN_THROWS();
771 	}
772 	config = php_pspell_config_object_from_zend_object(Z_OBJ_P(zcfg))->cfg;
773 
774 	pspell_config_replace(config, "save-repl", save ? "true" : "false");
775 
776 	RETURN_TRUE;
777 }
778 /* }}} */
779 
780 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(pspell)781 static PHP_MINFO_FUNCTION(pspell)
782 {
783 	php_info_print_table_start();
784 	php_info_print_table_row(2, "PSpell Support", "enabled");
785 	php_info_print_table_end();
786 }
787 /* }}} */
788 
789 #endif
790