xref: /PHP-5.3/ext/pspell/pspell.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Vlad Krupin <phpdevel@echospace.com>                         |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #define IS_EXT_MODULE
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include "php.h"
28 
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 
33 #if HAVE_PSPELL
34 
35 /* this will enforce compatibility in .12 version (broken after .11.2) */
36 #define USE_ORIGINAL_MANAGER_FUNCS
37 
38 #include "php_pspell.h"
39 #include <pspell.h>
40 #include "ext/standard/info.h"
41 
42 #define PSPELL_FAST 1L
43 #define PSPELL_NORMAL 2L
44 #define PSPELL_BAD_SPELLERS 3L
45 #define PSPELL_SPEED_MASK_INTERNAL 3L
46 #define PSPELL_RUN_TOGETHER 8L
47 
48 /* Largest ignored word can be 999 characters (this seems sane enough),
49  * and it takes 3 bytes to represent that (see pspell_config_ignore)
50  */
51 #define PSPELL_LARGEST_WORD 3
52 
53 static PHP_MINIT_FUNCTION(pspell);
54 static PHP_MINFO_FUNCTION(pspell);
55 static PHP_FUNCTION(pspell_new);
56 static PHP_FUNCTION(pspell_new_personal);
57 static PHP_FUNCTION(pspell_new_config);
58 static PHP_FUNCTION(pspell_check);
59 static PHP_FUNCTION(pspell_suggest);
60 static PHP_FUNCTION(pspell_store_replacement);
61 static PHP_FUNCTION(pspell_add_to_personal);
62 static PHP_FUNCTION(pspell_add_to_session);
63 static PHP_FUNCTION(pspell_clear_session);
64 static PHP_FUNCTION(pspell_save_wordlist);
65 static PHP_FUNCTION(pspell_config_create);
66 static PHP_FUNCTION(pspell_config_runtogether);
67 static PHP_FUNCTION(pspell_config_mode);
68 static PHP_FUNCTION(pspell_config_ignore);
69 static PHP_FUNCTION(pspell_config_personal);
70 static PHP_FUNCTION(pspell_config_dict_dir);
71 static PHP_FUNCTION(pspell_config_data_dir);
72 static PHP_FUNCTION(pspell_config_repl);
73 static PHP_FUNCTION(pspell_config_save_repl);
74 
75 /* {{{ arginfo */
76 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_new, 0, 0, 1)
77 	ZEND_ARG_INFO(0, language)
78 	ZEND_ARG_INFO(0, spelling)
79 	ZEND_ARG_INFO(0, jargon)
80 	ZEND_ARG_INFO(0, encoding)
81 	ZEND_ARG_INFO(0, mode)
82 ZEND_END_ARG_INFO()
83 
84 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_new_personal, 0, 0, 2)
85 	ZEND_ARG_INFO(0, personal)
86 	ZEND_ARG_INFO(0, language)
87 	ZEND_ARG_INFO(0, spelling)
88 	ZEND_ARG_INFO(0, jargon)
89 	ZEND_ARG_INFO(0, encoding)
90 	ZEND_ARG_INFO(0, mode)
91 ZEND_END_ARG_INFO()
92 
93 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_new_config, 0, 0, 1)
94 	ZEND_ARG_INFO(0, config)
95 ZEND_END_ARG_INFO()
96 
97 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_check, 0, 0, 2)
98 	ZEND_ARG_INFO(0, pspell)
99 	ZEND_ARG_INFO(0, word)
100 ZEND_END_ARG_INFO()
101 
102 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_suggest, 0, 0, 2)
103 	ZEND_ARG_INFO(0, pspell)
104 	ZEND_ARG_INFO(0, word)
105 ZEND_END_ARG_INFO()
106 
107 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_store_replacement, 0, 0, 3)
108 	ZEND_ARG_INFO(0, pspell)
109 	ZEND_ARG_INFO(0, misspell)
110 	ZEND_ARG_INFO(0, correct)
111 ZEND_END_ARG_INFO()
112 
113 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_add_to_personal, 0, 0, 2)
114 	ZEND_ARG_INFO(0, pspell)
115 	ZEND_ARG_INFO(0, word)
116 ZEND_END_ARG_INFO()
117 
118 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_add_to_session, 0, 0, 2)
119 	ZEND_ARG_INFO(0, pspell)
120 	ZEND_ARG_INFO(0, word)
121 ZEND_END_ARG_INFO()
122 
123 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_clear_session, 0, 0, 1)
124 	ZEND_ARG_INFO(0, pspell)
125 ZEND_END_ARG_INFO()
126 
127 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_save_wordlist, 0, 0, 1)
128 	ZEND_ARG_INFO(0, pspell)
129 ZEND_END_ARG_INFO()
130 
131 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_create, 0, 0, 1)
132 	ZEND_ARG_INFO(0, language)
133 	ZEND_ARG_INFO(0, spelling)
134 	ZEND_ARG_INFO(0, jargon)
135 	ZEND_ARG_INFO(0, encoding)
136 ZEND_END_ARG_INFO()
137 
138 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_runtogether, 0, 0, 2)
139 	ZEND_ARG_INFO(0, conf)
140 	ZEND_ARG_INFO(0, runtogether)
141 ZEND_END_ARG_INFO()
142 
143 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_mode, 0, 0, 2)
144 	ZEND_ARG_INFO(0, conf)
145 	ZEND_ARG_INFO(0, mode)
146 ZEND_END_ARG_INFO()
147 
148 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_ignore, 0, 0, 2)
149 	ZEND_ARG_INFO(0, conf)
150 	ZEND_ARG_INFO(0, ignore)
151 ZEND_END_ARG_INFO()
152 
153 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_personal, 0, 0, 2)
154 	ZEND_ARG_INFO(0, conf)
155 	ZEND_ARG_INFO(0, personal)
156 ZEND_END_ARG_INFO()
157 
158 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_dict_dir, 0, 0, 2)
159 	ZEND_ARG_INFO(0, conf)
160 	ZEND_ARG_INFO(0, directory)
161 ZEND_END_ARG_INFO()
162 
163 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_data_dir, 0, 0, 2)
164 	ZEND_ARG_INFO(0, conf)
165 	ZEND_ARG_INFO(0, directory)
166 ZEND_END_ARG_INFO()
167 
168 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_repl, 0, 0, 2)
169 	ZEND_ARG_INFO(0, conf)
170 	ZEND_ARG_INFO(0, repl)
171 ZEND_END_ARG_INFO()
172 
173 ZEND_BEGIN_ARG_INFO_EX(arginfo_pspell_config_save_repl, 0, 0, 2)
174 	ZEND_ARG_INFO(0, conf)
175 	ZEND_ARG_INFO(0, save)
176 ZEND_END_ARG_INFO()
177 /* }}} */
178 
179 /* {{{ pspell_functions[]
180  */
181 static const zend_function_entry pspell_functions[] = {
182 	PHP_FE(pspell_new,					arginfo_pspell_new)
183 	PHP_FE(pspell_new_personal,			arginfo_pspell_new_personal)
184 	PHP_FE(pspell_new_config,			arginfo_pspell_new_config)
185 	PHP_FE(pspell_check,				arginfo_pspell_check)
186 	PHP_FE(pspell_suggest,				arginfo_pspell_suggest)
187 	PHP_FE(pspell_store_replacement,	arginfo_pspell_store_replacement)
188 	PHP_FE(pspell_add_to_personal,		arginfo_pspell_add_to_personal)
189 	PHP_FE(pspell_add_to_session,		arginfo_pspell_add_to_session)
190 	PHP_FE(pspell_clear_session,		arginfo_pspell_clear_session)
191 	PHP_FE(pspell_save_wordlist,		arginfo_pspell_save_wordlist)
192 	PHP_FE(pspell_config_create,		arginfo_pspell_config_create)
193 	PHP_FE(pspell_config_runtogether,	arginfo_pspell_config_runtogether)
194 	PHP_FE(pspell_config_mode,			arginfo_pspell_config_mode)
195 	PHP_FE(pspell_config_ignore,		arginfo_pspell_config_ignore)
196 	PHP_FE(pspell_config_personal,		arginfo_pspell_config_personal)
197 	PHP_FE(pspell_config_dict_dir,		arginfo_pspell_config_dict_dir)
198 	PHP_FE(pspell_config_data_dir,		arginfo_pspell_config_data_dir)
199 	PHP_FE(pspell_config_repl,			arginfo_pspell_config_repl)
200 	PHP_FE(pspell_config_save_repl,		arginfo_pspell_config_save_repl)
201 	PHP_FE_END
202 };
203 /* }}} */
204 
205 static int le_pspell, le_pspell_config;
206 
207 zend_module_entry pspell_module_entry = {
208     STANDARD_MODULE_HEADER,
209 	"pspell", pspell_functions, PHP_MINIT(pspell), NULL, NULL, NULL, PHP_MINFO(pspell), NO_VERSION_YET, STANDARD_MODULE_PROPERTIES
210 };
211 
212 #ifdef COMPILE_DL_PSPELL
ZEND_GET_MODULE(pspell)213 ZEND_GET_MODULE(pspell)
214 #endif
215 
216 static void php_pspell_close(zend_rsrc_list_entry *rsrc TSRMLS_DC)
217 {
218 	PspellManager *manager = (PspellManager *)rsrc->ptr;
219 
220 	delete_pspell_manager(manager);
221 }
222 
php_pspell_close_config(zend_rsrc_list_entry * rsrc TSRMLS_DC)223 static void php_pspell_close_config(zend_rsrc_list_entry *rsrc TSRMLS_DC)
224 {
225 	PspellConfig *config = (PspellConfig *)rsrc->ptr;
226 
227 	delete_pspell_config(config);
228 }
229 
230 #define PSPELL_FETCH_CONFIG \
231 	config = (PspellConfig *) zend_list_find(conf, &type);	\
232 	if (config == NULL || type != le_pspell_config) {	\
233 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%ld is not a PSPELL config index", conf);	\
234 		RETURN_FALSE;	\
235 	}	\
236 
237 #define PSPELL_FETCH_MANAGER \
238 	manager = (PspellManager *) zend_list_find(scin, &type);	\
239 	if (!manager || type != le_pspell) {	\
240 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%ld is not a PSPELL result index", scin);	\
241 		RETURN_FALSE;	\
242 	}	\
243 
244 /* {{{ PHP_MINIT_FUNCTION
245  */
PHP_MINIT_FUNCTION(pspell)246 static PHP_MINIT_FUNCTION(pspell)
247 {
248 	REGISTER_LONG_CONSTANT("PSPELL_FAST", PSPELL_FAST, CONST_PERSISTENT | CONST_CS);
249 	REGISTER_LONG_CONSTANT("PSPELL_NORMAL", PSPELL_NORMAL, CONST_PERSISTENT | CONST_CS);
250 	REGISTER_LONG_CONSTANT("PSPELL_BAD_SPELLERS", PSPELL_BAD_SPELLERS, CONST_PERSISTENT | CONST_CS);
251 	REGISTER_LONG_CONSTANT("PSPELL_RUN_TOGETHER", PSPELL_RUN_TOGETHER, CONST_PERSISTENT | CONST_CS);
252 	le_pspell = zend_register_list_destructors_ex(php_pspell_close, NULL, "pspell", module_number);
253 	le_pspell_config = zend_register_list_destructors_ex(php_pspell_close_config, NULL, "pspell config", module_number);
254 	return SUCCESS;
255 }
256 /* }}} */
257 
258 /* {{{ proto int pspell_new(string language [, string spelling [, string jargon [, string encoding [, int mode]]]])
259    Load a dictionary */
PHP_FUNCTION(pspell_new)260 static PHP_FUNCTION(pspell_new)
261 {
262 	char *language, *spelling = NULL, *jargon = NULL, *encoding = NULL;
263 	int language_len, spelling_len = 0, jargon_len = 0, encoding_len = 0;
264 	long mode = 0L,  speed = 0L;
265 	int argc = ZEND_NUM_ARGS();
266 	int ind;
267 
268 #ifdef PHP_WIN32
269 	TCHAR aspell_dir[200];
270 	TCHAR data_dir[220];
271 	TCHAR dict_dir[220];
272 	HKEY hkey;
273 	DWORD dwType,dwLen;
274 #endif
275 
276 	PspellCanHaveError *ret;
277 	PspellManager *manager;
278 	PspellConfig *config;
279 
280 	if (zend_parse_parameters(argc TSRMLS_CC, "s|sssl", &language, &language_len, &spelling, &spelling_len,
281 		&jargon, &jargon_len, &encoding, &encoding_len, &mode) == FAILURE) {
282 		return;
283 	}
284 
285 	config = new_pspell_config();
286 
287 #ifdef PHP_WIN32
288 	/* If aspell was installed using installer, we should have a key
289 	 * pointing to the location of the dictionaries
290 	 */
291 	if (0 == RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Aspell", &hkey)) {
292 		LONG result;
293 		dwLen = sizeof(aspell_dir) - 1;
294 		result = RegQueryValueEx(hkey, "", NULL, &dwType, (LPBYTE)&aspell_dir, &dwLen);
295 		RegCloseKey(hkey);
296 		if (result == ERROR_SUCCESS) {
297 			strlcpy(data_dir, aspell_dir, sizeof(data_dir));
298 			strlcat(data_dir, "\\data", sizeof(data_dir));
299 			strlcpy(dict_dir, aspell_dir, sizeof(dict_dir));
300 			strlcat(dict_dir, "\\dict", sizeof(dict_dir));
301 
302 			pspell_config_replace(config, "data-dir", data_dir);
303 			pspell_config_replace(config, "dict-dir", dict_dir);
304 		}
305 	}
306 #endif
307 
308 	pspell_config_replace(config, "language-tag", language);
309 
310 	if (spelling_len) {
311 		pspell_config_replace(config, "spelling", spelling);
312 	}
313 
314 	if (jargon_len) {
315 		pspell_config_replace(config, "jargon", jargon);
316 	}
317 
318 	if (encoding_len) {
319 		pspell_config_replace(config, "encoding", encoding);
320 	}
321 
322 	if (argc > 4) {
323 		speed = mode & PSPELL_SPEED_MASK_INTERNAL;
324 
325 		/* First check what mode we want (how many suggestions) */
326 		if (speed == PSPELL_FAST) {
327 			pspell_config_replace(config, "sug-mode", "fast");
328 		} else if (speed == PSPELL_NORMAL) {
329 			pspell_config_replace(config, "sug-mode", "normal");
330 		} else if (speed == PSPELL_BAD_SPELLERS) {
331 			pspell_config_replace(config, "sug-mode", "bad-spellers");
332 		}
333 
334 		/* Then we see if run-together words should be treated as valid components */
335 		if (mode & PSPELL_RUN_TOGETHER) {
336 			pspell_config_replace(config, "run-together", "true");
337 		}
338 	}
339 
340 	ret = new_pspell_manager(config);
341 	delete_pspell_config(config);
342 
343 	if (pspell_error_number(ret) != 0) {
344 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "PSPELL couldn't open the dictionary. reason: %s", pspell_error_message(ret));
345 		delete_pspell_can_have_error(ret);
346 		RETURN_FALSE;
347 	}
348 
349 	manager = to_pspell_manager(ret);
350 	ind = zend_list_insert(manager, le_pspell);
351 	RETURN_LONG(ind);
352 }
353 /* }}} */
354 
355 /* {{{ proto int pspell_new_personal(string personal, string language [, string spelling [, string jargon [, string encoding [, int mode]]]])
356    Load a dictionary with a personal wordlist*/
PHP_FUNCTION(pspell_new_personal)357 static PHP_FUNCTION(pspell_new_personal)
358 {
359 	char *personal, *language, *spelling = NULL, *jargon = NULL, *encoding = NULL;
360 	int personal_len, language_len, spelling_len = 0, jargon_len = 0, encoding_len = 0;
361 	long mode = 0L,  speed = 0L;
362 	int argc = ZEND_NUM_ARGS();
363 	int ind;
364 
365 #ifdef PHP_WIN32
366 	TCHAR aspell_dir[200];
367 	TCHAR data_dir[220];
368 	TCHAR dict_dir[220];
369 	HKEY hkey;
370 	DWORD dwType,dwLen;
371 #endif
372 
373 	PspellCanHaveError *ret;
374 	PspellManager *manager;
375 	PspellConfig *config;
376 
377 	if (zend_parse_parameters(argc TSRMLS_CC, "ss|sssl", &personal, &personal_len, &language, &language_len,
378 		&spelling, &spelling_len, &jargon, &jargon_len, &encoding, &encoding_len, &mode) == FAILURE) {
379 		return;
380 	}
381 
382 	config = new_pspell_config();
383 
384 #ifdef PHP_WIN32
385 	/* If aspell was installed using installer, we should have a key
386 	 * pointing to the location of the dictionaries
387 	 */
388 	if (0 == RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Aspell", &hkey)) {
389 		LONG result;
390 		dwLen = sizeof(aspell_dir) - 1;
391 		result = RegQueryValueEx(hkey, "", NULL, &dwType, (LPBYTE)&aspell_dir, &dwLen);
392 		RegCloseKey(hkey);
393 		if (result == ERROR_SUCCESS) {
394 			strlcpy(data_dir, aspell_dir, sizeof(data_dir));
395 			strlcat(data_dir, "\\data", sizeof(data_dir));
396 			strlcpy(dict_dir, aspell_dir, sizeof(dict_dir));
397 			strlcat(dict_dir, "\\dict", sizeof(dict_dir));
398 
399 			pspell_config_replace(config, "data-dir", data_dir);
400 			pspell_config_replace(config, "dict-dir", dict_dir);
401 		}
402 	}
403 #endif
404 
405 	if (strlen(personal) != personal_len) {
406 		delete_pspell_config(config);
407 		RETURN_FALSE;
408 	}
409 
410 	if (PG(safe_mode) && (!php_checkuid(personal, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
411 		delete_pspell_config(config);
412 		RETURN_FALSE;
413 	}
414 
415 	if (php_check_open_basedir(personal TSRMLS_CC)) {
416 		delete_pspell_config(config);
417 		RETURN_FALSE;
418 	}
419 
420 	pspell_config_replace(config, "personal", personal);
421 	pspell_config_replace(config, "save-repl", "false");
422 
423 	pspell_config_replace(config, "language-tag", language);
424 
425 	if (spelling_len) {
426 		pspell_config_replace(config, "spelling", spelling);
427 	}
428 
429 	if (jargon_len) {
430 		pspell_config_replace(config, "jargon", jargon);
431 	}
432 
433 	if (encoding_len) {
434 		pspell_config_replace(config, "encoding", encoding);
435 	}
436 
437 	if (argc > 5) {
438 		speed = mode & PSPELL_SPEED_MASK_INTERNAL;
439 
440 		/* First check what mode we want (how many suggestions) */
441 		if (speed == PSPELL_FAST) {
442 			pspell_config_replace(config, "sug-mode", "fast");
443 		} else if (speed == PSPELL_NORMAL) {
444 			pspell_config_replace(config, "sug-mode", "normal");
445 		} else if (speed == PSPELL_BAD_SPELLERS) {
446 			pspell_config_replace(config, "sug-mode", "bad-spellers");
447 		}
448 
449 		/* Then we see if run-together words should be treated as valid components */
450 		if (mode & PSPELL_RUN_TOGETHER) {
451 			pspell_config_replace(config, "run-together", "true");
452 		}
453 	}
454 
455 	ret = new_pspell_manager(config);
456 	delete_pspell_config(config);
457 
458 	if (pspell_error_number(ret) != 0) {
459 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "PSPELL couldn't open the dictionary. reason: %s", pspell_error_message(ret));
460 		delete_pspell_can_have_error(ret);
461 		RETURN_FALSE;
462 	}
463 
464 	manager = to_pspell_manager(ret);
465 	ind = zend_list_insert(manager, le_pspell);
466 	RETURN_LONG(ind);
467 }
468 /* }}} */
469 
470 /* {{{ proto int pspell_new_config(int config)
471    Load a dictionary based on the given config */
PHP_FUNCTION(pspell_new_config)472 static PHP_FUNCTION(pspell_new_config)
473 {
474 	int type, ind;
475 	long conf;
476 	PspellCanHaveError *ret;
477 	PspellManager *manager;
478 	PspellConfig *config;
479 
480 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &conf) == FAILURE) {
481 		return;
482 	}
483 
484 	PSPELL_FETCH_CONFIG;
485 
486 	ret = new_pspell_manager(config);
487 
488 	if (pspell_error_number(ret) != 0) {
489 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "PSPELL couldn't open the dictionary. reason: %s", pspell_error_message(ret));
490 		delete_pspell_can_have_error(ret);
491 		RETURN_FALSE;
492 	}
493 
494 	manager = to_pspell_manager(ret);
495 	ind = zend_list_insert(manager, le_pspell);
496 	RETURN_LONG(ind);
497 }
498 /* }}} */
499 
500 /* {{{ proto bool pspell_check(int pspell, string word)
501    Returns true if word is valid */
PHP_FUNCTION(pspell_check)502 static PHP_FUNCTION(pspell_check)
503 {
504 	int type, word_len;
505 	long scin;
506 	char *word;
507 	PspellManager *manager;
508 
509 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &scin, &word, &word_len) == FAILURE) {
510 		return;
511 	}
512 
513 	PSPELL_FETCH_MANAGER;
514 
515 	if (pspell_manager_check(manager, word)) {
516 		RETURN_TRUE;
517 	} else {
518 		RETURN_FALSE;
519 	}
520 }
521 /* }}} */
522 
523 /* {{{ proto array pspell_suggest(int pspell, string word)
524    Returns array of suggestions */
PHP_FUNCTION(pspell_suggest)525 static PHP_FUNCTION(pspell_suggest)
526 {
527 	long scin;
528 	char *word;
529 	int word_len;
530 	PspellManager *manager;
531 	int type;
532 	const PspellWordList *wl;
533 	const char *sug;
534 
535 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &scin, &word, &word_len) == FAILURE) {
536 		return;
537 	}
538 
539 	PSPELL_FETCH_MANAGER;
540 
541 	array_init(return_value);
542 
543 	wl = pspell_manager_suggest(manager, word);
544 	if (wl) {
545 		PspellStringEmulation *els = pspell_word_list_elements(wl);
546 		while ((sug = pspell_string_emulation_next(els)) != 0) {
547 			add_next_index_string(return_value,(char *)sug,1);
548 		}
549 		delete_pspell_string_emulation(els);
550 	} else {
551 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "PSPELL had a problem. details: %s", pspell_manager_error_message(manager));
552 		RETURN_FALSE;
553 	}
554 }
555 /* }}} */
556 
557 /* {{{ proto bool pspell_store_replacement(int pspell, string misspell, string correct)
558    Notify the dictionary of a user-selected replacement */
PHP_FUNCTION(pspell_store_replacement)559 static PHP_FUNCTION(pspell_store_replacement)
560 {
561 	int type, miss_len, corr_len;
562 	long scin;
563 	char *miss, *corr;
564 	PspellManager *manager;
565 
566 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lss", &scin, &miss, &miss_len, &corr, &corr_len) == FAILURE) {
567 		return;
568 	}
569 
570 	PSPELL_FETCH_MANAGER;
571 
572 	pspell_manager_store_replacement(manager, miss, corr);
573 	if (pspell_manager_error_number(manager) == 0) {
574 		RETURN_TRUE;
575 	} else {
576 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "pspell_store_replacement() gave error: %s", pspell_manager_error_message(manager));
577 		RETURN_FALSE;
578 	}
579 }
580 /* }}} */
581 
582 /* {{{ proto bool pspell_add_to_personal(int pspell, string word)
583    Adds a word to a personal list */
PHP_FUNCTION(pspell_add_to_personal)584 static PHP_FUNCTION(pspell_add_to_personal)
585 {
586 	int type, word_len;
587 	long scin;
588 	char *word;
589 	PspellManager *manager;
590 
591 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &scin, &word, &word_len) == FAILURE) {
592 		return;
593 	}
594 
595 	PSPELL_FETCH_MANAGER;
596 
597 	/*If the word is empty, we have to return; otherwise we'll segfault! ouch!*/
598 	if (word_len == 0) {
599 		RETURN_FALSE;
600 	}
601 
602 	pspell_manager_add_to_personal(manager, word);
603 	if (pspell_manager_error_number(manager) == 0) {
604 		RETURN_TRUE;
605 	} else {
606 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "pspell_add_to_personal() gave error: %s", pspell_manager_error_message(manager));
607 		RETURN_FALSE;
608 	}
609 }
610 /* }}} */
611 
612 /* {{{ proto bool pspell_add_to_session(int pspell, string word)
613    Adds a word to the current session */
PHP_FUNCTION(pspell_add_to_session)614 static PHP_FUNCTION(pspell_add_to_session)
615 {
616 	int type, word_len;
617 	long scin;
618 	char *word;
619 	PspellManager *manager;
620 
621 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &scin, &word, &word_len) == FAILURE) {
622 		return;
623 	}
624 
625 	PSPELL_FETCH_MANAGER;
626 
627 	/*If the word is empty, we have to return; otherwise we'll segfault! ouch!*/
628 	if (word_len == 0) {
629 		RETURN_FALSE;
630 	}
631 
632 	pspell_manager_add_to_session(manager, word);
633 	if (pspell_manager_error_number(manager) == 0) {
634 		RETURN_TRUE;
635 	} else {
636 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "pspell_add_to_session() gave error: %s", pspell_manager_error_message(manager));
637 		RETURN_FALSE;
638 	}
639 }
640 /* }}} */
641 
642 /* {{{ proto bool pspell_clear_session(int pspell)
643    Clears the current session */
PHP_FUNCTION(pspell_clear_session)644 static PHP_FUNCTION(pspell_clear_session)
645 {
646 	int type;
647 	long scin;
648 	PspellManager *manager;
649 
650 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &scin) == FAILURE) {
651 		return;
652 	}
653 
654 	PSPELL_FETCH_MANAGER;
655 
656 	pspell_manager_clear_session(manager);
657 	if (pspell_manager_error_number(manager) == 0) {
658 		RETURN_TRUE;
659 	} else {
660 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "pspell_clear_session() gave error: %s", pspell_manager_error_message(manager));
661 		RETURN_FALSE;
662 	}
663 }
664 /* }}} */
665 
666 /* {{{ proto bool pspell_save_wordlist(int pspell)
667    Saves the current (personal) wordlist */
PHP_FUNCTION(pspell_save_wordlist)668 static PHP_FUNCTION(pspell_save_wordlist)
669 {
670 	int type;
671 	long scin;
672 	PspellManager *manager;
673 
674 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &scin) == FAILURE) {
675 		return;
676 	}
677 
678 	PSPELL_FETCH_MANAGER;
679 
680 	pspell_manager_save_all_word_lists(manager);
681 
682 	if (pspell_manager_error_number(manager) == 0) {
683 		RETURN_TRUE;
684 	} else {
685 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "pspell_save_wordlist() gave error: %s", pspell_manager_error_message(manager));
686 		RETURN_FALSE;
687 	}
688 
689 }
690 /* }}} */
691 
692 /* {{{ proto int pspell_config_create(string language [, string spelling [, string jargon [, string encoding]]])
693    Create a new config to be used later to create a manager */
PHP_FUNCTION(pspell_config_create)694 static PHP_FUNCTION(pspell_config_create)
695 {
696 	char *language, *spelling = NULL, *jargon = NULL, *encoding = NULL;
697 	int language_len, spelling_len = 0, jargon_len = 0, encoding_len = 0;
698 	int ind;
699 	PspellConfig *config;
700 
701 #ifdef PHP_WIN32
702 	TCHAR aspell_dir[200];
703 	TCHAR data_dir[220];
704 	TCHAR dict_dir[220];
705 	HKEY hkey;
706 	DWORD dwType,dwLen;
707 #endif
708 
709 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sss", &language, &language_len, &spelling, &spelling_len,
710 		&jargon, &jargon_len, &encoding, &encoding_len) == FAILURE) {
711 		return;
712 	}
713 
714 	config = new_pspell_config();
715 
716 #ifdef PHP_WIN32
717     /* If aspell was installed using installer, we should have a key
718      * pointing to the location of the dictionaries
719      */
720 	if (0 == RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Aspell", &hkey)) {
721 		LONG result;
722 		dwLen = sizeof(aspell_dir) - 1;
723 		result = RegQueryValueEx(hkey, "", NULL, &dwType, (LPBYTE)&aspell_dir, &dwLen);
724 		RegCloseKey(hkey);
725 		if (result == ERROR_SUCCESS) {
726 			strlcpy(data_dir, aspell_dir, sizeof(data_dir));
727 			strlcat(data_dir, "\\data", sizeof(data_dir));
728 			strlcpy(dict_dir, aspell_dir, sizeof(dict_dir));
729 			strlcat(dict_dir, "\\dict", sizeof(dict_dir));
730 
731 			pspell_config_replace(config, "data-dir", data_dir);
732 			pspell_config_replace(config, "dict-dir", dict_dir);
733 		}
734 	}
735 #endif
736 
737 	pspell_config_replace(config, "language-tag", language);
738 
739  	if (spelling_len) {
740 		pspell_config_replace(config, "spelling", spelling);
741 	}
742 
743 	if (jargon_len) {
744 		pspell_config_replace(config, "jargon", jargon);
745 	}
746 
747 	if (encoding_len) {
748 		pspell_config_replace(config, "encoding", encoding);
749 	}
750 
751 	/* By default I do not want to write anything anywhere because it'll try to write to $HOME
752 	which is not what we want */
753 	pspell_config_replace(config, "save-repl", "false");
754 
755 	ind = zend_list_insert(config, le_pspell_config);
756 	RETURN_LONG(ind);
757 }
758 /* }}} */
759 
760 /* {{{ proto bool pspell_config_runtogether(int conf, bool runtogether)
761    Consider run-together words as valid components */
PHP_FUNCTION(pspell_config_runtogether)762 static PHP_FUNCTION(pspell_config_runtogether)
763 {
764 	int type;
765 	long conf;
766 	zend_bool runtogether;
767 	PspellConfig *config;
768 
769 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lb", &conf, &runtogether) == FAILURE) {
770 		return;
771 	}
772 
773 	PSPELL_FETCH_CONFIG;
774 
775 	pspell_config_replace(config, "run-together", runtogether ? "true" : "false");
776 
777 	RETURN_TRUE;
778 }
779 /* }}} */
780 
781 /* {{{ proto bool pspell_config_mode(int conf, long mode)
782    Select mode for config (PSPELL_FAST, PSPELL_NORMAL or PSPELL_BAD_SPELLERS) */
PHP_FUNCTION(pspell_config_mode)783 static PHP_FUNCTION(pspell_config_mode)
784 {
785 	int type;
786 	long conf, mode;
787 	PspellConfig *config;
788 
789 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &conf, &mode) == FAILURE) {
790 		return;
791 	}
792 
793 	PSPELL_FETCH_CONFIG;
794 
795 	/* First check what mode we want (how many suggestions) */
796 	if (mode == PSPELL_FAST) {
797 		pspell_config_replace(config, "sug-mode", "fast");
798 	} else if (mode == PSPELL_NORMAL) {
799 		pspell_config_replace(config, "sug-mode", "normal");
800 	} else if (mode == PSPELL_BAD_SPELLERS) {
801 		pspell_config_replace(config, "sug-mode", "bad-spellers");
802 	}
803 
804 	RETURN_TRUE;
805 }
806 /* }}} */
807 
808 /* {{{ proto bool pspell_config_ignore(int conf, int ignore)
809    Ignore words <= n chars */
PHP_FUNCTION(pspell_config_ignore)810 static PHP_FUNCTION(pspell_config_ignore)
811 {
812 	int type;
813 	char ignore_str[MAX_LENGTH_OF_LONG + 1];
814 	long conf, ignore = 0L;
815 	PspellConfig *config;
816 
817 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &conf, &ignore) == FAILURE) {
818 		return;
819 	}
820 
821 	PSPELL_FETCH_CONFIG;
822 
823 	snprintf(ignore_str, sizeof(ignore_str), "%ld", ignore);
824 
825 	pspell_config_replace(config, "ignore", ignore_str);
826 	RETURN_TRUE;
827 }
828 /* }}} */
829 
pspell_config_path(INTERNAL_FUNCTION_PARAMETERS,char * option)830 static void pspell_config_path(INTERNAL_FUNCTION_PARAMETERS, char *option)
831 {
832 	int type;
833 	long conf;
834 	char *value;
835 	int value_len;
836 	PspellConfig *config;
837 
838 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &conf, &value, &value_len) == FAILURE) {
839 		return;
840 	}
841 
842 	if (strlen(value) != value_len) {
843 		RETURN_FALSE;
844 	}
845 
846 	PSPELL_FETCH_CONFIG;
847 
848 	if (PG(safe_mode) && (!php_checkuid(value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
849 		RETURN_FALSE;
850 	}
851 
852 	if (php_check_open_basedir(value TSRMLS_CC)) {
853 		RETURN_FALSE;
854 	}
855 
856 	pspell_config_replace(config, option, value);
857 
858 	RETURN_TRUE;
859 }
860 
861 /* {{{ proto bool pspell_config_personal(int conf, string personal)
862    Use a personal dictionary for this config */
PHP_FUNCTION(pspell_config_personal)863 static PHP_FUNCTION(pspell_config_personal)
864 {
865 	pspell_config_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, "personal");
866 }
867 /* }}} */
868 
869 /* {{{ proto bool pspell_config_dict_dir(int conf, string directory)
870    location of the main word list */
PHP_FUNCTION(pspell_config_dict_dir)871 static PHP_FUNCTION(pspell_config_dict_dir)
872 {
873 	pspell_config_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, "dict-dir");
874 }
875 /* }}} */
876 
877 /* {{{ proto bool pspell_config_data_dir(int conf, string directory)
878     location of language data files */
PHP_FUNCTION(pspell_config_data_dir)879 static PHP_FUNCTION(pspell_config_data_dir)
880 {
881 	pspell_config_path(INTERNAL_FUNCTION_PARAM_PASSTHRU, "data-dir");
882 }
883 /* }}} */
884 
885 /* {{{ proto bool pspell_config_repl(int conf, string repl)
886    Use a personal dictionary with replacement pairs for this config */
PHP_FUNCTION(pspell_config_repl)887 static PHP_FUNCTION(pspell_config_repl)
888 {
889 	int type;
890 	long conf;
891 	char *repl;
892 	int repl_len;
893 	PspellConfig *config;
894 
895 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &conf, &repl, &repl_len) == FAILURE) {
896 		return;
897 	}
898 
899 	PSPELL_FETCH_CONFIG;
900 
901 	pspell_config_replace(config, "save-repl", "true");
902 
903 	if (strlen(repl) != repl_len) {
904 		RETURN_FALSE;
905 	}
906 
907 	if (PG(safe_mode) && (!php_checkuid(repl, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
908 		RETURN_FALSE;
909 	}
910 
911 	if (php_check_open_basedir(repl TSRMLS_CC)) {
912 		RETURN_FALSE;
913 	}
914 
915 	pspell_config_replace(config, "repl", repl);
916 
917 	RETURN_TRUE;
918 }
919 /* }}} */
920 
921 /* {{{ proto bool pspell_config_save_repl(int conf, bool save)
922    Save replacement pairs when personal list is saved for this config */
PHP_FUNCTION(pspell_config_save_repl)923 static PHP_FUNCTION(pspell_config_save_repl)
924 {
925 	int type;
926 	long conf;
927 	zend_bool save;
928 	PspellConfig *config;
929 
930 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lb", &conf, &save) == FAILURE) {
931 		return;
932 	}
933 
934 	PSPELL_FETCH_CONFIG;
935 
936 	pspell_config_replace(config, "save-repl", save ? "true" : "false");
937 
938 	RETURN_TRUE;
939 }
940 /* }}} */
941 
942 /* {{{ PHP_MINFO_FUNCTION
943  */
PHP_MINFO_FUNCTION(pspell)944 static PHP_MINFO_FUNCTION(pspell)
945 {
946 	php_info_print_table_start();
947 	php_info_print_table_row(2, "PSpell Support", "enabled");
948 	php_info_print_table_end();
949 }
950 /* }}} */
951 
952 #endif
953 
954 /*
955  * Local variables:
956  * tab-width: 4
957  * c-basic-offset: 4
958  * End:
959  * vim600: sw=4 ts=4 fdm=marker
960  * vim<600: sw=4 ts=4
961  */
962