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