1 /*-------------------------------------------------------------------------
2  *
3  * dict_ispell.c
4  *		Ispell dictionary interface
5  *
6  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  *	  src/backend/tsearch/dict_ispell.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "commands/defrem.h"
17 #include "tsearch/dicts/spell.h"
18 #include "tsearch/ts_locale.h"
19 #include "tsearch/ts_utils.h"
20 #include "utils/builtins.h"
21 
22 
23 typedef struct
24 {
25 	StopList	stoplist;
26 	IspellDict	obj;
27 } DictISpell;
28 
29 Datum
30 dispell_init(PG_FUNCTION_ARGS)
31 {
32 	List	   *dictoptions = (List *) PG_GETARG_POINTER(0);
33 	DictISpell *d;
34 	bool		affloaded = false,
35 				dictloaded = false,
36 				stoploaded = false;
37 	ListCell   *l;
38 
39 	d = (DictISpell *) palloc0(sizeof(DictISpell));
40 
41 	NIStartBuild(&(d->obj));
42 
43 	foreach(l, dictoptions)
44 	{
45 		DefElem    *defel = (DefElem *) lfirst(l);
46 
47 		if (strcmp(defel->defname, "dictfile") == 0)
48 		{
49 			if (dictloaded)
50 				ereport(ERROR,
51 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
52 						 errmsg("multiple DictFile parameters")));
53 			NIImportDictionary(&(d->obj),
54 							   get_tsearch_config_filename(defGetString(defel),
55 														   "dict"));
56 			dictloaded = true;
57 		}
58 		else if (strcmp(defel->defname, "afffile") == 0)
59 		{
60 			if (affloaded)
61 				ereport(ERROR,
62 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
63 						 errmsg("multiple AffFile parameters")));
64 			NIImportAffixes(&(d->obj),
65 							get_tsearch_config_filename(defGetString(defel),
66 														"affix"));
67 			affloaded = true;
68 		}
69 		else if (strcmp(defel->defname, "stopwords") == 0)
70 		{
71 			if (stoploaded)
72 				ereport(ERROR,
73 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
74 						 errmsg("multiple StopWords parameters")));
75 			readstoplist(defGetString(defel), &(d->stoplist), lowerstr);
76 			stoploaded = true;
77 		}
78 		else
79 		{
80 			ereport(ERROR,
81 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
82 					 errmsg("unrecognized Ispell parameter: \"%s\"",
83 							defel->defname)));
84 		}
85 	}
86 
87 	if (affloaded && dictloaded)
88 	{
89 		NISortDictionary(&(d->obj));
90 		NISortAffixes(&(d->obj));
91 	}
92 	else if (!affloaded)
93 	{
94 		ereport(ERROR,
95 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
96 				 errmsg("missing AffFile parameter")));
97 	}
98 	else
99 	{
100 		ereport(ERROR,
101 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
102 				 errmsg("missing DictFile parameter")));
103 	}
104 
105 	NIFinishBuild(&(d->obj));
106 
107 	PG_RETURN_POINTER(d);
108 }
109 
110 Datum
111 dispell_lexize(PG_FUNCTION_ARGS)
112 {
113 	DictISpell *d = (DictISpell *) PG_GETARG_POINTER(0);
114 	char	   *in = (char *) PG_GETARG_POINTER(1);
115 	int32		len = PG_GETARG_INT32(2);
116 	char	   *txt;
117 	TSLexeme   *res;
118 	TSLexeme   *ptr,
119 			   *cptr;
120 
121 	if (len <= 0)
122 		PG_RETURN_POINTER(NULL);
123 
124 	txt = lowerstr_with_len(in, len);
125 	res = NINormalizeWord(&(d->obj), txt);
126 
127 	if (res == NULL)
128 		PG_RETURN_POINTER(NULL);
129 
130 	cptr = res;
131 	for (ptr = cptr; ptr->lexeme; ptr++)
132 	{
133 		if (searchstoplist(&(d->stoplist), ptr->lexeme))
134 		{
135 			pfree(ptr->lexeme);
136 			ptr->lexeme = NULL;
137 		}
138 		else
139 		{
140 			if (cptr != ptr)
141 				memcpy(cptr, ptr, sizeof(TSLexeme));
142 			cptr++;
143 		}
144 	}
145 	cptr->lexeme = NULL;
146 
147 	PG_RETURN_POINTER(res);
148 }
149